1use super::*; 2 3fn record(sent_now: bool) -> Record { 4 Record { response: "{}".into(), request_id: None, attempts: 1, took_ms: 150.0, answered_ms: 1_790_971_860_000.0, sent_now, version: 1, versions: 1 } 5} 6 7#[test] 8fn a_moment_is_written_in_utc() { 9 assert_eq!(when(0.0), "1970-01-01 00:00 UTC"); 10 assert_eq!(when(1_790_971_860_000.0), "2026-10-02 20:11 UTC"); 11 assert_eq!(when(1_790_985_599_999.0), "2026-10-02 23:59 UTC"); 12} 13 14#[test] 15fn only_the_ask_that_sent_a_request_says_so() { 16 assert_eq!(record(true).sent(), Sent::Now); 17 assert_eq!(record(true).kept().sent(), Sent::Before { at_ms: 1_790_971_860_000.0 }); 18 assert_eq!(record(false).sent(), Sent::Before { at_ms: 1_790_971_860_000.0 }); 19} 20 21#[test] 22fn messages_survive_the_trip_to_the_object() { 23 let asks = [ 24 Ask::Jev { input: "is it?".into(), wanted: Wanted::Facts(Vec::new()), request: "{}".into(), pick: Pick::Fresh }, 25 Ask::Llm { model: "@cf/x".into(), request: "{}".into(), pick: Pick::Version(2) }, 26 Ask::Asked { input: "is it?".into(), answers: vec![noul(0.55)], llm: false, listed: true, who: Some(Asker("ab".into())), browser: "0b5f".into() }, 27 Ask::Asked { input: "is it?".into(), answers: vec![noul(0.55)], llm: false, listed: true, who: None, browser: String::new() }, 28 Ask::Rate { input: "is it?".into(), who: Asker("ab".into()), vote: Vote::Down, browser: "0b5f".into() }, 29 Ask::Rating { input: "is it?".into(), who: None }, 30 Ask::Home, 31 Ask::Answer { input: "is it?".into() }, 32 ]; 33 for ask in asks { 34 assert_eq!(serde_json::from_str::<Ask>(&serde_json::to_string(&ask).unwrap()).unwrap(), ask); 35 } 36 let told = Told::Called(Called::Answered(record(true))); 37 assert_eq!(serde_json::from_str::<Told>(&serde_json::to_string(&told).unwrap()).unwrap(), told); 38} 39 40#[test] 41fn the_share_with_no_llm_is_a_whole_percentage_and_nothing_of_nothing() { 42 let stats = Stats { questions: 3, no_llm: 2, ..Stats::default() }; 43 assert_eq!(stats.no_llm_percent(), Some(67)); 44 assert_eq!(Stats::default().no_llm_percent(), None); 45} 46 47fn noul(p_yes: f64) -> Answer { 48 Answer { headline: "Yes?".into(), detail: Detail::Noul { p_yes } } 49} 50 51#[test] 52fn an_answer_says_its_type_and_its_numbers_in_a_line() { 53 assert_eq!(noul(0.55).line(), "Yes? · Noul · p(yes) = 0.55"); 54 let odds = Answer { headline: "28%.".into(), detail: Detail::Chance { p: 0.28 } }; 55 assert_eq!(odds.line(), "28%. · Noul · p = 0.28 · Jev thinks that's unlikely"); 56 let pick = Answer { 57 headline: "Pepperoni.".into(), 58 detail: Detail::Choice { 59 confidence: 0.95, 60 options: vec![("Pepperoni".into(), 0.9), ("Ham".into(), 0.06), ("Olive".into(), 0.03), ("Corn".into(), 0.01)], 61 }, 62 }; 63 assert_eq!(pick.line(), "Pepperoni. · Choice · confidence 0.95 · Pepperoni 90%, Ham 6%, Olive 3%"); 64 let grade = Answer { 65 headline: "Medium.".into(), 66 detail: Detail::Score { 67 score: 1.2, 68 confidence: 0.6, 69 levels: vec![("Mild".into(), 0.1), ("Medium".into(), 0.7), ("Hot".into(), 0.2)], 70 }, 71 }; 72 assert_eq!(grade.line(), "Medium. · Score · 1.2 of 2 · confidence 0.60 · Medium 70%, Hot 20%, Mild 10%"); 73} 74 75#[test] 76fn answers_kept_before_their_numbers_were_are_still_read() { 77 let old = stored(r#"["No?","Python."]"#); 78 assert_eq!(old.len(), 2); 79 assert_eq!(old[0], Answer { headline: "No?".into(), detail: Detail::Unknown }); 80 assert_eq!(old[0].line(), "No?"); 81 let new = serde_json::to_string(&vec![noul(0.55)]).unwrap(); 82 assert_eq!(stored(&new), [noul(0.55)]); 83 assert_eq!(stored("not json"), Vec::<Answer>::new()); 84} 85 86 87#[test] 88fn a_place_is_kept_only_if_it_looks_like_one() { 89 assert_eq!(Place::new(Some("us"), Some(" Austin ")), Place { country: Some("US".into()), city: Some("Austin".into()) }); 90 assert_eq!(Place::new(Some("T1"), None), Place::default()); 91 assert_eq!(Place::new(Some("<b>"), Some("a\nb")), Place::default()); 92 assert_eq!(Place::new(Some("GB"), Some(&"x".repeat(65))).city, None); 93 assert_eq!(Place::new(Some("GB"), None).flag().as_deref(), Some("🇬🇧")); 94 assert_eq!(Place::new(Some("DE"), Some("Zürich")).label(), "Zürich, DE"); 95 assert_eq!(Place::default().label(), "somewhere"); 96} 97 98#[test] 99fn a_place_comes_back_from_its_header_whole() { 100 assert_eq!(percent_decoded("S%C3%A3o+Paulo").as_deref(), Some("São Paulo")); 101 assert_eq!(percent_decoded("bad%2"), None); 102 assert_eq!(percent_decoded("%FF"), None); 103} 104 105#[test] 106fn places_are_counted_most_first() { 107 let at = |country: &str, city: &str| Place::new(Some(country), Some(city)); 108 let counted = places([at("US", "Austin"), at("GB", "London"), at("US", "Austin"), at("DE", "Berlin")]); 109 let labels: Vec<(String, u32)> = counted.iter().map(|(place, n)| (place.label(), *n)).collect(); 110 assert_eq!(labels, [("Austin, US".to_owned(), 2), ("Berlin, DE".to_owned(), 1), ("London, GB".to_owned(), 1)]); 111} 112 113#[test] 114fn an_unplaced_page_is_asked_to_reconnect_once_the_deploy_has_settled() { 115 let unplaced = Seen { place: Place::default(), passed: false, at_ms: 0.0, event: None }; 116 assert!(!Seen::stale(Some(&unplaced), REPLACE_AFTER_MS - 1.0)); 117 assert!(Seen::stale(Some(&unplaced), REPLACE_AFTER_MS)); 118 // Placed, or passed with no place to give: never. 119 assert!(!Seen::stale(Some(&Seen { passed: true, ..unplaced.clone() }), 1e12)); 120 // Accepted by code from before places: at once. 121 assert!(Seen::stale(None, 0.0)); 122} 123 124#[test] 125fn a_call_is_named_by_where_it_went_and_what_was_sent() { 126 let id = call_id("https://api.typesafe.ai/v1/systemone", "{}"); 127 assert_eq!(id.len(), 12); 128 assert_eq!(id, call_id("https://api.typesafe.ai/v1/systemone", "{}")); 129 assert_ne!(id, call_id("@cf/x", "{}")); 130 assert_ne!(id, call_id("https://api.typesafe.ai/v1/systemone", "{ }")); 131} 132 133#[test] 134fn a_record_from_before_versions_is_the_first_of_one() { 135 let old = r#"{"response":"{}","request_id":null,"attempts":1,"took_ms":1.0,"answered_ms":2.0,"sent_now":false}"#; 136 let record: Record = serde_json::from_str(old).unwrap(); 137 assert_eq!((record.version, record.versions), (1, 1)); 138 // An ask from a page from before picks the newest. 139 let ask: Ask = serde_json::from_str(r#"{"Llm":{"model":"@cf/x","request":"{}"}}"#).unwrap(); 140 assert_eq!(ask, Ask::Llm { model: "@cf/x".into(), request: "{}".into(), pick: Pick::Latest }); 141} 142 143#[test] 144fn pins_say_which_version_and_whether_to_send() { 145 let a = "aaaaaaaaaaaa"; 146 let b = "bbbbbbbbbbbb"; 147 let pins = Pins::parse(&format!("{a}:2;junk;{b}:0;<x>:fresh")); 148 assert_eq!(pins.0, vec![(a.to_owned(), Pick::Version(2))]); 149 assert!(pins.browsing()); 150 assert_eq!(pins.pick(a), Pick::Version(2)); 151 // Looking at an old version reads the rest, and sends none of it. 152 assert_eq!(pins.pick(b), Pick::KeptOnly); 153 // Asking again sends the rest as needed. 154 let again = Pins::parse(&format!("{a}:2;{a}:fresh")); 155 assert_eq!(again.pick(a), Pick::Fresh); 156 assert!(!again.browsing()); 157 assert_eq!(again.pick(b), Pick::Latest); 158 assert_eq!(Pins::parse("").pick(a), Pick::Latest); 159} 160 161#[test] 162fn the_admin_door_reads_and_never_writes_through_sql() { 163 for reads in [ 164 "SELECT * FROM events ORDER BY id DESC LIMIT 50", 165 " select name from sqlite_master where type = 'table';", 166 "WITH days AS (SELECT at_ms FROM events) SELECT COUNT(*) FROM days", 167 // What is in quotes is text, whatever it says. 168 "SELECT * FROM asked WHERE input LIKE '%delete; drop table asked%'", 169 "SELECT \"update\" FROM t", 170 ] { 171 assert!(reads_only(reads), "{reads}"); 172 } 173 for writes in [ 174 "DELETE FROM asked", 175 "UPDATE asked SET listed = 1", 176 "SELECT 1; DROP TABLE asked", 177 "WITH gone AS (SELECT 1) DELETE FROM asked", 178 "PRAGMA table_info(asked)", 179 "SELECT 'unclosed", 180 "", 181 ] { 182 assert!(!reads_only(writes), "{writes}"); 183 } 184} 185 186#[test] 187fn admin_messages_survive_the_trip() { 188 for ask in [ 189 Admin::Select { sql: "SELECT 1".into(), values: vec![serde_json::json!("x"), serde_json::json!(2)] }, 190 Admin::Moderate { input: "is it?".into(), listed: Some(false) }, 191 Admin::Moderate { input: "is it?".into(), listed: None }, 192 ] { 193 let wire = serde_json::to_string(&ask).unwrap(); 194 assert_eq!(serde_json::from_str::<Admin>(&wire).unwrap(), ask); 195 } 196}