use std::fmt::Display;
use std::path::Path;
use std::process::Command;
use std::sync::Arc;
use git_workarea::GitContext;
use crate::actions::test::refs::*;
use crate::host::{CommitStatus, CommitStatusState, HostedProject, HostingService, MergeRequest};
use crate::tests::mock::{MockData, MockMergeRequest, MockService};
use crate::tests::utils::test_workspace_dir;
const REPO_NAME: &str = "base";
const MR_ID: u64 = 1;
const MR_ID_OTHER: u64 = 2;
const COMMIT: &str = "7189cf557ba2c7c61881ff8669158710b94d8df1";
const COMMIT_OTHER: &str = "f6f8de8c7c5f1a081b14f5a47c7798268f383222";
fn test_refs_service() -> Arc<MockService> {
let base = MockData::repo(REPO_NAME);
let user = MockData::user("user");
MockData::builder()
.add_merge_request(MockMergeRequest::new(MR_ID, &user, COMMIT, &base, None))
.add_merge_request(MockMergeRequest::new(
MR_ID_OTHER,
&user,
COMMIT_OTHER,
&base,
None,
))
.add_project(base)
.add_user(user)
.service()
}
fn git_context(workspace_path: &Path) -> GitContext {
let origindir = workspace_path.join("origin");
let clone = Command::new("git")
.arg("clone")
.arg("--bare")
.arg(concat!(env!("CARGO_MANIFEST_DIR"), "/../.git"))
.arg(&origindir)
.output()
.unwrap();
if !clone.status.success() {
panic!(
"origin clone failed: {}",
String::from_utf8_lossy(&clone.stderr),
);
}
let gitdir = workspace_path.join("git");
let clone = Command::new("git")
.arg("clone")
.arg("--bare")
.arg(origindir)
.arg(&gitdir)
.output()
.unwrap();
if !clone.status.success() {
panic!(
"working clone failed: {}",
String::from_utf8_lossy(&clone.stderr),
);
}
GitContext::new(gitdir)
}
fn create_test(git: &GitContext, service: &Arc<MockService>) -> TestRefs {
let project = HostedProject {
name: REPO_NAME.into(),
service: Arc::clone(service) as Arc<dyn HostingService>,
};
TestRefs::new(git.clone(), project)
}
fn test_ref_exists<I>(ctx: &GitContext, namespace: &str, id: I) -> bool
where
I: Display,
{
let refname = format!("refs/{namespace}/{id}");
let remote = ctx
.git()
.arg("ls-remote")
.arg("--exit-code")
.arg("origin")
.arg(&refname)
.output()
.unwrap()
.status
.success();
let local = ctx
.git()
.arg("show-ref")
.arg("--quiet")
.arg("--verify")
.arg(&refname)
.status()
.unwrap()
.success();
assert_eq!(
remote, local,
"The `refs/{namespace}/...` refs should be the same on the local and remote repositories.",
);
remote && local
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum TestRefState {
Pushed,
Removed,
}
impl TestRefState {
fn description(self) -> &'static str {
match self {
TestRefState::Pushed => "pushed for testing",
TestRefState::Removed => "removed from testing",
}
}
}
fn check_commit_status(actual: &CommitStatus, mr: &MergeRequest, state: TestRefState) {
assert_eq!(actual.state, CommitStatusState::Success);
assert_eq!(actual.refname, Some(mr.source_branch.clone()));
assert_eq!(actual.name, "ghostflow-test");
assert_eq!(actual.description, state.description());
assert_eq!(actual.target_url, None);
}
#[test]
fn test_test_mr() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let service = test_refs_service();
let test = create_test(&ctx, &service);
let mr = service.merge_request(REPO_NAME, MR_ID).unwrap();
test.test_mr(&mr).unwrap();
assert!(test_ref_exists(&ctx, "test-topics", mr.id));
let mr_comments = service.mr_comments(mr.id);
let mr_commit_statuses = service.commit_statuses(&mr.commit.id);
assert_eq!(service.remaining_data(), 0);
assert_eq!(mr_comments.len(), 1);
assert_eq!(mr_comments[0], "This topic has been pushed for testing.");
assert_eq!(mr_commit_statuses.len(), 1);
check_commit_status(&mr_commit_statuses[0], &mr, TestRefState::Pushed);
}
#[test]
fn test_test_mr_namespace() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let service = test_refs_service();
let mut test = create_test(&ctx, &service);
let namespace = "test-topics-alternate";
test.ref_namespace(namespace);
let mr = service.merge_request(REPO_NAME, MR_ID).unwrap();
test.test_mr(&mr).unwrap();
assert!(test_ref_exists(&ctx, namespace, mr.id));
let mr_comments = service.mr_comments(mr.id);
let mr_commit_statuses = service.commit_statuses(&mr.commit.id);
assert_eq!(service.remaining_data(), 0);
assert_eq!(mr_comments.len(), 1);
assert_eq!(mr_comments[0], "This topic has been pushed for testing.");
assert_eq!(mr_commit_statuses.len(), 1);
check_commit_status(&mr_commit_statuses[0], &mr, TestRefState::Pushed);
}
#[test]
fn test_test_mr_quiet() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let service = test_refs_service();
let mut test = create_test(&ctx, &service);
test.quiet();
let mr = service.merge_request(REPO_NAME, MR_ID).unwrap();
test.test_mr(&mr).unwrap();
assert!(test_ref_exists(&ctx, "test-topics", mr.id));
let mr_commit_statuses = service.commit_statuses(&mr.commit.id);
assert_eq!(service.remaining_data(), 0);
assert_eq!(mr_commit_statuses.len(), 1);
check_commit_status(&mr_commit_statuses[0], &mr, TestRefState::Pushed);
}
#[test]
fn test_untest_mr() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let service = test_refs_service();
let test = create_test(&ctx, &service);
let mr = service.merge_request(REPO_NAME, MR_ID).unwrap();
test.test_mr(&mr).unwrap();
test.untest_mr(&mr).unwrap();
assert!(!test_ref_exists(&ctx, "test-topics", mr.id));
let mr_comments = service.mr_comments(mr.id);
let mr_commit_statuses = service.commit_statuses(&mr.commit.id);
assert_eq!(service.remaining_data(), 0);
assert_eq!(mr_comments.len(), 1);
assert_eq!(mr_comments[0], "This topic has been pushed for testing.");
assert_eq!(mr_commit_statuses.len(), 2);
check_commit_status(&mr_commit_statuses[0], &mr, TestRefState::Pushed);
check_commit_status(&mr_commit_statuses[1], &mr, TestRefState::Removed);
}
#[test]
fn test_untest_mr_untested() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let service = test_refs_service();
let test = create_test(&ctx, &service);
let mr = service.merge_request(REPO_NAME, MR_ID).unwrap();
test.untest_mr(&mr).unwrap();
assert!(!test_ref_exists(&ctx, "test-topics", mr.id));
assert_eq!(service.remaining_data(), 0);
}
#[test]
fn test_clear_all_tests() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let service = test_refs_service();
let test = create_test(&ctx, &service);
let mr = service.merge_request(REPO_NAME, MR_ID).unwrap();
let mr2 = service.merge_request(REPO_NAME, MR_ID_OTHER).unwrap();
test.test_mr(&mr).unwrap();
test.test_mr(&mr2).unwrap();
test.clear_all_mrs().unwrap();
assert!(!test_ref_exists(&ctx, "test-topics", mr.id));
assert!(!test_ref_exists(&ctx, "test-topics", mr2.id));
let mr_comments = service.mr_comments(mr.id);
let mr_commit_statuses = service.commit_statuses(&mr.commit.id);
let mr2_commit_comments = service.mr_comments(mr2.id);
let mr2_commit_statuses = service.commit_statuses(&mr2.commit.id);
assert_eq!(service.remaining_data(), 0);
assert_eq!(mr_comments.len(), 1);
assert_eq!(mr_comments[0], "This topic has been pushed for testing.");
assert_eq!(mr2_commit_comments.len(), 1);
assert_eq!(
mr2_commit_comments[0],
"This topic has been pushed for testing.",
);
assert_eq!(mr_commit_statuses.len(), 2);
check_commit_status(&mr_commit_statuses[0], &mr, TestRefState::Pushed);
check_commit_status(&mr_commit_statuses[1], &mr, TestRefState::Removed);
assert_eq!(mr2_commit_statuses.len(), 2);
check_commit_status(&mr2_commit_statuses[0], &mr2, TestRefState::Pushed);
check_commit_status(&mr2_commit_statuses[1], &mr2, TestRefState::Removed);
}
#[test]
fn test_clear_all_invalid() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let service = test_refs_service();
let test = create_test(&ctx, &service);
let id = "abc";
let update_ref = ctx
.git()
.arg("update-ref")
.arg(format!("refs/test-topics/{id}"))
.arg("HEAD")
.output()
.unwrap();
if !update_ref.status.success() {
panic!(
"update-ref failed: {}",
String::from_utf8_lossy(&update_ref.stderr),
);
}
test.clear_all_mrs().unwrap();
assert!(!test_ref_exists(&ctx, "test-topics", id));
assert_eq!(service.remaining_data(), 0);
}
#[test]
fn test_clear_all_no_mr() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let service = test_refs_service();
let test = create_test(&ctx, &service);
let id = 100;
let update_ref = ctx
.git()
.arg("update-ref")
.arg(format!("refs/test-topics/{id}"))
.arg("HEAD")
.output()
.unwrap();
if !update_ref.status.success() {
panic!(
"update-ref failed: {}",
String::from_utf8_lossy(&update_ref.stderr),
);
}
test.clear_all_mrs().unwrap();
assert!(!test_ref_exists(&ctx, "test-topics", id));
assert_eq!(service.remaining_data(), 0);
}