1//! `jev(row, q [, threshold])` (contract *SQL surface*): true when the 2//! Noul probability is at least the threshold, taken from the argument, 3//! then `jev.threshold`, then 0.5. It is evaluated by the jev scan and 4//! shares its judgments, and the cache, with `jev_prob`. 5 6use serde_json::Value; 7use tokio_postgres::error::SqlState; 8use support::mock_jev::{MockJev, Reply}; 9use support::{jev_instance, noul}; 10 11/// 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} 25 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}