1//! Fixture replay: the mock answers from recorded exchanges keyed by the 2//! exact request bytes, and a request the fixture lacks fails loudly with 3//! the request named, never reaching any network. The gates' own fixtures 4//! come from `tools/record-gates`; these tests pin the mechanism. 5 6use support::mock_jev::replay::{Fixture, Replay, Response}; 7use support::mock_jev::{MockJev, Reply}; 8use support::{jev_instance, noul}; 9 10const QUESTION: &str = "Does this convey urgency?"; 11const SQL: &str = "SELECT jev_prob(t, $1) FROM tickets t"; 12 13/// The bytes the extension sends for [`SQL`], captured from a mock. 14async fn request_bytes() -> String { 15 let mock = MockJev::start(|_| Reply::json(200, noul(0.95))).await; 16 let (_pg, client) = jev_instance(&mock).await; 17 client.query_one(SQL, &[&QUESTION]).await.expect("jev_prob runs"); 18 let requests = mock.requests(); 19 assert_eq!(requests.len(), 1); 20 String::from_utf8(requests[0].body.to_vec()).unwrap() 21} 22 23fn fixture(request: String) -> Fixture { 24 Fixture { 25 model: "jev-1.13.0".into(), 26 exchanges: vec![( 27 request, 28 vec![Response { 29 status: 200, 30 headers: vec![("x-typesafe-request-id".into(), "req_fixture".into())], 31 body: noul(0.42).to_string(), 32 }], 33 )], 34 } 35} 36 37#[tokio::test(flavor = "multi_thread")] 38async fn replays_a_recorded_answer_by_exact_bytes() { 39 let fixture = Fixture::parse(&fixture(request_bytes().await).to_json()).expect("round trips"); 40 let replay = Replay::new(&fixture); 41 let mock = MockJev::start(replay.responder()).await; 42 let (_pg, client) = jev_instance(&mock).await; 43 44 let row = client.query_one(SQL, &[&QUESTION]).await.expect("replayed"); 45 assert_eq!(row.get::<_, f64>(0), 0.42); 46 replay.assert_complete(); 47} 48 49#[tokio::test(flavor = "multi_thread")] 50async fn a_request_not_in_the_fixture_fails_naming_it() { 51 let replay = Replay::new(&fixture(request_bytes().await)); 52 let mock = MockJev::start(replay.responder()).await; 53 let (_pg, client) = jev_instance(&mock).await; 54 55 let err = client 56 .query_one(SQL, &[&"A different question"]) 57 .await 58 .expect_err("a changed request is not answered"); 59 let db = err.as_db_error().expect("a server error"); 60 assert!(db.message().contains("request not in fixture"), "{}", db.message()); 61 assert!(db.message().contains("A different question"), "{}", db.message()); 62 63 let misses = replay.misses(); 64 assert_eq!(misses.len(), 1); 65 let panic = std::panic::catch_unwind(|| replay.assert_complete()).expect_err("fails the test"); 66 let text = panic.downcast_ref::<String>().unwrap(); 67 assert!(text.contains("A different question"), "{text}"); 68 // The miss was reported; do not report it again on drop. 69 std::mem::forget(replay); 70}