postjevsql.git / tests / canonical.rs

The row is sent in a form that does not depend on the session, and loses no digit (review #2).

4use support::mock_jev::{MockJev, Reply};
5use support::{jev_instance, noul};
7#[tokio::test(flavor = "multi_thread")]
8async fn rows_are_session_independent_and_exact() {
9    let mock = MockJev::start(|_| Reply::json(200, noul(0.5))).await;
10    let (_pg, client) = jev_instance(&mock).await;
11    client
12        .batch_execute(
13            "CREATE TABLE facts (at timestamptz, big bigint, exact numeric, scaled numeric(40,20), f float8);
14             INSERT INTO facts VALUES ('2026-09-23 02:00:00-05', 9007199254740993, 12345678901234567890.123, 12345678901234567890.1, 0.1);
15             SET TimeZone = 'America/Chicago';",
16        )
17        .await
18        .unwrap();
19
20    client.query_one("SELECT jev_prob(r, 'q?') FROM facts r", &[]).await.expect("answered");
21    let body: serde_json::Value = serde_json::from_slice(&mock.requests()[0].body).unwrap();
22    assert_eq!(
23        body["state"],
24        serde_json::json!({
25            "at": "2026-09-23T07:00:00+00:00",
26            "big": "9007199254740993",
27            "exact": "12345678901234567890.123",
28            "scaled": "12345678901234567890.1",
29            "f": 0.1,
30        })
31    );
32
33    let zone: String = client.query_one("SHOW TimeZone", &[]).await.unwrap().get(0);
34    assert_eq!(zone, "America/Chicago", "the session's setting is untouched");
35}

Dates and times outside four-digit AD years, and timetz, are written as ECMAScript's toISOString writes them: astronomical years (1 BC is 0000), a sign and six digits outside 0000–9999, and UTC. to_json writes "… BC" suffixes, five-digit years and the value's own offset. Nested values are written the same way.

42#[tokio::test(flavor = "multi_thread")]
43async fn timestamps_are_rfc3339_at_every_edge() {
44    let mock = MockJev::start(|_| Reply::json(200, noul(0.5))).await;
45    let (_pg, client) = jev_instance(&mock).await;
46    client
47        .batch_execute(
48            "CREATE TYPE span AS (since date, at timetz);
49             CREATE TABLE edges (bc timestamptz, far timestamp, fraction timestamptz, first date, huge date,
50                                 wall timetz, wraps timetz, forever timestamptz, never date,
51                                 list timestamptz[], nested span);
52             INSERT INTO edges VALUES ('0044-03-15 12:00:00+00 BC', '10000-01-01 00:00:00', '2026-01-01 00:00:00.5+00',
53                                       '0001-01-01 BC', '5874897-12-31', '12:00:00-05', '23:30:00.25-05',
54                                       'infinity', '-infinity',
55                                       ARRAY['0001-12-31 23:59:59+00 BC', '2026-09-23 02:00:00-05']::timestamptz[],
56                                       ROW('0002-06-01 BC', '01:00:00+03')::span);
57             SET TimeZone = 'America/Chicago';",
58        )
59        .await
60        .unwrap();
61
62    client.query_one("SELECT jev_prob(r, 'q?') FROM edges r", &[]).await.expect("answered");
63    let body: serde_json::Value = serde_json::from_slice(&mock.requests()[0].body).unwrap();
64    assert_eq!(
65        body["state"],
66        serde_json::json!({
67            "bc": "-000043-03-15T12:00:00+00:00",
68            "far": "+010000-01-01T00:00:00",
69            "fraction": "2026-01-01T00:00:00.5+00:00",
70            "first": "0000-01-01",
71            "huge": "+5874897-12-31",
72            "wall": "17:00:00+00:00",
73            "wraps": "04:30:00.25+00:00",
74            "forever": "infinity",
75            "never": "-infinity",
76            "list": ["0000-12-31T23:59:59+00:00", "2026-09-23T07:00:00+00:00"],
77            "nested": {"since": "-000001-06-01", "at": "22:00:00+00:00"},
78        })
79    );
80}