use super::*;
use crate::fleet_ensure::{
generate::{
FreshEstateSeedRequest, initialize_fresh_estate_seed, tests::multi_component_source_toml,
},
workflow::readiness::{
FleetReadinessError, FleetReadinessRequest, ReadinessGenerationInputs, inspect,
},
};
use std::fs;
#[test]
fn completed_fresh_seed_rejects_before_build_or_network_and_explicit_inventory_passes() {
let root = crate::test_support::temp_dir("generation-preflight");
fs::create_dir_all(&root).unwrap();
let source = root.join("policy.toml");
let seed = root.join("seed.toml");
let operator = Principal::from_slice(&[20]);
let subnet = Principal::from_slice(&[21]).to_text();
let ledger = Principal::from_text(super::super::mainnet_cycles_ledger()).unwrap();
fs::write(
&source,
multi_component_source_toml(&operator.to_text(), &subnet, &subnet),
)
.unwrap();
initialize_fresh_estate_seed(&FreshEstateSeedRequest {
cycles_ledger: &ledger.to_text(),
management_creation_fee_cycles: 500_000_000_000,
source: &source,
seed: &seed,
})
.unwrap();
let request = FleetGenerationInputsRequest {
root: &root,
environment: "local",
fleet: "fleet",
source: &source,
seed: &seed,
};
validate_generation_inputs(&request, operator, ledger).unwrap();
assert!(!load(&request, None).unwrap().clean_reinstall);
let paths = EnsurePaths::under(&root, "local", "fleet");
fs::create_dir_all(paths.plan.parent().unwrap()).unwrap();
for name in [".DS_Store", "notes.txt", "plan.json.bak", ".plan.json.tmp"] {
fs::write(paths.plan.with_file_name(name), b"unrelated local metadata").unwrap();
}
validate_generation_inputs(&request, operator, ledger).unwrap();
assert!(!load(&request, None).unwrap().clean_reinstall);
write_completion(&paths);
let original_plan = fs::read(&paths.plan).unwrap();
let original_journal = fs::read(&paths.journal).unwrap();
let original_seed = fs::read(&seed).unwrap();
assert!(matches!(
validate_generation_inputs(&request, operator, ledger),
Err(FleetGenerateError::ResetRequiresExplicitInventory)
));
let readiness = FleetReadinessRequest {
workspace: &root,
environment: "local",
fleet: "fleet",
icp_executable: "must-not-run",
signing_identity: None,
operator,
cycles_ledger: ledger,
estimated_required_cycles: None,
desired: None,
conversion: None,
generation_inputs: Some(ReadinessGenerationInputs {
source: &source,
seed: &seed,
}),
};
assert!(
matches!(inspect(&readiness), Err(FleetReadinessError::GenerationInputs(error))
if matches!(*error, FleetGenerateError::ResetRequiresExplicitInventory))
);
assert_eq!(fs::read(&seed).unwrap(), original_seed);
assert_eq!(fs::read(&paths.plan).unwrap(), original_plan);
assert_eq!(fs::read(&paths.journal).unwrap(), original_journal);
assert!(!paths.lock.exists());
let mut inventory: EstateSeed = load_toml(&seed, "seed").unwrap();
inventory.fresh_estate = false;
inventory.coordinator = Principal::from_slice(&[30]).to_text();
inventory.roots[0].root = Principal::from_slice(&[31]).to_text();
inventory.roots[0].store = Principal::from_slice(&[32]).to_text();
inventory.roots[0].pool_imports = vec![Principal::from_slice(&[33]).to_text()];
fs::write(&seed, toml::to_string(&inventory).unwrap()).unwrap();
validate_generation_inputs(&request, operator, ledger).unwrap();
let current = load(&request, None).unwrap();
assert_reset_funding_projection(&request, &inventory, operator, ledger);
assert!(current.clean_reinstall);
assert_eq!(
current.initialization,
Some(BootstrapCoordinatorSelection::Initialize)
);
assert!(matches!(
validate_generation_inputs(&request, Principal::from_slice(&[40]), ledger),
Err(FleetGenerateError::Authority(_))
));
assert!(matches!(
validate_generation_inputs(&request, operator, Principal::from_slice(&[40])),
Err(FleetGenerateError::Authority(_))
));
inventory.roots[0].pool_imports = vec![inventory.coordinator.clone()];
fs::write(&seed, toml::to_string(&inventory).unwrap()).unwrap();
assert!(matches!(
validate_generation_inputs(&request, operator, ledger),
Err(FleetGenerateError::SeedTopology(_))
));
assert_eq!(fs::read(&paths.plan).unwrap(), original_plan);
assert_eq!(fs::read(&paths.journal).unwrap(), original_journal);
assert_unreadable_predecessor(&request, &paths, &mut inventory, &readiness);
fs::remove_dir_all(root).unwrap();
}
fn assert_unreadable_predecessor(
request: &FleetGenerationInputsRequest<'_>,
paths: &EnsurePaths,
inventory: &mut EstateSeed,
readiness: &FleetReadinessRequest<'_>,
) {
let operator = readiness.operator;
let ledger = readiness.cycles_ledger;
inventory.roots[0].pool_imports = vec![Principal::from_slice(&[33]).to_text()];
fs::write(request.seed, toml::to_string(&inventory).unwrap()).unwrap();
fs::write(
&paths.plan,
b"unfinished predecessor plan, not current JSON",
)
.unwrap();
fs::write(&paths.state, b"obsolete application schema").unwrap();
fs::write(&paths.journal, b"malformed predecessor journal").unwrap();
fs::write(
paths.plan.with_file_name("capacity-import.json"),
b"retained import",
)
.unwrap();
validate_generation_inputs(request, operator, ledger).unwrap();
assert_reset_funding_projection(request, inventory, operator, ledger);
assert!(load(request, None).unwrap().clean_reinstall);
assert!(
crate::fleet_ensure::workflow::clean_reinstall::selected(
request.root,
"local",
"fleet",
true,
false
)
.unwrap()
);
assert_eq!(
fs::read(&paths.plan).unwrap(),
b"unfinished predecessor plan, not current JSON"
);
assert!(matches!(
inspect(readiness),
Err(FleetReadinessError::Network(_))
));
}
fn write_completion(paths: &EnsurePaths) {
fs::create_dir_all(paths.plan.parent().unwrap()).unwrap();
let operation = "ab".repeat(32);
let digest = "cd".repeat(32);
fs::write(
&paths.plan,
serde_json::to_vec(&serde_json::json!({
"environment": "local", "fleet": "fleet", "operation_id": operation,
"plan_sha256": digest, "scope": "full",
}))
.unwrap(),
)
.unwrap();
fs::write(
&paths.journal,
serde_json::to_vec(&serde_json::json!({
"fleet": "fleet", "operation_id": operation, "plan_sha256": digest,
"completion": "converged", "effects": [{"state": "applied"}],
}))
.unwrap(),
)
.unwrap();
}
fn assert_reset_funding_projection(
request: &FleetGenerationInputsRequest<'_>,
inventory: &EstateSeed,
operator: Principal,
ledger: Principal,
) {
let targets = bootstrap_funding_targets(request, operator, ledger).unwrap();
assert!(matches!(
bootstrap_funding_targets(request, Principal::anonymous(), ledger),
Err(FleetGenerateError::Authority(_))
));
assert!(matches!(
bootstrap_funding_targets(request, operator, Principal::anonymous()),
Err(FleetGenerateError::Authority(_))
));
assert_eq!(
targets
.iter()
.map(|target| target.principal.as_str())
.collect::<Vec<_>>(),
[
&inventory.coordinator,
&inventory.roots[0].root,
&inventory.roots[0].store
]
);
assert!(targets.iter().all(|target| target.observation_burn_cycles
== super::super::GENERATED_RETAINED_MAXIMUM_OBSERVATION_BURN_CYCLES));
assert_eq!(targets[2].minimum_cycles, 0);
}