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 ]); 27 for kind in [NOUL, CHOICE, SCORE, CHANCE] { 28 known.learn(kind, Value::Bool(reads.contains(&kind))); 29 } 30 known 31} 32 33#[test] 34fn with_nothing_known_every_jev_fact_is_asked_for_at_once() { 35 let next = Network::lmjtfy().next(&Known::default()); 36 assert_eq!(next, Next::Ask(vec![Fact::Answerable, Fact::Several, NOUL, CHOICE, SCORE, CHANCE, Fact::Scale, Fact::Yes, Fact::Degree, Fact::Chance, Fact::Fit])); 37} 38 39#[test] 40fn what_jev_cannot_judge_ends_there() { 41 let all = facts(false, true, &[CHOICE], false, true); 42 assert_eq!(Network::lmjtfy().next(&all), Next::End(End::NotAQuestion)); 43} 44 45#[test] 46fn one_yes_or_no_question_is_answered_without_the_llm() { 47 let network = Network::lmjtfy(); 48 let all = facts(true, false, &[NOUL], false, true); 49 assert_eq!(network.next(&all), Next::Done); 50 assert_eq!(network.shows(&all), [Fact::Yes]); 51 assert!(network.wants(&all).is_empty()); 52} 53 54#[test] 55fn a_how_much_question_is_graded_unless_it_needs_its_own_scale() { 56 let network = Network::lmjtfy(); 57 let general = facts(true, false, &[SCORE], false, true); 58 assert_eq!(network.next(&general), Next::Done); 59 assert_eq!(network.shows(&general), [Fact::Degree]); 60 61 let bespoke = facts(true, false, &[SCORE], true, true); 62 assert_eq!(network.next(&bespoke), Next::Do(Effect::Draft(Want::Scale))); 63 assert!(network.shows(&bespoke).is_empty()); 64 assert_eq!(network.wants(&bespoke), [Want::Scale]); 65} 66 67#[test] 68fn a_how_likely_question_is_answered_with_jevs_own_probability_and_no_llm() { 69 let network = Network::lmjtfy(); 70 let odds = facts(true, false, &[CHANCE], true, true); 71 assert_eq!(network.next(&odds), Next::Done); 72 assert_eq!(network.shows(&odds), [Fact::Chance]); 73 assert!(network.wants(&odds).is_empty()); 74} 75 76#[test] 77fn a_pick_or_several_questions_go_to_the_llm() { 78 let network = Network::lmjtfy(); 79 assert_eq!(network.next(&facts(true, false, &[CHOICE], false, true)), Next::Do(Effect::Draft(Want::Options))); 80 for reads in [NOUL, CHOICE, SCORE, CHANCE] { 81 let several = facts(true, true, &[reads], false, true); 82 assert_eq!(network.next(&several), Next::Do(Effect::Draft(Want::Split)), "{reads:?}"); 83 // Nothing is answered directly when it is several questions. 84 assert!(network.shows(&several).is_empty()); 85 } 86} 87 88#[test] 89fn when_jev_is_split_both_readings_are_answered() { 90 let network = Network::lmjtfy(); 91 // A yes-or-no and a pick: the yes-or-no is in hand, the pick is drafted. 92 let split = facts(true, false, &[NOUL, CHOICE], false, true); 93 assert_eq!(network.shows(&split), [Fact::Yes]); 94 assert_eq!(network.next(&split), Next::Do(Effect::Draft(Want::Options))); 95 // A yes-or-no and a how-much: both are in hand, and no LLM is asked. 96 let free = facts(true, false, &[NOUL, SCORE], false, true); 97 assert_eq!(network.shows(&free), [Fact::Yes, Fact::Degree]); 98 assert_eq!(network.next(&free), Next::Done); 99 // A pick and a how-much that needs its scale: one call writes both. 100 let both = facts(true, false, &[CHOICE, SCORE], true, true); 101 assert_eq!(network.wants(&both), [Want::Options, Want::Scale]); 102} 103 104#[test] 105fn drafts_are_judged_and_then_the_query_is_done() { 106 let network = Network::lmjtfy(); 107 let mut all = facts(true, false, &[CHOICE], false, true); 108 all.learn(Fact::Drafted, YES); 109 assert_eq!(network.next(&all), Next::Do(Effect::Judge)); 110 all.learn(Fact::Judged, YES); 111 assert_eq!(network.next(&all), Next::Done); 112 assert_eq!(network.shows(&all), [Fact::Judged]); 113 all.learn(Fact::Judged, NO); 114 assert_eq!(network.next(&all), Next::Done); 115 assert!(network.shows(&all).is_empty()); 116 all.learn(Fact::Drafted, NO); 117 assert_eq!(network.next(&all), Next::Done); 118} 119 120#[test] 121fn every_way_the_jev_facts_can_fall_ends_or_does_something() { 122 let network = Network::lmjtfy(); 123 let readings: [&[Fact]; 9] = [ 124 &[NOUL], 125 &[CHOICE], 126 &[SCORE], 127 &[CHANCE], 128 &[NOUL, CHOICE], 129 &[NOUL, SCORE], 130 &[CHOICE, SCORE], 131 &[NOUL, CHANCE], 132 &[SCORE, CHANCE], 133 ]; 134 for answerable in [true, false] { 135 for several in [true, false] { 136 for reads in readings { 137 for scale in [true, false] { 138 let all = facts(answerable, several, reads, scale, true); 139 let next = network.next(&all); 140 assert!(!matches!(next, Next::Ask(_)), "{all:?}"); 141 // An answerable question is either answered already or on its way to the LLM. 142 if answerable { 143 assert!(matches!(next, Next::Do(_)) || !network.shows(&all).is_empty(), "{all:?}"); 144 } 145 } 146 } 147 } 148 } 149} 150 151#[test] 152fn only_a_fit_question_jev_could_answer_is_listed() { 153 let network = Network::lmjtfy(); 154 assert!(network.notes(&known(&[(Fact::Answerable, YES), (Fact::Fit, YES)]), Note::List)); 155 assert!(!network.notes(&known(&[(Fact::Answerable, YES), (Fact::Fit, NO)]), Note::List)); 156 assert!(!network.notes(&known(&[(Fact::Answerable, NO), (Fact::Fit, YES)]), Note::List)); 157 assert!(!network.notes(&known(&[(Fact::Answerable, YES)]), Note::List)); 158} 159 160#[test] 161fn a_note_never_decides_a_step() { 162 let network = Network::lmjtfy(); 163 let all = facts(true, false, &[NOUL, CHOICE], false, true); 164 let (by, next) = network.decide(&all); 165 assert_eq!(next, Next::Do(Effect::Draft(Want::Options))); 166 assert_eq!(network.terminals[by.unwrap()].name, "draft options"); 167 assert_eq!(network.decide(&Known::default()).0, None); 168} 169 170#[test] 171fn rules_share_the_tests_they_have_in_common() { 172 let network = Network::lmjtfy(); 173 let tests: usize = RULES.iter().map(|rule| rule.when.len()).sum(); 174 assert_eq!(tests, 27); 175 // "one answerable question" is five rules' first two tests, and two joins. 176 let one = network.joins.iter().filter(|join| join.depth == 2 && network.alphas[join.alpha] == ONE[1]).count(); 177 assert_eq!(one, 1); 178 assert_eq!(network.terminals.len(), RULES.len()); 179 assert!(network.joins.len() < tests); 180} 181 182#[test] 183fn a_failed_test_fails_everything_after_it_and_nothing_before() { 184 let network = Network::lmjtfy(); 185 let some = known(&[(Fact::Answerable, YES), (Fact::Several, NO), (NOUL, NO)]); 186 let direct = network.terminals.iter().find(|terminal| terminal.name == "answer yes or no").unwrap(); 187 assert_eq!(network.join(direct.join, &some), State::Fails); 188 let options = network.terminals.iter().find(|terminal| terminal.name == "draft options").unwrap(); 189 assert_eq!(network.join(options.join, &some), State::Waits); 190 let one = network.joins[options.join].left.unwrap(); 191 assert_eq!(network.join(one, &some), State::Holds); 192} 193 194#[test] 195fn only_what_a_holding_rule_stands_on_is_used() { 196 let network = Network::lmjtfy(); 197 let all = facts(true, false, &[CHOICE], false, false); 198 let (_, alphas) = network.used(&all); 199 let used: Vec<Test> = network.alphas.iter().zip(&alphas).filter(|(_, used)| **used).map(|(test, _)| *test).collect(); 200 assert_eq!(used, [Test::Is(Fact::Answerable, YES), Test::Is(Fact::Several, NO), Test::Is(CHOICE, YES)]); 201}