xmtp_db/test_utils/
impls.rs

1use diesel::result::DatabaseErrorKind;
2/// Extra trait implementations for xmtp_db types
3use rand::{
4    Rng,
5    distributions::{Distribution, Standard},
6    prelude::IteratorRandom,
7};
8use xmtp_proto::types::Cursor;
9
10use crate::{
11    DuplicateItem, NotFound, StorageError, refresh_state::EntityKind,
12    sql_key_store::SqlKeyStoreError,
13};
14
15// choose a random db error in StorageError
16// only cover errors that can happen in db access
17impl Distribution<StorageError> for Standard {
18    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> StorageError {
19        match rng.gen_range(0..=13) {
20            0 => StorageError::DieselConnect(rand_diesel_conn_err(rng)),
21            1 => StorageError::DieselResult(rand_diesel_result(rng)),
22            2 => StorageError::NotFound(rand::random()),
23            3 => StorageError::Duplicate(DuplicateItem::WelcomeId(Some(Cursor::default()))),
24            4 => rand::random(),
25            5 => StorageError::IntentionalRollback,
26            6 => StorageError::DbSerialize,
27            7 => StorageError::DbDeserialize,
28            8 => StorageError::Builder(derive_builder::UninitializedFieldError::new("test field")),
29            10 => rand::random(), // platform
30            11 => StorageError::Prost(prost::DecodeError::new("test random decode error")),
31            12 => StorageError::Connection(rand::random()),
32            13 => StorageError::Conversion(xmtp_proto::ConversionError::Unspecified(
33                "random test error",
34            )),
35            _ => unreachable!(),
36        }
37    }
38}
39
40impl Distribution<SqlKeyStoreError> for Standard {
41    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> SqlKeyStoreError {
42        use SqlKeyStoreError::*;
43        match rng.gen_range(0..=5) {
44            0 => UnsupportedValueTypeBytes,
45            1 => UnsupportedMethod,
46            2 => SerializationError,
47            3 => NotFound,
48            4 => Storage(rand_diesel_result(rng)),
49            5 => Connection(rand::random()),
50            _ => unreachable!(),
51        }
52    }
53}
54
55impl Distribution<NotFound> for Standard {
56    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> NotFound {
57        match rng.gen_range(0..=13) {
58            0 => NotFound::GroupByWelcome(Cursor::default()),
59            1 => NotFound::GroupById(Vec::new()),
60            2 => NotFound::InstallationTimeForGroup(Vec::new()),
61            3 => NotFound::InboxIdForAddress("random test inbox".into()),
62            4 => NotFound::MessageById(Vec::new()),
63            5 => NotFound::DmByInbox("random dm by inbox".into()),
64            6 => NotFound::IntentForToPublish(i32::MAX),
65            7 => NotFound::IntentForPublish(i32::MAX),
66            8 => NotFound::IntentForCommitted(i32::MIN),
67            9 => NotFound::IntentById(i32::MIN),
68            10 => NotFound::RefreshStateByIdKindAndOriginator(
69                Vec::new(),
70                EntityKind::ApplicationMessage,
71                0,
72            ),
73            11 => NotFound::CipherSalt("random salt for testing".into()),
74            12 => NotFound::SyncGroup(xmtp_common::rand_array::<32>().into()),
75            13 => NotFound::MlsGroup,
76            _ => unreachable!(),
77        }
78    }
79}
80
81impl Distribution<crate::ConnectionError> for Standard {
82    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> crate::ConnectionError {
83        match rng.gen_range(0..=1) {
84            0 => crate::ConnectionError::Database(rand_diesel_result(rng)),
85            1 => crate::ConnectionError::Platform(rand::random()),
86            _ => unreachable!(),
87        }
88    }
89}
90
91fn rand_diesel_conn_err<R: Rng + ?Sized>(rng: &mut R) -> diesel::ConnectionError {
92    use diesel::ConnectionError::*;
93    vec![
94        diesel::ConnectionError::InvalidCString(
95            std::ffi::CString::new(b"f\0oo".to_vec()).unwrap_err(),
96        ),
97        BadConnection("rand bad connection err".into()),
98        InvalidConnectionUrl("bad conn url".into()),
99        CouldntSetupConfiguration(rand_diesel_result(rng)),
100    ]
101    .into_iter()
102    .choose(rng)
103    .unwrap()
104}
105
106fn rand_diesel_result<R: Rng + ?Sized>(rng: &mut R) -> diesel::result::Error {
107    use diesel::result::Error::*;
108    vec![
109        InvalidCString(std::ffi::CString::new(b"f\0oo".to_vec()).unwrap_err()),
110        DatabaseError(
111            rand_db_error(rng),
112            Box::new("Random Db Test Error".to_string()),
113        ),
114        diesel::result::Error::NotFound,
115        QueryBuilderError(Box::new(std::io::Error::other("Rand query builder error"))),
116        DeserializationError(Box::new(std::io::Error::other(
117            "rand deserialization error",
118        ))),
119        SerializationError(Box::new(std::io::Error::other("Rand serialization error"))),
120        RollbackTransaction,
121        AlreadyInTransaction,
122        NotInTransaction,
123    ]
124    .into_iter()
125    .choose(rng)
126    .unwrap()
127}
128
129fn rand_db_error<R: Rng + ?Sized>(rng: &mut R) -> DatabaseErrorKind {
130    use DatabaseErrorKind::*;
131    vec![
132        UniqueViolation,
133        ForeignKeyViolation,
134        UnableToSendCommand,
135        SerializationFailure,
136        ReadOnlyTransaction,
137        RestrictViolation,
138        NotNullViolation,
139        CheckViolation,
140        ExclusionViolation,
141        ClosedConnection,
142        Unknown,
143    ]
144    .into_iter()
145    .choose(rng)
146    .unwrap()
147}
148
149#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
150mod native {
151    use crate::PlatformStorageError;
152
153    use super::*;
154    impl Distribution<PlatformStorageError> for Standard {
155        fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> PlatformStorageError {
156            match rng.gen_range(0..=9) {
157                0 => PlatformStorageError::DbConnection(rand_r2d2_err(rng)),
158                1 => PlatformStorageError::PoolNeedsConnection,
159                2 => PlatformStorageError::SqlCipherNotLoaded,
160                3 => PlatformStorageError::SqlCipherKeyIncorrect,
161                4 => PlatformStorageError::DieselResult(rand_diesel_result(rng)),
162                5 => PlatformStorageError::NotFound(rand::random()),
163                6 => PlatformStorageError::Io(std::io::Error::other("test io error")),
164                7 => PlatformStorageError::FromHex(hex::FromHexError::OddLength),
165                8 => PlatformStorageError::DieselConnect(rand_diesel_conn_err(rng)),
166                9 => {
167                    PlatformStorageError::Boxed(Box::new(std::io::Error::other("test boxed error")))
168                }
169                _ => unreachable!(),
170            }
171        }
172    }
173
174    fn rand_r2d2_err<R: Rng + ?Sized>(rng: &mut R) -> diesel::r2d2::Error {
175        match rng.gen_range(0..=1) {
176            0 => diesel::r2d2::Error::ConnectionError(rand_diesel_conn_err(rng)),
177            1 => diesel::r2d2::Error::QueryError(rand_diesel_result(rng)),
178            _ => unreachable!(),
179        }
180    }
181}
182
183#[cfg(all(target_family = "wasm", target_os = "unknown"))]
184mod wasm {
185    use crate::PlatformStorageError;
186
187    use super::*;
188    impl Distribution<crate::PlatformStorageError> for Standard {
189        fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> crate::PlatformStorageError {
190            match rng.gen_range(0..=2) {
191                0 => PlatformStorageError::SAH(sqlite_wasm_rs::sahpool_vfs::OpfsSAHError::Generic(
192                    "rand test opfs err".to_string(),
193                )),
194                1 => PlatformStorageError::Connection(rand_diesel_conn_err(rng)),
195                2 => PlatformStorageError::DieselResult(rand_diesel_result(rng)),
196                _ => unreachable!(),
197            }
198        }
199    }
200}