use serde_json::{Map, Value};
use super::*;
use crate::event::Payload;
fn bytes(payload: &Payload) -> String {
serde_json::to_string(payload).unwrap()
}
fn job() -> Locator {
Locator::parse("file:sim/lobby/room1@8ea61adc1c8339d02a5c665ba8280a85151ee549").unwrap()
}
fn hand_written_started(
task: &str,
goal: &str,
parent: Option<RunId>,
predecessor: Option<RunId>,
) -> Payload {
let mut started = Map::new();
started.insert("task".to_owned(), Value::String(task.to_owned()));
started.insert("goal".to_owned(), Value::String(goal.to_owned()));
started.insert("job".to_owned(), Value::String(job().to_string()));
if let Some(parent) = parent {
started.insert("parent".to_owned(), Value::String(parent.to_string()));
}
if let Some(predecessor) = predecessor {
started.insert(
"predecessor".to_owned(),
Value::String(predecessor.to_string()),
);
}
started.insert(
"skills".to_owned(),
Value::Array(
[
("read", B3Hash::digest(b"read")),
("edit", B3Hash::digest(b"edit")),
]
.into_iter()
.map(|(name, hash)| {
let mut row = Map::new();
row.insert("name".to_owned(), Value::String(name.to_owned()));
row.insert("hash".to_owned(), Value::String(hash.to_string()));
Value::Object(row)
})
.collect(),
),
);
Payload::new(started).unwrap()
}
fn typed_started(parent: Option<RunId>, predecessor: Option<RunId>) -> RunStarted {
RunStarted {
task: "walk two waves".to_owned(),
goal: "let an intervention land between them".to_owned(),
job: Some(job()),
parent,
predecessor,
dispatched_by: None,
policy: None,
naming: None,
opening: None,
effort: None,
agent: None,
skills: vec![
SkillPin {
name: "read".to_owned(),
hash: B3Hash::digest(b"read"),
},
SkillPin {
name: "edit".to_owned(),
hash: B3Hash::digest(b"edit"),
},
],
}
}
#[test]
fn a_bare_dispatch_writes_the_bytes_the_hand_written_map_wrote() {
let old = hand_written_started(
"walk two waves",
"let an intervention land between them",
None,
None,
);
let new = Payload::of(&typed_started(None, None)).unwrap();
assert_eq!(bytes(&new), bytes(&old));
assert!(!bytes(&new).contains("parent"), "{}", bytes(&new));
assert!(!bytes(&new).contains("predecessor"), "{}", bytes(&new));
}
#[test]
fn a_fork_and_a_succession_add_exactly_their_own_key() {
let parent = RunId::from_bytes([7u8; 16]);
let predecessor = RunId::from_bytes([9u8; 16]);
for (parent, predecessor) in [
(Some(parent), None),
(None, Some(predecessor)),
(Some(parent), Some(predecessor)),
] {
let old = hand_written_started(
"walk two waves",
"let an intervention land between them",
parent,
predecessor,
);
let new = Payload::of(&typed_started(parent, predecessor)).unwrap();
assert_eq!(bytes(&new), bytes(&old));
}
}
#[test]
fn the_golden_fixture_line_reads_back_and_writes_itself_again() {
let raw = r#"{"goal":"let an intervention land between them","job":"file:sim/lobby/room1@8ea61adc1c8339d02a5c665ba8280a85151ee549","skills":[],"task":"walk two waves"}"#;
let payload: Payload = serde_json::from_str(raw).unwrap();
let read: RunStarted = payload.read().unwrap();
assert_eq!(read.task, "walk two waves");
assert_eq!(read.job, Some(job()));
assert!(read.skills.is_empty());
assert_eq!(bytes(&Payload::of(&read).unwrap()), raw);
}
#[test]
fn a_run_started_line_records_its_policy() {
use crate::model::{AdmissionRequirement, LandingPolicy, Mode};
let started = RunStarted {
policy: Some(RunPolicy {
mode: Mode::Work,
write: crate::WriteLimit::Create,
admit: AdmissionRequirement::Tested,
landing: LandingPolicy::Experiment,
}),
..typed_started(None, None)
};
let payload = Payload::of(&started).unwrap();
assert_eq!(
serde_json::to_value(&payload).unwrap()["policy"],
serde_json::json!({
"mode": "work",
"write": "create",
"admit": "tested",
"landing": "experiment",
})
);
assert_eq!(payload.read::<RunStarted>().unwrap(), started);
}
#[test]
fn a_run_started_line_records_the_naming_it_froze() {
let naming = B3Hash::digest(b"{\"person\":\"2youg1\"}");
let started = RunStarted {
naming: Some(naming),
..typed_started(None, None)
};
let payload = Payload::of(&started).unwrap();
assert_eq!(
serde_json::to_value(&payload).unwrap()["naming"],
serde_json::json!(naming.to_string())
);
assert_eq!(payload.read::<RunStarted>().unwrap(), started);
let bare = Payload::of(&typed_started(None, None)).unwrap();
assert!(!bytes(&bare).contains("naming"), "{}", bytes(&bare));
}
#[test]
fn a_run_started_line_records_the_effort_it_froze() {
let started = RunStarted {
effort: Some(crate::model::Effort::High),
..typed_started(None, None)
};
let payload = Payload::of(&started).unwrap();
assert_eq!(
serde_json::to_value(&payload).unwrap()["effort"],
serde_json::json!("high")
);
assert_eq!(payload.read::<RunStarted>().unwrap(), started);
let bare = Payload::of(&typed_started(None, None)).unwrap();
assert!(!bytes(&bare).contains("effort"), "{}", bytes(&bare));
}
#[test]
fn an_empty_payload_reads_as_a_run_that_said_nothing() {
let read: RunStarted = Payload::empty().read().unwrap();
assert_eq!(read, RunStarted::default());
assert_eq!(read.job, None);
}
#[test]
fn an_unknown_key_passes_so_a_newer_writer_does_not_break_this_reader() {
let payload: Payload = serde_json::from_str(r#"{"task":"t","mood":"brisk"}"#).unwrap();
let read: RunStarted = payload.read().unwrap();
assert_eq!(read.task, "t");
}
#[test]
fn a_fork_writes_the_two_keys_runtime_fork_wrote() {
let from = RunId::from_bytes([3u8; 16]);
let mut map = Map::new();
map.insert("from".to_owned(), Value::String(from.to_string()));
map.insert("at_seq".to_owned(), Value::from(Seq::new(41).value()));
let old = Payload::new(map).unwrap();
let new = Payload::of(&RunForked {
from,
at_seq: Seq::new(41),
})
.unwrap();
assert_eq!(bytes(&new), bytes(&old));
let back: RunForked = new.read().unwrap();
assert_eq!(back.from, from);
assert_eq!(back.at_seq, Seq::new(41));
}
fn hand_written_frozen(completion: &Completion) -> Payload {
let mut map = Map::new();
completion.extend_payload(&mut map).unwrap();
Payload::new(map).unwrap()
}
fn cited(kind: EventKind) -> crate::EventRef {
let draft = crate::EventDraft {
run: RunId::CITY,
t: crate::TimeMs::new(0),
who: "city".to_owned(),
addr: None,
kind,
data: Payload::empty(),
ig: false,
};
crate::EventRecord::from_draft(draft, Seq::new(12), crate::ledger::GENESIS_PREV).to_ref()
}
#[test]
fn a_finished_run_writes_the_bytes_completion_extend_payload_wrote() {
let done = Completion::Done(
crate::Evidence::new(vec![
cited(EventKind::ModelReturned),
cited(EventKind::ToolResult),
])
.unwrap(),
);
let new = Payload::of(&RunFrozen::of(&done)).unwrap();
assert_eq!(bytes(&new), bytes(&hand_written_frozen(&done)));
let back: RunFrozen = new.read().unwrap();
assert_eq!(back.completion, "done");
assert_eq!(
back.evidence
.unwrap_or_default()
.first()
.map(|one| one.kind),
Some(EventKind::ModelReturned)
);
}
#[test]
fn the_two_endings_that_cite_nothing_leave_the_key_absent() {
for completion in [Completion::Limit, Completion::Cancelled] {
let new = Payload::of(&RunFrozen::of(&completion)).unwrap();
assert_eq!(bytes(&new), bytes(&hand_written_frozen(&completion)));
assert!(!bytes(&new).contains("evidence"), "{}", bytes(&new));
}
}
#[test]
fn an_eval_line_keeps_the_bytes_its_ledger_already_holds() {
let predecessor = RunId::from_bytes([5u8; 16]);
let wire = format!(
"{{\"after\":[\"a\",\"\"],\"before\":[\"a\",\"b\"],\"kept\":1,\"lost\":[1],\"predecessor\":\"{predecessor}\",\"probe\":\"handoff\",\"version\":2}}"
);
let payload = Payload::of(&EvalRun {
probe: "handoff".to_owned(),
version: 2,
predecessor,
kept: 1,
lost: vec![1],
before: vec!["a".to_owned(), "b".to_owned()],
after: vec!["a".to_owned(), String::new()],
})
.unwrap();
assert_eq!(bytes(&payload), wire);
assert_eq!(serde_json::from_str::<Payload>(&wire).unwrap(), payload);
}