1//! Release gate "layout parity", replayed (`tests/fixtures/layout_parity.json`, 2//! from `tools/record-gates --send`). The row-as-state requests are sent 3//! by the extension and must match the recording byte for byte. Row in 4//! instructions is unreachable from the extension until this gate passes 5//! (`LayoutParity` has no values), so its recorded exchange is checked 6//! for an answer per row rather than replayed. 7 8use support::gates::{QUESTION, ROWS, fixture, setup_people}; 9use support::mock_jev::MockJev; 10use support::mock_jev::replay::Replay; 11 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}