postjevsql.git / tests / replay.rs

Fixture replay: the mock answers from recorded exchanges keyed by the exact request bytes, and a request the fixture lacks fails loudly with the request named, never reaching any network. The gates' own fixtures come from tools/record-gates; these tests pin the mechanism.

6use support::mock_jev::replay::{Fixture, Replay, Response};
7use support::mock_jev::{MockJev, Reply};
8use support::{jev_instance, noul};
10const QUESTION: &str = "Does this convey urgency?";
11const SQL: &str = "SELECT jev_prob(t, $1) FROM tickets t";

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}
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}