1use super::*;
2
3impl StoreDatabase {
4 pub async fn store_write_journal_counts_for_test(&self) -> Result<(i64, i64), DbError> {
5 self.call_store(|session| session.store_write_journal_counts_for_test())
6 .await
7 }
8
9 pub async fn seed_prepared_audience_write_for_test(
10 &self,
11 write_id: WriteId,
12 changeset_hash: ObjectHash,
13 ) -> Result<(), DbError> {
14 self.call_store(move |session| {
15 session.seed_prepared_audience_write_for_test(&write_id, changeset_hash)
16 })
17 .await
18 }
19
20 pub async fn persist_prepared_audience_objects_for_test(
21 &self,
22 write_id: WriteId,
23 remotes: Vec<coven_protocol::remote_object::RemoteObjectRecord>,
24 packages: Vec<crate::PreparedAudiencePackage>,
25 blobs: Vec<crate::PreparedAudienceBlob>,
26 ) -> Result<(), DbError> {
27 self.call_store(move |session| {
28 session
29 .persist_prepared_audience_objects_for_test(&write_id, &remotes, &packages, &blobs)
30 })
31 .await
32 }
33
34 pub async fn seed_local_release_rows_for_test(
35 &self,
36 routing_encryption: Option<coven_keys::encryption::EncryptionService>,
37 note_id: &str,
38 photo_id: &str,
39 cloud_path: &str,
40 bytes: &[u8],
41 ) {
42 let note_id = note_id.to_string();
43 let photo_id = photo_id.to_string();
44 let cloud_path = cloud_path.to_string();
45 let size = i64::try_from(bytes.len()).expect("test blob size fits SQLite");
46 let hash = coven_protocol::blob::content_hash(bytes);
47 self.run_host_store_write_for_test(routing_encryption, None, move |transaction| {
48 transaction
49 .execute(
50 "INSERT INTO notes (id, title, body, shared, _updated_at, created_at)
51 VALUES (?1, 'Release', NULL, 0,
52 '0000000001000-0000-A', '2026-01-01')",
53 [¬e_id],
54 )
55 .map_err(DbError::from)?;
56 transaction
57 .execute(
58 "INSERT INTO note_photos
59 (id, note_id, kind, size, hash, _updated_at, created_at, cloud_path)
60 VALUES (?1, ?2, 'image', ?3, ?4,
61 '0000000001000-0000-A', '2026-01-01', ?5)",
62 rusqlite::params![photo_id, note_id, size, hash, cloud_path],
63 )
64 .map(|_| ())
65 .map_err(DbError::from)
66 })
67 .await
68 .expect("seed exact release rows");
69 }
70
71 pub async fn register_external_blob_for_test(
72 &self,
73 table: &str,
74 row_id: &str,
75 path: &std::path::Path,
76 ) {
77 let reference = self
78 .row_blob_ref(table, row_id)
79 .await
80 .expect("load exact Local row blob reference");
81 let path = path.to_path_buf();
82 self.call_store(move |session| session.register_external_blob_for_test(&reference, &path))
83 .await
84 .expect("register exact external blob reference");
85 }
86
87 pub async fn enqueue_blob_upload_for_test(
88 &self,
89 root_table: &str,
90 root_id: &str,
91 reference: &coven_protocol::blob::RowBlobRef,
92 source_path: &std::path::Path,
93 created_at: &str,
94 ) -> Result<(), DbError> {
95 let reference = reference.clone();
96 let root_table = root_table.to_string();
97 let root_id = root_id.to_string();
98 let source_path = source_path.to_path_buf();
99 let created_at = created_at.to_string();
100 self.call_store(move |session| {
101 session.enqueue_blob_upload_for_test(
102 &root_table,
103 &root_id,
104 &reference,
105 &source_path,
106 &created_at,
107 )
108 })
109 .await
110 }
111
112 pub async fn insert_fixture_position_for_test(
113 &self,
114 note_id: &str,
115 ) -> Result<(), crate::HostWriteError<DbError>> {
116 let note_id = note_id.to_string();
117 self.run_host_store_write_for_test(None, None, move |transaction| {
118 transaction
119 .execute(
120 "INSERT INTO notes (id, title, shared, _updated_at, created_at)
121 VALUES (?1, 'fixture position', 1,
122 '0000000001000-0000-A', '2026-01-01')",
123 [note_id],
124 )
125 .map(|_| ())
126 .map_err(DbError::from)
127 })
128 .await
129 .map(|_| ())
130 }
131
132 pub async fn run_host_store_write_for_test<R>(
133 &self,
134 routing_encryption: Option<coven_keys::encryption::EncryptionService>,
135 blob_staging: Option<Box<dyn crate::AudienceBlobMoveStaging>>,
136 operation: impl for<'context, 'connection> FnOnce(
137 crate::SqlContext<'context, 'connection>,
138 ) -> Result<R, DbError>
139 + Send
140 + 'static,
141 ) -> Result<coven_protocol::write::WriteReceipt<R>, crate::HostWriteError<DbError>>
142 where
143 R: Send + 'static,
144 {
145 crate::StoreRowWrites::new(self.clone())
146 .execute(
147 crate::HostWriteOperation::new(crate::WriteBatch::new(), operation),
148 routing_encryption,
149 blob_staging,
150 )
151 .await
152 }
153
154 pub async fn cleanup_intent_count_for_test(
155 &self,
156 namespace: &str,
157 blob_id: &str,
158 ) -> Result<i64, DbError> {
159 let namespace = namespace.to_string();
160 let blob_id = blob_id.to_string();
161 self.call_store(move |session| session.cleanup_intent_count_for_test(&namespace, &blob_id))
162 .await
163 }
164
165 pub async fn coven_table_exists_for_test(
166 &self,
167 table: crate::DatabaseTestTable,
168 ) -> Result<bool, DbError> {
169 self.call_store(move |session| session.coven_table_exists_for_test(table))
170 .await
171 }
172
173 pub async fn install_store_write_failure_trigger_for_test(&self) -> Result<(), DbError> {
174 self.call_store(|session| session.install_store_write_failure_trigger_for_test())
175 .await
176 }
177
178 pub async fn remove_store_write_failure_trigger_for_test(&self) -> Result<(), DbError> {
179 self.call_store(|session| session.remove_store_write_failure_trigger_for_test())
180 .await
181 }
182
183 pub async fn write_blob_facts_for_test(&self, write_id: WriteId) -> Result<String, DbError> {
184 self.call_store(move |session| session.write_blob_facts_for_test(&write_id))
185 .await
186 }
187
188 pub async fn install_test_active_circle(
189 &self,
190 label: String,
191 ) -> Result<coven_protocol::circle::CircleId, DbError> {
192 self.call_store(move |session| Ok(session.install_test_active_circle(&label)))
193 .await
194 }
195
196 pub async fn install_test_active_circles(
197 &self,
198 labels: Vec<String>,
199 ) -> Result<Vec<coven_protocol::circle::CircleId>, DbError> {
200 self.call_store(move |session| {
201 Ok(labels
202 .iter()
203 .map(|label| session.install_test_active_circle(label))
204 .collect())
205 })
206 .await
207 }
208
209 pub async fn install_test_inactive_circle(
210 &self,
211 label: String,
212 ) -> Result<coven_protocol::circle::CircleId, DbError> {
213 self.call_store(move |session| Ok(session.install_test_inactive_circle(&label)))
214 .await
215 }
216
217 pub async fn install_test_active_circle_with_control(
218 &self,
219 label: String,
220 ) -> Result<
221 (
222 coven_protocol::circle::CircleId,
223 coven_protocol::circle::CircleControlCoord,
224 ),
225 DbError,
226 > {
227 self.call_store(move |session| Ok(session.install_test_active_circle_with_control(&label)))
228 .await
229 }
230
231 pub async fn insert_write_status_for_test(
232 &self,
233 write_id: WriteId,
234 status: coven_protocol::write::WriteStatus,
235 ) -> Result<(), DbError> {
236 let base = serde_json::json!({ "dependencies": {} }).to_string();
237 let status = serde_json::to_string(&status)
238 .map_err(|error| DbError::context("serialize write status", error))?;
239 self.call_store(move |session| {
240 session.insert_write_status_for_test(&write_id, &status, &base)
241 })
242 .await
243 }
244
245 pub async fn delete_write_for_test(&self, write_id: WriteId) -> Result<(), DbError> {
246 self.call_store(move |session| session.delete_write_for_test(&write_id))
247 .await
248 }
249
250 pub async fn store_write_partition_for_test(
251 &self,
252 write_id: &WriteId,
253 ) -> Result<Vec<u8>, DbError> {
254 let write_id = write_id.clone();
255 self.call_store(move |session| session.store_write_partition_for_test(&write_id))
256 .await
257 }
258
259 pub async fn write_blob_lease_count_for_test(
260 &self,
261 write_id: &WriteId,
262 ) -> Result<i64, DbError> {
263 let write_id = write_id.clone();
264 self.call_store(move |session| session.write_blob_lease_count_for_test(&write_id))
265 .await
266 }
267
268 pub async fn latest_materialized_commit_coordinate_for_test(
269 &self,
270 ) -> Result<(String, u64), DbError> {
271 self.call_store(|session| session.latest_materialized_commit_coordinate_for_test())
272 .await
273 }
274
275 pub async fn test_query_optional_text(&self, sql: String) -> Result<Option<String>, DbError> {
279 self.call_store(move |session| session.test_query_optional_text(&sql))
280 .await
281 }
282
283 pub async fn replay_row_count_for_test(
284 &self,
285 root: coven_protocol::store_commit::StoreRootRef,
286 table: String,
287 ) -> Result<i64, DbError> {
288 self.call_store(move |session| session.replay_row_count_for_test(&root, &table))
289 .await
290 }
291
292 pub async fn compare_circle_bootstrap_replay_with_missing_coverage_for_test(
293 &self,
294 root: coven_protocol::store_commit::StoreRootRef,
295 routing_key: coven_protocol::circle::RowRoutingKey,
296 historical_id: String,
297 late_id: String,
298 ) -> Result<(i64, i64, i64, i64), DbError> {
299 self.call_store(move |session| {
300 session.compare_circle_bootstrap_replay_with_missing_coverage_for_test(
301 &root,
302 &routing_key,
303 &historical_id,
304 &late_id,
305 )
306 })
307 .await
308 }
309
310 pub async fn circle_bootstrap_coverage_count_for_test(
311 &self,
312 circle_id: coven_protocol::circle::CircleId,
313 ) -> Result<i64, DbError> {
314 self.call_store(move |session| session.circle_bootstrap_coverage_count_for_test(circle_id))
315 .await
316 }
317
318 pub async fn reject_missing_circle_bootstrap_payload_claim_for_test(
319 &self,
320 circle_id: coven_protocol::circle::CircleId,
321 ) -> Result<String, DbError> {
322 self.call_store(move |session| {
323 session.reject_missing_circle_bootstrap_payload_claim_for_test(circle_id)
324 })
325 .await
326 }
327
328 pub async fn reject_changed_circle_bootstrap_image_hash_for_test(
329 &self,
330 circle_id: coven_protocol::circle::CircleId,
331 root: coven_protocol::store_commit::StoreRootRef,
332 activation_commit: &StoreBatchCommitRef,
333 ) -> Result<String, DbError> {
334 let activation_commit = activation_commit.clone();
335 self.call_store(move |session| {
336 session.reject_changed_circle_bootstrap_image_hash_for_test(
337 circle_id,
338 &root,
339 &activation_commit,
340 )
341 })
342 .await
343 }
344
345 pub async fn circle_bootstrap_failure_state_for_test(
346 &self,
347 blob_id: String,
348 circle_id: coven_protocol::circle::CircleId,
349 control: String,
350 remote_object_id: String,
351 ) -> Result<(bool, bool, bool, bool), DbError> {
352 self.call_store(move |session| {
353 session.circle_bootstrap_failure_state_for_test(
354 &blob_id,
355 circle_id,
356 &control,
357 remote_object_id,
358 )
359 })
360 .await
361 }
362
363 pub async fn circle_bootstrap_replay_for_control_for_test(
364 &self,
365 circle_id: coven_protocol::circle::CircleId,
366 control: coven_protocol::circle::CircleControlCoord,
367 ) -> Result<Option<coven_protocol::circle_activation::VerifiedCircleImage>, DbError> {
368 self.call_store(move |session| {
369 session.circle_bootstrap_replay_for_control_for_test(circle_id, &control)
370 })
371 .await
372 }
373
374 pub async fn forge_circle_close_exclusion_for_test(
375 &self,
376 circle_id: coven_protocol::circle::CircleId,
377 ) -> Result<(), DbError> {
378 self.call_store(move |session| session.forge_circle_close_exclusion_for_test(circle_id))
379 .await
380 }
381
382 pub async fn transfer_prepared_write_to_for_test(
383 &self,
384 destination: &Self,
385 write_id: &WriteId,
386 ) -> Result<(), DbError> {
387 let source_write_id = write_id.clone();
388 let transfer = self
389 .call_store(move |session| session.export_prepared_write(&source_write_id))
390 .await?;
391
392 let destination_write_id = write_id.clone();
393 destination
394 .call_store(move |session| {
395 session.import_prepared_write(&destination_write_id, transfer)
396 })
397 .await
398 }
399}