131 lines
4.3 KiB
Rust
131 lines
4.3 KiB
Rust
use metacrate_grid_agent::{
|
|
ACCEPTANCE_SCHEMA_VERSION, ACCEPTANCE_SECRET_CANARY, AcceptanceBudgets,
|
|
AcceptanceEvidenceWriter, AcceptanceRecord, AcceptanceStatus, LiveGridOptIns,
|
|
run_deterministic_acceptance,
|
|
};
|
|
use std::collections::BTreeMap;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
fn temporary(name: &str) -> PathBuf {
|
|
std::env::temp_dir().join(format!(
|
|
"metacrate-acceptance-{}-{name}",
|
|
SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_nanos()
|
|
))
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn deterministic_gate_writes_ordered_redacted_evidence_and_drains() {
|
|
let path = temporary("gate.jsonl");
|
|
run_deterministic_acceptance(&path, AcceptanceBudgets::default())
|
|
.await
|
|
.expect("acceptance gate");
|
|
let text = fs::read_to_string(&path).expect("evidence");
|
|
assert!(!text.contains(ACCEPTANCE_SECRET_CANARY));
|
|
let records = text
|
|
.lines()
|
|
.map(|line| serde_json::from_str::<AcceptanceRecord>(line).expect("record"))
|
|
.collect::<Vec<_>>();
|
|
assert_eq!(records.len(), 7);
|
|
assert!(
|
|
records
|
|
.iter()
|
|
.all(|record| record.status == AcceptanceStatus::Passed)
|
|
);
|
|
assert_eq!(
|
|
records
|
|
.iter()
|
|
.map(|record| record.sequence)
|
|
.collect::<Vec<_>>(),
|
|
vec![1, 2, 3, 4, 5, 6, 7]
|
|
);
|
|
for required in [
|
|
"headless_startup",
|
|
"control_conformance",
|
|
"chat_scheduling",
|
|
"clean_shutdown",
|
|
"maintenance_reconnect",
|
|
"policy_redaction_and_protocol_audit",
|
|
] {
|
|
assert!(records.iter().any(|record| record.stage == required));
|
|
}
|
|
assert!(
|
|
run_deterministic_acceptance(&path, AcceptanceBudgets::default())
|
|
.await
|
|
.is_err(),
|
|
"evidence is never overwritten"
|
|
);
|
|
fs::remove_file(path).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn live_actions_require_exact_independent_confirmations() {
|
|
let mut values = BTreeMap::new();
|
|
values.insert("METACRATE_AGENT_LIVE_CHAT_IM", "CHAT-IM");
|
|
let action_without_login =
|
|
LiveGridOptIns::from_environment(|name| values.get(name).map(ToString::to_string));
|
|
assert!(action_without_login.validate().is_err());
|
|
values.insert("METACRATE_AGENT_LIVE_LOGIN", "LOGIN");
|
|
values.insert("METACRATE_AGENT_LIVE_BUILD", "wrong");
|
|
let confirmed =
|
|
LiveGridOptIns::from_environment(|name| values.get(name).map(ToString::to_string))
|
|
.validate()
|
|
.unwrap();
|
|
assert!(confirmed.login && confirmed.chat_and_im);
|
|
assert!(!confirmed.reversible_build);
|
|
}
|
|
|
|
#[test]
|
|
fn evidence_writer_rejects_secret_markers_and_schema_is_committed() {
|
|
let path = temporary("unsafe.jsonl");
|
|
let mut writer = AcceptanceEvidenceWriter::create(&path).unwrap();
|
|
let record = AcceptanceRecord {
|
|
schema_version: ACCEPTANCE_SCHEMA_VERSION,
|
|
sequence: 1,
|
|
unix_millis: 0,
|
|
package_version: "test".into(),
|
|
source_revision: "test".into(),
|
|
rust_toolchain: "test".into(),
|
|
command: "test".into(),
|
|
profile: "test".into(),
|
|
grid_type: "test".into(),
|
|
endpoint_capabilities: Vec::new(),
|
|
stage: "canary".into(),
|
|
status: AcceptanceStatus::Failed,
|
|
duration_millis: 0,
|
|
budget_millis: None,
|
|
outcome_code: ACCEPTANCE_SECRET_CANARY.into(),
|
|
evidence_sha256: "0".repeat(64),
|
|
metrics: serde_json::Map::new(),
|
|
limitations: Vec::new(),
|
|
};
|
|
assert!(writer.write(&record).is_err());
|
|
drop(writer);
|
|
fs::remove_file(path).unwrap();
|
|
|
|
let workspace = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
|
|
let schema =
|
|
fs::read_to_string(workspace.join("config/grid-agent-acceptance-evidence.schema.json"))
|
|
.unwrap();
|
|
let schema: serde_json::Value = serde_json::from_str(&schema).unwrap();
|
|
assert_eq!(
|
|
schema["properties"]["schema_version"]["const"],
|
|
ACCEPTANCE_SCHEMA_VERSION
|
|
);
|
|
let report = fs::read_to_string(workspace.join("docs/grid-agent-acceptance.md")).unwrap();
|
|
for required in [
|
|
"Deterministic gate",
|
|
"Live-grid matrix",
|
|
"Resource budgets",
|
|
"Remaining limitations",
|
|
"#128",
|
|
"#134",
|
|
] {
|
|
assert!(report.contains(required), "report missing {required}");
|
|
}
|
|
}
|