use std::path::Path;
use kernel::{Address, AxCode, Seq};
use super::*;
use crate::assembly::{hands, init_city};
use accounting::worker::RunWorker;
fn break_the_first_line(city_root: &Path) {
let first = storage::ledger_segments_at(&kernel::layout::CityLayout::new(city_root).ledger())
.unwrap()
.remove(0);
let mut bytes = std::fs::read(&first).unwrap();
let at = bytes
.windows(4)
.position(|held| held == b"\"t\":")
.and_then(|key| key.checked_add(4))
.unwrap();
bytes[at] = if bytes[at] == b'1' { b'2' } else { b'1' };
std::fs::write(&first, bytes).unwrap();
}
#[test]
fn a_chain_broken_under_a_served_city_refuses_the_next_command_with_the_audits_reason() {
let dir = tempfile::tempdir().unwrap();
init_city(dir.path()).unwrap();
let mut worker = RunWorker::new(
dir.path(),
Diagnostics::off(),
hands(gateway::Custodian::in_memory()),
)
.unwrap();
break_the_first_line(dir.path());
let storage::ChainAudit::Broken(reason) =
storage::audit_chain(&kernel::layout::CityLayout::new(dir.path()).ledger()).unwrap()
else {
panic!("the tampered ledger still audits whole");
};
audit_in_background(
worker.chain_under_audit(storage::ChainHalt::awaiting_proof()),
Diagnostics::off(),
monotonic_now(),
)
.unwrap()
.join()
.unwrap();
let refused = worker
.handle(wire::Command::CreateBuilding {
addr: Address::parse("lab").unwrap(),
template: wire::TemplateName::parse("minimal").unwrap(),
idem: kernel::IdemKey::derive(&RunId::CITY, Seq::FIRST, b"create"),
})
.err();
assert_eq!(refused, Some(reason));
}
#[test]
fn an_audit_that_cannot_read_the_ledger_trips_the_halt() {
let dir = tempfile::tempdir().unwrap();
let missing = dir.path().join("no-ledger-here");
let reason = storage::audit_chain(&missing).unwrap_err().into_ax();
let halt = storage::ChainHalt::awaiting_proof();
report_proof(
ChainUnderAudit {
halt: halt.clone(),
ledger_dir: missing.clone(),
at: Seq::FIRST,
records: storage::ProofRecords::read_only(&dir.path().join("records")),
},
Diagnostics::off(),
monotonic_now(),
);
assert_eq!(halt.reason(), Some(reason));
}
#[test]
fn a_served_worker_takes_commands_once_its_history_is_proved() {
let dir = tempfile::tempdir().unwrap();
init_city(dir.path()).unwrap();
let mut worker = RunWorker::new(
dir.path(),
Diagnostics::off(),
hands(gateway::Custodian::in_memory()),
)
.unwrap();
let create = |name: &str| wire::Command::CreateBuilding {
addr: Address::parse(name).unwrap(),
template: wire::TemplateName::parse("minimal").unwrap(),
idem: kernel::IdemKey::derive(&RunId::CITY, Seq::FIRST, name.as_bytes()),
};
let watch = worker.chain_under_audit(storage::ChainHalt::awaiting_proof());
let before = worker
.handle(create("lab"))
.map_err(|refused| *refused.code());
audit_in_background(watch, Diagnostics::off(), monotonic_now())
.unwrap()
.join()
.unwrap();
let after = worker.handle(create("lab")).map(|_| ());
assert_eq!(
(before.err(), after),
(Some(AxCode::HistoryUnproven), Ok(()))
);
}