//! The approval posts in the state file, for marking them at a restart; an M4a state file still //! loads; and the turn of a session (M4b spec, sections 6 and 8). Do not edit. #[path = "support/tmp.rs"] mod tmp; use gatewayd::state::{ApprovalPosts, InFlight, State, StateError}; use tmp::TempDir; const DM: &str = "d0000000000000000000000000"; const ROOT: &str = "r0000000000000000000000000"; fn id(n: u32) -> String { format!("p{n:025}") } fn shown(approval: u64, posts: &[u32]) -> ApprovalPosts { ApprovalPosts { approval, channel: DM.to_string(), posts: posts.iter().map(|n| id(*n)).collect(), } } #[test] fn approval_posts_survive_a_restart_and_are_taken_once() { let dir = TempDir::new("state-approvals"); let path = dir.path().join("gateway/state.json"); let mut s = State::load(&path).unwrap(); s.add_approval(shown(42, &[1, 2])).unwrap(); s.add_approval(shown(43, &[3])).unwrap(); s.add_approval(shown(42, &[4])).unwrap(); s.remove_approval(43).unwrap(); s.remove_approval(99).unwrap(); let mut again = State::load(&path).unwrap(); assert_eq!( again.take_approvals().unwrap(), [shown(42, &[4])], "the later posting of 42 replaced the first" ); assert!( State::load(&path) .unwrap() .take_approvals() .unwrap() .is_empty(), "taking is saved" ); } #[test] fn an_m4a_state_file_loads() { let dir = TempDir::new("state-m4a"); let text = format!(r#"{{"channels":{{"{DM}":5}},"recent":[],"threads":[],"in_flight":[]}}"#); let path = dir.write("state.json", &text); let mut s = State::load(&path).unwrap(); assert_eq!(s.since(DM), Some(5)); assert!(s.take_approvals().unwrap().is_empty()); } #[test] fn bad_approval_posts_are_refused() { let dir = TempDir::new("state-approvals-bad"); for (n, approvals) in [ r#"[{"approval":1,"channel":"../x","posts":["p0000000000000000000000001"]}]"#, r#"[{"approval":1,"channel":"d0000000000000000000000000","posts":[]}]"#, r#"[{"approval":1,"channel":"d0000000000000000000000000","posts":["short"]}]"#, r#"[{"approval":1,"channel":"d0000000000000000000000000","posts":["p0000000000000000000000001"],"extra":1}]"#, r#"[{"approval":-1,"channel":"d0000000000000000000000000","posts":["p0000000000000000000000001"]}]"#, ] .iter() .enumerate() { let text = format!(r#"{{"channels":{{}},"recent":[],"threads":[],"in_flight":[],"approvals":{approvals}}}"#); let path = dir.write(&format!("s{n}.json"), &text); assert!(matches!(State::load(&path), Err(StateError::Read(..))), "{approvals}"); } } #[test] fn the_turn_of_a_session() { let dir = TempDir::new("state-turn-of"); let mut s = State::load(&dir.path().join("state.json")).unwrap(); let turn = InFlight { session: format!("mm-{ROOT}"), channel: DM.to_string(), root: ROOT.to_string(), }; assert_eq!(s.turn_of(&turn.session), None); s.start_turn(turn.clone()).unwrap(); assert_eq!(s.turn_of(&turn.session), Some(turn.clone())); assert_eq!(s.turn_of("mm-other"), None); s.end_turn(&turn.session).unwrap(); assert_eq!(s.turn_of(&turn.session), None); }