use crate::api;
use crate::api::remote::client;
use crate::error::OxenError;
use crate::model::RemoteRepository;
use crate::view::merge::{Mergeable, MergeableResponse};
pub async fn mergability(
remote_repo: &RemoteRepository,
base: &str,
head: &str,
) -> Result<Mergeable, OxenError> {
let uri = format!("/merge/{base}..{head}");
let url = api::endpoint::url_from_repo(remote_repo, &uri)?;
log::debug!("url: {url}");
let client = client::new_for_url(&url)?;
match client.get(&url).send().await {
Ok(res) => {
let body = client::parse_json_body(&url, res).await?;
log::debug!("got body: {}", body);
let response: Result<MergeableResponse, serde_json::Error> =
serde_json::from_str(&body);
match response {
Ok(val) => Ok(val.mergeable),
Err(err) => Err(OxenError::basic_str(format!(
"api::remote::merger::mergability error parsing response from {url}\n\nErr {err:?} \n\n{body}"
))),
}
}
Err(err) => {
let err =
format!("api::remote::merger::mergability Request failed: {url}\nErr {err:?}");
Err(OxenError::basic_str(err))
}
}
}
#[cfg(test)]
mod tests {
use crate::constants::DEFAULT_REMOTE_NAME;
use crate::error::OxenError;
use crate::test;
use crate::{api, command};
#[tokio::test]
async fn test_remote_merger_no_commits() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|local_repo, remote_repo| async move {
let base = "main";
let head = "add-data";
command::create_checkout(&local_repo, head)?;
command::push_remote_branch(&local_repo, DEFAULT_REMOTE_NAME, head).await?;
let mergability = api::remote::merger::mergability(&remote_repo, base, head).await?;
assert!(mergability.is_mergeable);
assert_eq!(mergability.commits.len(), 0);
Ok(remote_repo)
})
.await
}
#[tokio::test]
async fn test_remote_merger_base_is_ahead() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|local_repo, remote_repo| async move {
let base = "main";
let head = "add-data";
command::create_checkout(&local_repo, head)?;
command::push_remote_branch(&local_repo, DEFAULT_REMOTE_NAME, head).await?;
command::checkout(&local_repo, base).await?;
let path = local_repo.path.join("file_1.txt");
test::write_txt_file_to_path(&path, "hello")?;
command::add(&local_repo, &path)?;
command::commit(&local_repo, "adding file 1")?;
command::push_remote_branch(&local_repo, DEFAULT_REMOTE_NAME, base).await?;
let mergability = api::remote::merger::mergability(&remote_repo, base, head).await?;
assert!(mergability.is_mergeable);
assert_eq!(mergability.commits.len(), 0);
Ok(remote_repo)
})
.await
}
#[tokio::test]
async fn test_remote_merger_mergable_multiple_commits() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|local_repo, remote_repo| async move {
let base = "main";
let head = "add-data";
command::create_checkout(&local_repo, head)?;
command::push_remote_branch(&local_repo, DEFAULT_REMOTE_NAME, head).await?;
let path = local_repo.path.join("README.md");
test::write_txt_file_to_path(&path, "I am the README now")?;
command::add(&local_repo, &path)?;
let path = local_repo.path.join("file_1.txt");
test::write_txt_file_to_path(&path, "hello")?;
command::add(&local_repo, &path)?;
command::commit(&local_repo, "adding file 1")?;
let path = local_repo.path.join("file_2.txt");
test::write_txt_file_to_path(&path, "world")?;
command::add(&local_repo, &path)?;
command::commit(&local_repo, "adding file 2")?;
command::push_remote_branch(&local_repo, DEFAULT_REMOTE_NAME, head).await?;
let mergability = api::remote::merger::mergability(&remote_repo, base, head).await?;
assert!(mergability.is_mergeable);
assert_eq!(mergability.commits.len(), 2);
assert_eq!(mergability.conflicts.len(), 0);
Ok(remote_repo)
})
.await
}
#[tokio::test]
async fn test_remote_merger_multiple_commits_conflict_head_is_ahead() -> Result<(), OxenError> {
test::run_training_data_fully_sync_remote(|local_repo, remote_repo| async move {
let base = "main";
let head = "add-data";
command::create_checkout(&local_repo, head)?;
command::push_remote_branch(&local_repo, DEFAULT_REMOTE_NAME, head).await?;
let path = local_repo.path.join("README.md");
test::write_txt_file_to_path(&path, "I am the README now")?;
command::add(&local_repo, &path)?;
let path = local_repo.path.join("file_1.txt");
test::write_txt_file_to_path(&path, "hello")?;
command::add(&local_repo, &path)?;
command::commit(&local_repo, "adding file 1")?;
let path = local_repo.path.join("file_2.txt");
test::write_txt_file_to_path(&path, "world")?;
command::add(&local_repo, &path)?;
command::commit(&local_repo, "adding file 2")?;
command::push_remote_branch(&local_repo, DEFAULT_REMOTE_NAME, head).await?;
command::checkout(&local_repo, base).await?;
let path = local_repo.path.join("README.md");
test::write_txt_file_to_path(&path, "I am on main conflicting the README")?;
command::add(&local_repo, &path)?;
command::commit(&local_repo, "modifying readme on main")?;
command::push_remote_branch(&local_repo, DEFAULT_REMOTE_NAME, base).await?;
let mergability = api::remote::merger::mergability(&remote_repo, base, head).await?;
assert!(!mergability.is_mergeable);
assert_eq!(mergability.commits.len(), 2);
assert_eq!(mergability.conflicts.len(), 1);
Ok(remote_repo)
})
.await
}
}