use std::env; use anyhow::Context; use base64::URL_SAFE_NO_PAD; use chrono::{Duration, Utc}; 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}, }; #[macro_use] extern crate rocket; extern crate anyhow; async fn run_pytest(markers: &'static str, invite: bool) -> 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"); let (tx, rx) = channel(); let rocket = build().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 { let db = rocket.state::().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, Utc::now() + 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) .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<()> { env::set_var("ROCKET_INVITE_ONLY", "false"); run_pytest("not invite", false).await } #[async_test] async fn invite_only() -> anyhow::Result<()> { env::set_var("ROCKET_INVITE_ONLY", "true"); env::set_var("ROCKET_INVITE_ADMIN_ADDRESS", "test.account@test-auth"); run_pytest("invite", true).await }