db_core/
tests.rs

1// Copyright (C) 2022  Aravinth Manivannan <realaravinth@batsense.net>
2// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! Test utilities
7use crate::errors::*;
8use crate::prelude::*;
9
10/// easy traffic pattern
11pub const TRAFFIC_PATTERN: TrafficPattern = TrafficPattern {
12    avg_traffic: 500,
13    peak_sustainable_traffic: 5_000,
14    broke_my_site_traffic: Some(10_000),
15};
16
17/// levels for complex captcha config
18pub const LEVELS: [Level; 3] = [
19    Level {
20        difficulty_factor: 1,
21        visitor_threshold: 1,
22    },
23    Level {
24        difficulty_factor: 2,
25        visitor_threshold: 2,
26    },
27    Level {
28        difficulty_factor: 3,
29        visitor_threshold: 3,
30    },
31];
32
33/// test all database functions
34pub async fn database_works<'a, T: MCDatabase>(
35    db: &T,
36    p: &Register<'a>,
37    c: &CreateCaptcha<'a>,
38    l: &[Level],
39    tp: &TrafficPattern,
40    an: &AddNotification<'a>,
41) {
42    assert!(db.ping().await, "ping test");
43
44    if db.username_exists(p.username).await.unwrap() {
45        db.delete_user(p.username).await.unwrap();
46        assert!(
47            !db.username_exists(p.username).await.unwrap(),
48            "user is deleted so username shouldn't exist"
49        );
50    }
51
52    assert!(matches!(
53        db.get_secret(&p.username).await,
54        Err(DBError::AccountNotFound)
55    ));
56
57    db.register(p).await.unwrap();
58
59    assert!(matches!(db.register(&p).await, Err(DBError::UsernameTaken)));
60
61    // testing get secret
62    let secret = db.get_secret(p.username).await.unwrap();
63    assert_eq!(secret.secret, p.secret, "user secret matches");
64
65    // testing update secret: setting secret = username
66    db.update_secret(p.username, p.username).await.unwrap();
67
68    let secret = db.get_secret(p.username).await.unwrap();
69    assert_eq!(
70        secret.secret, p.username,
71        "user secret matches username; as set by previous step"
72    );
73
74    // testing get_password
75
76    // with username
77    let name_hash = db.get_password(&Login::Username(p.username)).await.unwrap();
78    assert_eq!(name_hash.hash, p.hash, "user password matches");
79
80    assert_eq!(name_hash.username, p.username, "username matches");
81
82    // with email
83    let mut name_hash = db
84        .get_password(&Login::Email(p.email.as_ref().unwrap()))
85        .await
86        .unwrap();
87    assert_eq!(name_hash.hash, p.hash, "user password matches");
88    assert_eq!(name_hash.username, p.username, "username matches");
89
90    // testing get_email
91    assert_eq!(
92        db.get_email(p.username)
93            .await
94            .unwrap()
95            .as_ref()
96            .unwrap()
97            .as_str(),
98        p.email.unwrap()
99    );
100
101    // testing email exists
102    assert!(
103        db.email_exists(p.email.as_ref().unwrap()).await.unwrap(),
104        "user is registered so email should exist"
105    );
106    assert!(
107        db.username_exists(p.username).await.unwrap(),
108        "user is registered so username should exist"
109    );
110
111    // update password test. setting password = username
112    name_hash.hash = name_hash.username.clone();
113    db.update_password(&name_hash).await.unwrap();
114
115    let name_hash = db.get_password(&Login::Username(p.username)).await.unwrap();
116    assert_eq!(
117        name_hash.hash, p.username,
118        "user password matches with changed value"
119    );
120    assert_eq!(name_hash.username, p.username, "username matches");
121
122    // update username to p.email
123    assert!(
124        !db.username_exists(p.email.as_ref().unwrap()).await.unwrap(),
125        "user with p.email doesn't exist. pre-check to update username to p.email"
126    );
127    db.update_username(p.username, p.email.as_ref().unwrap())
128        .await
129        .unwrap();
130    assert!(
131        db.username_exists(p.email.as_ref().unwrap()).await.unwrap(),
132        "user with p.email exist post-update"
133    );
134
135    // deleting user for re-registration with email = None
136    db.delete_user(p.email.as_ref().unwrap()).await.unwrap();
137    assert!(
138        !db.username_exists(p.email.as_ref().unwrap()).await.unwrap(),
139        "user is deleted so username shouldn't exist"
140    );
141
142    // register with email = None
143    let mut p2 = p.clone();
144    p2.email = None;
145    db.register(&p2).await.unwrap();
146    assert!(
147        db.username_exists(p2.username).await.unwrap(),
148        "user is registered so username should exist"
149    );
150    assert!(
151        !db.email_exists(p.email.as_ref().unwrap()).await.unwrap(),
152        "user registration with email is deleted; so email shouldn't exist"
153    );
154
155    // testing get_email = None
156    assert_eq!(db.get_email(p.username).await.unwrap(), None);
157
158    // testing update email
159    let update_email = UpdateEmail {
160        username: p.username,
161        new_email: p.email.as_ref().unwrap(),
162    };
163    db.update_email(&update_email).await.unwrap();
164    println!(
165        "null user email: {}",
166        db.email_exists(p.email.as_ref().unwrap()).await.unwrap()
167    );
168    assert!(
169        db.email_exists(p.email.as_ref().unwrap()).await.unwrap(),
170        "user was with empty email but email is set; so email should exist"
171    );
172
173    /*
174     * test notification workflows
175     * 1. Add notifications: a minimum of two, to mark as read and test if it has affected it
176     * 2. Get unread notifications
177     * 3. Mark a notification read, check if it has affected Step #2
178     */
179
180    // 1. add notification
181    db.create_notification(an).await.unwrap();
182    db.create_notification(an).await.unwrap();
183
184    // 2. Get notifications
185    let notifications = db.get_all_unread_notifications(an.to).await.unwrap();
186    assert_eq!(notifications.len(), 2);
187    assert_eq!(notifications[0].heading.as_ref().unwrap(), an.heading);
188
189    // 3. mark a notification read
190    db.mark_notification_read(an.to, notifications[0].id.unwrap())
191        .await
192        .unwrap();
193    let new_notifications = db.get_all_unread_notifications(an.to).await.unwrap();
194    assert_eq!(new_notifications.len(), 1);
195
196    // create captcha
197    db.create_captcha(p.username, c).await.unwrap();
198    assert!(db.captcha_exists(None, c.key).await.unwrap());
199    assert!(db.captcha_exists(Some(p.username), c.key).await.unwrap());
200
201    // get secret from captcha key
202    let secret_from_captcha = db.get_secret_from_captcha(&c.key).await.unwrap();
203    assert_eq!(secret_from_captcha.secret, p.secret, "user secret matches");
204
205    // get captcha configuration
206    let captcha = db.get_captcha_config(p.username, c.key).await.unwrap();
207    assert_eq!(captcha.key, c.key);
208    assert_eq!(captcha.duration, c.duration);
209    assert_eq!(captcha.description, c.description);
210
211    // get all captchas that belong to user
212    let all_user_captchas = db.get_all_user_captchas(p.username).await.unwrap();
213    assert_eq!(all_user_captchas.len(), 1);
214    assert_eq!(all_user_captchas[0], captcha);
215
216    // get captcha cooldown duration
217    assert_eq!(db.get_captcha_cooldown(c.key).await.unwrap(), c.duration);
218
219    // add traffic pattern
220    db.add_traffic_pattern(p.username, c.key, tp).await.unwrap();
221    assert_eq!(
222        &db.get_traffic_pattern(p.username, c.key).await.unwrap(),
223        tp
224    );
225
226    // get all traffic patterns
227    let patterns = db.get_all_easy_captchas(10, 0).await.unwrap();
228    assert_eq!(patterns.get(0).as_ref().unwrap().key, c.key);
229    assert_eq!(&patterns.get(0).unwrap().traffic_pattern, tp);
230
231    // delete traffic pattern
232    db.delete_traffic_pattern(p.username, c.key).await.unwrap();
233    assert!(
234        matches!(
235            db.get_traffic_pattern(p.username, c.key).await,
236            Err(DBError::TrafficPatternNotFound)
237        ),
238        "deletion successful; traffic pattern no longer exists"
239    );
240
241    // add captcha levels
242    db.add_captcha_levels(p.username, c.key, l).await.unwrap();
243
244    // get captcha levels with username
245    let levels = db
246        .get_captcha_levels(Some(p.username), c.key)
247        .await
248        .unwrap();
249    assert_eq!(levels, l);
250    // get captcha levels without username
251    let levels = db.get_captcha_levels(None, c.key).await.unwrap();
252    assert_eq!(levels, l);
253
254    /*
255     * Test stats
256     * 1. record fetch config
257     * 2. record solve
258     * 3. record token verify
259     * 4. fetch config fetches
260     * 5. fetch solves
261     * 6. fetch token verify
262     */
263
264    assert!(db
265        .fetch_config_fetched(p.username, c.key)
266        .await
267        .unwrap()
268        .is_empty());
269    assert!(db.fetch_solve(p.username, c.key).await.unwrap().is_empty());
270    assert!(db
271        .fetch_confirm(p.username, c.key)
272        .await
273        .unwrap()
274        .is_empty());
275
276    db.record_fetch(c.key).await.unwrap();
277    db.record_solve(c.key).await.unwrap();
278    db.record_confirm(c.key).await.unwrap();
279
280    // analytics start
281    db.analytics_create_psuedo_id_if_not_exists(c.key)
282        .await
283        .unwrap();
284    let psuedo_id = db
285        .analytics_get_psuedo_id_from_capmaign_id(c.key)
286        .await
287        .unwrap();
288    assert_eq!(
289        vec![psuedo_id.clone()],
290        db.analytics_get_all_psuedo_ids(0).await.unwrap()
291    );
292    assert!(db.analytics_get_all_psuedo_ids(1).await.unwrap().is_empty());
293
294    db.analytics_create_psuedo_id_if_not_exists(c.key)
295        .await
296        .unwrap();
297    assert_eq!(
298        psuedo_id,
299        db.analytics_get_psuedo_id_from_capmaign_id(c.key)
300            .await
301            .unwrap()
302    );
303
304    assert_eq!(
305        c.key,
306        db.analytics_get_capmaign_id_from_psuedo_id(&psuedo_id)
307            .await
308            .unwrap()
309    );
310
311    let analytics = CreatePerformanceAnalytics {
312        time: 1,
313        difficulty_factor: 1,
314        worker_type: "wasm".into(),
315    };
316
317    assert_eq!(
318        db.stats_get_num_logs_under_time(analytics.time)
319            .await
320            .unwrap(),
321        0
322    );
323
324    db.analysis_save(c.key, &analytics).await.unwrap();
325    assert_eq!(
326        db.stats_get_num_logs_under_time(analytics.time)
327            .await
328            .unwrap(),
329        1
330    );
331    assert_eq!(
332        db.stats_get_num_logs_under_time(analytics.time - 1)
333            .await
334            .unwrap(),
335        0
336    );
337    let limit = 50;
338    let mut offset = 0;
339    let a = db.analytics_fetch(c.key, limit, offset).await.unwrap();
340    assert_eq!(a[0].time, analytics.time);
341    assert_eq!(a[0].difficulty_factor, analytics.difficulty_factor);
342    assert_eq!(a[0].worker_type, analytics.worker_type);
343    offset += 1;
344    assert!(db
345        .analytics_fetch(c.key, limit, offset)
346        .await
347        .unwrap()
348        .is_empty());
349
350    db.analytics_delete_all_records_for_campaign(c.key)
351        .await
352        .unwrap();
353    assert_eq!(db.analytics_fetch(c.key, 1000, 0).await.unwrap().len(), 0);
354    assert!(!db.analytics_captcha_is_published(c.key).await.unwrap());
355
356    let rest_analytics = [
357        CreatePerformanceAnalytics {
358            time: 2,
359            difficulty_factor: 2,
360            worker_type: "wasm".into(),
361        },
362        CreatePerformanceAnalytics {
363            time: 3,
364            difficulty_factor: 3,
365            worker_type: "wasm".into(),
366        },
367        CreatePerformanceAnalytics {
368            time: 4,
369            difficulty_factor: 4,
370            worker_type: "wasm".into(),
371        },
372        CreatePerformanceAnalytics {
373            time: 5,
374            difficulty_factor: 5,
375            worker_type: "wasm".into(),
376        },
377    ];
378    for a in rest_analytics.iter() {
379        db.analysis_save(c.key, &a).await.unwrap();
380    }
381    assert!(db
382        .stats_get_entry_at_location_for_time_limit_asc(1, 2)
383        .await
384        .unwrap()
385        .is_none());
386    assert_eq!(
387        db.stats_get_entry_at_location_for_time_limit_asc(2, 1)
388            .await
389            .unwrap(),
390        Some(2)
391    );
392    assert_eq!(
393        db.stats_get_entry_at_location_for_time_limit_asc(3, 2)
394            .await
395            .unwrap(),
396        Some(3)
397    );
398
399    db.analytics_delete_all_records_for_campaign(c.key)
400        .await
401        .unwrap();
402    // analytics end
403
404    // nonce tracking start
405    assert_eq!(
406        db.get_max_nonce_for_level(c.key, l[0].difficulty_factor)
407            .await
408            .unwrap(),
409        0
410    );
411    db.update_max_nonce_for_level(c.key, l[0].difficulty_factor, 1000)
412        .await
413        .unwrap();
414    assert_eq!(
415        db.get_max_nonce_for_level(c.key, l[0].difficulty_factor)
416            .await
417            .unwrap(),
418        1000
419    );
420    db.update_max_nonce_for_level(c.key, l[0].difficulty_factor, 10_000)
421        .await
422        .unwrap();
423    assert_eq!(
424        db.get_max_nonce_for_level(c.key, l[0].difficulty_factor)
425            .await
426            .unwrap(),
427        10_000
428    );
429    // nonce tracking end
430
431    assert_eq!(db.fetch_solve(p.username, c.key).await.unwrap().len(), 1);
432    assert_eq!(
433        db.fetch_config_fetched(p.username, c.key)
434            .await
435            .unwrap()
436            .len(),
437        1
438    );
439    assert_eq!(db.fetch_solve(p.username, c.key).await.unwrap().len(), 1);
440    assert_eq!(db.fetch_confirm(p.username, c.key).await.unwrap().len(), 1);
441
442    // update captcha key; set key = username;
443    db.update_captcha_key(p.username, c.key, p.username)
444        .await
445        .unwrap();
446    // checking for captcha with old key; shouldn't exist
447    assert!(!db.captcha_exists(Some(p.username), c.key).await.unwrap());
448    // checking for captcha with new key; shouldn exist
449    assert!(db
450        .captcha_exists(Some(p.username), p.username)
451        .await
452        .unwrap());
453
454    // delete captcha levels
455    db.delete_captcha_levels(p.username, c.key).await.unwrap();
456
457    // update captcha; set description = username and duration *= duration;
458    let mut c2 = c.clone();
459    c2.duration *= c2.duration;
460    c2.description = p.username;
461    db.update_captcha_metadata(p.username, &c2).await.unwrap();
462
463    // delete captcha; updated key = p.username so invoke delete with it
464    db.delete_captcha(p.username, p.username).await.unwrap();
465    assert!(!db.captcha_exists(Some(p.username), c.key).await.unwrap());
466}