Release gate "layout parity", replayed (tests/fixtures/layout_parity.json,
from tools/record-gates --send). The row-as-state requests are sent
by the extension and must match the recording byte for byte. Row in
instructions is unreachable from the extension until this gate passes
(LayoutParity has no values), so its recorded exchange is checked
for an answer per row rather than replayed.
12#[tokio::test(flavor = "multi_thread")] 13async fn row_as_state_replays_and_row_in_instructions_answers_every_row() { 14 let fixture = fixture("layout_parity"); 15 let replay = Replay::new(&fixture); 16 let mock = MockJev::start(replay.responder()).await; 17 let (_pg, client) = support::jev_instance(&mock).await; 18 setup_people(&client).await.expect("setup"); 19 20 let rows = client 21 .query("SELECT name, jev_prob(p, $1) FROM people p", &[&QUESTION]) 22 .await 23 .expect("replayed"); 24 assert_eq!(rows.len(), ROWS.len()); 25 assert_eq!(mock.requests().len(), ROWS.len(), "one request per row, as state"); 26 replay.assert_complete(); 27 28 // The one exchange the extension did not send: every row a question. 29 let sent: Vec<String> = mock.requests().iter().map(|r| String::from_utf8(r.body.to_vec()).unwrap()).collect(); 30 let unsent: Vec<_> = fixture.exchanges.iter().filter(|(request, _)| !sent.contains(request)).collect(); 31 assert_eq!(unsent.len(), 1, "row in instructions is one request"); 32 let (request, responses) = unsent[0]; 33 let request: serde_json::Value = serde_json::from_str(request).unwrap(); 34 assert_eq!(request["state"], serde_json::json!({})); 35 let body: serde_json::Value = serde_json::from_str(&responses[0].body).unwrap(); 36 for (i, row) in ROWS.iter().enumerate() { 37 let row: serde_json::Value = serde_json::from_str(row).unwrap(); 38 assert_eq!(request["questions"][format!("r{i}")]["instructions"]["row"], row); 39 let p = body["answers"][format!("r{i}")]["noul"].as_f64(); 40 assert!(p.is_some_and(|p| (0.0..=1.0).contains(&p)), "r{i} answered: {body}"); 41 } 42}