Skip to main content

syncular_testkit/
crdt.rs

1use serde_json::{json, Value};
2use syncular_runtime::client::{CrdtFieldMaterialization, CrdtFieldWriteReceipt, SyncularClient};
3use syncular_runtime::crdt_field::CrdtFieldId;
4use syncular_runtime::diesel_sqlite::DieselSqliteStore;
5use syncular_runtime::native::NativeSyncularClient;
6use syncular_runtime::transport::SyncTransport;
7
8pub fn crdt_field_id(
9    table: impl Into<String>,
10    row_id: impl Into<String>,
11    field: impl Into<String>,
12) -> CrdtFieldId {
13    CrdtFieldId::new(table, row_id, field)
14}
15
16pub fn crdt_field_request_json(table: &str, row_id: &str, field: &str) -> String {
17    json!({
18        "table": table,
19        "rowId": row_id,
20        "field": field
21    })
22    .to_string()
23}
24
25pub fn crdt_field_text_request_json(
26    table: &str,
27    row_id: &str,
28    field: &str,
29    next_text: &str,
30) -> String {
31    json!({
32        "table": table,
33        "rowId": row_id,
34        "field": field,
35        "nextText": next_text
36    })
37    .to_string()
38}
39
40pub fn apply_crdt_field_text<T>(
41    client: &mut SyncularClient<DieselSqliteStore, T>,
42    table: &str,
43    row_id: &str,
44    field_name: &str,
45    next_text: &str,
46) -> CrdtFieldWriteReceipt
47where
48    T: SyncTransport,
49{
50    let field = client
51        .open_crdt_field(crdt_field_id(table, row_id, field_name))
52        .expect("open CRDT field");
53    client
54        .apply_crdt_field_text(&field, next_text)
55        .expect("apply CRDT field text")
56}
57
58pub fn assert_crdt_field_materializes<T>(
59    client: &mut SyncularClient<DieselSqliteStore, T>,
60    table: &str,
61    row_id: &str,
62    field_name: &str,
63    expected: Value,
64) -> CrdtFieldMaterialization
65where
66    T: SyncTransport,
67{
68    let field = client
69        .open_crdt_field(crdt_field_id(table, row_id, field_name))
70        .expect("open CRDT field");
71    let materialized = client
72        .materialize_crdt_field(&field)
73        .expect("materialize CRDT field");
74    assert_eq!(materialized.value, expected, "unexpected CRDT value");
75    assert!(
76        !materialized.state_vector_base64.is_empty(),
77        "CRDT state vector should not be empty"
78    );
79    materialized
80}
81
82pub fn assert_crdt_field_text_nonblank<T>(
83    client: &mut SyncularClient<DieselSqliteStore, T>,
84    table: &str,
85    row_id: &str,
86    field_name: &str,
87) -> CrdtFieldMaterialization
88where
89    T: SyncTransport,
90{
91    let field = client
92        .open_crdt_field(crdt_field_id(table, row_id, field_name))
93        .expect("open CRDT field");
94    let materialized = client
95        .materialize_crdt_field(&field)
96        .expect("materialize CRDT field");
97    let text = materialized
98        .value
99        .as_str()
100        .expect("CRDT field should materialize to text");
101    assert!(!text.is_empty(), "CRDT text field should not blank");
102    materialized
103}
104
105pub fn apply_native_crdt_field_text(
106    client: &mut NativeSyncularClient,
107    table: &str,
108    row_id: &str,
109    field_name: &str,
110    next_text: &str,
111) -> Value {
112    let json = client
113        .apply_crdt_field_text_json(&crdt_field_text_request_json(
114            table, row_id, field_name, next_text,
115        ))
116        .expect("apply native CRDT field text");
117    serde_json::from_str(&json).expect("native CRDT write receipt JSON")
118}
119
120pub fn assert_native_crdt_field_materializes(
121    client: &mut NativeSyncularClient,
122    table: &str,
123    row_id: &str,
124    field_name: &str,
125    expected: Value,
126) -> Value {
127    let json = client
128        .materialize_crdt_field_json(&crdt_field_request_json(table, row_id, field_name))
129        .expect("materialize native CRDT field");
130    let materialized: Value =
131        serde_json::from_str(&json).expect("native CRDT materialization JSON");
132    assert_eq!(
133        materialized["value"], expected,
134        "unexpected native CRDT value"
135    );
136    assert!(
137        materialized["stateVectorBase64"]
138            .as_str()
139            .is_some_and(|value| !value.is_empty()),
140        "native CRDT state vector should not be empty"
141    );
142    materialized
143}