lmjtfy.git / packages / rules / src / tests.rs
tests.rsannotatedtests.rssource218 lines · 8.6 KB · raw
1use super::*;
2
3fn known(facts: &[(Fact, Value)]) -> Known {
4    let mut known = Known::default();
5    for (fact, value) in facts {
6        known.learn(*fact, *value);
7    }
8    known
9}
10
11const NOUL: Fact = Fact::Reads(Kind::Noul);
12const CHOICE: Fact = Fact::Reads(Kind::Choice);
13const SCORE: Fact = Fact::Reads(Kind::Score);
14const CHANCE: Fact = Fact::Reads(Kind::Chance);
15
16/// What Jev's one request teaches, with `reads` the readings that hold.
17fn facts(answerable: bool, several: bool, reads: &[Fact], scale: bool, fit: bool) -> Known {
18    let mut known = known(&[
19        (Fact::Answerable, Value::Bool(answerable)),
20        (Fact::Several, Value::Bool(several)),
21        (Fact::Scale, Value::Bool(scale)),
22        (Fact::Yes, Value::Given),
23        (Fact::Degree, Value::Given),
24        (Fact::Chance, Value::Given),
25        (Fact::Fit, Value::Bool(fit)),
26        (Fact::Whole, YES),
27    ]);
28    for kind in [NOUL, CHOICE, SCORE, CHANCE] {
29        known.learn(kind, Value::Bool(reads.contains(&kind)));
30    }
31    known
32}
33
34#[test]
35fn with_nothing_known_every_jev_fact_is_asked_for_at_once() {
36    let next = Network::lmjtfy().next(&Known::default());
37    assert_eq!(next, Next::Ask(vec![Fact::Answerable, Fact::Several, NOUL, CHOICE, SCORE, CHANCE, Fact::Scale, Fact::Yes, Fact::Degree, Fact::Chance, Fact::Fit, Fact::Whole]));
38}
39
40#[test]
41fn what_jev_cannot_judge_ends_there() {
42    let all = facts(false, true, &[CHOICE], false, true);
43    assert_eq!(Network::lmjtfy().next(&all), Next::End(End::NotAQuestion));
44}
45
46#[test]
47fn one_yes_or_no_question_is_answered_without_the_llm() {
48    let network = Network::lmjtfy();
49    let all = facts(true, false, &[NOUL], false, true);
50    assert_eq!(network.next(&all), Next::Done);
51    assert_eq!(network.shows(&all), [Fact::Yes]);
52    assert!(network.wants(&all).is_empty());
53}
54
55#[test]
56fn a_how_much_question_is_graded_unless_it_needs_its_own_scale() {
57    let network = Network::lmjtfy();
58    let general = facts(true, false, &[SCORE], false, true);
59    assert_eq!(network.next(&general), Next::Done);
60    assert_eq!(network.shows(&general), [Fact::Degree]);
61
62    let bespoke = facts(true, false, &[SCORE], true, true);
63    assert_eq!(network.next(&bespoke), Next::Do(Effect::Draft(Want::Scale)));
64    assert!(network.shows(&bespoke).is_empty());
65    assert_eq!(network.wants(&bespoke), [Want::Scale]);
66}
67
68#[test]
69fn a_how_likely_question_is_answered_with_jevs_own_probability_and_no_llm() {
70    let network = Network::lmjtfy();
71    let odds = facts(true, false, &[CHANCE], true, true);
72    assert_eq!(network.next(&odds), Next::Done);
73    assert_eq!(network.shows(&odds), [Fact::Chance]);
74    assert!(network.wants(&odds).is_empty());
75}
76
77#[test]
78fn a_pick_or_several_questions_go_to_the_llm() {
79    let network = Network::lmjtfy();
80    assert_eq!(network.next(&facts(true, false, &[CHOICE], false, true)), Next::Do(Effect::Draft(Want::Options)));
81    for reads in [NOUL, CHOICE, SCORE, CHANCE] {
82        let several = facts(true, true, &[reads], false, true);
83        assert_eq!(network.next(&several), Next::Do(Effect::Draft(Want::Split)), "{reads:?}");
84        // Nothing is answered directly when it is several questions.
85        assert!(network.shows(&several).is_empty());
86    }
87}
88
89#[test]
90fn when_jev_is_split_both_readings_are_answered() {
91    let network = Network::lmjtfy();
92    // A yes-or-no and a pick: the yes-or-no is in hand, the pick is drafted.
93    let split = facts(true, false, &[NOUL, CHOICE], false, true);
94    assert_eq!(network.shows(&split), [Fact::Yes]);
95    assert_eq!(network.next(&split), Next::Do(Effect::Draft(Want::Options)));
96    // A yes-or-no and a how-much: both are in hand, and no LLM is asked.
97    let free = facts(true, false, &[NOUL, SCORE], false, true);
98    assert_eq!(network.shows(&free), [Fact::Yes, Fact::Degree]);
99    assert_eq!(network.next(&free), Next::Done);
100    // A pick and a how-much that needs its scale: one call writes both.
101    let both = facts(true, false, &[CHOICE, SCORE], true, true);
102    assert_eq!(network.wants(&both), [Want::Options, Want::Scale]);
103}
104
105#[test]
106fn drafts_are_judged_and_then_the_query_is_done() {
107    let network = Network::lmjtfy();
108    let mut all = facts(true, false, &[CHOICE], false, true);
109    all.learn(Fact::Drafted, YES);
110    assert_eq!(network.next(&all), Next::Do(Effect::Judge));
111    all.learn(Fact::Judged, YES);
112    assert_eq!(network.next(&all), Next::Done);
113    assert_eq!(network.shows(&all), [Fact::Judged]);
114    all.learn(Fact::Judged, NO);
115    assert_eq!(network.next(&all), Next::Done);
116    assert!(network.shows(&all).is_empty());
117    all.learn(Fact::Drafted, NO);
118    assert_eq!(network.next(&all), Next::Done);
119}
120
121#[test]
122fn every_way_the_jev_facts_can_fall_ends_or_does_something() {
123    let network = Network::lmjtfy();
124    let readings: [&[Fact]; 9] = [
125        &[NOUL],
126        &[CHOICE],
127        &[SCORE],
128        &[CHANCE],
129        &[NOUL, CHOICE],
130        &[NOUL, SCORE],
131        &[CHOICE, SCORE],
132        &[NOUL, CHANCE],
133        &[SCORE, CHANCE],
134    ];
135    for answerable in [true, false] {
136        for several in [true, false] {
137            for reads in readings {
138                for scale in [true, false] {
139                    let all = facts(answerable, several, reads, scale, true);
140                    let next = network.next(&all);
141                    assert!(!matches!(next, Next::Ask(_)), "{all:?}");
142                    // An answerable question is either answered already or on its way to the LLM.
143                    if answerable {
144                        assert!(matches!(next, Next::Do(_)) || !network.shows(&all).is_empty(), "{all:?}");
145                    }
146                }
147            }
148        }
149    }
150}
151
152#[test]
153fn only_a_fit_question_jev_could_answer_is_listed() {
154    let network = Network::lmjtfy();
155    assert!(network.notes(&known(&[(Fact::Answerable, YES), (Fact::Fit, YES)]), Note::List));
156    assert!(!network.notes(&known(&[(Fact::Answerable, YES), (Fact::Fit, NO)]), Note::List));
157    assert!(!network.notes(&known(&[(Fact::Answerable, NO), (Fact::Fit, YES)]), Note::List));
158    assert!(!network.notes(&known(&[(Fact::Answerable, YES)]), Note::List));
159}
160
161#[test]
162fn half_a_question_is_noted_and_still_answered() {
163    let network = Network::lmjtfy();
164    let whole = facts(true, false, &[NOUL], false, true);
165    assert!(!network.notes(&whole, Note::Unfinished));
166    // One that stops part way is said to, and what happens to it is the same.
167    let mut half = whole.clone();
168    half.learn(Fact::Whole, NO);
169    assert!(network.notes(&half, Note::Unfinished));
170    assert_eq!((network.next(&half), network.shows(&half)), (network.next(&whole), network.shows(&whole)));
171    // Until Jev has said, it is not called unfinished.
172    half.forget(Fact::Whole);
173    assert!(!network.notes(&half, Note::Unfinished));
174    assert_eq!(network.next(&half), Next::Ask(vec![Fact::Whole]));
175}
176
177#[test]
178fn a_note_never_decides_a_step() {
179    let network = Network::lmjtfy();
180    let all = facts(true, false, &[NOUL, CHOICE], false, true);
181    let (by, next) = network.decide(&all);
182    assert_eq!(next, Next::Do(Effect::Draft(Want::Options)));
183    assert_eq!(network.terminals[by.unwrap()].name, "draft options");
184    assert_eq!(network.decide(&Known::default()).0, None);
185}
186
187#[test]
188fn rules_share_the_tests_they_have_in_common() {
189    let network = Network::lmjtfy();
190    let tests: usize = RULES.iter().map(|rule| rule.when.len()).sum();
191    assert_eq!(tests, 28);
192    // "one answerable question" is five rules' first two tests, and two joins.
193    let one = network.joins.iter().filter(|join| join.depth == 2 && network.alphas[join.alpha] == ONE[1]).count();
194    assert_eq!(one, 1);
195    assert_eq!(network.terminals.len(), RULES.len());
196    assert!(network.joins.len() < tests);
197}
198
199#[test]
200fn a_failed_test_fails_everything_after_it_and_nothing_before() {
201    let network = Network::lmjtfy();
202    let some = known(&[(Fact::Answerable, YES), (Fact::Several, NO), (NOUL, NO)]);
203    let direct = network.terminals.iter().find(|terminal| terminal.name == "answer yes or no").unwrap();
204    assert_eq!(network.join(direct.join, &some), State::Fails);
205    let options = network.terminals.iter().find(|terminal| terminal.name == "draft options").unwrap();
206    assert_eq!(network.join(options.join, &some), State::Waits);
207    let one = network.joins[options.join].left.unwrap();
208    assert_eq!(network.join(one, &some), State::Holds);
209}
210
211#[test]
212fn only_what_a_holding_rule_stands_on_is_used() {
213    let network = Network::lmjtfy();
214    let all = facts(true, false, &[CHOICE], false, false);
215    let (_, alphas) = network.used(&all);
216    let used: Vec<Test> = network.alphas.iter().zip(&alphas).filter(|(_, used)| **used).map(|(test, _)| *test).collect();
217    assert_eq!(used, [Test::Is(Fact::Answerable, YES), Test::Is(Fact::Several, NO), Test::Is(CHOICE, YES)]);
218}