[+] Track repo deletions
This commit is contained in:
@@ -255,6 +255,105 @@ fn uninstall_webhook_deletes_matching_github_hook() {
|
||||
handle.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_repo_deletes_github_repo() {
|
||||
let (api_url, handle) = one_request_server("204 No Content", "", |request| {
|
||||
assert!(
|
||||
request.starts_with("DELETE /repos/alice/repo "),
|
||||
"request was {request}"
|
||||
);
|
||||
assert!(
|
||||
request
|
||||
.to_ascii_lowercase()
|
||||
.contains("authorization: bearer secret"),
|
||||
"request was {request}"
|
||||
);
|
||||
});
|
||||
let site = SiteConfig {
|
||||
api_url: Some(api_url),
|
||||
..site(ProviderKind::Github, None)
|
||||
};
|
||||
|
||||
ProviderClient::new(&site)
|
||||
.unwrap()
|
||||
.delete_repo(
|
||||
&EndpointConfig {
|
||||
site: "github".to_string(),
|
||||
kind: NamespaceKind::User,
|
||||
namespace: "alice".to_string(),
|
||||
},
|
||||
"repo",
|
||||
)
|
||||
.unwrap();
|
||||
handle.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_repo_deletes_url_encoded_gitlab_project() {
|
||||
let (api_url, handle) = one_request_server("202 Accepted", "", |request| {
|
||||
assert!(
|
||||
request.starts_with("DELETE /projects/parent%2Falice%2Frepo "),
|
||||
"request was {request}"
|
||||
);
|
||||
assert!(
|
||||
request
|
||||
.to_ascii_lowercase()
|
||||
.contains("private-token: secret"),
|
||||
"request was {request}"
|
||||
);
|
||||
});
|
||||
let site = SiteConfig {
|
||||
api_url: Some(api_url),
|
||||
..site(ProviderKind::Gitlab, None)
|
||||
};
|
||||
|
||||
ProviderClient::new(&site)
|
||||
.unwrap()
|
||||
.delete_repo(
|
||||
&EndpointConfig {
|
||||
site: "gitlab".to_string(),
|
||||
kind: NamespaceKind::Group,
|
||||
namespace: "parent/alice".to_string(),
|
||||
},
|
||||
"repo",
|
||||
)
|
||||
.unwrap();
|
||||
handle.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_repo_deletes_gitea_repo() {
|
||||
let (api_url, handle) = one_request_server("204 No Content", "", |request| {
|
||||
assert!(
|
||||
request.starts_with("DELETE /repos/alice/repo "),
|
||||
"request was {request}"
|
||||
);
|
||||
assert!(
|
||||
request
|
||||
.to_ascii_lowercase()
|
||||
.contains("authorization: token secret"),
|
||||
"request was {request}"
|
||||
);
|
||||
});
|
||||
let site = SiteConfig {
|
||||
api_url: Some(api_url),
|
||||
..site(ProviderKind::Gitea, None)
|
||||
};
|
||||
|
||||
ProviderClient::new(&site)
|
||||
.unwrap()
|
||||
.delete_repo(
|
||||
&EndpointConfig {
|
||||
site: "gitea".to_string(),
|
||||
kind: NamespaceKind::User,
|
||||
namespace: "alice".to_string(),
|
||||
},
|
||||
"repo",
|
||||
)
|
||||
.unwrap();
|
||||
handle.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn open_pull_request_posts_github_pull_when_missing() {
|
||||
let (api_url, handle) = request_server(
|
||||
|
||||
@@ -174,6 +174,140 @@ fn branch_deletion_decisions_ignore_internal_conflict_branches() {
|
||||
assert!(blocked.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repo_deletion_decision_propagates_previous_synced_repo_deletion() {
|
||||
let mirror = test_mirror();
|
||||
let mut previous = BTreeMap::new();
|
||||
previous.insert(
|
||||
"github_alice".to_string(),
|
||||
remote_ref_state("a", &[("main", "111")]),
|
||||
);
|
||||
previous.insert(
|
||||
"gitea_alice".to_string(),
|
||||
remote_ref_state("b", &[("main", "111")]),
|
||||
);
|
||||
let mut current = BTreeMap::new();
|
||||
current.insert(
|
||||
"gitea_alice".to_string(),
|
||||
remote_ref_state("b", &[("main", "111")]),
|
||||
);
|
||||
|
||||
let decision = repo_deletion_decision(
|
||||
&mirror,
|
||||
&[endpoint_repo("gitea")],
|
||||
Some(&previous),
|
||||
¤t,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
decision,
|
||||
RepoDeletionDecision::Propagate {
|
||||
deleted_remotes: vec!["github_alice".to_string()],
|
||||
target_remotes: vec!["gitea_alice".to_string()],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repo_deletion_decision_conflicts_when_remaining_repo_changed() {
|
||||
let mirror = test_mirror();
|
||||
let mut previous = BTreeMap::new();
|
||||
previous.insert(
|
||||
"github_alice".to_string(),
|
||||
remote_ref_state("a", &[("main", "111")]),
|
||||
);
|
||||
previous.insert(
|
||||
"gitea_alice".to_string(),
|
||||
remote_ref_state("b", &[("main", "111")]),
|
||||
);
|
||||
let mut current = BTreeMap::new();
|
||||
current.insert(
|
||||
"gitea_alice".to_string(),
|
||||
remote_ref_state("changed", &[("main", "222")]),
|
||||
);
|
||||
|
||||
let decision = repo_deletion_decision(
|
||||
&mirror,
|
||||
&[endpoint_repo("gitea")],
|
||||
Some(&previous),
|
||||
¤t,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
decision,
|
||||
RepoDeletionDecision::Conflict {
|
||||
deleted_remotes: vec!["github_alice".to_string()],
|
||||
changed_remotes: vec!["gitea_alice".to_string()],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repo_deletion_decision_removes_state_when_deleted_everywhere() {
|
||||
let mirror = test_mirror();
|
||||
let mut previous = BTreeMap::new();
|
||||
previous.insert(
|
||||
"github_alice".to_string(),
|
||||
remote_ref_state("a", &[("main", "111")]),
|
||||
);
|
||||
previous.insert(
|
||||
"gitea_alice".to_string(),
|
||||
remote_ref_state("b", &[("main", "111")]),
|
||||
);
|
||||
|
||||
let decision = repo_deletion_decision(&mirror, &[], Some(&previous), &BTreeMap::new());
|
||||
|
||||
assert_eq!(
|
||||
decision,
|
||||
RepoDeletionDecision::DeletedEverywhere {
|
||||
deleted_remotes: vec!["github_alice".to_string(), "gitea_alice".to_string()],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repo_deletion_decision_removes_partial_state_when_deleted_everywhere() {
|
||||
let mirror = test_mirror();
|
||||
let mut previous = BTreeMap::new();
|
||||
previous.insert(
|
||||
"gitea_alice".to_string(),
|
||||
remote_ref_state("b", &[("main", "111")]),
|
||||
);
|
||||
|
||||
let decision = repo_deletion_decision(&mirror, &[], Some(&previous), &BTreeMap::new());
|
||||
|
||||
assert_eq!(
|
||||
decision,
|
||||
RepoDeletionDecision::DeletedEverywhere {
|
||||
deleted_remotes: vec!["github_alice".to_string(), "gitea_alice".to_string()],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repo_deletion_decision_ignores_repos_not_previously_synced_everywhere() {
|
||||
let mirror = test_mirror();
|
||||
let mut previous = BTreeMap::new();
|
||||
previous.insert(
|
||||
"gitea_alice".to_string(),
|
||||
remote_ref_state("b", &[("main", "111")]),
|
||||
);
|
||||
let mut current = BTreeMap::new();
|
||||
current.insert(
|
||||
"gitea_alice".to_string(),
|
||||
remote_ref_state("b", &[("main", "111")]),
|
||||
);
|
||||
|
||||
let decision = repo_deletion_decision(
|
||||
&mirror,
|
||||
&[endpoint_repo("gitea")],
|
||||
Some(&previous),
|
||||
¤t,
|
||||
);
|
||||
|
||||
assert_eq!(decision, RepoDeletionDecision::None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conflict_branch_prefixes_are_reversible_not_slug_collisions() {
|
||||
let slash_branch = conflict_pr_branch_prefix("release/foo");
|
||||
@@ -219,3 +353,34 @@ fn test_remotes() -> Vec<RemoteSpec> {
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn test_mirror() -> MirrorConfig {
|
||||
MirrorConfig {
|
||||
name: "sync-1".to_string(),
|
||||
endpoints: vec![endpoint("github"), endpoint("gitea")],
|
||||
create_missing: true,
|
||||
visibility: crate::config::Visibility::Private,
|
||||
allow_force: false,
|
||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||
}
|
||||
}
|
||||
|
||||
fn endpoint(site: &str) -> EndpointConfig {
|
||||
EndpointConfig {
|
||||
site: site.to_string(),
|
||||
kind: crate::config::NamespaceKind::User,
|
||||
namespace: "alice".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn endpoint_repo(site: &str) -> EndpointRepo {
|
||||
EndpointRepo {
|
||||
endpoint: endpoint(site),
|
||||
repo: crate::provider::RemoteRepo {
|
||||
name: "repo".to_string(),
|
||||
clone_url: format!("https://{site}.invalid/alice/repo.git"),
|
||||
private: true,
|
||||
description: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user