postjevsql.git / tests / threshold.rs

jev(row, q [, threshold]) (contract SQL surface): true when the Noul probability is at least the threshold, taken from the argument, then jev.threshold, then 0.5. It is evaluated by the jev scan and shares its judgments, and the cache, with jev_prob.

6use serde_json::Value;
7use tokio_postgres::error::SqlState;
8use support::mock_jev::{MockJev, Reply};
9use support::{jev_instance, noul};

Answers 0.3, 0.6, 0.9 and 0.5 about tickets 1 to 4.

12async fn graded() -> MockJev {
13    MockJev::start(|req| {
14        let body: Value = serde_json::from_slice(&req.body).unwrap();
15        let p = match body["state"]["id"].as_i64() {
16            Some(1) => 0.3,
17            Some(2) => 0.6,
18            Some(3) => 0.9,
19            _ => 0.5,
20        };
21        Reply::json(200, noul(p))
22    })
23    .await
24}
26async fn ids(client: &tokio_postgres::Client, sql: &str) -> Vec<i32> {
27    client.query(sql, &[]).await.unwrap().iter().map(|r| r.get(0)).collect()
28}
29
30#[tokio::test(flavor = "multi_thread")]
31async fn argument_then_setting_then_half() {
32    let mock = graded().await;
33    let (_pg, client) = jev_instance(&mock).await;
34    client
35        .batch_execute("INSERT INTO tickets VALUES (2, 'b'), (3, 'c'), (4, 'd')")
36        .await
37        .unwrap();
38    let urgent = "SELECT id FROM tickets t WHERE jev(t, 'Urgent?') ORDER BY id";
39
40    // Neither: 0.5, and a probability equal to it passes.
41    assert_eq!(ids(&client, urgent).await, [2, 3, 4]);
42
43    // The setting.
44    client.batch_execute("SET jev.threshold = 0.7").await.unwrap();
45    assert_eq!(ids(&client, urgent).await, [3]);
46
47    // The argument, over the setting, both lower and higher.
48    assert_eq!(ids(&client, "SELECT id FROM tickets t WHERE jev(t, 'Urgent?', 0.2) ORDER BY id").await, [1, 2, 3, 4]);
49    assert_eq!(ids(&client, "SELECT id FROM tickets t WHERE jev(t, 'Urgent?', 0.95) ORDER BY id").await, [] as [i32; 0]);
50
51    // RESET falls back to 0.5.
52    client.batch_execute("RESET jev.threshold").await.unwrap();
53    assert_eq!(ids(&client, urgent).await, [2, 3, 4]);
54
55    // In the select list, beside jev_prob, one judgment per row.
56    let rows = client
57        .query("SELECT id, jev(t, 'Urgent?', 0.55), jev_prob(t, 'Urgent?') FROM tickets t ORDER BY id", &[])
58        .await
59        .unwrap();
60    let got: Vec<(i32, bool, f64)> = rows.iter().map(|r| (r.get(0), r.get(1), r.get(2))).collect();
61    assert_eq!(got, [(1, false, 0.3), (2, true, 0.6), (3, true, 0.9), (4, false, 0.5)]);
62
63    // Every statement after the first was answered from the cache: the
64    // threshold is applied to the stored probability, not asked.
65    assert_eq!(mock.requests().len(), 4);
66}
67
68#[tokio::test(flavor = "multi_thread")]
69async fn thresholds_outside_zero_to_one_are_refused() {
70    let mock = graded().await;
71    let (_pg, client) = jev_instance(&mock).await;
72
73    let err = client.batch_execute("SET jev.threshold = 1.5").await.expect_err("refused");
74    assert_eq!(err.as_db_error().expect("an ERROR").code(), &SqlState::INVALID_PARAMETER_VALUE);
75
76    let err = client
77        .query("SELECT id FROM tickets t WHERE jev(t, 'Urgent?', 2)", &[])
78        .await
79        .expect_err("refused");
80    assert_eq!(err.as_db_error().expect("an ERROR").code(), &SqlState::INVALID_PARAMETER_VALUE);
81    assert!(mock.requests().is_empty(), "refused before sending");
82}