summaryrefslogtreecommitdiff
path: root/tests/integration.rs
blob: 99c7a4fa102a5a7a04e4119c0a324871f58454a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use std::env;

use anyhow::Context;
use base64::URL_SAFE_NO_PAD;
use futures::channel::oneshot::channel;
use minor_skulk::{build, db::Db};
use password_hash::rand_core::OsRng;
use rand::RngCore;
use rocket::{
    fairing::AdHoc,
    tokio::{process::Command, spawn},
};
use time::{Duration, OffsetDateTime};

#[macro_use]
extern crate rocket;
extern crate anyhow;

async fn run_pytest(
    markers: &'static str,
    port_offset: u16,
    invite_admin: Option<&'static str>,
) -> anyhow::Result<()> {
    dotenv::dotenv().ok();
    // at this point this is only a test runner to be used by the nix build.
    env::set_var("ROCKET_LOG_LEVEL", "off");
    // allow multiple runs of the server, with different port offsets.
    let port = 8000 + port_offset;
    let mail_port = 2525 + port_offset;

    let (tx, rx) = channel();
    let rocket = rocket::build();
    let figment = rocket
        .figment()
        .clone()
        .merge((rocket::Config::PORT, port))
        .merge(("location", format!("http://localhost:{}", port)))
        .merge(("mail_port", mail_port));
    let figment = if let Some(admin) = invite_admin {
        figment.merge(("invite_only", true)).merge(("invite_admin_address", admin))
    } else {
        figment.merge(("invite_only", false))
    };
    let rocket = build(rocket.configure(figment)).await?.attach(AdHoc::on_liftoff(
        "notify startup",
        move |rocket| {
            Box::pin(async move {
                // add an invite code as-if generated during startup and move it to an
                // env var, emulating the user looking at the logs and copying the code.
                // invite_only needs this to function.
                if invite_admin.is_some() {
                    let db = rocket.state::<Db>().unwrap();
                    let tx = db.begin().await.unwrap();
                    let mut code = [0; 32];
                    OsRng.fill_bytes(&mut code);
                    let code = base64::encode_config(code, URL_SAFE_NO_PAD);
                    tx.add_invite_code(&code, OffsetDateTime::now_utc() + Duration::minutes(5))
                        .await
                        .unwrap();
                    tx.commit().await.unwrap();
                    env::set_var("INVITE_CODE", code);
                }

                tx.send(()).unwrap();
            })
        },
    ));

    spawn(async move { rocket.launch().await });
    let test = spawn(async move {
        rx.await.expect("test trigger failed");
        let mut child = Command::new("pytest")
            .arg("-vvv")
            .arg("-m")
            .arg(markers)
            .env("API_PORT", port.to_string())
            .env("MAIL_PORT", mail_port.to_string())
            .kill_on_drop(true)
            .spawn()
            .expect("failed to spawn");
        child.wait().await
    });

    assert!(test.await.context("starting pytest")?.context("running pytest")?.success());

    Ok(())
}

#[async_test]
async fn open() -> anyhow::Result<()> {
    run_pytest("not invite", 100, None).await
}

#[async_test]
async fn invite_only() -> anyhow::Result<()> {
    run_pytest("invite", 200, Some("test.account.invite@test-auth")).await
}