Skip to main content

max / makenotwork

9.9 KB · 297 lines History Blame Raw
1 use crate::harness::TestHarness;
2
3 #[tokio::test]
4 async fn endorse_post_happy_path() {
5 let mut h = TestHarness::new().await;
6 let author_id = h.login_as("author").await;
7 let comm_id = h.create_community("Test", "test").await;
8 let cat_id = h.create_category(comm_id, "General", "general").await;
9 h.add_membership(author_id, comm_id, "member").await;
10
11 let thread_id = h
12 .create_thread_with_post(cat_id, author_id, "Good Post", "Content")
13 .await;
14
15 let posts = mt_db::queries::list_posts_in_thread(&h.db, thread_id)
16 .await
17 .unwrap();
18 let post_id = posts[0].id;
19
20 // Login as a different user
21 let endorser_id = h.login_as("endorser").await;
22 h.add_membership(endorser_id, comm_id, "member").await;
23
24 // GET thread page for CSRF
25 let thread_url = format!("/p/test/general/{}", thread_id);
26 h.client.get(&thread_url).await;
27
28 let endorse_url = format!("/p/test/general/{}/posts/{}/endorse", thread_id, post_id);
29 let resp = h.client.post_form(&endorse_url, "").await;
30
31 assert!(
32 resp.status.is_redirection(),
33 "Expected redirect after endorse, got {}",
34 resp.status
35 );
36
37 // Verify DB row exists
38 let endorsements =
39 mt_db::queries::list_endorsements_for_posts(&h.db, &[post_id])
40 .await
41 .unwrap();
42 assert_eq!(endorsements.len(), 1);
43 assert_eq!(endorsements[0].endorser_id, endorser_id);
44 }
45
46 #[tokio::test]
47 async fn toggle_endorsement_removes() {
48 let mut h = TestHarness::new().await;
49 let author_id = h.login_as("author2").await;
50 let comm_id = h.create_community("Test", "test").await;
51 let cat_id = h.create_category(comm_id, "General", "general").await;
52 h.add_membership(author_id, comm_id, "member").await;
53
54 let thread_id = h
55 .create_thread_with_post(cat_id, author_id, "Toggle Post", "Content")
56 .await;
57
58 let posts = mt_db::queries::list_posts_in_thread(&h.db, thread_id)
59 .await
60 .unwrap();
61 let post_id = posts[0].id;
62
63 let toggler_id = h.login_as("toggler").await;
64 h.add_membership(toggler_id, comm_id, "member").await;
65
66 let thread_url = format!("/p/test/general/{}", thread_id);
67 h.client.get(&thread_url).await;
68
69 let endorse_url = format!("/p/test/general/{}/posts/{}/endorse", thread_id, post_id);
70
71 // First endorse
72 h.client.post_form(&endorse_url, "").await;
73 let endorsements =
74 mt_db::queries::list_endorsements_for_posts(&h.db, &[post_id])
75 .await
76 .unwrap();
77 assert_eq!(endorsements.len(), 1, "Should have 1 endorsement after first toggle");
78
79 // GET again for fresh CSRF
80 h.client.get(&thread_url).await;
81
82 // Second endorse (un-endorse)
83 h.client.post_form(&endorse_url, "").await;
84 let endorsements =
85 mt_db::queries::list_endorsements_for_posts(&h.db, &[post_id])
86 .await
87 .unwrap();
88 assert_eq!(endorsements.len(), 0, "Should have 0 endorsements after second toggle");
89 }
90
91 #[tokio::test]
92 async fn self_endorse_rejected() {
93 let mut h = TestHarness::new().await;
94 let author_id = h.login_as("selfendorser").await;
95 let comm_id = h.create_community("Test", "test").await;
96 let cat_id = h.create_category(comm_id, "General", "general").await;
97 h.add_membership(author_id, comm_id, "member").await;
98
99 let thread_id = h
100 .create_thread_with_post(cat_id, author_id, "My Post", "Content")
101 .await;
102
103 let posts = mt_db::queries::list_posts_in_thread(&h.db, thread_id)
104 .await
105 .unwrap();
106 let post_id = posts[0].id;
107
108 // GET thread page for CSRF
109 let thread_url = format!("/p/test/general/{}", thread_id);
110 h.client.get(&thread_url).await;
111
112 let endorse_url = format!("/p/test/general/{}/posts/{}/endorse", thread_id, post_id);
113 let resp = h.client.post_form(&endorse_url, "").await;
114
115 assert_eq!(resp.status.as_u16(), 403, "Self-endorsement should be 403");
116 }
117
118 #[tokio::test]
119 async fn endorse_requires_login() {
120 let mut h = TestHarness::new().await;
121 let author_id = h.login_as("loginauthor").await;
122 let comm_id = h.create_community("Test", "test").await;
123 let cat_id = h.create_category(comm_id, "General", "general").await;
124 h.add_membership(author_id, comm_id, "member").await;
125
126 let thread_id = h
127 .create_thread_with_post(cat_id, author_id, "Login Test", "Content")
128 .await;
129
130 let posts = mt_db::queries::list_posts_in_thread(&h.db, thread_id)
131 .await
132 .unwrap();
133 let post_id = posts[0].id;
134
135 // Login as a different user, then logout by creating a fresh harness client
136 // Actually, just don't login — create a fresh harness without login
137 let mut h2 = TestHarness::new().await;
138 // GET some page for CSRF
139 h2.client.get("/").await;
140
141 let endorse_url = format!("/p/test/general/{}/posts/{}/endorse", thread_id, post_id);
142 let resp = h2.client.post_form(&endorse_url, "").await;
143
144 assert!(
145 resp.status.is_redirection(),
146 "Expected redirect to login, got {}",
147 resp.status
148 );
149 }
150
151 #[tokio::test]
152 async fn endorse_removed_post_rejected() {
153 let mut h = TestHarness::new().await;
154 let author_id = h.login_as("removedauthor").await;
155 let comm_id = h.create_community("Test", "test").await;
156 let cat_id = h.create_category(comm_id, "General", "general").await;
157 h.add_membership(author_id, comm_id, "member").await;
158
159 let thread_id = h
160 .create_thread_with_post(cat_id, author_id, "Removed Post", "Content")
161 .await;
162
163 let posts = mt_db::queries::list_posts_in_thread(&h.db, thread_id)
164 .await
165 .unwrap();
166 let post_id = posts[0].id;
167
168 // Mod-remove the post
169 mt_db::mutations::mod_remove_post(&h.db, post_id, author_id)
170 .await
171 .unwrap();
172
173 let endorser_id = h.login_as("removedendorser").await;
174 h.add_membership(endorser_id, comm_id, "member").await;
175
176 let thread_url = format!("/p/test/general/{}", thread_id);
177 h.client.get(&thread_url).await;
178
179 let endorse_url = format!("/p/test/general/{}/posts/{}/endorse", thread_id, post_id);
180 let resp = h.client.post_form(&endorse_url, "").await;
181
182 assert_eq!(resp.status.as_u16(), 403, "Endorsing removed post should be 403");
183 }
184
185 #[tokio::test]
186 async fn endorsement_count_visible_to_author() {
187 let mut h = TestHarness::new().await;
188 let author_id = h.login_as("countauthor").await;
189 let comm_id = h.create_community("Test", "test").await;
190 let cat_id = h.create_category(comm_id, "General", "general").await;
191 h.add_membership(author_id, comm_id, "member").await;
192
193 let thread_id = h
194 .create_thread_with_post(cat_id, author_id, "Count Test", "Content")
195 .await;
196
197 let posts = mt_db::queries::list_posts_in_thread(&h.db, thread_id)
198 .await
199 .unwrap();
200 let post_id = posts[0].id;
201
202 // Insert endorsement directly via DB (avoids needing to switch users)
203 let endorser_id = uuid::Uuid::new_v4();
204 sqlx::query("INSERT INTO users (mnw_account_id, username, display_name) VALUES ($1, $2, $2)")
205 .bind(endorser_id)
206 .bind("countendorser")
207 .execute(&h.db)
208 .await
209 .unwrap();
210 mt_db::mutations::toggle_endorsement(&h.db, post_id, endorser_id)
211 .await
212 .unwrap();
213
214 // Author is still logged in — view thread
215 let thread_url = format!("/p/test/general/{}", thread_id);
216 let resp = h.client.get(&thread_url).await;
217
218 assert!(
219 resp.text.contains("endorsement-count"),
220 "Author should see endorsement count"
221 );
222 }
223
224 #[tokio::test]
225 async fn endorsement_count_visible_to_mod() {
226 let mut h = TestHarness::new().await;
227 let author_id = h.login_as("modcountauthor").await;
228 let comm_id = h.create_community("Test", "test").await;
229 let cat_id = h.create_category(comm_id, "General", "general").await;
230 h.add_membership(author_id, comm_id, "member").await;
231
232 let thread_id = h
233 .create_thread_with_post(cat_id, author_id, "Mod Count Test", "Content")
234 .await;
235
236 let posts = mt_db::queries::list_posts_in_thread(&h.db, thread_id)
237 .await
238 .unwrap();
239 let post_id = posts[0].id;
240
241 // Have someone endorse via DB
242 let endorser_id = h.login_as("modcountendorser").await;
243 h.add_membership(endorser_id, comm_id, "member").await;
244 mt_db::mutations::toggle_endorsement(&h.db, post_id, endorser_id)
245 .await
246 .unwrap();
247
248 // Login as moderator and view thread
249 let mod_id = h.login_as("modviewer").await;
250 h.add_membership(mod_id, comm_id, "moderator").await;
251
252 let thread_url = format!("/p/test/general/{}", thread_id);
253 let resp = h.client.get(&thread_url).await;
254
255 assert!(
256 resp.text.contains("endorsement-count"),
257 "Mod should see endorsement count"
258 );
259 }
260
261 #[tokio::test]
262 async fn endorsement_count_hidden_from_others() {
263 let mut h = TestHarness::new().await;
264 let author_id = h.login_as("hiddenauthor").await;
265 let comm_id = h.create_community("Test", "test").await;
266 let cat_id = h.create_category(comm_id, "General", "general").await;
267 h.add_membership(author_id, comm_id, "member").await;
268
269 let thread_id = h
270 .create_thread_with_post(cat_id, author_id, "Hidden Count", "Content")
271 .await;
272
273 let posts = mt_db::queries::list_posts_in_thread(&h.db, thread_id)
274 .await
275 .unwrap();
276 let post_id = posts[0].id;
277
278 // Have someone endorse via DB
279 let endorser_id = h.login_as("hiddenendorser").await;
280 h.add_membership(endorser_id, comm_id, "member").await;
281 mt_db::mutations::toggle_endorsement(&h.db, post_id, endorser_id)
282 .await
283 .unwrap();
284
285 // Login as a third user (not author, not endorser, not mod)
286 let _bystander_id = h.login_as("bystander").await;
287 h.add_membership(_bystander_id, comm_id, "member").await;
288
289 let thread_url = format!("/p/test/general/{}", thread_id);
290 let resp = h.client.get(&thread_url).await;
291
292 assert!(
293 !resp.text.contains("endorsement-count"),
294 "Bystander should NOT see endorsement count"
295 );
296 }
297