use super::*;
#[test]
fn install_truth_gate_persists_machine_readable_receipt() {
let root = temp_dir("canic-install-truth-receipt-json");
let config_path = root.join("apps/demo/canic.toml");
fs::create_dir_all(config_path.parent().expect("config parent")).expect("create config dir");
fs::write(
&config_path,
demo_config_source(
r"
",
),
)
.expect("write config");
write_wasm_gz_artifact(&root, "root", b"root-artifact");
write_local_network_authority(&root, "local");
let options = local_demo_install_options(&root);
let check = current_install_deployment_truth_check_at(
&options,
&root,
&root,
&config_path,
"demo",
"2026-05-22T00:00:00Z".to_string(),
)
.expect("deployment truth check");
let receipt = install_deployment_truth_gate_receipt(
&check,
"unix:1770000000".to_string(),
vec![artifact_gate_phase_receipt(
&check,
"unix:1770000000",
Some("unix:1770000001".to_string()),
)],
artifact_gate_role_phase_receipts(&check),
);
let fleet = sample_fleet_key();
let path = write_install_deployment_truth_receipt(&root, fleet, &receipt)
.expect("write deployment truth receipt");
let expected_path = install_deployment_truth_receipt_path(&root, fleet, &receipt);
assert_eq!(path, expected_path);
assert_eq!(
path.parent(),
Some(sample_fleet_receipt_dir(&root).as_path())
);
assert!(
path.file_name()
.and_then(|name| name.to_str())
.is_some_and(|name| {
!name.contains(':')
&& Path::new(name)
.extension()
.is_some_and(|ext| ext.eq_ignore_ascii_case("json"))
}),
"unexpected receipt path: {}",
path.display()
);
let decoded: DeploymentReceiptV1 =
serde_json::from_slice(&fs::read(&path).expect("read receipt")).expect("decode receipt");
assert_eq!(decoded, receipt);
fs::remove_dir_all(root).expect("clean temp dir");
}
#[test]
fn install_truth_phase_receipt_records_emit_manifest_evidence() {
let root = temp_dir("canic-install-truth-emit-manifest-receipt");
let config_path = root.join("apps/demo/canic.toml");
fs::create_dir_all(config_path.parent().expect("config parent")).expect("create config dir");
fs::write(
&config_path,
r#"
controllers = []
[app]
name = "demo"
init_mode = "enabled"
[roles.root]
kind = "root"
package = "root"
[roles.app]
kind = "canister"
package = "app"
[roles.project_registry]
kind = "canister"
package = "project_registry"
[roles.oracle_pokemon]
kind = "canister"
package = "oracle_pokemon"
[roles.user_hub]
kind = "canister"
package = "user_hub"
[roles.user_shard]
kind = "canister"
package = "user_shard"
[roles.scale_hub]
kind = "canister"
package = "scale_hub"
[roles.scale_replica]
kind = "canister"
package = "scale"
[roles.role_baseline]
kind = "canister"
package = "role_baseline"
[roles.worker]
kind = "canister"
package = "worker"
[app.whitelist]
"#,
)
.expect("write config");
write_wasm_gz_artifact(&root, "root", b"root-artifact");
write_local_network_authority(&root, "local");
let options = InstallRootOptions {
root_canister: "root".to_string(),
root_build_target: "root".to_string(),
environment: "local".to_string(),
fleet_name: "demo".to_string(),
icp_root: Some(root.clone()),
build_profile: Some(CanisterBuildProfile::Fast),
config_path: Some("apps/demo/canic.toml".to_string()),
fleet_install_input_path: None,
expected_app: Some("demo".to_string()),
interactive_config_selection: false,
deployment_plan_override: None,
};
let check = current_install_deployment_truth_check_at(
&options,
&root,
&root,
&config_path,
"demo",
"2026-05-22T00:00:00Z".to_string(),
)
.expect("deployment truth check");
let receipt = install_deployment_truth_phase_receipt(
&check,
InstallPhaseLabel::EMIT_MANIFEST,
"unix:1770000002".to_string(),
Some("unix:1770000003".to_string()),
"emit root release-set manifest",
ObservationStatusV1::Observed,
vec!["manifest_path:/tmp/manifest.json".to_string()],
);
assert_eq!(
receipt.operation_status,
DeploymentExecutionStatusV1::Complete
);
assert_eq!(receipt.operation_id, "local:local:demo:check:emit_manifest");
assert_eq!(receipt.phase_receipts.len(), 1);
assert_eq!(receipt.phase_receipts[0].phase, "emit_manifest");
assert_eq!(
receipt.phase_receipts[0].verified_postcondition.status,
ObservationStatusV1::Observed
);
assert_eq!(
receipt.phase_receipts[0].verified_postcondition.evidence,
vec!["manifest_path:/tmp/manifest.json".to_string()]
);
fs::remove_dir_all(root).expect("clean temp dir");
}
#[test]
fn install_truth_completed_phase_receipt_records_pre_gate_evidence() {
let (root, check) = demo_install_deployment_truth_check("canic-install-truth-pre-gate-phase");
let execution_context = current_install_execution_context(&root, &root, "local");
let scope = InstallReceiptScope {
icp_root: &root,
fleet: sample_fleet_key(),
check: &check,
execution_context: Some(&execution_context),
};
let path = write_completed_install_phase_receipt(
scope,
CompletedInstallPhase {
phase: InstallPhaseLabel::BUILD_ARTIFACTS,
attempted_action: "build configured install targets",
started_at: "unix:1770000004".to_string(),
finished_at: Some("unix:1770000005".to_string()),
evidence: vec!["build_target:root".to_string()],
role_names: vec!["root".to_string()],
},
)
.expect("write completed phase receipt");
let receipt: DeploymentReceiptV1 =
serde_json::from_slice(&fs::read(path).expect("read receipt")).expect("decode receipt");
assert_eq!(
receipt.operation_id,
"local:local:demo:check:build_artifacts"
);
assert_eq!(
receipt.operation_status,
DeploymentExecutionStatusV1::Complete
);
assert_eq!(receipt.phase_receipts[0].phase, "build_artifacts");
assert_eq!(
receipt.phase_receipts[0].verified_postcondition.evidence,
vec!["build_target:root".to_string()]
);
assert_eq!(receipt.role_phase_receipts.len(), 1);
assert_eq!(receipt.role_phase_receipts[0].role, "root");
assert_eq!(receipt.role_phase_receipts[0].phase, "build_artifacts");
assert_eq!(
receipt.role_phase_receipts[0].result,
crate::deployment_truth::RolePhaseResultV1::Applied
);
let execution_context = receipt
.execution_context
.expect("completed phase receipt should include execution context");
assert_eq!(
execution_context.backend,
crate::deployment_truth::DeploymentExecutorBackendV1::CurrentCli
);
assert!(
execution_context
.artifact_roots
.iter()
.any(|root| { root.ends_with(".icp/local/canisters") })
);
fs::remove_dir_all(root).expect("clean temp dir");
}
#[test]
fn install_truth_latest_receipt_uses_newest_persisted_receipt() {
let root = temp_dir("canic-install-truth-latest-receipt");
let receipt_dir = sample_fleet_receipt_dir(&root);
fs::create_dir_all(&receipt_dir).expect("create receipt dir");
let older = receipt_dir.join("unix_100-local_demo_check_materialize_artifacts.json");
let newer = receipt_dir.join("unix_200-local_demo_check_materialize_artifacts.json");
let ignored = receipt_dir.join("unix_300-local_demo_check_materialize_artifacts.txt");
fs::write(&older, "{}").expect("write older receipt");
fs::write(&newer, "{}").expect("write newer receipt");
fs::write(ignored, "{}").expect("write ignored file");
let latest = latest_deployment_truth_receipt_path_from_root(&root, sample_fleet_key())
.expect("latest receipt")
.expect("receipt exists");
assert_eq!(latest, newer);
fs::remove_dir_all(root).expect("clean temp dir");
}