use super::*;
#[test]
fn builds_subtree_plan_from_registry() {
let plan = build_backup_plan(BackupPlanBuildInput {
selected_canister_id: Some(APP.to_string()),
selected_scope_kind: BackupScopeKind::Subtree,
registry: ®istry(),
..plan_input()
})
.expect("build subtree plan");
assert_eq!(plan.selected_subtree_root.as_deref(), Some(APP));
assert!(!plan.root_included);
assert_eq!(
plan.targets
.iter()
.map(|target| target.canister_id.as_str())
.collect::<Vec<_>>(),
vec![APP, WORKER]
);
assert!(
plan.phases
.iter()
.all(|phase| phase.target_canister_id.as_deref() != Some(ROOT))
);
assert_operation_order(
&plan,
&[
("validate-topology", None),
("validate-control-authority", None),
("validate-snapshot-read-authority", None),
("validate-quiescence-policy", None),
("stop-renrk-eyaaa-aaaaa-aaada-cai", Some(APP)),
("stop-rno2w-sqaaa-aaaaa-aaacq-cai", Some(WORKER)),
("snapshot-renrk-eyaaa-aaaaa-aaada-cai", Some(APP)),
("snapshot-rno2w-sqaaa-aaaaa-aaacq-cai", Some(WORKER)),
("start-rno2w-sqaaa-aaaaa-aaacq-cai", Some(WORKER)),
("start-renrk-eyaaa-aaaaa-aaada-cai", Some(APP)),
],
);
}
#[test]
fn builds_root_omitted_deployment_plan_without_root_target() {
let plan = build_backup_plan(BackupPlanBuildInput {
selected_canister_id: None,
selected_scope_kind: BackupScopeKind::NonRootDeployment,
registry: ®istry(),
..plan_input()
})
.expect("build root-omitted deployment plan");
assert_eq!(plan.selected_subtree_root, None);
assert!(!plan.root_included);
assert_eq!(
plan.targets
.iter()
.map(|target| target.canister_id.as_str())
.collect::<Vec<_>>(),
vec![APP, WORKER]
);
}
#[test]
fn builder_rejects_root_subtree_without_maintenance() {
let err = build_backup_plan(BackupPlanBuildInput {
selected_canister_id: Some(ROOT.to_string()),
selected_scope_kind: BackupScopeKind::Subtree,
registry: ®istry(),
..plan_input()
})
.expect_err("normal root subtree should reject");
std::assert_matches!(err, BackupPlanError::RootIncludedWithoutMaintenance);
}
#[test]
fn resolves_principal_and_role_selectors() {
let registry = registry();
assert_eq!(
resolve_backup_selector(®istry, APP).expect("resolve principal"),
APP
);
assert_eq!(
resolve_backup_selector(®istry, "app").expect("resolve role"),
APP
);
}
#[test]
fn rejects_ambiguous_role_selector() {
let mut registry = registry();
registry.push(RegistryEntry {
pid: OTHER_WORKER.to_string(),
role: Some("worker".to_string()),
kind: Some("replica".to_string()),
parent_pid: Some(APP.to_string()),
module_hash: None,
});
let err =
resolve_backup_selector(®istry, "worker").expect_err("ambiguous role should reject");
std::assert_matches!(
err,
BackupPlanError::AmbiguousSelector { selector, matches }
if selector == "worker" && matches == vec![WORKER.to_string(), OTHER_WORKER.to_string()]
);
}
#[test]
fn rejects_unknown_selector() {
let err =
resolve_backup_selector(®istry(), "missing-role").expect_err("missing selector rejects");
assert!(
matches!(err, BackupPlanError::UnknownSelector(selector) if selector == "missing-role")
);
}