#
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}