1use super::Coven;
5use crate::*;
6use coven_crypto::custody::{Keychain, KeyringCustody, StoreKeychain};
7use coven_foundation::files::StoreDirectories;
8use coven_foundation::network_watch::{NetworkSource, TestNetwork};
9use std::path::{Path, PathBuf};
10use std::sync::Arc;
11
12#[cfg(test)]
13#[path = "test_utils_tests.rs"]
14mod tests;
15
16impl Coven {
17 pub fn in_memory(app_dir: PathBuf) -> Self {
23 Self::in_memory_reporting_to(app_dir, crate::task::HeardBugs::shared())
24 }
25
26 pub fn in_memory_reporting_to(app_dir: PathBuf, bugs: crate::task::BugSinkRef) -> Self {
29 Self::testing(app_dir, Keychain::in_memory("coven-test").expect("valid service name"), bugs)
30 }
31
32 pub fn in_memory_with_saved_keychain(app_dir: PathBuf, path: &Path) -> Result<Self, KeyError> {
37 Ok(Self::testing(
38 app_dir,
39 Keychain::in_memory_with_saved_device_entries("coven-test", path)?,
40 crate::task::HeardBugs::shared(),
41 ))
42 }
43
44 pub fn with_block_store(app_dir: PathBuf) -> Self {
48 Self::testing(
49 app_dir,
50 Keychain::in_memory_block_store("coven-test").expect("valid service name"),
51 crate::task::HeardBugs::shared(),
52 )
53 }
54
55 #[cfg(test)]
58 pub(crate) fn with_keychain(app_dir: PathBuf, keychain: Arc<Keychain>) -> Self {
59 Self::testing(app_dir, keychain, crate::task::HeardBugs::shared())
60 }
61
62 #[cfg(test)]
65 pub(crate) fn with_keychain_reporting_to(
66 app_dir: PathBuf,
67 keychain: Arc<Keychain>,
68 bugs: crate::task::BugSinkRef,
69 ) -> Self {
70 Self::testing(app_dir, keychain, bugs)
71 }
72
73 #[cfg(test)]
76 pub(crate) fn directories(&self) -> &StoreDirectories {
77 &self.directories
78 }
79
80 fn testing(app_dir: PathBuf, keychain: Arc<Keychain>, bugs: crate::task::BugSinkRef) -> Self {
81 Self {
82 directories: StoreDirectories::new(app_dir, bugs),
83 keychain,
84 network: NetworkSource::Test(TestNetwork::new(true, false)),
85 }
86 }
87
88 pub fn another_device(&self, app_dir: PathBuf) -> Self {
94 Self::testing(app_dir, self.keychain.another_device(), self.directories.bugs().clone())
95 }
96
97 pub fn set_up_new_device(&self, app_dir: PathBuf) -> Self {
101 Self::testing(app_dir, self.keychain.set_up_new_device(), self.directories.bugs().clone())
102 }
103
104 pub fn restore_new_device_from_cloud(&self, app_dir: PathBuf) -> Self {
108 Self::testing(
109 app_dir,
110 self.keychain.restore_new_device_from_cloud(),
111 self.directories.bugs().clone(),
112 )
113 }
114
115 pub fn back_up_to_cloud(&self) {
119 self.keychain.back_up_to_cloud();
120 }
121
122 pub fn set_end_to_end_encryption(&self, available: bool) {
125 self.keychain.set_end_to_end_encryption(available);
126 }
127
128 pub async fn create_closed_store(
130 &self,
131 name: &str,
132 ids: IdSourceRef,
133 ) -> Result<StoreDir, StoreCreationError> {
134 let handle = self
135 .builder()
136 .id_source(ids)
137 .synced_tables(Vec::new())
138 .migrations(Vec::new())
139 .create_store(name)
140 .await?;
141 let directory = self.store_dir(&handle.store_id());
142 handle.close().await?;
143 Ok(directory)
144 }
145
146 pub fn create_store_dir(
148 &self,
149 id: StoreId,
150 ids: &dyn IdSource,
151 ) -> Result<StoreDir, crate::DirectoryCreationError> {
152 self.directories.create_store_dir(id, ids)
153 }
154
155 fn drive_network(&self, change: impl FnOnce(&TestNetwork)) {
157 match &self.network {
158 NetworkSource::Test(network) => change(network),
159 NetworkSource::Platform | NetworkSource::Refused(_) => {
160 panic!("an installation made for a test drives its network")
161 }
162 }
163 }
164
165 pub fn join_network(&self, metered: bool) {
168 self.drive_network(|network| network.join(metered));
169 }
170
171 pub fn lose_network(&self) {
173 self.drive_network(TestNetwork::lose);
174 }
175
176 pub fn network_cannot_tell(&self) {
179 self.drive_network(TestNetwork::cannot_tell);
180 }
181
182 pub fn set_network_metered(&self, metered: bool) {
184 self.drive_network(|network| network.set_metered(metered));
185 }
186
187 pub fn keep_store_keys(
189 &self,
190 directory: &StoreDir,
191 keys: &StoreKeyring,
192 ) -> Result<(), KeyError> {
193 let keychain = Arc::new(StoreKeychain::new(self.keychain.clone(), directory.id()));
194 Custody::<StoreKeyring>::persist(&KeyringCustody::new(keychain), keys)
195 }
196
197 pub fn save_device_keychain(&self, path: &Path) -> Result<(), KeyError> {
202 self.keychain.save_device_entries(path)
203 }
204
205 pub fn lose_storage_credentials(&self, store: StoreId) -> Result<(), KeyError> {
210 StoreKeychain::new(self.keychain.clone(), store).delete_storage_credentials()
211 }
212
213 #[cfg(test)]
215 pub(crate) fn store_keychain(&self, store: StoreId) -> StoreKeychain {
216 StoreKeychain::new(self.keychain.clone(), store)
217 }
218
219 #[cfg(test)]
221 pub(crate) fn person_entries(&self, store: StoreId) -> coven_crypto::custody::PersonEntries {
222 coven_crypto::custody::PersonEntries::new(self.keychain.clone(), store)
223 }
224
225 #[cfg(test)]
227 pub(crate) fn refuse_person_entry_operations(&self, refuse: bool) {
228 self.keychain.refuse_person_entry_operations(refuse);
229 }
230
231 #[cfg(test)]
233 pub(crate) async fn readable_stores(&self) -> Vec<StoreInfo> {
234 self.stores()
235 .await
236 .unwrap()
237 .into_iter()
238 .map(|listed| match listed {
239 ListedStore::Readable(info) => info,
240 ListedStore::Unreadable { id, reason } => {
241 panic!("store {id} is listed as unreadable: {reason}")
242 }
243 })
244 .collect()
245 }
246
247 pub fn fail_next_keychain_operation(&self) {
249 self.keychain.fail_next_operation();
250 }
251
252 #[cfg(test)]
253 pub(crate) fn fail_keychain_operations_after(&self, successes: &[usize]) {
254 self.keychain.fail_operations_after(successes);
255 }
256}