use std::{process::{Command, Child}, env::temp_dir, path::PathBuf, fs::remove_dir_all, time::Duration, panic}; use nom_nom_gc::models::{Configuration, AppState, User, UserUuid, BinaryCache, Project}; use tokio::time::sleep; use uuid::Uuid; use anyhow::{anyhow, Result, Context}; struct TestDB { path: PathBuf, pid: Child, db_name: String, port: u16 } async fn setup_db() -> Result { let mut dbdir = temp_dir(); let dir_uuid = Uuid::new_v4(); let db_name = "nom-nom-integration-test".to_string(); let port: u16 = 12345; dbdir.push(dir_uuid.to_string()); let dbdir_str = dbdir.to_str().unwrap(); Command::new("initdb") .arg(&dbdir) .spawn()? .wait()?; let db_proc_handle = Command::new("postgres") .args(["-D", dbdir_str, "-c", &format!("unix_socket_directories={}", dbdir_str), "-c", "listen_addresses=", "-c", &format!("port={}", &port.to_string())]) .spawn()?; sleep(Duration::from_secs(1)).await; Command::new("createdb") .args([ "-h", dbdir_str, "-p", &port.to_string(), &db_name]) .spawn()? .wait()?; Ok(TestDB{ path: dbdir, pid: db_proc_handle, db_name, port }) } fn teardown_db(mut db: TestDB) -> Result <()> { println!("Stopping postgres"); db.pid.kill()?; remove_dir_all(db.path)?; Ok(()) } /** This function is the actual test main function. We can't inline it in main, we need to make sure to teardown the DB setup even if it fails. */ async fn run_test_db(db: &TestDB) -> Result<()> { let mut dbpath = db.path.to_str().unwrap().to_string(); dbpath.push('/'); let conf = Configuration { url: "http://localhost:9000".to_string(), db_port: Some(db.port), db_host: Some(dbpath), db_name: db.db_name.clone(), s3_bucket: "dummy".to_string(), s3_endpoint: "dummy".to_string(), s3_region: "dummy".to_string(), s3_access_key_filepath: "dummy".to_string(), s3_secret_key_filepath: "dummy".to_string(), }; let state = AppState::new(conf).await; state.run_migrations().await?; let test_user = User { uuid: UserUuid(Uuid::new_v4()), name: "test-user".to_owned() }; state.save_user(&test_user).await.context("should save user")?; let reg_uuid = state.generate_registration_uuid(&test_user.uuid).await.context("should generate registration uuid for test user")?; let usr2 = state.retrieve_registration_user(®_uuid).await?; let usr2 = usr2.ok_or_else(||anyhow!("state.retrieve_registration_user(®_uuid) returns Nothing"))?; if test_user != usr2 { return Err(anyhow!("test_user != usr2: {:?} != {:?}", test_user, usr2)); } let binary_cache = BinaryCache { name: "test-cache".to_string(), access_key: "access key".to_string(), secret_key: "secret key".to_string(), region: "reg-01".to_string(), endpoint_url: "localhost://".to_string(), bucket: "nix-cache".to_string() }; state.create_binary_cache(&binary_cache).await?; let project = Project { name: "super-duper-project".to_string(), latest_closure_generation: 0 }; state.create_project(&binary_cache, &project).await?; let token = state.create_project_token(&project).await?; let project2 = state.get_project(&token).await?; if project != project2 { return Err(anyhow!("project != project2: {:?} != {:?}", project, project2)); } assert_eq!(project, project2); Ok(()) } #[tokio::test] async fn test_db() { let mdb = setup_db().await; let db = mdb.expect("setup db"); let res = run_test_db(&db).await; teardown_db(db).expect("Failed to teardown DB."); res.unwrap_or_else(|e| panic!("{}", e)); }