Skip to main content

syncular_testkit/
assertions.rs

1use serde_json::Value;
2use std::time::Duration;
3use syncular_runtime::client::SyncularClient;
4use syncular_runtime::diesel_sqlite::DieselSqliteStore;
5use syncular_runtime::store::{ConflictSummary, OutboxSummary};
6use syncular_runtime::transport::{SyncAuthHeaders, SyncTransport};
7
8use crate::app_server::{AppTestServer, AppTestServerCommit};
9use crate::http::TestHttpRequest;
10use crate::transport::{BlobUploadRecord, TestTransportHandle};
11
12pub fn assert_outbox_empty<T>(
13    client: &mut SyncularClient<DieselSqliteStore, T>,
14) -> Vec<OutboxSummary>
15where
16    T: SyncTransport,
17{
18    let summaries = client.outbox_summaries().expect("outbox summaries");
19    assert_eq!(summaries.len(), 0, "expected empty outbox: {summaries:?}");
20    summaries
21}
22
23pub fn assert_outbox_statuses<T>(
24    client: &mut SyncularClient<DieselSqliteStore, T>,
25    expected: &[&str],
26) -> Vec<OutboxSummary>
27where
28    T: SyncTransport,
29{
30    let summaries = client.outbox_summaries().expect("outbox summaries");
31    let actual = summaries
32        .iter()
33        .map(|summary| summary.status.as_str())
34        .collect::<Vec<_>>();
35    assert_eq!(actual, expected, "unexpected outbox statuses");
36    summaries
37}
38
39pub fn assert_outbox_count<T>(
40    client: &mut SyncularClient<DieselSqliteStore, T>,
41    expected: usize,
42) -> Vec<OutboxSummary>
43where
44    T: SyncTransport,
45{
46    let summaries = client.outbox_summaries().expect("outbox summaries");
47    assert_eq!(
48        summaries.len(),
49        expected,
50        "unexpected outbox count: {summaries:?}"
51    );
52    summaries
53}
54
55pub fn assert_latest_outbox_status<T>(
56    client: &mut SyncularClient<DieselSqliteStore, T>,
57    expected: &str,
58) -> OutboxSummary
59where
60    T: SyncTransport,
61{
62    let summaries = client.outbox_summaries().expect("outbox summaries");
63    let latest = summaries.last().expect("latest outbox summary").clone();
64    assert_eq!(latest.status, expected, "unexpected latest outbox status");
65    latest
66}
67
68pub fn assert_conflict_count<T>(
69    client: &mut SyncularClient<DieselSqliteStore, T>,
70    expected: usize,
71) -> Vec<ConflictSummary>
72where
73    T: SyncTransport,
74{
75    let conflicts = client.conflict_summaries().expect("conflict summaries");
76    assert_eq!(
77        conflicts.len(),
78        expected,
79        "unexpected conflict count: {conflicts:?}"
80    );
81    conflicts
82}
83
84pub fn assert_no_conflicts<T>(
85    client: &mut SyncularClient<DieselSqliteStore, T>,
86) -> Vec<ConflictSummary>
87where
88    T: SyncTransport,
89{
90    assert_conflict_count(client, 0)
91}
92
93pub fn assert_table_row_count<T>(
94    client: &mut SyncularClient<DieselSqliteStore, T>,
95    table: &str,
96    expected: usize,
97) -> Vec<Value>
98where
99    T: SyncTransport,
100{
101    let rows_json = client.list_table_json(table).expect("table rows");
102    let rows: Vec<Value> = serde_json::from_str(&rows_json).expect("table rows json");
103    assert_eq!(rows.len(), expected, "unexpected row count for {table}");
104    rows
105}
106
107pub fn assert_table_has_row<T>(
108    client: &mut SyncularClient<DieselSqliteStore, T>,
109    table: &str,
110    primary_key: &str,
111    row_id: &str,
112) -> Value
113where
114    T: SyncTransport,
115{
116    let rows_json = client.list_table_json(table).expect("table rows");
117    let rows: Vec<Value> = serde_json::from_str(&rows_json).expect("table rows json");
118    rows.into_iter()
119        .find(|row| row.get(primary_key).and_then(Value::as_str) == Some(row_id))
120        .unwrap_or_else(|| panic!("expected row {table}.{primary_key}={row_id}"))
121}
122
123pub fn assert_blob_upload_queue<T>(
124    client: &mut SyncularClient<DieselSqliteStore, T>,
125    pending: i64,
126    uploading: i64,
127    failed: i64,
128) where
129    T: SyncTransport,
130{
131    let stats = client.blob_upload_queue_stats().expect("blob queue stats");
132    assert_eq!(stats.pending, pending, "unexpected pending blob uploads");
133    assert_eq!(
134        stats.uploading, uploading,
135        "unexpected uploading blob uploads"
136    );
137    assert_eq!(stats.failed, failed, "unexpected failed blob uploads");
138}
139
140pub fn assert_blob_cache<T>(
141    client: &mut SyncularClient<DieselSqliteStore, T>,
142    count: i64,
143    total_bytes: i64,
144) where
145    T: SyncTransport,
146{
147    let stats = client.blob_cache_stats().expect("blob cache stats");
148    assert_eq!(stats.count, count, "unexpected cached blob count");
149    assert_eq!(
150        stats.total_bytes, total_bytes,
151        "unexpected cached blob bytes"
152    );
153}
154
155pub fn assert_blob_upload_count(
156    handle: &TestTransportHandle,
157    expected: usize,
158) -> Vec<BlobUploadRecord> {
159    let uploads = handle.blob_uploads();
160    assert_eq!(
161        uploads.len(),
162        expected,
163        "unexpected blob upload count: {uploads:?}"
164    );
165    uploads
166}
167
168pub fn assert_blob_uploaded(handle: &TestTransportHandle, hash: &str) -> BlobUploadRecord {
169    handle
170        .blob_uploads()
171        .into_iter()
172        .find(|upload| upload.blob.hash == hash)
173        .unwrap_or_else(|| panic!("expected uploaded blob {hash}"))
174}
175
176pub fn assert_app_server_row_count(
177    server: &AppTestServer,
178    table: &str,
179    expected: usize,
180) -> Vec<Value> {
181    let rows = server.rows(table);
182    assert_eq!(
183        rows.len(),
184        expected,
185        "unexpected AppTestServer row count for {table}: {rows:?}"
186    );
187    rows
188}
189
190pub fn assert_app_server_has_row(server: &AppTestServer, table: &str, row_id: &str) -> Value {
191    server
192        .row(table, row_id)
193        .unwrap_or_else(|| panic!("expected AppTestServer row {table}.{row_id}"))
194}
195
196pub fn assert_app_server_missing_row(server: &AppTestServer, table: &str, row_id: &str) {
197    assert!(
198        server.row(table, row_id).is_none(),
199        "expected missing AppTestServer row {table}.{row_id}"
200    );
201}
202
203pub fn assert_app_server_commit_count(
204    server: &AppTestServer,
205    expected: usize,
206    timeout: Duration,
207) -> Vec<AppTestServerCommit> {
208    let commits = server.wait_for_commit_count(expected, timeout);
209    assert_eq!(
210        commits.len(),
211        expected,
212        "unexpected AppTestServer commit count: {commits:?}"
213    );
214    commits
215}
216
217pub fn assert_app_server_auth_header(
218    server: &AppTestServer,
219    name: &str,
220    expected: &str,
221) -> SyncAuthHeaders {
222    let name = name.to_ascii_lowercase();
223    server
224        .auth_headers()
225        .into_iter()
226        .find(|headers| headers.get(&name).map(String::as_str) == Some(expected))
227        .unwrap_or_else(|| panic!("expected AppTestServer auth header {name}={expected}"))
228}
229
230pub fn assert_http_request_count(
231    requests: &[TestHttpRequest],
232    expected: usize,
233) -> &[TestHttpRequest] {
234    assert_eq!(
235        requests.len(),
236        expected,
237        "unexpected HTTP request count: {requests:?}"
238    );
239    requests
240}
241
242pub fn assert_http_request_header(
243    request: &TestHttpRequest,
244    name: &str,
245    expected: &str,
246) -> TestHttpRequest {
247    assert_eq!(
248        request.header(name),
249        Some(expected),
250        "unexpected HTTP request header {name} on {} {}",
251        request.method,
252        request.path
253    );
254    request.clone()
255}