lmjtfy.git / packages / archive / src / tests.rs
tests.rsannotatedtests.rssource241 lines · 11.1 KB · raw
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::Comment { input: "is it?".into(), who: Asker("ab".into()), browser: "0b5f".into(), comment: "why <b>not</b>?".into() },
31        Ask::Home,
32        Ask::Answer { input: "is it?".into() },
33    ];
34    for ask in asks {
35        assert_eq!(serde_json::from_str::<Ask>(&serde_json::to_string(&ask).unwrap()).unwrap(), ask);
36    }
37    let told = Told::Called(Called::Answered(record(true)));
38    assert_eq!(serde_json::from_str::<Told>(&serde_json::to_string(&told).unwrap()).unwrap(), told);
39}
40
41#[test]
42fn the_share_with_no_llm_is_a_whole_percentage_and_nothing_of_nothing() {
43    let stats = Stats { questions: 3, no_llm: 2, ..Stats::default() };
44    assert_eq!(stats.no_llm_percent(), Some(67));
45    assert_eq!(Stats::default().no_llm_percent(), None);
46}
47
48fn noul(p_yes: f64) -> Answer {
49    Answer { headline: "Yes?".into(), detail: Detail::Noul { p_yes } }
50}
51
52#[test]
53fn an_answer_says_its_type_and_its_numbers_in_a_line() {
54    assert_eq!(noul(0.55).line(), "Yes? · Noul · p(yes) = 0.55");
55    let odds = Answer { headline: "28%.".into(), detail: Detail::Chance { p: 0.28 } };
56    assert_eq!(odds.line(), "28%. · Noul · p = 0.28 · Jev thinks that's unlikely");
57    let pick = Answer {
58        headline: "Pepperoni.".into(),
59        detail: Detail::Choice {
60            confidence: 0.95,
61            options: vec![("Pepperoni".into(), 0.9), ("Ham".into(), 0.06), ("Olive".into(), 0.03), ("Corn".into(), 0.01)],
62        },
63    };
64    assert_eq!(pick.line(), "Pepperoni. · Choice · confidence 0.95 · Pepperoni 90%, Ham 6%, Olive 3%");
65    let grade = Answer {
66        headline: "Medium.".into(),
67        detail: Detail::Score {
68            score: 1.2,
69            confidence: 0.6,
70            levels: vec![("Mild".into(), 0.1), ("Medium".into(), 0.7), ("Hot".into(), 0.2)],
71        },
72    };
73    assert_eq!(grade.line(), "Medium. · Score · 1.2 of 2 · confidence 0.60 · Medium 70%, Hot 20%, Mild 10%");
74}
75
76#[test]
77fn answers_kept_before_their_numbers_were_are_still_read() {
78    let old = stored(r#"["No?","Python."]"#);
79    assert_eq!(old.len(), 2);
80    assert_eq!(old[0], Answer { headline: "No?".into(), detail: Detail::Unknown });
81    assert_eq!(old[0].line(), "No?");
82    let new = serde_json::to_string(&vec![noul(0.55)]).unwrap();
83    assert_eq!(stored(&new), [noul(0.55)]);
84    assert_eq!(stored("not json"), Vec::<Answer>::new());
85}
86
87
88#[test]
89fn a_place_is_kept_only_if_it_looks_like_one() {
90    assert_eq!(Place::new(Some("us"), Some(" Austin ")), Place { country: Some("US".into()), city: Some("Austin".into()) });
91    assert_eq!(Place::new(Some("T1"), None), Place::default());
92    assert_eq!(Place::new(Some("<b>"), Some("a\nb")), Place::default());
93    assert_eq!(Place::new(Some("GB"), Some(&"x".repeat(65))).city, None);
94    assert_eq!(Place::new(Some("GB"), None).flag().as_deref(), Some("🇬🇧"));
95    assert_eq!(Place::new(Some("DE"), Some("Zürich")).label(), "Zürich, DE");
96    assert_eq!(Place::default().label(), "somewhere");
97}
98
99#[test]
100fn a_place_comes_back_from_its_header_whole() {
101    assert_eq!(percent_decoded("S%C3%A3o+Paulo").as_deref(), Some("São Paulo"));
102    assert_eq!(percent_decoded("bad%2"), None);
103    assert_eq!(percent_decoded("%FF"), None);
104}
105
106#[test]
107fn places_are_counted_most_first() {
108    let at = |country: &str, city: &str| Place::new(Some(country), Some(city));
109    let counted = places([at("US", "Austin"), at("GB", "London"), at("US", "Austin"), at("DE", "Berlin")]);
110    let labels: Vec<(String, u32)> = counted.iter().map(|(place, n)| (place.label(), *n)).collect();
111    assert_eq!(labels, [("Austin, US".to_owned(), 2), ("Berlin, DE".to_owned(), 1), ("London, GB".to_owned(), 1)]);
112}
113
114#[test]
115fn an_unplaced_page_is_asked_to_reconnect_once_the_deploy_has_settled() {
116    let unplaced = Seen { place: Place::default(), passed: false, at_ms: 0.0, event: None };
117    assert!(!Seen::stale(Some(&unplaced), REPLACE_AFTER_MS - 1.0));
118    assert!(Seen::stale(Some(&unplaced), REPLACE_AFTER_MS));
119    // Placed, or passed with no place to give: never.
120    assert!(!Seen::stale(Some(&Seen { passed: true, ..unplaced.clone() }), 1e12));
121    // Accepted by code from before places: at once.
122    assert!(Seen::stale(None, 0.0));
123}
124
125#[test]
126fn a_call_is_named_by_where_it_went_and_what_was_sent() {
127    let id = call_id("https://api.typesafe.ai/v1/systemone", "{}");
128    assert_eq!(id.len(), 12);
129    assert_eq!(id, call_id("https://api.typesafe.ai/v1/systemone", "{}"));
130    assert_ne!(id, call_id("@cf/x", "{}"));
131    assert_ne!(id, call_id("https://api.typesafe.ai/v1/systemone", "{ }"));
132}
133
134#[test]
135fn a_record_from_before_versions_is_the_first_of_one() {
136    let old = r#"{"response":"{}","request_id":null,"attempts":1,"took_ms":1.0,"answered_ms":2.0,"sent_now":false}"#;
137    let record: Record = serde_json::from_str(old).unwrap();
138    assert_eq!((record.version, record.versions), (1, 1));
139    // An ask from a page from before picks the newest.
140    let ask: Ask = serde_json::from_str(r#"{"Llm":{"model":"@cf/x","request":"{}"}}"#).unwrap();
141    assert_eq!(ask, Ask::Llm { model: "@cf/x".into(), request: "{}".into(), pick: Pick::Latest });
142}
143
144#[test]
145fn pins_say_which_version_and_whether_to_send() {
146    let a = "aaaaaaaaaaaa";
147    let b = "bbbbbbbbbbbb";
148    let pins = Pins::parse(&format!("{a}:2;junk;{b}:0;<x>:fresh"));
149    assert_eq!(pins.0, vec![(a.to_owned(), Pick::Version(2))]);
150    assert!(pins.browsing());
151    assert_eq!(pins.pick(a), Pick::Version(2));
152    // Looking at an old version reads the rest, and sends none of it.
153    assert_eq!(pins.pick(b), Pick::KeptOnly);
154    // Asking again sends the rest as needed.
155    let again = Pins::parse(&format!("{a}:2;{a}:fresh"));
156    assert_eq!(again.pick(a), Pick::Fresh);
157    assert!(!again.browsing());
158    assert_eq!(again.pick(b), Pick::Latest);
159    assert_eq!(Pins::parse("").pick(a), Pick::Latest);
160}
161
162#[test]
163fn the_admin_door_reads_and_never_writes_through_sql() {
164    for reads in [
165        "SELECT * FROM events ORDER BY id DESC LIMIT 50",
166        "  select name from sqlite_master where type = 'table';",
167        "WITH days AS (SELECT at_ms FROM events) SELECT COUNT(*) FROM days",
168        // What is in quotes is text, whatever it says.
169        "SELECT * FROM asked WHERE input LIKE '%delete; drop table asked%'",
170        "SELECT \"update\" FROM t",
171        // The function, which only reads.
172        "SELECT replace(path, '/', ' ') FROM events",
173        "SELECT REPLACE (path, '/', ' '), replaced FROM events",
174    ] {
175        assert!(reads_only(reads), "{reads}");
176    }
177    for writes in [
178        "DELETE FROM asked",
179        "UPDATE asked SET listed = 1",
180        "SELECT 1; DROP TABLE asked",
181        "WITH gone AS (SELECT 1) DELETE FROM asked",
182        // The statement, which writes.
183        "WITH one AS (SELECT 1) REPLACE INTO asked (input) SELECT 'x'",
184        "SELECT 1 replace",
185        "PRAGMA table_info(asked)",
186        "SELECT 'unclosed",
187        "",
188    ] {
189        assert!(!reads_only(writes), "{writes}");
190    }
191}
192
193#[test]
194fn admin_messages_survive_the_trip() {
195    for ask in [
196        Admin::Select { sql: "SELECT 1".into(), values: vec![serde_json::json!("x"), serde_json::json!(2)] },
197        Admin::Moderate { input: "is it?".into(), listed: Some(false) },
198        Admin::Moderate { input: "is it?".into(), listed: None },
199    ] {
200        let wire = serde_json::to_string(&ask).unwrap();
201        assert_eq!(serde_json::from_str::<Admin>(&wire).unwrap(), ask);
202    }
203}
204
205#[test]
206fn what_is_typed_finds_the_questions_it_could_begin() {
207    let entry = |input: &str, times: u32| Entry { input: input.to_owned(), answers: Vec::new(), asked_ms: 0.0, times };
208    let listed = Listed::new([entry("Is a hot dog a sandwich?", 3), entry("is a slot machine a good retirement plan?", 9), entry("Which is a better pet?", 20), entry("How hot is the sun?", 1)]);
209    let found = |typed: &str| listed.suggest(typed).into_iter().map(|entry| entry.input).collect::<Vec<_>>();
210    // Those that begin with it come first, the most asked of them first, whatever the case.
211    assert_eq!(found("Is a"), ["is a slot machine a good retirement plan?", "Is a hot dog a sandwich?", "Which is a better pet?"]);
212    // A word that begins there is found too; the middle of a word is not.
213    assert_eq!(found("hot"), ["Is a hot dog a sandwich?", "How hot is the sun?"]);
214    assert!(found("andwich").is_empty());
215    // Nothing until three characters are typed, and never more than five.
216    assert!(found("is").is_empty() && found("  ").is_empty());
217    let many = Listed::new((0..9).map(|n| entry(&format!("what is {n}?"), n)));
218    assert_eq!(many.suggest("what ").len(), SUGGESTED);
219    // The message crosses as JSON.
220    let ask = Ask::Suggest { typed: "is a".into() };
221    assert_eq!(serde_json::from_str::<Ask>(&serde_json::to_string(&ask).unwrap()).unwrap(), ask);
222}
223
224#[test]
225fn a_comment_is_trimmed_and_refused_when_empty_or_too_long() {
226    use crate::{MOST_COMMENT, Unsaid, clean_comment};
227    assert_eq!(clean_comment("  fair enough \r\n and more\u{7}  "), Ok("fair enough \n and more".to_owned()));
228    assert_eq!(clean_comment("<script>alert(1)</script>"), Ok("<script>alert(1)</script>".to_owned()), "kept as written: escaping is the page's job");
229    assert_eq!(clean_comment(""), Err(Unsaid::Empty));
230    assert_eq!(clean_comment(" \n\t \u{0}"), Err(Unsaid::Empty));
231    assert!(clean_comment(&"é".repeat(MOST_COMMENT)).is_ok(), "characters are counted, not bytes");
232    assert_eq!(clean_comment(&"é".repeat(MOST_COMMENT + 1)), Err(Unsaid::TooLong));
233    assert!(clean_comment(&format!("  {}  ", "a".repeat(MOST_COMMENT))).is_ok(), "the cap is of the trimmed text");
234}
235
236#[test]
237fn what_became_of_a_comment_survives_the_trip() {
238    for told in [Told::Commented(Ok(())), Told::Commented(Err(crate::Unsaid::NoVote))] {
239        assert_eq!(serde_json::from_str::<Told>(&serde_json::to_string(&told).unwrap()).unwrap(), told);
240    }
241}