Skip to main content

max / makenotwork

3.6 KB · 101 lines History Blame Raw
1 //! Invite code creation, redemption, and limit enforcement tests.
2
3 use crate::harness::TestHarness;
4
5 #[tokio::test]
6 async fn invite_create_as_creator() {
7 let mut h = TestHarness::new().await;
8 let user_id = h.signup("invcreator", "invcreator@test.com", "password123").await;
9 h.grant_creator(user_id).await;
10 h.client.post_form("/logout", "").await;
11 h.login("invcreator", "password123").await;
12
13 let resp = h.client.post_form("/api/invites/create", "").await;
14 assert!(resp.status.is_success(), "Create invite failed: {} {}", resp.status, resp.text);
15 assert!(resp.text.contains("Invite code:"), "Response should contain 'Invite code:', got: {}", resp.text);
16 assert!(resp.text.contains("makenot.work/join?invite="), "Response should contain invite link");
17 }
18
19 #[tokio::test]
20 async fn invite_non_creator_blocked() {
21 let mut h = TestHarness::new().await;
22 let _user_id = h.signup("invnoncreator", "invnoncreator@test.com", "password123").await;
23
24 let resp = h.client.post_form("/api/invites/create", "").await;
25 assert!(resp.text.contains("creator access"), "Non-creator should see 'creator access' error, got: {}", resp.text);
26 }
27
28 #[tokio::test]
29 async fn invite_redeem_on_signup() {
30 let mut h = TestHarness::new().await;
31
32 // Creator generates an invite
33 let creator_id = h.signup("inviter", "inviter@test.com", "password123").await;
34 h.grant_creator(creator_id).await;
35 h.client.post_form("/logout", "").await;
36 h.login("inviter", "password123").await;
37
38 let resp = h.client.post_form("/api/invites/create", "").await;
39 assert!(resp.status.is_success());
40
41 // Extract the formatted code (XXXX-XXXX-XXXX) from response
42 let text = &resp.text;
43 let code_start = text.find("Invite code: ").expect("should contain 'Invite code: '") + "Invite code: ".len();
44 let code_end = text[code_start..].find(" ").map(|i| code_start + i).unwrap_or(code_start + 14);
45 let formatted_code = &text[code_start..code_end];
46
47 // Log out, sign up a new user with the invite code
48 h.client.post_form("/logout", "").await;
49 h.client.fetch_csrf_token().await;
50 let body = format!(
51 "username=invitee&email=invitee@test.com&password=password123&invite_code={}",
52 urlencoding::encode(formatted_code),
53 );
54 let resp = h.client.post_form("/join/step/account", &body).await;
55 assert!(
56 resp.status.is_success() || resp.status.is_redirection(),
57 "Signup with invite failed: {} {}",
58 resp.status,
59 resp.text,
60 );
61
62 // Verify the invite was redeemed in the DB
63 let redeemed: Option<(uuid::Uuid,)> = sqlx::query_as(
64 "SELECT redeemed_by_id FROM invite_codes WHERE creator_id = $1 AND redeemed_by_id IS NOT NULL"
65 )
66 .bind(creator_id)
67 .fetch_optional(&h.db)
68 .await
69 .unwrap();
70
71 assert!(redeemed.is_some(), "Invite code should have been redeemed");
72 }
73
74 #[tokio::test]
75 async fn invite_limit_enforced() {
76 let mut h = TestHarness::new().await;
77 let user_id = h.signup("invlimit", "invlimit@test.com", "password123").await;
78 h.grant_creator(user_id).await;
79 h.client.post_form("/logout", "").await;
80 h.login("invlimit", "password123").await;
81
82 // Create 5 invites (the limit)
83 for i in 0..5 {
84 let resp = h.client.post_form("/api/invites/create", "").await;
85 assert!(
86 resp.text.contains("Invite code:"),
87 "Invite {} should succeed, got: {}",
88 i + 1,
89 resp.text,
90 );
91 }
92
93 // 6th should be rejected
94 let resp = h.client.post_form("/api/invites/create", "").await;
95 assert!(
96 resp.text.contains("limit"),
97 "6th invite should mention 'limit', got: {}",
98 resp.text,
99 );
100 }
101