From ec885812bef4616561e5fd260b22d53a692cd688 Mon Sep 17 00:00:00 2001 From: ABCxFF <79597906+ABCxFF@users.noreply.github.com> Date: Thu, 2 Jan 2025 19:00:41 +0000 Subject: [PATCH] chore(users): migrate tests --- Cargo.lock | 6 +- packages/services/user/Cargo.toml | 10 ++- .../user/tests/avatar_upload_complete.rs | 62 ++++++++++++++++ packages/services/user/tests/get.rs | 72 +++++++++++++++++++ .../services/user/tests/identity_create.rs | 35 +++++++++ .../services/user/tests/identity_delete.rs | 41 +++++++++++ packages/services/user/tests/identity_get.rs | 42 +++++++++++ .../user/tests/pending_delete_toggle.rs | 67 +++++++++++++++++ .../services/user/tests/profile_validate.rs | 18 +++++ packages/services/user/tests/resolve_email.rs | 35 +++++++++ packages/services/user/tests/team_list.rs | 49 +++++++++++++ packages/services/user/tests/token_create.rs | 18 +++++ 12 files changed, 451 insertions(+), 4 deletions(-) create mode 100644 packages/services/user/tests/avatar_upload_complete.rs create mode 100644 packages/services/user/tests/get.rs create mode 100644 packages/services/user/tests/identity_create.rs create mode 100644 packages/services/user/tests/identity_delete.rs create mode 100644 packages/services/user/tests/identity_get.rs create mode 100644 packages/services/user/tests/pending_delete_toggle.rs create mode 100644 packages/services/user/tests/profile_validate.rs create mode 100644 packages/services/user/tests/resolve_email.rs create mode 100644 packages/services/user/tests/team_list.rs create mode 100644 packages/services/user/tests/token_create.rs diff --git a/Cargo.lock b/Cargo.lock index eca20175ed..d7a4efdfe5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15902,7 +15902,10 @@ dependencies = [ "chirp-workflow", "cluster", "email-address-parser", + "faker-user", "linode", + "rand", + "reqwest 0.11.27", "rivet-config", "rivet-operation", "serde", @@ -15912,7 +15915,7 @@ dependencies = [ "upload-complete", "upload-file-list", "upload-get", - "user-identity-get", + "upload-prepare", ] [[package]] @@ -16096,7 +16099,6 @@ dependencies = [ "upload-list-for-user", "upload-prepare", "user", - "user-identity-delete", ] [[package]] diff --git a/packages/services/user/Cargo.toml b/packages/services/user/Cargo.toml index 8406ec5a71..b1329396cd 100644 --- a/packages/services/user/Cargo.toml +++ b/packages/services/user/Cargo.toml @@ -19,8 +19,14 @@ token-create.workspace = true upload-file-list.workspace = true upload-get.workspace = true upload-complete.workspace = true -user-identity-get.workspace = true [dependencies.sqlx] workspace = true -default-features = false \ No newline at end of file +default-features = false + +[dev-dependencies] +faker-user.workspace = true +rand = "0.8" +reqwest = "0.11" +upload-get.workspace = true +upload-prepare.workspace = true \ No newline at end of file diff --git a/packages/services/user/tests/avatar_upload_complete.rs b/packages/services/user/tests/avatar_upload_complete.rs new file mode 100644 index 0000000000..37caf9abc8 --- /dev/null +++ b/packages/services/user/tests/avatar_upload_complete.rs @@ -0,0 +1,62 @@ +use chirp_workflow::prelude::*; +use rivet_operation::prelude::proto::backend; + +const TEST_BODY: &[u8] = b"test file"; + +#[workflow_test] +async fn empty(ctx: TestCtx) { + let user_res = op!([ctx] faker_user {}).await.unwrap(); + let user_id = user_res.user_id.unwrap().as_uuid(); + + // Create the upload + let upload_prepare_res = op!([ctx] upload_prepare { + bucket: "bucket-user-avatar".into(), + files: vec![ + backend::upload::PrepareFile { + path: "image.png".to_owned(), + mime: Some("image/png".into()), + content_length: TEST_BODY.len() as u64, + ..Default::default() + }, + ], + }) + .await + .unwrap(); + let upload_id = upload_prepare_res.upload_id.unwrap(); + let presigned_request = upload_prepare_res.presigned_requests.first().unwrap(); + + tracing::info!("writing test files"); + let res = reqwest::Client::new() + .put(&presigned_request.url) + .body(TEST_BODY.to_vec()) + .header("content-type", "image/png") + .send() + .await + .expect("failed to upload"); + if res.status().is_success() { + tracing::info!("uploaded successfully"); + } else { + panic!( + "failed to upload ({}): {:?}", + res.status(), + res.text().await + ); + } + + ctx.op(::user::ops::avatar_upload_complete::Input { + user_id: user_id, + upload_id: upload_id.as_uuid() + }) + .await + .unwrap(); + + let uploads_res = op!([ctx] upload_get { + upload_ids: vec![upload_id] + }) + .await + .unwrap(); + + let upload = uploads_res.uploads.first().unwrap(); + + assert!(upload.complete_ts.is_some(), "Upload did not complete"); +} diff --git a/packages/services/user/tests/get.rs b/packages/services/user/tests/get.rs new file mode 100644 index 0000000000..ab54e6d194 --- /dev/null +++ b/packages/services/user/tests/get.rs @@ -0,0 +1,72 @@ +use chirp_workflow::prelude::*; +use proto::backend::{pkg::*}; +use rivet_operation::prelude::proto; +use rand::Rng; + +#[workflow_test] +async fn empty(ctx: TestCtx) { + let res = ctx.op(::user::ops::get::Input { + user_ids: Vec::new(), + }) + .await + .unwrap(); + assert!(res.users.is_empty()); +} + +#[workflow_test] +async fn fetch(ctx: TestCtx) { + struct TestUser { + user_id: Option, + display_name: String, + account_number: i64, + bio: String, + } + + // Generate test users + let mut users = std::iter::repeat_with(|| TestUser { + user_id: None, + display_name: util::faker::display_name(), + account_number: rand::thread_rng().gen_range(1..10000), + bio: util::faker::ident(), + }) + .take(8) + .collect::>(); + + // Insert test users + for user in &mut users { + let user_res = op!([ctx] faker_user { }).await.unwrap(); + let user_id = user_res.user_id.unwrap().as_uuid(); + + msg!([ctx] user::msg::profile_set(user_id) -> user::msg::update { + user_id: Some(user_id.into()), + display_name: Some(user.display_name.clone()), + account_number: Some(user.account_number as u32), + bio: Some(user.bio.clone()), + }) + .await + .unwrap(); + + user.user_id = Some(user_id); + } + + // Fetch the users + let res = ctx.op(::user::ops::get::Input { + user_ids: users.iter().map(|u| u.user_id.unwrap()).collect(), + }) + .await + .unwrap(); + + // Validate the users + assert_eq!(users.len(), res.users.len()); + for user in &users { + let user_res = res + .users + .iter() + .find(|u| u.user_id.unwrap().as_uuid() == user.user_id.unwrap()) + .expect("user not returned"); + + assert_eq!(user.display_name, user_res.display_name); + assert_eq!(user.account_number, user_res.account_number as i64); + assert_eq!(user.bio, user_res.bio); + } +} diff --git a/packages/services/user/tests/identity_create.rs b/packages/services/user/tests/identity_create.rs new file mode 100644 index 0000000000..d133afb077 --- /dev/null +++ b/packages/services/user/tests/identity_create.rs @@ -0,0 +1,35 @@ +use chirp_workflow::prelude::*; +use rivet_operation::prelude::proto::backend; + +#[workflow_test] +async fn email(ctx: TestCtx) { + let user_res = op!([ctx] faker_user { + ..Default::default() + }) + .await + .unwrap(); + let user_id = user_res.user_id.unwrap().as_uuid(); + + let email = util::faker::email(); + ctx.op(::user::ops::identity::create::Input { + user_id: user_id, + identity: backend::user_identity::Identity { + kind: Some(backend::user_identity::identity::Kind::Email( + backend::user_identity::identity::Email { + email: email.clone(), + } + )), + }, + }) + .await + .unwrap(); + + let (sql_exists,) = sqlx::query_as::<_, (bool,)>( + "SELECT EXISTS (SELECT 1 FROM db_user_identity.emails WHERE email = $1)", + ) + .bind(&email) + .fetch_one(&ctx.crdb().await.unwrap()) + .await + .unwrap(); + assert!(sql_exists, "identity not created"); +} diff --git a/packages/services/user/tests/identity_delete.rs b/packages/services/user/tests/identity_delete.rs new file mode 100644 index 0000000000..fb60977b58 --- /dev/null +++ b/packages/services/user/tests/identity_delete.rs @@ -0,0 +1,41 @@ +use chirp_workflow::prelude::*; +use rivet_operation::prelude::proto::backend; + +#[workflow_test] +async fn empty(ctx: TestCtx) { + let user_res = op!([ctx] faker_user { + ..Default::default() + }) + .await + .unwrap(); + let user_id = user_res.user_id.unwrap().as_uuid(); + + let email = util::faker::email(); + ctx.op(::user::ops::identity::create::Input { + user_id: user_id, + identity: backend::user_identity::Identity { + kind: Some(backend::user_identity::identity::Kind::Email( + backend::user_identity::identity::Email { + email: email.clone() + } + )), + }, + }) + .await + .unwrap(); + + ctx.op(::user::ops::identity::delete::Input { + user_ids: vec![user_id], + }) + .await + .unwrap(); + + let (sql_exists,) = sqlx::query_as::<_, (bool,)>( + "SELECT EXISTS (SELECT 1 FROM db_user_identity.emails WHERE email = $1)", + ) + .bind(&email) + .fetch_one(&ctx.crdb().await.unwrap()) + .await + .unwrap(); + assert!(!sql_exists, "identity not deleted"); +} diff --git a/packages/services/user/tests/identity_get.rs b/packages/services/user/tests/identity_get.rs new file mode 100644 index 0000000000..cae9379a9c --- /dev/null +++ b/packages/services/user/tests/identity_get.rs @@ -0,0 +1,42 @@ +use chirp_workflow::prelude::*; +use rivet_operation::prelude::proto::backend; + +#[workflow_test] +async fn empty(ctx: TestCtx) { + let user_res = op!([ctx] faker_user { + ..Default::default() + }) + .await + .unwrap(); + let user_id = user_res.user_id.unwrap().as_uuid(); + + let email = util::faker::email(); + ctx.op(::user::ops::identity::create::Input { + user_id: user_id, + identity: backend::user_identity::Identity { + kind: Some(backend::user_identity::identity::Kind::Email( + backend::user_identity::identity::Email { + email: email.clone() + } + )), + }, + }) + .await + .unwrap(); + + let res = ctx.op(::user::ops::identity::get::Input { + user_ids: vec![user_id, Uuid::new_v4()], + }) + .await + .unwrap(); + assert_eq!(1, res.users.len()); + assert_eq!( + 1, + res.users + .iter() + .find(|u| u.user_id == user_id) + .unwrap() + .identities + .len() + ); +} diff --git a/packages/services/user/tests/pending_delete_toggle.rs b/packages/services/user/tests/pending_delete_toggle.rs new file mode 100644 index 0000000000..c992d388d0 --- /dev/null +++ b/packages/services/user/tests/pending_delete_toggle.rs @@ -0,0 +1,67 @@ +use chirp_workflow::prelude::*; +use rivet_operation::prelude::proto::backend; + +#[workflow_test] +async fn empty(ctx: TestCtx) { + let user_res = op!([ctx] faker_user {}).await.unwrap(); + let user_id = user_res.user_id.as_ref().unwrap().as_uuid(); + + // Register user + let email = util::faker::email(); + let _res = ctx.op(::user::ops::identity::create::Input { + user_id: user_id, + identity: backend::user_identity::Identity { + kind: Some(backend::user_identity::identity::Kind::Email( + backend::user_identity::identity::Email { + email: email.clone() + } + )), + }, + }) + .await + .unwrap(); + + ctx.op(::user::ops::pending_delete_toggle::Input { + user_id: user_id, + active: true, + }) + .await + .unwrap(); + + let (delete_request_ts,): (Option,) = sqlx::query_as(indoc!( + " + SELECT delete_request_ts + FROM db_user.users + WHERE + user_id = $1 + ", + )) + .bind(user_id) + .fetch_one(&ctx.crdb().await.unwrap()) + .await + .unwrap(); + + assert!(delete_request_ts.is_some()); + + ctx.op(::user::ops::pending_delete_toggle::Input { + user_id: user_id, + active: false, + }) + .await + .unwrap(); + + let (delete_request_ts,): (Option,) = sqlx::query_as(indoc!( + " + SELECT delete_request_ts + FROM db_user.users + WHERE + user_id = $1 + ", + )) + .bind(user_id) + .fetch_one(&ctx.crdb().await.unwrap()) + .await + .unwrap(); + + assert!(delete_request_ts.is_none()); +} diff --git a/packages/services/user/tests/profile_validate.rs b/packages/services/user/tests/profile_validate.rs new file mode 100644 index 0000000000..205039102d --- /dev/null +++ b/packages/services/user/tests/profile_validate.rs @@ -0,0 +1,18 @@ +use chirp_workflow::prelude::*; + +#[workflow_test] +async fn empty(ctx: TestCtx) { + let user_res = op!([ctx] faker_user {}).await.unwrap(); + let user_id = user_res.user_id.unwrap().as_uuid(); + + let res = ctx.op(::user::ops::profile_validate::Input { + user_id: user_id, + display_name: Some(" bad display name".to_owned()), + account_number: Some(10000), + bio: Some("bad\n\n\n\n\n\nbio".to_owned()) + }) + .await + .unwrap(); + + assert_eq!(res.errors.len(), 3, "validation failed"); +} diff --git a/packages/services/user/tests/resolve_email.rs b/packages/services/user/tests/resolve_email.rs new file mode 100644 index 0000000000..5a82efed89 --- /dev/null +++ b/packages/services/user/tests/resolve_email.rs @@ -0,0 +1,35 @@ +use chirp_workflow::prelude::*; +use rivet_operation::prelude::proto::backend; + +#[workflow_test] +async fn empty(ctx: TestCtx) { + let user_res = op!([ctx] faker_user { + ..Default::default() + }) + .await + .unwrap(); + let user_id = user_res.user_id.as_ref().unwrap().as_uuid(); + + let email = util::faker::email(); + ctx.op(::user::ops::identity::create::Input { + user_id: user_id, + identity: backend::user_identity::Identity { + kind: Some(backend::user_identity::identity::Kind::Email( + backend::user_identity::identity::Email { + email: email.clone() + } + )), + }, + }) + .await + .unwrap(); + + let res = ctx.op(::user::ops::resolve_email::Input { + emails: vec![email.clone(), util::faker::email()], + }) + .await + .unwrap(); + assert_eq!(1, res.users.len()); + let user = res.users.first().unwrap(); + assert_eq!(user_id, user.user_id); +} diff --git a/packages/services/user/tests/team_list.rs b/packages/services/user/tests/team_list.rs new file mode 100644 index 0000000000..00b18e682f --- /dev/null +++ b/packages/services/user/tests/team_list.rs @@ -0,0 +1,49 @@ +use std::collections::HashMap; + +use chirp_workflow::prelude::*; +use rivet_operation::prelude::proto; +use proto::backend::{pkg::*}; + +#[workflow_test] +async fn empty(ctx: TestCtx) { + let user_a = Uuid::new_v4(); + let user_b = Uuid::new_v4(); + let user_c = Uuid::new_v4(); + + let team_a = Uuid::new_v4(); + let team_b = Uuid::new_v4(); + let team_c = Uuid::new_v4(); + + let members = vec![ + (user_a, team_a), + (user_a, team_b), + (user_b, team_a), + (user_b, team_b), + (user_b, team_c), + ]; + for (user_id, team_id) in &members { + msg!([ctx] team::msg::member_create(team_id, user_id) -> team::msg::member_create_complete { + team_id: Some((*team_id).into()), + user_id: Some((*user_id).into()), + invitation: None, + }) + .await + .unwrap(); + } + + let res = ctx.op(::user::ops::team_list::Input { + user_ids: vec![user_a, user_b, user_c], + }) + .await + .unwrap(); + + assert_eq!(3, res.users.len()); + let users_map = res + .users + .iter() + .map(|u| (u.user_id, u.teams.len())) + .collect::>(); + assert_eq!(2, *users_map.get(&user_a).unwrap()); + assert_eq!(3, *users_map.get(&user_b).unwrap()); + assert_eq!(0, *users_map.get(&user_c).unwrap()); +} diff --git a/packages/services/user/tests/token_create.rs b/packages/services/user/tests/token_create.rs new file mode 100644 index 0000000000..b88621b5e6 --- /dev/null +++ b/packages/services/user/tests/token_create.rs @@ -0,0 +1,18 @@ +use chirp_workflow::prelude::*; +use rivet_operation::prelude::proto::backend; + +#[workflow_test] +async fn empty(ctx: TestCtx) { + let user_res = op!([ctx] faker_user {}).await.unwrap(); + let user_id = user_res.user_id.as_ref().unwrap().as_uuid(); + + let res = ctx.op(::user::ops::token_create::Input { + user_id: user_id, + client: backend::net::ClientInfo {..Default::default()} + }) + .await + .unwrap(); + + assert!(res.token.starts_with("usr")); + assert!(res.refresh_token.starts_with("usr_rf")); +}