//! When sync began (M5c spec, section 5): the ledger's first line, `began`, once; a ledger read //! back that does not start with it, or has a second, is damaged; and conclusions matched by their //! peer as well as their workspace and text. Do not edit. #[path = "support/ledger.rs"] mod ledger; #[path = "support/tmp.rs"] mod tmp; use ledger::{ Home, TIME, began, began_line, conclude, decided, edit, line, sent, time, to_trusted, turn, }; use memoryd::honcho::ledger::{Action, Ledger, LedgerError, Workspace}; const POINTER: &str = "; see docs/runbook.md#honcho-ledger-damaged"; #[test] fn the_began_line() { assert_eq!( serde_json::to_string(&began()).unwrap(), format!(r#"{{"type":"began","time":"{TIME}"}}"#) ); assert_eq!( began_line(), format!("{{\"type\":\"began\",\"time\":\"{TIME}\"}}\n") ); } #[test] fn began_is_kept_and_read_back() { let home = Home::new("began-kept"); let fresh = home.open(); assert_eq!(fresh.began(), None); let mut ledger = home.begun(); assert_eq!(ledger.began(), Some(time())); ledger .append(decided(&turn("chat-1", 1), to_trusted("kyle"))) .unwrap(); assert_eq!(home.text().lines().next(), Some(began_line().trim_end())); let again = home.open(); assert_eq!(again.began(), Some(time())); assert_eq!(again.counts().decided, 1, "began is no item"); } #[test] fn began_only_first_and_once() { let home = Home::new("began-once"); let mut ledger = home.begun(); let err = ledger.append(began()).unwrap_err(); assert!(matches!(err, LedgerError::BeganNotFirst), "{err:?}"); assert_eq!(err.to_string(), "sync began, but not on the first line"); let home = Home::new("began-late"); let mut ledger = home.open(); ledger .append(decided(&turn("chat-1", 1), to_trusted("kyle"))) .unwrap(); assert!(matches!( ledger.append(began()), Err(LedgerError::BeganNotFirst) )); } /// `open`'s error for `text`: its line and its display. fn damaged(tag: &str, text: &str) -> (usize, String, Home) { let home = Home::new(tag); home.write(text); let err = Ledger::open(&home.path).unwrap_err(); let shown = err.to_string(); match err { LedgerError::Damaged { line, .. } => (line, shown, home), other => panic!("not Damaged: {other:?}"), } } #[test] fn a_ledger_that_does_not_start_with_began_is_damaged() { let first = line(&decided(&turn("chat-1", 1), to_trusted("kyle"))); let (number, shown, home) = damaged("began-missing", &first); assert_eq!(number, 1); assert_eq!( shown, format!( "{}:1: the first line is not when sync began{POINTER}", home.path.display() ) ); let twice = format!("{}{first}{}", began_line(), began_line()); let (number, shown, home) = damaged("began-twice", &twice); assert_eq!(number, 3); assert_eq!( shown, format!( "{}:3: sync began, but not on the first line{POINTER}", home.path.display() ) ); // An empty ledger has no line to be wrong: it is empty. let home = Home::new("began-empty"); home.write(""); assert_eq!(home.open().began(), None); } fn unconclude_about(peer: &str, text: &str) -> Action { Action::Unconclude { workspace: Workspace::Trusted, peer: peer.to_string(), text: text.to_string(), } } #[test] fn a_conclusion_is_found_by_its_peer_too() { let home = Home::new("began-peer"); let mut ledger = home.begun(); let (owner, agent, undo) = (edit("e1", 0), edit("e2", 0), edit("e3", 0)); ledger .append(decided(&owner, conclude(Workspace::Trusted, "Uses tabs."))) .unwrap(); let mut about_agent = conclude(Workspace::Trusted, "Uses tabs."); if let Action::Conclude { peer, .. } = &mut about_agent { *peer = "boxmaker0".to_string(); } ledger.append(decided(&agent, about_agent)).unwrap(); ledger.append(sent(&owner, &["c-owner"])).unwrap(); ledger.append(sent(&agent, &["c-agent"])).unwrap(); assert_eq!( ledger.conclusion(Workspace::Trusted, "kyle", "Uses tabs."), Some((owner.clone(), "c-owner".to_string())) ); assert_eq!( ledger.conclusion(Workspace::Trusted, "boxmaker0", "Uses tabs."), Some((agent.clone(), "c-agent".to_string())) ); // The owner's is undone: the agent's stays. ledger .append(decided(&undo, unconclude_about("kyle", "Uses tabs."))) .unwrap(); ledger.append(sent(&undo, &["c-owner"])).unwrap(); assert_eq!( ledger.conclusion(Workspace::Trusted, "kyle", "Uses tabs."), None ); assert_eq!( ledger.conclusion(Workspace::Trusted, "boxmaker0", "Uses tabs."), Some((agent, "c-agent".to_string())) ); let again = home.open(); assert_eq!( again.conclusion(Workspace::Trusted, "kyle", "Uses tabs."), None ); }