diff --git a/api b/api index 74701aa..dc27262 160000 --- a/api +++ b/api @@ -1 +1 @@ -Subproject commit 74701aa0c5bd96c4efc323d3031825be879a6988 +Subproject commit dc272623285bfb88fead3ca18e16e6fa6939e49f diff --git a/db b/db index 658ad01..c7091e9 160000 --- a/db +++ b/db @@ -1 +1 @@ -Subproject commit 658ad01b931487e9613a500caa5e28e4bcb7a5de +Subproject commit c7091e9ed4cd95ff585c56b87a060cf90ae42383 diff --git a/e2e/Cargo.lock b/e2e/Cargo.lock index e515e4e..396c0e2 100644 --- a/e2e/Cargo.lock +++ b/e2e/Cargo.lock @@ -224,6 +224,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures-channel" version = "0.3.28" @@ -320,6 +329,16 @@ dependencies = [ "digest", ] +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -945,9 +964,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toad" -version = "1.0.0-beta.6" +version = "1.0.0-beta.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b305d4763b0236558486735374e796a028567a8d6f6a4c94c07096b73573ba0a" +checksum = "6b4d56ca31b3b83e311136e8e425dc62b8190ed72e2677485431a419493828f2" dependencies = [ "embedded-time", "log", @@ -1039,9 +1058,9 @@ dependencies = [ [[package]] name = "toad-msg" -version = "1.0.0-beta.2" +version = "1.0.0-beta.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63f5ff0cb4b95ec5eb83db517a2301d6a5a5917d94124140dff2fafa283a41f5" +checksum = "219f76dce7b054dc71ff7dff5409b450f821b2bdeccf67d95d3577aa029fe737" dependencies = [ "blake2", "tinyvec", @@ -1051,6 +1070,7 @@ dependencies = [ "toad-len", "toad-macros", "toad-map", + "url", ] [[package]] @@ -1179,6 +1199,17 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/e2e/Cargo.toml b/e2e/Cargo.toml index 43f155a..3a0d24e 100644 --- a/e2e/Cargo.toml +++ b/e2e/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" [dependencies] postgres = {path = "../api/postgres/postgres"} -toad = "1.0.0-beta.6" -toad-msg = "1.0.0-beta.2" +toad = "1.0.0-beta.8" +toad-msg = "1.0.0-beta.5" nb = "1.1" simple_logger = "4.2" serde_json = "1" diff --git a/e2e/src/lib.rs b/e2e/src/lib.rs index 640aa45..a2760fb 100644 --- a/e2e/src/lib.rs +++ b/e2e/src/lib.rs @@ -12,12 +12,18 @@ use std::collections::HashMap; use std::fs::OpenOptions; use std::io::{Read, Write}; use std::net::{SocketAddr, UdpSocket}; +use std::ops::DerefMut; use std::process::{Command, Stdio}; -use std::sync::Once; +use std::sync::{Once, Mutex, Barrier, Arc}; use std::thread::JoinHandle; use std::time::Duration; pub use __toad_aliases::Toad; +use toad::net::Addrd; +use toad::platform::Platform; +use toad::resp::code; +use toad_msg::alloc::Message; +use toad_msg::{Code, Type, MessageOptions}; pub fn run(script: S, init: R, mut fold_lines: F) -> R where S: AsRef, @@ -25,6 +31,7 @@ pub fn run(script: S, init: R, mut fold_lines: F) -> R { let mut child = Command::new("bash").args(["-c", script.as_ref()]) .current_dir("../") + .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() @@ -42,7 +49,6 @@ pub fn run(script: S, init: R, mut fold_lines: F) -> R r = out_str.split('\n').skip(seen).fold(r, |mut r, line| { if !line.trim().is_empty() { r = fold_lines(r, line); - std::io::stdout().flush().unwrap(); } seen += 1; r @@ -57,8 +63,9 @@ pub fn run(script: S, init: R, mut fold_lines: F) -> R let exit = child.try_wait().unwrap(); if exit.map(|e| e.success()) == Some(false) { + let mut err = child.stderr.take().unwrap(); let mut e = String::new(); - child.stderr.take().unwrap().read_to_string(&mut e).unwrap(); + err.read_to_string(&mut e).unwrap(); panic!("{}", e); } else if exit.is_some() { do_fold!(); @@ -72,32 +79,45 @@ pub fn run(script: S, init: R, mut fold_lines: F) -> R pub struct Fixture { pub toad: &'static Toad, pub api_addr: no_std_net::SocketAddr, - pub api_pid: usize, + pub api_thread: JoinHandle<()>, pub pg: postgres::Client, } impl Drop for Fixture { fn drop(&mut self) { - run(format!("kill {}", self.api_pid), (), |_, _| ()); + let mut api_thread: JoinHandle<()> = std::thread::spawn(|| ()); + std::mem::swap(&mut self.api_thread, &mut api_thread); + + let msg = Message::builder(Type::Con, Code::POST).path("debug/shutdown") + .build(); + + let shutdown = Addrd(msg, self.api_addr); + let rep = nb::block!({ + std::thread::sleep(std::time::Duration::from_millis(500)); + self.toad.send_req(&shutdown) + }).unwrap(); + if rep.data().code != code::CREATED { + eprintln!("{} != 2.01", String::from_iter(rep.data().code.to_human())); + } } } pub fn init() -> Fixture { - struct ApiThreadPorts { - used: Vec, - available: Vec, - } + struct ApiThreadPorts(Vec); - static mut SHARED: Option<(Toad, ApiThreadPorts)> = None; + static mut SHARED: Option<(Toad, Mutex)> = None; static INIT: Once = Once::new(); INIT.call_once(|| { simple_logger::init_with_level(log::Level::Info).unwrap(); log::info!("[init] build db"); - run("cd db; docker compose up -d 2>&1; ./scripts/build.sh 2>&1", - (), - |_, _| print!(".")); + run("cd api; cargo build", (), |_, _| ()); + + run("cd db; ./scripts/build.sh 2>&1", (), |_, _| { + print!("."); + std::io::stdout().flush().unwrap(); + }); println!(); let toad = Toad::try_new("127.0.0.1:3999".parse::().unwrap(), @@ -111,8 +131,7 @@ pub fn init() -> Fixture { unsafe { SHARED = Some((toad, - ApiThreadPorts { used: vec![], - available: available_ports })); + Mutex::new(ApiThreadPorts(available_ports)))); } }); @@ -121,27 +140,29 @@ pub fn init() -> Fixture { } log::info!("[init] start api"); - let ports = &mut unsafe { SHARED.as_mut() }.unwrap().1; - let port = ports.available.first().unwrap(); - ports.used.push(*port); + let mut ports = unsafe { SHARED.as_ref() }.unwrap().1.lock().unwrap(); + let port = ports.deref_mut().0.remove(0); + drop(ports); let env = HashMap::from([("POSTGRES_HOST", "127.0.0.1".to_string()), - ("POSTGRES_PORT", "5432".to_string()), + ("POSTGRES_PORT", "5433".to_string()), ("POSTGRES_USER", "postgres".to_string()), ("POSTGRES_PASS", "password".to_string()), + ("ENVIRON", "debug".to_string()), ("API_ADDR", format!("127.0.0.1:{port}"))]); let env_sh = env.iter() .fold(String::new(), |b, (k, v)| format!("{b} {k}=\"{v}\"")); - let api_pid = run(format!("cd api; {env_sh} cargo run 1>../tmp/log.{port} 2>../tmp/log.{port} & echo $!; disown -h"), String::new(), |s, pid| format!("{s}{pid}")); - let api_pid = usize::from_str_radix(&api_pid.trim(), 10).unwrap(); + let api_thread = std::thread::spawn(move || { + run(format!("echo '' > ./tmp/log.{port}; (cd api; {env_sh} target/debug/api 2>&1) 1>>./tmp/log.{port}"), (), |_, _| ()); + }); Fixture { toad: &unsafe {SHARED.as_ref()}.unwrap().0, api_addr: env.get("API_ADDR").unwrap().parse().unwrap(), - api_pid, - pg: postgres::Client::connect("host=127.0.0.1 port=5432 dbname=dnim user=postgres password=password", postgres::NoTls).unwrap(), + api_thread, + pg: postgres::Client::connect("host=127.0.0.1 port=5433 dbname=dnim user=postgres password=password", postgres::NoTls).unwrap(), } } diff --git a/e2e/src/user_signup.rs b/e2e/src/user_signup.rs index 5c2bff7..cccb3af 100644 --- a/e2e/src/user_signup.rs +++ b/e2e/src/user_signup.rs @@ -22,11 +22,11 @@ pub fn user_signup() { let msg = Addrd(msg, fx.api_addr); let rep = nb::block!({ - std::thread::sleep(std::time::Duration::from_micros(500)); + std::thread::sleep(std::time::Duration::from_millis(100)); fx.toad.send_req(&msg) }).unwrap(); - assert_eq!(rep.data().code, toad::resp::code::CONTENT); + assert_eq!(rep.data().code, toad::resp::code::CREATED); assert_eq!(rep.data().content_format(), Some(ContentFormat::Json)); let rep_json = serde_json::from_slice::(rep.data().payload().as_bytes()).unwrap(); @@ -42,9 +42,39 @@ pub fn user_signup() { .as_str() .unwrap(); + let login = rep_json.as_object() + .unwrap() + .get("links") + .unwrap() + .as_object() + .unwrap() + .get("login") + .unwrap() + .as_str() + .unwrap(); + + let login = + Message::builder(Type::Con, Code::POST).path(login) + .accept(ContentFormat::Json) + .content_format(ContentFormat::Json) + .payload(serde_json::to_vec(&serde_json::json!({ + "tag": "foo", + "password": "bingus" + })).unwrap()) + .build(); + let login = Addrd(login, fx.api_addr); + let rep = nb::block!({ + std::thread::sleep(std::time::Duration::from_millis(100)); + fx.toad.send_req(&login) + }).unwrap(); + assert_eq!(rep.data().code, toad::resp::code::CREATED); + assert_eq!(rep.data().content_format(), Some(ContentFormat::Json)); + let rep_json = + serde_json::from_slice::(rep.data().payload().as_bytes()).unwrap(); + let session = rep_json.as_object() .unwrap() - .get("session_id") + .get("session") .unwrap() .as_str() .unwrap(); @@ -54,15 +84,15 @@ pub fn user_signup() { .accept(ContentFormat::Json) .content_format(ContentFormat::Json) .payload(serde_json::to_vec(&serde_json::json!({ - session: session + "session": session })).unwrap()) .build(); - let get_signup = Addrd(get_groups, fx.api_addr); + let get_groups = Addrd(get_groups, fx.api_addr); let rep = nb::block!({ std::thread::sleep(std::time::Duration::from_millis(100)); - fx.toad.send_req(&get_signup) + fx.toad.send_req(&get_groups) }).unwrap(); assert_eq!(rep.data().code, toad::resp::code::CONTENT); @@ -70,11 +100,15 @@ pub fn user_signup() { let rep_json = serde_json::from_slice::(rep.data().payload().as_bytes()).unwrap(); - let unverified_email = - rep_json.as_array().unwrap().iter().any(|g| { - g.as_object().unwrap().get("tag").unwrap().as_str() - == Some("unverified_email") - }); + let groups = + rep_json.as_array() + .unwrap() + .iter() + .map(|g| g.as_object().unwrap().get("name").unwrap().as_str().unwrap()).collect::>(); - assert!(unverified_email); + if !groups.contains(&"unverified_email") { + panic!("expected {groups:?} to contain 'unverified_email'"); + } + + drop(fx); }