Skip to main content

max / makenotwork

9.8 KB · 328 lines History Blame Raw
1 //! Tests for platform admin routes.
2
3 use crate::harness::TestHarness;
4 use uuid::Uuid;
5
6 #[sqlx::test]
7 async fn non_admin_gets_404(_pool: sqlx::PgPool) {
8 let mut h = TestHarness::new().await;
9 let _user = h.login_as("regular").await;
10
11 let resp = h.client.get("/_admin").await;
12 assert_eq!(resp.status, axum::http::StatusCode::NOT_FOUND);
13 }
14
15 #[sqlx::test]
16 async fn admin_can_see_dashboard(_pool: sqlx::PgPool) {
17 let admin_id = Uuid::new_v4();
18 let mut h = TestHarness::new_with_admin(admin_id).await;
19 let _admin = h.login_as("admin").await;
20
21 // Re-login with the correct admin_id since login_as generates a random UUID
22 sqlx::query("UPDATE users SET mnw_account_id = $1 WHERE username = 'admin'")
23 .bind(admin_id)
24 .execute(&h.db)
25 .await
26 .unwrap();
27
28 // Re-establish session with correct user_id
29 h.client.get("/").await;
30 let body = serde_json::json!({
31 "user_id": admin_id.to_string(),
32 "username": "admin",
33 });
34 h.client.post_json("/_test/login", &body.to_string()).await;
35
36 let resp = h.client.get("/_admin").await;
37 assert_eq!(resp.status, axum::http::StatusCode::OK);
38 assert!(resp.text.contains("Platform Admin"));
39 }
40
41 #[sqlx::test]
42 async fn admin_can_suspend_community(_pool: sqlx::PgPool) {
43 let admin_id = Uuid::new_v4();
44 let mut h = TestHarness::new_with_admin(admin_id).await;
45
46 // Set up admin session
47 sqlx::query(
48 "INSERT INTO users (mnw_account_id, username, display_name)
49 VALUES ($1, 'admin', 'Admin') ON CONFLICT DO NOTHING",
50 )
51 .bind(admin_id)
52 .execute(&h.db)
53 .await
54 .unwrap();
55 h.client.get("/").await;
56 let body = serde_json::json!({ "user_id": admin_id.to_string(), "username": "admin" });
57 h.client.post_json("/_test/login", &body.to_string()).await;
58
59 let community_id = h.create_community("Test Community", "test").await;
60
61 let resp = h.client.post_form(
62 &format!("/_admin/communities/{community_id}/suspend"),
63 "reason=policy+violation",
64 ).await;
65 assert!(resp.status.is_redirection() || resp.status == axum::http::StatusCode::OK);
66
67 // Verify community is now suspended (returns 403)
68 // Verify the suspension stuck in the DB
69 let suspended: bool = sqlx::query_scalar(
70 "SELECT suspended_at IS NOT NULL FROM communities WHERE id = $1",
71 )
72 .bind(community_id)
73 .fetch_one(&h.db)
74 .await
75 .unwrap();
76 assert!(suspended);
77 }
78
79 #[sqlx::test]
80 async fn admin_can_unsuspend_community(_pool: sqlx::PgPool) {
81 let admin_id = Uuid::new_v4();
82 let mut h = TestHarness::new_with_admin(admin_id).await;
83
84 sqlx::query(
85 "INSERT INTO users (mnw_account_id, username, display_name)
86 VALUES ($1, 'admin', 'Admin') ON CONFLICT DO NOTHING",
87 )
88 .bind(admin_id)
89 .execute(&h.db)
90 .await
91 .unwrap();
92 h.client.get("/").await;
93 let body = serde_json::json!({ "user_id": admin_id.to_string(), "username": "admin" });
94 h.client.post_json("/_test/login", &body.to_string()).await;
95
96 let community_id = h.create_community("Test", "test").await;
97
98 // Suspend it
99 sqlx::query("UPDATE communities SET suspended_at = now(), suspension_reason = 'test' WHERE id = $1")
100 .bind(community_id)
101 .execute(&h.db)
102 .await
103 .unwrap();
104
105 let resp = h.client.post_form(
106 &format!("/_admin/communities/{community_id}/unsuspend"),
107 "",
108 ).await;
109 assert!(resp.status.is_redirection() || resp.status == axum::http::StatusCode::OK);
110
111 let suspended: bool = sqlx::query_scalar(
112 "SELECT suspended_at IS NOT NULL FROM communities WHERE id = $1",
113 )
114 .bind(community_id)
115 .fetch_one(&h.db)
116 .await
117 .unwrap();
118 assert!(!suspended);
119 }
120
121 #[sqlx::test]
122 async fn admin_can_suspend_user(_pool: sqlx::PgPool) {
123 let admin_id = Uuid::new_v4();
124 let mut h = TestHarness::new_with_admin(admin_id).await;
125
126 sqlx::query(
127 "INSERT INTO users (mnw_account_id, username, display_name)
128 VALUES ($1, 'admin', 'Admin') ON CONFLICT DO NOTHING",
129 )
130 .bind(admin_id)
131 .execute(&h.db)
132 .await
133 .unwrap();
134 h.client.get("/").await;
135 let body = serde_json::json!({ "user_id": admin_id.to_string(), "username": "admin" });
136 h.client.post_json("/_test/login", &body.to_string()).await;
137
138 // Create a user to suspend
139 let target_id = Uuid::new_v4();
140 sqlx::query(
141 "INSERT INTO users (mnw_account_id, username, display_name) VALUES ($1, 'baduser', 'Bad User')",
142 )
143 .bind(target_id)
144 .execute(&h.db)
145 .await
146 .unwrap();
147
148 let resp = h.client.post_form(
149 &format!("/_admin/users/{target_id}/suspend"),
150 "reason=abuse",
151 ).await;
152 assert!(resp.status.is_redirection() || resp.status == axum::http::StatusCode::OK);
153
154 let suspended: bool = sqlx::query_scalar(
155 "SELECT suspended_at IS NOT NULL FROM users WHERE mnw_account_id = $1",
156 )
157 .bind(target_id)
158 .fetch_one(&h.db)
159 .await
160 .unwrap();
161 assert!(suspended);
162 }
163
164 #[sqlx::test]
165 async fn admin_can_unsuspend_user(_pool: sqlx::PgPool) {
166 let admin_id = Uuid::new_v4();
167 let mut h = TestHarness::new_with_admin(admin_id).await;
168
169 sqlx::query(
170 "INSERT INTO users (mnw_account_id, username, display_name)
171 VALUES ($1, 'admin', 'Admin') ON CONFLICT DO NOTHING",
172 )
173 .bind(admin_id)
174 .execute(&h.db)
175 .await
176 .unwrap();
177 h.client.get("/").await;
178 let body = serde_json::json!({ "user_id": admin_id.to_string(), "username": "admin" });
179 h.client.post_json("/_test/login", &body.to_string()).await;
180
181 let target_id = Uuid::new_v4();
182 sqlx::query(
183 "INSERT INTO users (mnw_account_id, username, display_name, suspended_at, suspension_reason)
184 VALUES ($1, 'baduser', 'Bad User', now(), 'abuse')",
185 )
186 .bind(target_id)
187 .execute(&h.db)
188 .await
189 .unwrap();
190
191 let resp = h.client.post_form(
192 &format!("/_admin/users/{target_id}/unsuspend"),
193 "",
194 ).await;
195 assert!(resp.status.is_redirection() || resp.status == axum::http::StatusCode::OK);
196
197 let suspended: bool = sqlx::query_scalar(
198 "SELECT suspended_at IS NOT NULL FROM users WHERE mnw_account_id = $1",
199 )
200 .bind(target_id)
201 .fetch_one(&h.db)
202 .await
203 .unwrap();
204 assert!(!suspended);
205 }
206
207 #[sqlx::test]
208 async fn admin_search_finds_users(_pool: sqlx::PgPool) {
209 let admin_id = Uuid::new_v4();
210 let mut h = TestHarness::new_with_admin(admin_id).await;
211
212 sqlx::query(
213 "INSERT INTO users (mnw_account_id, username, display_name)
214 VALUES ($1, 'admin', 'Admin') ON CONFLICT DO NOTHING",
215 )
216 .bind(admin_id)
217 .execute(&h.db)
218 .await
219 .unwrap();
220 h.client.get("/").await;
221 let body = serde_json::json!({ "user_id": admin_id.to_string(), "username": "admin" });
222 h.client
223 .post_json("/_test/login", &body.to_string())
224 .await;
225
226 // Create a searchable user
227 let target_id = Uuid::new_v4();
228 sqlx::query(
229 "INSERT INTO users (mnw_account_id, username, display_name)
230 VALUES ($1, 'findableuser', 'Findable User')",
231 )
232 .bind(target_id)
233 .execute(&h.db)
234 .await
235 .unwrap();
236
237 let resp = h.client.get("/_admin?q=findableuser").await;
238 assert_eq!(resp.status, axum::http::StatusCode::OK);
239 assert!(
240 resp.text.contains("findableuser"),
241 "Search results should include matching user"
242 );
243 }
244
245 #[sqlx::test]
246 async fn admin_invalid_uuid_returns_400(_pool: sqlx::PgPool) {
247 let admin_id = Uuid::new_v4();
248 let mut h = TestHarness::new_with_admin(admin_id).await;
249
250 sqlx::query(
251 "INSERT INTO users (mnw_account_id, username, display_name)
252 VALUES ($1, 'admin', 'Admin') ON CONFLICT DO NOTHING",
253 )
254 .bind(admin_id)
255 .execute(&h.db)
256 .await
257 .unwrap();
258 h.client.get("/").await;
259 let body = serde_json::json!({ "user_id": admin_id.to_string(), "username": "admin" });
260 h.client
261 .post_json("/_test/login", &body.to_string())
262 .await;
263
264 let resp = h
265 .client
266 .post_form("/_admin/communities/not-a-uuid/suspend", "reason=test")
267 .await;
268 // parse_uuid returns 404 (hides admin routes from probing)
269 assert_eq!(resp.status, axum::http::StatusCode::NOT_FOUND);
270 }
271
272 #[sqlx::test]
273 async fn admin_suspend_creates_mod_log_entry(_pool: sqlx::PgPool) {
274 let admin_id = Uuid::new_v4();
275 let mut h = TestHarness::new_with_admin(admin_id).await;
276
277 sqlx::query(
278 "INSERT INTO users (mnw_account_id, username, display_name)
279 VALUES ($1, 'admin', 'Admin') ON CONFLICT DO NOTHING",
280 )
281 .bind(admin_id)
282 .execute(&h.db)
283 .await
284 .unwrap();
285 h.client.get("/").await;
286 let body = serde_json::json!({ "user_id": admin_id.to_string(), "username": "admin" });
287 h.client
288 .post_json("/_test/login", &body.to_string())
289 .await;
290
291 let community_id = h.create_community("Test", "test").await;
292
293 h.client
294 .post_form(
295 &format!("/_admin/communities/{community_id}/suspend"),
296 "reason=policy+violation",
297 )
298 .await;
299
300 // Verify mod_log entry exists
301 let count: i64 = sqlx::query_scalar(
302 "SELECT COUNT(*) FROM mod_log WHERE action = 'suspend_community' AND actor_id = $1",
303 )
304 .bind(admin_id)
305 .fetch_one(&h.db)
306 .await
307 .unwrap();
308 assert_eq!(count, 1, "Should have a mod_log entry for suspend_community");
309 }
310
311 #[sqlx::test]
312 async fn non_admin_post_to_suspend_returns_404(_pool: sqlx::PgPool) {
313 let admin_id = Uuid::new_v4();
314 let mut h = TestHarness::new_with_admin(admin_id).await;
315 let _user = h.login_as("regular").await;
316
317 let community_id = h.create_community("Test", "test").await;
318
319 let resp = h
320 .client
321 .post_form(
322 &format!("/_admin/communities/{community_id}/suspend"),
323 "reason=test",
324 )
325 .await;
326 assert_eq!(resp.status, axum::http::StatusCode::NOT_FOUND);
327 }
328