Skip to main content

max / makenotwork

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