use crate::core;
use crate::core::versions::MinOxenVersion;
use crate::error::OxenError;
use crate::model::{Branch, LocalRepository};
use crate::opts::PushOpts;
pub async fn push(repo: &LocalRepository) -> Result<Branch, OxenError> {
match repo.min_version() {
MinOxenVersion::V0_10_0 => panic!("v0.10.0 is deprecated"),
_ => core::v_latest::push::push(repo).await,
}
}
pub async fn push_remote_branch(
repo: &LocalRepository,
opts: &PushOpts,
) -> Result<Branch, OxenError> {
match repo.min_version() {
MinOxenVersion::V0_10_0 => panic!("v0.10.0 is deprecated"),
_ => core::v_latest::push::push_remote_branch(repo, opts).await,
}
}
#[cfg(test)]
mod tests {
use crate::api;
use crate::command;
use crate::constants;
use crate::constants::{AVG_CHUNK_SIZE, DEFAULT_BRANCH_NAME, DEFAULT_REMOTE_NAME};
use crate::core::progress::push_progress::PushProgress;
use crate::error::OxenError;
use crate::model::merkle_tree::node::MerkleTreeNode;
use crate::opts::RmOpts;
use crate::opts::{CloneOpts, PushOpts};
use crate::repositories;
use crate::test;
use crate::util;
use crate::view::entries::EMetadataEntry;
use futures::future;
use std::collections::HashSet;
use std::path::PathBuf;
use std::sync::Arc;
#[tokio::test]
async fn test_command_push_one_commit() -> Result<(), OxenError> {
test::run_training_data_repo_test_no_commits_async(|repo| async {
let mut repo = repo;
let train_dir = repo.path.join("train");
let num_files = util::fs::rcount_files_in_dir(&train_dir);
repositories::add(&repo, &train_dir).await?;
let readme_path = repo.path.join("README.md");
let readme_path = test::write_txt_file_to_path(readme_path, "Ready to train 🏋️♂️")?;
repositories::add(&repo, &readme_path).await?;
let commit = repositories::commit(&repo, "Adding training data")?;
let remote_repo = test::create_remote_repo(&repo).await?;
let remote = test::repo_remote_url_from(&repo.dirname());
command::config::set_remote(&mut repo, constants::DEFAULT_REMOTE_NAME, &remote)?;
repositories::push(&repo).await?;
let page_num = 1;
let page_size = num_files + 10;
let entries =
api::client::dir::list(&remote_repo, &commit.id, "train", page_num, page_size)
.await?;
assert_eq!(entries.total_entries, num_files);
assert_eq!(entries.entries.len(), num_files);
let readme_path = repo.path.join("README.md");
let download_path = repo.path.join("README_2.md");
api::client::entries::download_entry(&remote_repo, "README.md", &download_path, "main")
.await?;
let readme_1_contents = util::fs::read_from_path(&download_path)?;
let readme_2_contents = util::fs::read_from_path(&readme_path)?;
assert_eq!(readme_1_contents, readme_2_contents);
api::client::repositories::delete(&remote_repo).await?;
future::ok::<(), OxenError>(()).await
})
.await
}
#[tokio::test]
async fn test_command_push_inbetween_two_commits() -> Result<(), OxenError> {
test::run_training_data_repo_test_no_commits_async(|repo| async {
let mut repo = repo;
let train_dir = repo.path.join("train");
let num_train_files = util::fs::rcount_files_in_dir(&train_dir);
repositories::add(&repo, &train_dir).await?;
repositories::commit(&repo, "Adding training data")?;
let remote_repo = test::create_remote_repo(&repo).await?;
let remote = test::repo_remote_url_from(&repo.dirname());
command::config::set_remote(&mut repo, constants::DEFAULT_REMOTE_NAME, &remote)?;
repositories::push(&repo).await?;
let test_dir = repo.path.join("test");
let num_test_files = util::fs::count_files_in_dir(&test_dir);
repositories::add(&repo, &test_dir).await?;
let commit = repositories::commit(&repo, "Adding test data")?;
repositories::push(&repo).await?;
let page_num = 1;
let page_size = num_train_files + num_test_files + 5;
let train_entries =
api::client::dir::list(&remote_repo, &commit.id, "/train", page_num, page_size)
.await?;
let test_entries =
api::client::dir::list(&remote_repo, &commit.id, "/test", page_num, page_size)
.await?;
assert_eq!(
train_entries.total_entries + test_entries.total_entries,
num_train_files + num_test_files
);
assert_eq!(
train_entries.entries.len() + test_entries.entries.len(),
num_train_files + num_test_files
);
api::client::repositories::delete(&remote_repo).await?;
future::ok::<(), OxenError>(()).await
})
.await
}
#[tokio::test]
async fn test_command_push_after_two_commits() -> Result<(), OxenError> {
test::run_training_data_repo_test_no_commits_async(|repo| async {
let mut repo = repo;
let train_dir = repo.path.join("train");
repositories::add(&repo, &train_dir).await?;
repositories::commit(&repo, "Adding training data")?;
let test_dir = repo.path.join("test");
let num_test_files = util::fs::rcount_files_in_dir(&test_dir);
repositories::add(&repo, &test_dir).await?;
let commit = repositories::commit(&repo, "Adding test data")?;
let remote_repo = test::create_remote_repo(&repo).await?;
let remote = test::repo_remote_url_from(&repo.dirname());
command::config::set_remote(&mut repo, constants::DEFAULT_REMOTE_NAME, &remote)?;
repositories::push(&repo).await?;
let page_num = 1;
let entries =
api::client::dir::list(&remote_repo, &commit.id, ".", page_num, 10).await?;
assert_eq!(entries.total_entries, 2);
assert_eq!(entries.entries.len(), 2);
let page_size = num_test_files + 10;
let entries =
api::client::dir::list(&remote_repo, &commit.id, "test", page_num, page_size)
.await?;
assert_eq!(entries.total_entries, num_test_files);
assert_eq!(entries.entries.len(), num_test_files);
api::client::repositories::delete(&remote_repo).await?;
future::ok::<(), OxenError>(()).await
})
.await
}
#[tokio::test]
async fn test_latest_commit_is_computed_properly() -> Result<(), OxenError> {
test::run_empty_local_repo_test_async(|repo| async {
let mut repo = repo;
let readme_path = repo.path.join("README.md");
let readme_path = test::write_txt_file_to_path(readme_path, "README")?;
repositories::add(&repo, &readme_path).await?;
let first_commit_id = repositories::commit(&repo, "Adding README")?;
let data_dir = repo.path.join("data");
util::fs::create_dir_all(&data_dir)?;
let num_dirs = 5;
for i in 0..num_dirs {
let dir_path = data_dir.join(format!("{i}"));
util::fs::create_dir_all(&dir_path)?;
let file_path = dir_path.join("file.txt");
let file_path = test::write_txt_file_to_path(file_path, format!("file -> {i}"))?;
repositories::add(&repo, &file_path).await?;
repositories::commit(&repo, &format!("Adding file -> data/{i}/file.txt"))?;
}
let file_path = data_dir.join("2").join("file.txt");
let file_path = test::write_txt_file_to_path(file_path, "modified file")?;
repositories::add(&repo, &file_path).await?;
let last_commit = repositories::commit(&repo, "Modifying file again")?;
let remote_repo = test::create_remote_repo(&repo).await?;
let remote = test::repo_remote_url_from(&repo.dirname());
command::config::set_remote(&mut repo, constants::DEFAULT_REMOTE_NAME, &remote)?;
repositories::push(&repo).await?;
let page_num = 1;
let entries =
api::client::dir::list(&remote_repo, &last_commit.id, ".", page_num, 10).await?;
assert_eq!(entries.total_entries, 2);
assert_eq!(entries.entries.len(), 2);
let data_entry = entries.entries.iter().find(|e| e.filename() == "data");
assert!(data_entry.is_some());
let data_entry = data_entry.unwrap();
assert_eq!(data_entry.filename(), "data");
assert_eq!(
data_entry.latest_commit().as_ref().unwrap().id,
last_commit.id
);
let readme_entry = entries.entries.iter().find(|e| e.filename() == "README.md");
assert!(readme_entry.is_some());
let readme_entry = readme_entry.unwrap();
assert_eq!(readme_entry.filename(), "README.md");
assert_eq!(
readme_entry.latest_commit().as_ref().unwrap().id,
first_commit_id.id
);
let page_num = 1;
let entries =
api::client::dir::list(&remote_repo, &last_commit.id, "data/3", page_num, 10)
.await?;
assert_eq!(entries.total_entries, 1);
assert_eq!(entries.entries.len(), 1);
let entry = entries.entries.first().unwrap();
assert_eq!(entry.filename(), "file.txt");
assert_eq!(
entry.latest_commit().as_ref().unwrap().message,
"Adding file -> data/3/file.txt"
);
api::client::repositories::delete(&remote_repo).await?;
future::ok::<(), OxenError>(()).await
})
.await
}
#[tokio::test]
async fn test_command_push_after_two_commits_adding_dot() -> Result<(), OxenError> {
test::run_training_data_repo_test_no_commits_async(|repo| async {
let mut repo = repo;
let train_dir = repo.path.join("train");
repositories::add(&repo, &train_dir).await?;
repositories::commit(&repo, "Adding training data")?;
let full_dir = &repo.path;
let num_files = util::fs::count_items_in_dir(full_dir);
repositories::add(&repo, full_dir).await?;
let commit = repositories::commit(&repo, "Adding rest of data")?;
let remote_repo = test::create_remote_repo(&repo).await?;
let remote = test::repo_remote_url_from(&repo.dirname());
command::config::set_remote(&mut repo, constants::DEFAULT_REMOTE_NAME, &remote)?;
repositories::push(&repo).await?;
let page_num = 1;
let page_size = num_files + 10;
let entries =
api::client::dir::list(&remote_repo, &commit.id, ".", page_num, page_size).await?;
assert_eq!(entries.total_entries, num_files);
assert_eq!(entries.entries.len(), num_files);
api::client::repositories::delete(&remote_repo).await?;
future::ok::<(), OxenError>(()).await
})
.await
}
#[tokio::test]
async fn test_cannot_push_if_remote_not_set() -> Result<(), OxenError> {
test::run_training_data_repo_test_no_commits_async(|repo| async move {
let train_dirname = "train";
let train_dir = repo.path.join(train_dirname);
repositories::add(&repo, &train_dir).await?;
repositories::commit(&repo, "Adding training data")?;
let result = repositories::push(&repo).await;
assert!(result.is_err());
Ok(())
})
.await
}
#[tokio::test]
async fn test_push_branch_with_with_no_new_commits() -> Result<(), OxenError> {
test::run_training_data_repo_test_no_commits_async(|mut repo| async move {
let train_path = repo.path.join("train");
repositories::add(&repo, &train_path).await?;
repositories::commit(&repo, "Adding train dir")?;
let remote_repo = test::create_remote_repo(&repo).await?;
let remote = test::repo_remote_url_from(&repo.dirname());
command::config::set_remote(&mut repo, constants::DEFAULT_REMOTE_NAME, &remote)?;
repositories::push(&repo).await?;
let new_branch_name = "my-branch";
repositories::branches::create_checkout(&repo, new_branch_name)?;
let opts = PushOpts {
remote: DEFAULT_REMOTE_NAME.to_string(),
branch: new_branch_name.to_string(),
..Default::default()
};
repositories::push::push_remote_branch(&repo, &opts).await?;
let remote_branches = api::client::branches::list(&remote_repo).await?;
assert_eq!(2, remote_branches.len());
api::client::repositories::delete(&remote_repo).await?;
Ok(())
})
.await
}
#[tokio::test]
async fn test_cannot_push_two_separate_empty_roots() -> Result<(), OxenError> {
test::run_no_commit_remote_repo_test(|remote_repo| async move {
let ret_repo = remote_repo.clone();
test::run_empty_dir_test_async(|first_repo_dir| async move {
println!("test_cannot_push_two_separate_empty_roots clone first repo");
let first_cloned_repo = repositories::clone_url(
&remote_repo.remote.url,
&first_repo_dir.join("first_repo"),
)
.await?;
test::run_empty_dir_test_async(|second_repo_dir| async move {
println!("test_cannot_push_two_separate_empty_roots clone second repo");
let second_cloned_repo = repositories::clone_url(
&remote_repo.remote.url,
&second_repo_dir.join("second_repo"),
)
.await?;
let new_file = "new_file.txt";
let new_file_path = first_cloned_repo.path.join(new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "new file")?;
repositories::add(&first_cloned_repo, &new_file_path).await?;
repositories::commit(&first_cloned_repo, "Adding first file path.")?;
repositories::push(&first_cloned_repo).await?;
let new_file = "new_file_2.txt";
let new_file_path = second_cloned_repo.path.join(new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "new file 2")?;
repositories::add(&second_cloned_repo, &new_file_path).await?;
repositories::commit(&second_cloned_repo, "Adding second file path.")?;
let new_file = "new_file_3.txt";
let new_file_path = second_cloned_repo.path.join(new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "new file 3")?;
repositories::add(&second_cloned_repo, &new_file_path).await?;
repositories::commit(&second_cloned_repo, "Adding third file path.")?;
let result = repositories::push(&second_cloned_repo).await;
assert!(result.is_err());
Ok(())
})
.await?;
Ok(())
})
.await?;
Ok(ret_repo)
})
.await
}
#[tokio::test]
async fn test_cannot_push_two_separate_repos() -> Result<(), OxenError> {
test::run_training_data_repo_test_fully_committed_async(|mut repo_1| async move {
test::run_training_data_repo_test_fully_committed_async(|mut repo_2| async move {
let new_file = "new_file.txt";
let new_file_path = repo_1.path.join(new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "new file")?;
repositories::add(&repo_1, &new_file_path).await?;
repositories::commit(&repo_1, "Adding first file path.")?;
let remote = test::repo_remote_url_from(&repo_1.dirname());
command::config::set_remote(&mut repo_1, constants::DEFAULT_REMOTE_NAME, &remote)?;
test::create_remote_repo(&repo_1).await?;
repositories::push(&repo_1).await?;
let new_file = "new_file_2.txt";
let new_file_path = repo_2.path.join(new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "new file 2")?;
repositories::add(&repo_2, &new_file_path).await?;
repositories::commit(&repo_2, "Adding second file path.")?;
let new_file = "new_file_3.txt";
let new_file_path = repo_2.path.join(new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "new file 3")?;
repositories::add(&repo_2, &new_file_path).await?;
repositories::commit(&repo_2, "Adding third file path.")?;
command::config::set_remote(&mut repo_2, constants::DEFAULT_REMOTE_NAME, &remote)?;
let result = repositories::push(&repo_2).await;
assert!(result.is_err());
Ok(())
})
.await?;
Ok(())
})
.await
}
#[tokio::test]
async fn test_push_many_commits_default_branch() -> Result<(), OxenError> {
test::run_many_local_commits_empty_sync_remote_test(|local_repo, remote_repo| async move {
let history =
api::client::commits::list_commit_history(&remote_repo, DEFAULT_BRANCH_NAME)
.await?;
assert_eq!(history.len(), 0);
repositories::push(&local_repo).await?;
let history =
api::client::commits::list_commit_history(&remote_repo, DEFAULT_BRANCH_NAME)
.await?;
assert_eq!(history.len(), 25);
Ok(remote_repo)
})
.await
}
#[tokio::test]
async fn test_push_many_commits_new_branch() -> Result<(), OxenError> {
test::run_many_local_commits_empty_sync_remote_test(|local_repo, remote_repo| async move {
let history =
api::client::commits::list_commit_history(&remote_repo, DEFAULT_BRANCH_NAME)
.await?;
assert_eq!(history.len(), 0);
let new_branch_name = "my-branch";
repositories::branches::create_checkout(&local_repo, new_branch_name)?;
let new_file = "new_file.txt";
let new_file_path = local_repo.path.join(new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "new file")?;
repositories::add(&local_repo, &new_file_path).await?;
repositories::commit(&local_repo, "Adding first file path.")?;
let opts = PushOpts {
remote: DEFAULT_REMOTE_NAME.to_string(),
branch: new_branch_name.to_string(),
..Default::default()
};
repositories::push::push_remote_branch(&local_repo, &opts).await?;
let history_new =
api::client::commits::list_commit_history(&remote_repo, new_branch_name).await?;
assert_eq!(history_new.len(), 26);
let history_main =
api::client::commits::list_commit_history(&remote_repo, DEFAULT_BRANCH_NAME).await;
log::debug!("history_main: {history_main:?}");
assert!(history_main.is_err());
repositories::checkout(&local_repo, DEFAULT_BRANCH_NAME).await?;
repositories::push(&local_repo).await?;
let history_main =
api::client::commits::list_commit_history(&remote_repo, DEFAULT_BRANCH_NAME)
.await?;
assert_eq!(history_main.len(), 25);
Ok(remote_repo)
})
.await
}
#[tokio::test]
async fn test_cannot_push_while_another_user_is_pushing() -> Result<(), OxenError> {
test::run_no_commit_remote_repo_test(|remote_repo| async move {
let ret_repo = remote_repo.clone();
test::run_empty_dir_test_async(|first_repo_dir| async move {
let first_cloned_repo = repositories::clone_url(
&remote_repo.remote.url,
&first_repo_dir.join("first_repo"),
)
.await?;
test::run_empty_dir_test_async(|second_repo_dir| async move {
let second_cloned_repo = repositories::clone_url(
&remote_repo.remote.url,
&second_repo_dir.join("second_repo"),
)
.await?;
let new_file = "new_file.txt";
let new_file_path = first_cloned_repo.path.join(new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "new file")?;
repositories::add(&first_cloned_repo, &new_file_path).await?;
repositories::commit(&first_cloned_repo, "Adding first file path.")?;
repositories::push(&first_cloned_repo).await?;
let new_file = "new_file_2.txt";
let new_file_path = second_cloned_repo.path.join(new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "new file 2")?;
repositories::add(&second_cloned_repo, &new_file_path).await?;
repositories::commit(&second_cloned_repo, "Adding second file path.")?;
let new_file = "new_file_3.txt";
let new_file_path = second_cloned_repo.path.join(new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "new file 3")?;
repositories::add(&second_cloned_repo, &new_file_path).await?;
repositories::commit(&second_cloned_repo, "Adding third file path.")?;
let result = repositories::push(&second_cloned_repo).await;
assert!(result.is_err());
Ok(())
})
.await?;
Ok(())
})
.await?;
Ok(ret_repo)
})
.await
}
#[tokio::test]
async fn test_tree_cannot_push_two_separate_cloned_repos() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|_, remote_repo_1| async move {
let remote_repo_1_copy = remote_repo_1.clone();
test::run_training_data_fully_sync_remote(|_, remote_repo_2| async move {
let remote_repo_2_copy = remote_repo_2.clone();
test::run_empty_dir_test_async(|first_repo_dir| async move {
let first_cloned_repo = repositories::clone_url(
&remote_repo_1.remote.url,
&first_repo_dir.join("first_repo_dir"),
)
.await?;
test::run_empty_dir_test_async(|second_repo_dir| async move {
let mut second_cloned_repo = repositories::clone_url(
&remote_repo_2.remote.url,
&second_repo_dir.join("second_repo_dir"),
)
.await?;
let new_file = "new_file.txt";
let new_file_path = first_cloned_repo.path.join(new_file);
let new_file_path =
test::write_txt_file_to_path(new_file_path, "new file")?;
repositories::add(&first_cloned_repo, &new_file_path).await?;
repositories::commit(&first_cloned_repo, "Adding first file path.")?;
repositories::push(&first_cloned_repo).await?;
let first_remote = test::repo_remote_url_from(&first_cloned_repo.dirname());
command::config::set_remote(
&mut second_cloned_repo,
constants::DEFAULT_REMOTE_NAME,
&first_remote,
)?;
let new_file = "new_file_2.txt";
let new_file_path = second_cloned_repo.path.join(new_file);
let new_file_path =
test::write_txt_file_to_path(new_file_path, "new file 2")?;
repositories::add(&second_cloned_repo, &new_file_path).await?;
repositories::commit(&second_cloned_repo, "Adding second file path.")?;
let new_file = "new_file_3.txt";
let new_file_path = second_cloned_repo.path.join(new_file);
let new_file_path =
test::write_txt_file_to_path(new_file_path, "new file 3")?;
repositories::add(&second_cloned_repo, &new_file_path).await?;
repositories::commit(&second_cloned_repo, "Adding third file path.")?;
let result = repositories::push(&second_cloned_repo).await;
assert!(result.is_err());
Ok(())
})
.await?;
Ok(())
})
.await?;
Ok(remote_repo_2_copy)
})
.await?;
Ok(remote_repo_1_copy)
})
.await
}
#[tokio::test]
async fn test_tree_cannot_push_when_remote_repo_is_ahead_same_file() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|_, remote_repo| async move {
let remote_repo_copy = remote_repo.clone();
test::run_empty_dir_test_async(|user_a_repo_dir| async move {
let user_a_repo_dir_copy = user_a_repo_dir.join("user_a_repo");
let user_a_repo = repositories::clone_url(
&remote_repo.remote.url,
&user_a_repo_dir_copy.join("new_repo"),
)
.await?;
test::run_empty_dir_test_async(|user_b_repo_dir| async move {
let user_b_repo_dir_copy = user_b_repo_dir.join("user_b_repo");
let user_b_repo = repositories::clone_url(
&remote_repo.remote.url,
&user_b_repo_dir_copy.join("New_repo"),
)
.await?;
let mod_file = "README.md";
let a_mod_file_path = user_a_repo.path.join(mod_file);
let a_mod_file_path =
test::write_txt_file_to_path(a_mod_file_path, "I am the README now")?;
repositories::add(&user_a_repo, &a_mod_file_path).await?;
let commit_a =
repositories::commit(&user_a_repo, "User A modifying the README.")?;
log::debug!("commit_a: {commit_a}");
repositories::push(&user_a_repo).await?;
let b_mod_file_path = user_b_repo.path.join(mod_file);
let b_mod_file_path =
test::write_txt_file_to_path(b_mod_file_path, "I be the README now.")?;
repositories::add(&user_b_repo, &b_mod_file_path).await?;
let commit_b =
repositories::commit(&user_b_repo, "User B modifying the README.")?;
log::debug!("commit_b: {commit_b}");
let first_push_result = repositories::push(&user_b_repo).await;
log::debug!("first_push_result: {first_push_result:?}");
assert!(first_push_result.is_err());
let result = repositories::pull(&user_b_repo).await;
assert!(result.is_err());
let status = repositories::status(&user_b_repo)?;
assert!(status.has_merge_conflicts());
println!("passed has_merge_conflicts");
status.print();
let b_mod_file_path = user_b_repo.path.join(mod_file);
let b_mod_file_path = test::write_txt_file_to_path(
b_mod_file_path,
"No for real. I be the README now.",
)?;
println!("passed write_txt_file_to_path");
repositories::add(&user_b_repo, &b_mod_file_path).await?;
println!("passed add");
repositories::commit(&user_b_repo, "User B resolving conflicts.")?;
println!("passed commit");
let third_push_result = repositories::push(&user_b_repo).await;
assert!(third_push_result.is_ok());
Ok(())
})
.await?;
Ok(())
})
.await?;
Ok(remote_repo_copy)
})
.await
}
#[tokio::test]
async fn test_tree_cannot_push_when_remote_is_many_commits_ahead_tree_conflicts()
-> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|_, remote_repo| async move {
let remote_repo_copy = remote_repo.clone();
test::run_empty_dir_test_async(|user_a_repo_dir| async move {
let user_a_repo = repositories::clone_url(
&remote_repo.remote.url,
&user_a_repo_dir.join("new_repo"),
)
.await?;
let files = util::fs::rlist_paths_in_dir(&user_a_repo_dir);
for item in files {
log::debug!("\nfile or dir: {item:?}\n")
}
test::run_empty_dir_test_async(|user_b_repo_dir| async move {
let user_b_repo = repositories::clone_url(
&remote_repo.remote.url,
&user_b_repo_dir.join("new_repo"),
)
.await?;
let modify_path_a = user_a_repo
.path
.join("annotations")
.join("train")
.join("annotations.txt");
let modify_path_b = user_b_repo
.path
.join("annotations")
.join("train")
.join("annotations.txt");
test::write_txt_file_to_path(&modify_path_a, "new file")?;
repositories::add(&user_a_repo, &modify_path_a).await?;
repositories::commit(&user_a_repo, "Adding first file path.")?;
repositories::push(&user_a_repo).await?;
test::write_txt_file_to_path(&modify_path_b, "newer file")?;
repositories::add(&user_b_repo, &modify_path_b).await?;
repositories::commit(&user_b_repo, "User B adding second file path.")?;
let res = repositories::push(&user_b_repo).await;
assert!(res.is_err());
Ok(())
})
.await?;
Ok(())
})
.await?;
Ok(remote_repo_copy)
})
.await
}
#[tokio::test]
async fn test_tree_cannot_push_tree_conflict_deleted_file() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|_, remote_repo| async move {
let remote_repo_copy = remote_repo.clone();
test::run_empty_dir_test_async(|user_a_repo_dir| async move {
let user_a_repo = repositories::clone_url(
&remote_repo.remote.url,
&user_a_repo_dir.join("new_repo"),
)
.await?;
let files = util::fs::rlist_paths_in_dir(&user_a_repo_dir);
for item in files {
log::debug!("\nfile or dir: {item:?}\n")
}
test::run_empty_dir_test_async(|user_b_repo_dir| async move {
let user_b_repo = repositories::clone_url(
&remote_repo.remote.url,
&user_b_repo_dir.join("new_repo"),
)
.await?;
let modify_path_a = user_a_repo
.path
.join("annotations")
.join("train")
.join("annotations.txt");
let modify_path_b = user_b_repo
.path
.join("annotations")
.join("train")
.join("annotations.txt");
let _add_path_b = user_b_repo
.path
.join("annotations")
.join("train")
.join("averynewfile.txt");
let files = util::fs::rlist_paths_in_dir(
&user_b_repo.path.join("annotations").join("train"),
);
for item in files {
log::debug!("\npre file or dir: {item:?}\n")
}
test::write_txt_file_to_path(&modify_path_a, "fancy new file contents")?;
repositories::add(&user_a_repo, &modify_path_a).await?;
let commit_a =
repositories::commit(&user_a_repo, "modifying first file path.")?;
repositories::push(&user_a_repo).await?;
util::fs::remove_file(&modify_path_b)?;
let files = util::fs::rlist_paths_in_dir(
&user_b_repo.path.join("annotations").join("train"),
);
for item in files {
log::debug!("\npost file or dir: {item:?}\n")
}
repositories::add(&user_b_repo, &modify_path_b).await?;
let head = repositories::commits::head_commit(&user_b_repo)?;
let pre_b =
repositories::tree::get_root_with_children(&user_b_repo, &head)?.unwrap();
log::debug!("b head before is {head:?}");
let maybe_b_entry = pre_b.get_by_path(
PathBuf::from("annotations")
.join("train")
.join("annotations.txt"),
)?;
log::debug!("maybe_b_entry before commit is {maybe_b_entry:?}");
let commit_b =
repositories::commit(&user_b_repo, "user B deleting file path.")?;
let head = repositories::commits::head_commit(&user_b_repo)?;
let post_b =
repositories::tree::get_root_with_children(&user_b_repo, &head)?.unwrap();
let maybe_b_entry = post_b.get_by_path(
PathBuf::from("annotations")
.join("train")
.join("annotations.txt"),
)?;
log::debug!("maybe_b_entry after commitis {maybe_b_entry:?}");
log::debug!("commit_a is {commit_a:?}");
log::debug!("commit_b is {commit_b:?}");
let commit_a =
repositories::commits::get_by_id(&user_a_repo, &commit_a.id)?.unwrap();
let commit_b =
repositories::commits::get_by_id(&user_b_repo, &commit_b.id)?.unwrap();
log::debug!("commit_a pre is {commit_a:?}");
log::debug!("commit_b pre is {commit_b:?}");
let res = repositories::push(&user_b_repo).await;
log::debug!("here's the result and why it failed: {res:?}");
assert!(res.is_err());
Ok(())
})
.await?;
Ok(())
})
.await?;
Ok(remote_repo_copy)
})
.await
}
#[tokio::test]
async fn test_push_move_entire_directory() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|local_repo, remote_repo| async move {
let train_images = local_repo.path.join("train");
let new_path = local_repo.path.join("images").join("train");
util::fs::create_dir_all(local_repo.path.join("images"))?;
util::fs::rename(&train_images, &new_path)?;
repositories::add(&local_repo, new_path).await?;
let mut rm_opts = RmOpts::from_path("train");
rm_opts.recursive = true;
repositories::rm(&local_repo, &rm_opts)?;
let commit =
repositories::commit(&local_repo, "Moved all the train image files to images/")?;
repositories::push(&local_repo).await?;
let path = PathBuf::from("");
let page = 1;
let page_size = 100;
let dir_entries =
api::client::dir::list(&remote_repo, &commit.id, &path, page, page_size).await?;
assert!(
!dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "train")
);
assert!(
dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "images")
);
let new_file = local_repo.path.join("new_file.txt");
util::fs::write(&new_file, "I am a new file")?;
repositories::add(&local_repo, new_file).await?;
let commit = repositories::commit(&local_repo, "Added a new file")?;
repositories::push(&local_repo).await?;
let dir_entries =
api::client::dir::list(&remote_repo, &commit.id, &path, page, page_size).await?;
assert!(
dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "new_file.txt")
);
Ok(remote_repo)
})
.await
}
#[tokio::test]
async fn test_push_only_one_modified_file() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|local_repo, remote_repo| async move {
let readme_path = local_repo.path.join("README.md");
let new_path = local_repo.path.join("README2.md");
util::fs::rename(&readme_path, &new_path)?;
repositories::add(&local_repo, new_path).await?;
let rm_opts = RmOpts::from_path("README.md");
repositories::rm(&local_repo, &rm_opts)?;
let commit = repositories::commit(&local_repo, "Moved the readme")?;
repositories::push(&local_repo).await?;
let dir_entries =
api::client::dir::list(&remote_repo, &commit.id, &PathBuf::from(""), 1, 100)
.await?;
assert!(
dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "README2.md")
);
assert!(
!dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "README.md")
);
Ok(remote_repo)
})
.await
}
#[tokio::test]
async fn test_push_root_subtree_depth_1() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|_local_repo, remote_repo| async move {
let cloned_remote = remote_repo.clone();
test::run_empty_dir_test_async(|dir| async move {
let mut opts = CloneOpts::new(&remote_repo.remote.url, dir.join("new_repo"));
opts.fetch_opts.subtree_paths = Some(vec![PathBuf::from(".")]);
opts.fetch_opts.depth = Some(1);
let local_repo = repositories::clone::clone(&opts).await?;
let readme_file = local_repo.path.join("ANOTHER_FILE.md");
util::fs::write_to_path(
&readme_file,
r"
Q: How can I version a giant dataset of images?
A: Oxen.ai is a great tool for this! It can handle any size dataset, and is optimized for speed.
",
)?;
repositories::add(&local_repo, &readme_file).await?;
let commit = repositories::commit(&local_repo, "Added another file")?;
let result = repositories::push(&local_repo).await;
println!("push result: {result:?}");
assert!(result.is_ok());
let dir_entries =
api::client::dir::list(&remote_repo, &commit.id, &PathBuf::from(""), 1, 100)
.await?;
assert!(
dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "ANOTHER_FILE.md")
);
Ok(())
})
.await?;
Ok(cloned_remote)
})
.await
}
#[tokio::test]
async fn test_push_annotations_test_subtree() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|_local_repo, remote_repo| async move {
let cloned_remote = remote_repo.clone();
test::run_empty_dir_test_async(|dir| async move {
let mut opts = CloneOpts::new(&remote_repo.remote.url, dir.join("new_repo"));
opts.fetch_opts.subtree_paths =
Some(vec![PathBuf::from("annotations").join("test")]);
let local_repo = repositories::clone::clone(&opts).await?;
let annotations_test_dir = local_repo.path.join("annotations").join("test");
let readme_file = annotations_test_dir.join("README.md");
util::fs::write_to_path(
&readme_file,
r"
Q: What is a faster alternative to DVC?
A: Checkout Oxen.ai
",
)?;
repositories::add(&local_repo, &readme_file).await?;
let commit = repositories::commit(&local_repo, "adding README.md to the test dir")?;
let result = repositories::push(&local_repo).await;
println!("push result: {result:?}");
assert!(result.is_ok());
let dir_entries = api::client::dir::list(
&remote_repo,
&commit.id,
&PathBuf::from("annotations").join("test"),
1,
100,
)
.await?;
println!("dir_entries: {dir_entries:?}");
assert!(
dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "README.md")
);
Ok(())
})
.await?;
Ok(cloned_remote)
})
.await
}
#[tokio::test]
async fn test_push_subtree_nlp_classification() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|_, remote_repo| async move {
let remote_repo_copy = remote_repo.clone();
test::run_empty_dir_test_async(|repos_base_dir| async move {
let user_a_repo_dir = repos_base_dir.join("user_a_repo");
let mut clone_opts = CloneOpts::new(&remote_repo.remote.url, &user_a_repo_dir);
clone_opts.fetch_opts.subtree_paths =
Some(vec![PathBuf::from("nlp").join("classification")]);
clone_opts.fetch_opts.depth = Some(2);
let user_a_repo = repositories::clone(&clone_opts).await?;
println!("user_a_repo: {user_a_repo:?}");
let new_file = PathBuf::from("nlp")
.join("classification")
.join("new_data.tsv");
let new_file_path = user_a_repo.path.join(&new_file);
let new_file_path = test::write_txt_file_to_path(new_file_path, "image\tlabel")?;
repositories::add(&user_a_repo, &new_file_path).await?;
let commit =
repositories::commit(&user_a_repo, "Adding nlp/classification/new_data.tsv")?;
repositories::push(&user_a_repo).await?;
let dir_entries = api::client::dir::list(
&remote_repo,
&commit.id,
&PathBuf::from("nlp").join("classification"),
1,
100,
)
.await?;
assert!(
dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "new_data.tsv")
);
let root_dir_entries =
api::client::dir::list(&remote_repo, &commit.id, &PathBuf::from(""), 1, 100)
.await?;
assert!(
root_dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "README.md")
);
Ok(())
})
.await?;
Ok(remote_repo_copy)
})
.await
}
#[tokio::test]
async fn test_push_partial_clone_nlp_classification() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|_, remote_repo| async move {
let remote_repo_copy = remote_repo.clone();
test::run_empty_dir_test_async(|repos_base_dir| async move {
let user_a_repo_dir = repos_base_dir.join("user_a_repo");
let mut clone_opts = CloneOpts::new(&remote_repo.remote.url, &user_a_repo_dir);
clone_opts.fetch_opts.subtree_paths =
Some(vec![PathBuf::from("nlp").join("classification")]);
clone_opts.fetch_opts.depth = Some(2);
let user_a_repo = repositories::clone(&clone_opts).await?;
let new_file_1 = PathBuf::from("nlp")
.join("classification")
.join("new_partial_data_1.tsv");
let new_file_path_1 = user_a_repo.path.join(&new_file_1);
let new_file_path_1 =
test::write_txt_file_to_path(new_file_path_1, "image\tlabel1")?;
repositories::add(&user_a_repo, &new_file_path_1).await?;
let new_file_2 = PathBuf::from("nlp")
.join("classification")
.join("new_partial_data_2.tsv");
let new_file_path_2 = user_a_repo.path.join(&new_file_2);
let new_file_path_2 =
test::write_txt_file_to_path(new_file_path_2, "image\tlabel2")?;
repositories::add(&user_a_repo, &new_file_path_2).await?;
let existing_file_path = user_a_repo
.path
.join("nlp/classification/existing_file.tsv");
let modified_file_path =
test::write_txt_file_to_path(existing_file_path, "image\tmodified_label")?;
repositories::add(&user_a_repo, &modified_file_path).await?;
let commit = repositories::commit(
&user_a_repo,
"Adding new partial data files and modifying existing file",
)?;
repositories::push(&user_a_repo).await?;
let dir_entries = api::client::dir::list(
&remote_repo,
&commit.id,
&PathBuf::from("nlp").join("classification"),
1,
100,
)
.await?;
assert!(
dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "new_partial_data_1.tsv")
);
assert!(
dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "new_partial_data_2.tsv")
);
assert!(
dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "existing_file.tsv")
);
let metadata_entry = dir_entries
.entries
.iter()
.find(|entry| entry.filename() == "existing_file.tsv")
.unwrap();
let metadata_entry = match metadata_entry {
EMetadataEntry::MetadataEntry(entry) => entry,
_ => panic!("Expected a metadata entry"),
};
api::client::entries::download_file(
&remote_repo,
metadata_entry,
&PathBuf::from("nlp/classification/existing_file.tsv"),
&user_a_repo
.path
.join("nlp/classification/existing_file.tsv"),
&commit.id,
)
.await?;
let modified_file_content = std::fs::read_to_string(
user_a_repo
.path
.join("nlp/classification/existing_file.tsv"),
)?;
assert_eq!(modified_file_content, "image\tmodified_label");
let root_dir_entries =
api::client::dir::list(&remote_repo, &commit.id, &PathBuf::from(""), 1, 100)
.await?;
assert!(
root_dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "README.md")
);
let classification_dir_entries = api::client::dir::list(
&remote_repo,
&commit.id,
&PathBuf::from("nlp").join("classification"),
1,
100,
)
.await?;
assert!(!classification_dir_entries.entries.is_empty());
let root_dir_entries =
api::client::dir::list(&remote_repo, &commit.id, &PathBuf::from(""), 1, 100)
.await?;
assert!(
root_dir_entries
.entries
.iter()
.any(|entry| entry.filename() == "README.md")
);
Ok(())
})
.await?;
Ok(remote_repo_copy)
})
.await
}
#[tokio::test]
async fn test_push_file_with_exact_avg_chunk_size() -> Result<(), OxenError> {
test::run_readme_remote_repo_test(|local_repo, remote_repo| async move {
let local_repo = local_repo.clone();
let remote_repo = remote_repo.clone();
let file_path = local_repo.path.join("exact_chunk_size_file.bin");
let file_data: Vec<u8> = vec![42; AVG_CHUNK_SIZE as usize];
util::fs::write_data(&file_path, &file_data)?;
let metadata = util::fs::metadata(&file_path)?;
assert_eq!(
metadata.len(),
AVG_CHUNK_SIZE,
"File size should be exactly AVG_CHUNK_SIZE"
);
repositories::add(&local_repo, &file_path).await?;
let commit_msg = "Add file with exactly AVG_CHUNK_SIZE bytes";
let commit = repositories::commit(&local_repo, commit_msg)?;
let branch = repositories::push(&local_repo).await?;
let remote_commit_opt =
api::client::commits::get_by_id(&remote_repo, &commit.id).await?;
assert!(remote_commit_opt.is_some(), "Remote commit should exist");
let remote_repo_clone = remote_repo.clone();
test::run_empty_dir_test_async(|temp_dir| async move {
let download_path = temp_dir.join("downloaded_file.bin");
repositories::download(
&remote_repo_clone,
"exact_chunk_size_file.bin",
&download_path,
&branch.name,
)
.await?;
assert!(download_path.exists(), "Downloaded file should exist");
let downloaded_metadata = util::fs::metadata(&download_path)?;
assert_eq!(
downloaded_metadata.len(),
metadata.len(),
"Downloaded file size should match the original file size"
);
let downloaded_data = util::fs::read_bytes_from_path(&download_path)?;
assert_eq!(
downloaded_data, file_data,
"Downloaded file contents should match the original data"
);
Ok(())
})
.await?;
Ok(remote_repo)
})
.await
}
#[tokio::test]
async fn test_merge_conflict_push_failure() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|original_repo, remote_repo| async move {
let remote_repo_copy = remote_repo.clone();
let dir1_path = original_repo.path.join("dir1");
util::fs::create_dir_all(&dir1_path)?;
let file1_path = dir1_path.join("file1.txt");
util::fs::write(&file1_path, "Original content")?;
repositories::add(&original_repo, &file1_path).await?;
repositories::commit(&original_repo, "Add dir1/file1.txt")?;
repositories::push(&original_repo).await?;
test::run_empty_dir_test_async(|clone_dir| async move {
let clone_repo_path = clone_dir.join("cloned_repo");
let clone_repo =
repositories::clone_url(&remote_repo.remote.url, &clone_repo_path).await?;
let clone_file1_path = clone_repo.path.join("dir1").join("file1.txt");
util::fs::write(&clone_file1_path, "Clone modified content")?;
repositories::add(&clone_repo, &clone_file1_path).await?;
repositories::commit(&clone_repo, "Modify file1.txt in clone")?;
repositories::push(&clone_repo).await?;
let original_file1_path = original_repo.path.join("dir1").join("file1.txt");
util::fs::write(&original_file1_path, "Original repo modified content")?;
repositories::add(&original_repo, &original_file1_path).await?;
repositories::commit(&original_repo, "Modify file1.txt in original")?;
let head_before_pull = repositories::commits::head_commit(&original_repo)?;
let pull_result = repositories::pull(&original_repo).await;
assert!(
pull_result.is_err(),
"Pull should fail due to merge conflict"
);
let head_after_pull = repositories::commits::head_commit(&original_repo)?;
assert_eq!(
head_before_pull.id, head_after_pull.id,
"Head commit should not change when pull fails due to merge conflict"
);
let status = repositories::status(&original_repo)?;
assert!(status.has_merge_conflicts(), "Should have merge conflicts");
let push_result = repositories::push(&original_repo).await;
assert!(
push_result.is_err(),
"Push should fail due to merge conflict"
);
Ok(())
})
.await?;
Ok(remote_repo_copy)
})
.await
}
#[tokio::test]
async fn test_create_nodes_before_starting_push() -> Result<(), OxenError> {
test::run_readme_remote_repo_test(|local_repo, remote_repo| async move {
let new_file = local_repo.path.join("new_file.txt");
util::fs::write(&new_file, "I am a new file")?;
repositories::add(&local_repo, &new_file).await?;
let commit = repositories::commit(&local_repo, "Added a new file")?;
let progress = Arc::new(PushProgress::new());
progress.set_message("Collecting missing nodes...");
let mut candidate_nodes: HashSet<MerkleTreeNode> = HashSet::new();
let Some(commit_node) =
repositories::tree::get_root_with_children(&local_repo, &commit)?
else {
return Err(OxenError::basic_str("Err: Root not found"));
};
candidate_nodes.insert(commit_node.clone());
commit_node.walk_tree_without_leaves(|node| {
candidate_nodes.insert(node.clone());
progress.set_message(format!(
"Collecting missing nodes... {}",
candidate_nodes.len()
));
});
progress.set_message(format!("Pushing {} nodes...", candidate_nodes.len()));
api::client::tree::create_nodes(
&local_repo,
&remote_repo,
candidate_nodes
.clone()
.into_iter()
.map(|node| node.hash)
.collect(),
&progress,
)
.await?;
repositories::push(&local_repo).await?;
let new_path = PathBuf::from("new_file.txt");
let _found_node =
api::client::tree::get_node_hash_by_path(&remote_repo, &commit.id, new_path)
.await?;
Ok(remote_repo)
})
.await
}
#[tokio::test]
async fn test_push_large_file_and_clone_verify() -> Result<(), OxenError> {
test::run_empty_local_repo_test_async(|local_repo| async move {
let file_size = 100 * 1024 * 1024; let file_path = local_repo.path.join("large_file.bin");
let file_data: Vec<u8> = vec![42; file_size];
util::fs::write_data(&file_path, &file_data)?;
let metadata = util::fs::metadata(&file_path)?;
assert_eq!(
metadata.len(),
file_size as u64,
"File size should be exactly 100MB"
);
println!("Adding and committing 100MB file...");
repositories::add(&local_repo, &file_path).await?;
let commit = repositories::commit(&local_repo, "Add 100MB file")?;
println!("Setting up remote repository...");
let remote_repo = test::create_remote_repo(&local_repo).await?;
let mut local_repo_mut = local_repo.clone();
command::config::set_remote(
&mut local_repo_mut,
constants::DEFAULT_REMOTE_NAME,
&remote_repo.remote.url,
)?;
println!("Pushing 100MB file to remote...");
repositories::push(&local_repo_mut).await?;
let remote_commit_opt =
api::client::commits::get_by_id(&remote_repo, &commit.id).await?;
assert!(remote_commit_opt.is_some(), "Remote commit should exist");
let remote_repo_clone = remote_repo.clone();
let file_data_clone = file_data.clone();
test::run_empty_dir_test_async(|clone_dir| async move {
println!("Cloning repository to verify files...");
let clone_repo_path = clone_dir.join("cloned_repo");
let clone_repo =
repositories::clone_url(&remote_repo_clone.remote.url, &clone_repo_path)
.await?;
let cloned_file_path = clone_repo.path.join("large_file.bin");
assert!(
cloned_file_path.exists(),
"Cloned file should exist at {cloned_file_path:?}"
);
let cloned_metadata = util::fs::metadata(&cloned_file_path)?;
assert_eq!(
cloned_metadata.len(),
file_size as u64,
"Cloned file size should match original (100MB)"
);
println!("Verifying file contents match...");
let cloned_data = util::fs::read_bytes_from_path(&cloned_file_path)?;
assert_eq!(
cloned_data.len(),
file_data_clone.len(),
"Cloned file data length should match original"
);
assert_eq!(
cloned_data, file_data_clone,
"Cloned file contents should match the original data"
);
println!("Successfully verified 100MB file after clone!");
Ok(())
})
.await?;
api::client::repositories::delete(&remote_repo).await?;
Ok(())
})
.await
}
#[tokio::test]
async fn test_push_large_file_in_subdir_and_clone_verify() -> Result<(), OxenError> {
test::run_empty_local_repo_test_async(|local_repo| async move {
let sub_dir = local_repo.path.join("data").join("models");
util::fs::create_dir_all(&sub_dir)?;
let file_size = 11 * 1024 * 1024; let file_path = sub_dir.join("weights.bin");
let file_data: Vec<u8> = (0..file_size).map(|i| (i % 256) as u8).collect();
util::fs::write_data(&file_path, &file_data)?;
repositories::add(&local_repo, &local_repo.path).await?;
let commit = repositories::commit(&local_repo, "Add large file in subdir")?;
let remote_repo = test::create_remote_repo(&local_repo).await?;
let mut local_repo_mut = local_repo.clone();
command::config::set_remote(
&mut local_repo_mut,
constants::DEFAULT_REMOTE_NAME,
&remote_repo.remote.url,
)?;
repositories::push(&local_repo_mut).await?;
let remote_commit = api::client::commits::get_by_id(&remote_repo, &commit.id).await?;
assert!(remote_commit.is_some(), "Remote commit should exist");
let remote_repo_clone = remote_repo.clone();
let file_data_clone = file_data.clone();
test::run_empty_dir_test_async(|clone_dir| async move {
let clone_repo_path = clone_dir.join("cloned_repo");
let clone_repo =
repositories::clone_url(&remote_repo_clone.remote.url, &clone_repo_path)
.await?;
let cloned_file = clone_repo
.path
.join("data")
.join("models")
.join("weights.bin");
assert!(
cloned_file.exists(),
"Cloned file should exist at {cloned_file:?}"
);
let cloned_metadata = util::fs::metadata(&cloned_file)?;
assert_eq!(
cloned_metadata.len(),
file_size as u64,
"Cloned file size should match original"
);
let cloned_data = util::fs::read_bytes_from_path(&cloned_file)?;
assert_eq!(
cloned_data, file_data_clone,
"Cloned file contents should match the original data"
);
Ok(())
})
.await?;
api::client::repositories::delete(&remote_repo).await?;
Ok(())
})
.await
}
}