feat: user signup flow almost done
This commit is contained in:
parent
aafca8b714
commit
aa6a90d3f0
2
api
2
api
@ -1 +1 @@
|
|||||||
Subproject commit 74701aa0c5bd96c4efc323d3031825be879a6988
|
Subproject commit dc272623285bfb88fead3ca18e16e6fa6939e49f
|
2
db
2
db
@ -1 +1 @@
|
|||||||
Subproject commit 658ad01b931487e9613a500caa5e28e4bcb7a5de
|
Subproject commit c7091e9ed4cd95ff585c56b87a060cf90ae42383
|
39
e2e/Cargo.lock
generated
39
e2e/Cargo.lock
generated
@ -224,6 +224,15 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
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]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.28"
|
version = "0.3.28"
|
||||||
@ -320,6 +329,16 @@ dependencies = [
|
|||||||
"digest",
|
"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]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@ -945,9 +964,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toad"
|
name = "toad"
|
||||||
version = "1.0.0-beta.6"
|
version = "1.0.0-beta.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b305d4763b0236558486735374e796a028567a8d6f6a4c94c07096b73573ba0a"
|
checksum = "6b4d56ca31b3b83e311136e8e425dc62b8190ed72e2677485431a419493828f2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embedded-time",
|
"embedded-time",
|
||||||
"log",
|
"log",
|
||||||
@ -1039,9 +1058,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toad-msg"
|
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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "63f5ff0cb4b95ec5eb83db517a2301d6a5a5917d94124140dff2fafa283a41f5"
|
checksum = "219f76dce7b054dc71ff7dff5409b450f821b2bdeccf67d95d3577aa029fe737"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2",
|
"blake2",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
@ -1051,6 +1070,7 @@ dependencies = [
|
|||||||
"toad-len",
|
"toad-len",
|
||||||
"toad-macros",
|
"toad-macros",
|
||||||
"toad-map",
|
"toad-map",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1179,6 +1199,17 @@ dependencies = [
|
|||||||
"tinyvec",
|
"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]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -7,8 +7,8 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
postgres = {path = "../api/postgres/postgres"}
|
postgres = {path = "../api/postgres/postgres"}
|
||||||
toad = "1.0.0-beta.6"
|
toad = "1.0.0-beta.8"
|
||||||
toad-msg = "1.0.0-beta.2"
|
toad-msg = "1.0.0-beta.5"
|
||||||
nb = "1.1"
|
nb = "1.1"
|
||||||
simple_logger = "4.2"
|
simple_logger = "4.2"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
@ -12,12 +12,18 @@ use std::collections::HashMap;
|
|||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::net::{SocketAddr, UdpSocket};
|
use std::net::{SocketAddr, UdpSocket};
|
||||||
|
use std::ops::DerefMut;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::sync::Once;
|
use std::sync::{Once, Mutex, Barrier, Arc};
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub use __toad_aliases::Toad;
|
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<S, F, R>(script: S, init: R, mut fold_lines: F) -> R
|
pub fn run<S, F, R>(script: S, init: R, mut fold_lines: F) -> R
|
||||||
where S: AsRef<str>,
|
where S: AsRef<str>,
|
||||||
@ -25,6 +31,7 @@ pub fn run<S, F, R>(script: S, init: R, mut fold_lines: F) -> R
|
|||||||
{
|
{
|
||||||
let mut child = Command::new("bash").args(["-c", script.as_ref()])
|
let mut child = Command::new("bash").args(["-c", script.as_ref()])
|
||||||
.current_dir("../")
|
.current_dir("../")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
@ -42,7 +49,6 @@ pub fn run<S, F, R>(script: S, init: R, mut fold_lines: F) -> R
|
|||||||
r = out_str.split('\n').skip(seen).fold(r, |mut r, line| {
|
r = out_str.split('\n').skip(seen).fold(r, |mut r, line| {
|
||||||
if !line.trim().is_empty() {
|
if !line.trim().is_empty() {
|
||||||
r = fold_lines(r, line);
|
r = fold_lines(r, line);
|
||||||
std::io::stdout().flush().unwrap();
|
|
||||||
}
|
}
|
||||||
seen += 1;
|
seen += 1;
|
||||||
r
|
r
|
||||||
@ -57,8 +63,9 @@ pub fn run<S, F, R>(script: S, init: R, mut fold_lines: F) -> R
|
|||||||
|
|
||||||
let exit = child.try_wait().unwrap();
|
let exit = child.try_wait().unwrap();
|
||||||
if exit.map(|e| e.success()) == Some(false) {
|
if exit.map(|e| e.success()) == Some(false) {
|
||||||
|
let mut err = child.stderr.take().unwrap();
|
||||||
let mut e = String::new();
|
let mut e = String::new();
|
||||||
child.stderr.take().unwrap().read_to_string(&mut e).unwrap();
|
err.read_to_string(&mut e).unwrap();
|
||||||
panic!("{}", e);
|
panic!("{}", e);
|
||||||
} else if exit.is_some() {
|
} else if exit.is_some() {
|
||||||
do_fold!();
|
do_fold!();
|
||||||
@ -72,32 +79,45 @@ pub fn run<S, F, R>(script: S, init: R, mut fold_lines: F) -> R
|
|||||||
pub struct Fixture {
|
pub struct Fixture {
|
||||||
pub toad: &'static Toad,
|
pub toad: &'static Toad,
|
||||||
pub api_addr: no_std_net::SocketAddr,
|
pub api_addr: no_std_net::SocketAddr,
|
||||||
pub api_pid: usize,
|
pub api_thread: JoinHandle<()>,
|
||||||
pub pg: postgres::Client,
|
pub pg: postgres::Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Fixture {
|
impl Drop for Fixture {
|
||||||
fn drop(&mut self) {
|
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 {
|
pub fn init() -> Fixture {
|
||||||
struct ApiThreadPorts {
|
struct ApiThreadPorts(Vec<usize>);
|
||||||
used: Vec<usize>,
|
|
||||||
available: Vec<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
static mut SHARED: Option<(Toad, ApiThreadPorts)> = None;
|
static mut SHARED: Option<(Toad, Mutex<ApiThreadPorts>)> = None;
|
||||||
|
|
||||||
static INIT: Once = Once::new();
|
static INIT: Once = Once::new();
|
||||||
INIT.call_once(|| {
|
INIT.call_once(|| {
|
||||||
simple_logger::init_with_level(log::Level::Info).unwrap();
|
simple_logger::init_with_level(log::Level::Info).unwrap();
|
||||||
|
|
||||||
log::info!("[init] build db");
|
log::info!("[init] build db");
|
||||||
run("cd db; docker compose up -d 2>&1; ./scripts/build.sh 2>&1",
|
run("cd api; cargo build", (), |_, _| ());
|
||||||
(),
|
|
||||||
|_, _| print!("."));
|
run("cd db; ./scripts/build.sh 2>&1", (), |_, _| {
|
||||||
|
print!(".");
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
});
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
let toad = Toad::try_new("127.0.0.1:3999".parse::<SocketAddr>().unwrap(),
|
let toad = Toad::try_new("127.0.0.1:3999".parse::<SocketAddr>().unwrap(),
|
||||||
@ -111,8 +131,7 @@ pub fn init() -> Fixture {
|
|||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
SHARED = Some((toad,
|
SHARED = Some((toad,
|
||||||
ApiThreadPorts { used: vec![],
|
Mutex::new(ApiThreadPorts(available_ports))));
|
||||||
available: available_ports }));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,27 +140,29 @@ pub fn init() -> Fixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log::info!("[init] start api");
|
log::info!("[init] start api");
|
||||||
let ports = &mut unsafe { SHARED.as_mut() }.unwrap().1;
|
let mut ports = unsafe { SHARED.as_ref() }.unwrap().1.lock().unwrap();
|
||||||
let port = ports.available.first().unwrap();
|
let port = ports.deref_mut().0.remove(0);
|
||||||
ports.used.push(*port);
|
drop(ports);
|
||||||
|
|
||||||
let env = HashMap::from([("POSTGRES_HOST", "127.0.0.1".to_string()),
|
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_USER", "postgres".to_string()),
|
||||||
("POSTGRES_PASS", "password".to_string()),
|
("POSTGRES_PASS", "password".to_string()),
|
||||||
|
("ENVIRON", "debug".to_string()),
|
||||||
("API_ADDR", format!("127.0.0.1:{port}"))]);
|
("API_ADDR", format!("127.0.0.1:{port}"))]);
|
||||||
|
|
||||||
let env_sh = env.iter()
|
let env_sh = env.iter()
|
||||||
.fold(String::new(), |b, (k, v)| format!("{b} {k}=\"{v}\""));
|
.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_thread = std::thread::spawn(move || {
|
||||||
let api_pid = usize::from_str_radix(&api_pid.trim(), 10).unwrap();
|
run(format!("echo '' > ./tmp/log.{port}; (cd api; {env_sh} target/debug/api 2>&1) 1>>./tmp/log.{port}"), (), |_, _| ());
|
||||||
|
});
|
||||||
|
|
||||||
Fixture {
|
Fixture {
|
||||||
toad: &unsafe {SHARED.as_ref()}.unwrap().0,
|
toad: &unsafe {SHARED.as_ref()}.unwrap().0,
|
||||||
api_addr: env.get("API_ADDR").unwrap().parse().unwrap(),
|
api_addr: env.get("API_ADDR").unwrap().parse().unwrap(),
|
||||||
api_pid,
|
api_thread,
|
||||||
pg: postgres::Client::connect("host=127.0.0.1 port=5432 dbname=dnim user=postgres password=password", postgres::NoTls).unwrap(),
|
pg: postgres::Client::connect("host=127.0.0.1 port=5433 dbname=dnim user=postgres password=password", postgres::NoTls).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,11 +22,11 @@ pub fn user_signup() {
|
|||||||
let msg = Addrd(msg, fx.api_addr);
|
let msg = Addrd(msg, fx.api_addr);
|
||||||
|
|
||||||
let rep = nb::block!({
|
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)
|
fx.toad.send_req(&msg)
|
||||||
}).unwrap();
|
}).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));
|
assert_eq!(rep.data().content_format(), Some(ContentFormat::Json));
|
||||||
let rep_json =
|
let rep_json =
|
||||||
serde_json::from_slice::<serde_json::Value>(rep.data().payload().as_bytes()).unwrap();
|
serde_json::from_slice::<serde_json::Value>(rep.data().payload().as_bytes()).unwrap();
|
||||||
@ -42,9 +42,39 @@ pub fn user_signup() {
|
|||||||
.as_str()
|
.as_str()
|
||||||
.unwrap();
|
.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::<serde_json::Value>(rep.data().payload().as_bytes()).unwrap();
|
||||||
|
|
||||||
let session = rep_json.as_object()
|
let session = rep_json.as_object()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get("session_id")
|
.get("session")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str()
|
.as_str()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -54,15 +84,15 @@ pub fn user_signup() {
|
|||||||
.accept(ContentFormat::Json)
|
.accept(ContentFormat::Json)
|
||||||
.content_format(ContentFormat::Json)
|
.content_format(ContentFormat::Json)
|
||||||
.payload(serde_json::to_vec(&serde_json::json!({
|
.payload(serde_json::to_vec(&serde_json::json!({
|
||||||
session: session
|
"session": session
|
||||||
})).unwrap())
|
})).unwrap())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let get_signup = Addrd(get_groups, fx.api_addr);
|
let get_groups = Addrd(get_groups, fx.api_addr);
|
||||||
|
|
||||||
let rep = nb::block!({
|
let rep = nb::block!({
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
fx.toad.send_req(&get_signup)
|
fx.toad.send_req(&get_groups)
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
assert_eq!(rep.data().code, toad::resp::code::CONTENT);
|
assert_eq!(rep.data().code, toad::resp::code::CONTENT);
|
||||||
@ -70,11 +100,15 @@ pub fn user_signup() {
|
|||||||
let rep_json =
|
let rep_json =
|
||||||
serde_json::from_slice::<serde_json::Value>(rep.data().payload().as_bytes()).unwrap();
|
serde_json::from_slice::<serde_json::Value>(rep.data().payload().as_bytes()).unwrap();
|
||||||
|
|
||||||
let unverified_email =
|
let groups =
|
||||||
rep_json.as_array().unwrap().iter().any(|g| {
|
rep_json.as_array()
|
||||||
g.as_object().unwrap().get("tag").unwrap().as_str()
|
.unwrap()
|
||||||
== Some("unverified_email")
|
.iter()
|
||||||
});
|
.map(|g| g.as_object().unwrap().get("name").unwrap().as_str().unwrap()).collect::<Vec<_>>();
|
||||||
|
|
||||||
assert!(unverified_email);
|
if !groups.contains(&"unverified_email") {
|
||||||
|
panic!("expected {groups:?} to contain 'unverified_email'");
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(fx);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user