Skip to main content

max / makenotwork

3.0 KB · 74 lines History Blame Raw
1 //! Site access gate (`ACCESS_GATE=fan_plus_or_creator`): the testnot-style
2 //! gate that restricts the whole site to creators and Fan+ members.
3
4 use crate::harness::{BuildOptions, TestHarness};
5 use makenotwork::config::AccessGate;
6 use sqlx::PgPool;
7
8 fn location(resp: &crate::harness::client::TestResponse) -> String {
9 resp.headers
10 .get("location")
11 .and_then(|v| v.to_str().ok())
12 .unwrap_or("")
13 .to_string()
14 }
15
16 /// Insert a verified user directly (the HTTP signup flow is itself gated on
17 /// testnot, so the test seeds the DB and authenticates via the exempt /login).
18 async fn seed_user(pool: &PgPool, username: &str, can_create_projects: bool) {
19 let hash = makenotwork::auth::hash_password("password123").expect("hash");
20 sqlx::query(
21 "INSERT INTO users (username, email, password_hash, email_verified, can_create_projects)
22 VALUES ($1, $2, $3, true, $4)",
23 )
24 .bind(username)
25 .bind(format!("{username}@example.com"))
26 .bind(&hash)
27 .bind(can_create_projects)
28 .execute(pool)
29 .await
30 .expect("seed user");
31 }
32
33 #[tokio::test]
34 async fn access_gate_restricts_to_fan_plus_or_creator() {
35 let mut h = TestHarness::build(BuildOptions {
36 access_gate: AccessGate::FanPlusOrCreator,
37 ..Default::default()
38 })
39 .await;
40 seed_user(&h.db, "gatecreator", true).await;
41 seed_user(&h.db, "plainfan", false).await;
42
43 // Anonymous visitor → bounced to login with the gate notice.
44 let r = h.client.get("/").await;
45 assert!(r.status.is_redirection(), "anon should be redirected, got {}", r.status);
46 assert!(location(&r).starts_with("/login"), "anon should land on login, got {}", location(&r));
47
48 // Exempt paths stay reachable while the gate is on, or login is impossible.
49 assert_eq!(h.client.get("/login").await.status.as_u16(), 200, "login page must be reachable");
50 assert_eq!(h.client.get("/health").await.status.as_u16(), 200, "health must be reachable");
51
52 // A logged-in plain fan (no creator, no Fan+) is still blocked — the gate
53 // is stricter than ordinary auth.
54 h.login("plainfan", "password123").await;
55 let r = h.client.get("/library").await;
56 assert!(r.status.is_redirection(), "plain fan should be gated, got {}", r.status);
57 assert!(location(&r).starts_with("/login"), "plain fan should land on login");
58
59 // A creator passes the gate (library renders 200 for the logged-in owner).
60 h.client.post_form("/logout", "").await;
61 h.login("gatecreator", "password123").await;
62 let r = h.client.get("/library").await;
63 assert_eq!(r.status.as_u16(), 200, "creator should pass the gate: {} {}", r.status, r.text);
64 }
65
66 #[tokio::test]
67 async fn access_gate_open_serves_public_site() {
68 // Default (Open) — the public landing renders for an anonymous visitor,
69 // proving the gate is off unless explicitly enabled.
70 let mut h = TestHarness::new().await;
71 let r = h.client.get("/").await;
72 assert_eq!(r.status.as_u16(), 200, "open site should serve landing to anon: {}", r.status);
73 }
74