Skip to main content

max / makenotwork

8.2 KB · 259 lines History Blame Raw
1 //! Page route tests: public pages, user/project/item detail, RSS feeds, discover.
2 //!
3 //! These test that page routes render successfully and contain expected content.
4 //! Complements the API workflow tests which focus on JSON endpoints.
5
6 use crate::harness::TestHarness;
7 use serde_json::Value;
8
9 #[tokio::test]
10 async fn public_pages_render() {
11 let mut h = TestHarness::new().await;
12
13 // Landing page
14 let resp = h.client.get("/").await;
15 assert_eq!(resp.status, 200, "Landing page should return 200");
16
17 // Login page
18 let resp = h.client.get("/login").await;
19 assert_eq!(resp.status, 200, "Login page should return 200");
20
21 // Join page
22 let resp = h.client.get("/join").await;
23 assert_eq!(resp.status, 200, "Join page should return 200");
24
25 // Pricing page
26 let resp = h.client.get("/pricing").await;
27 assert_eq!(resp.status, 200, "Pricing page should return 200");
28
29 // Discover page
30 let resp = h.client.get("/discover").await;
31 assert_eq!(resp.status, 200, "Discover page should return 200");
32
33 // Discover with filters
34 let resp = h.client.get("/discover?mode=projects").await;
35 assert_eq!(resp.status, 200, "Discover projects mode should return 200");
36
37 // Tag tree browser
38 let resp = h.client.get("/discover/tags").await;
39 assert_eq!(resp.status, 200, "Tag tree should return 200");
40 }
41
42 #[tokio::test]
43 async fn user_and_project_pages() {
44 let mut h = TestHarness::new().await;
45
46 // Setup: creator with public project and item
47 let user_id = h.signup("pagetest", "pagetest@example.com", "password123").await;
48 h.grant_creator(user_id).await;
49 h.client.post_form("/logout", "").await;
50 h.login("pagetest", "password123").await;
51
52 let resp = h
53 .client
54 .post_form("/api/projects", "slug=test-proj&title=Test+Project")
55 .await;
56 let project: Value = resp.json();
57 let project_id = project["id"].as_str().unwrap();
58
59 // Make project public
60 h.client
61 .put_json(
62 &format!("/api/projects/{}", project_id),
63 r#"{"is_public": true}"#,
64 )
65 .await;
66
67 // Create a text item and make it public
68 let resp = h
69 .client
70 .post_form(
71 &format!("/api/projects/{}/items", project_id),
72 "title=Test+Article&item_type=text&is_public=true&price_cents=0",
73 )
74 .await;
75 assert!(resp.status.is_success(), "Create item failed: {}", resp.text);
76 let item: Value = resp.json();
77 let item_id = item["id"].as_str().unwrap();
78
79 // Add text content. The store page renders an excerpt from the first
80 // paragraph (full body lives on /l/{id}), so keep the asserted phrase
81 // in paragraph one.
82 h.client
83 .put_json(
84 &format!("/api/items/{}/text", item_id),
85 "{\"body\": \"Test content for page rendering.\\n\\n# Hello\\n\\nMore body below.\"}",
86 )
87 .await;
88
89 // Log out to test as anonymous user
90 h.client.post_form("/logout", "").await;
91
92 // ── User profile page ──
93 let resp = h.client.get("/u/pagetest").await;
94 assert_eq!(resp.status, 200, "User page should return 200");
95 assert!(
96 resp.text.contains("Test Project"),
97 "User page should show project title"
98 );
99
100 // ── Project page ──
101 let resp = h.client.get("/p/test-proj").await;
102 assert_eq!(resp.status, 200, "Project page should return 200");
103 assert!(
104 resp.text.contains("Test Article"),
105 "Project page should show item title"
106 );
107
108 // ── Item page (text reader) ──
109 let resp = h.client.get(&format!("/i/{}", item_id)).await;
110 assert_eq!(resp.status, 200, "Item page should return 200");
111 assert!(
112 resp.text.contains("Test content"),
113 "Item page should render text content"
114 );
115
116 // ── 404 for nonexistent user/project ──
117 let resp = h.client.get("/u/nonexistent-user-xyz").await;
118 assert_eq!(resp.status, 404, "Nonexistent user should return 404");
119
120 let resp = h.client.get("/p/nonexistent-project-xyz").await;
121 assert_eq!(resp.status, 404, "Nonexistent project should return 404");
122 }
123
124 #[tokio::test]
125 async fn rss_feeds() {
126 let mut h = TestHarness::new().await;
127
128 // Setup: creator with public project and item
129 let user_id = h.signup("rsstest", "rsstest@example.com", "password123").await;
130 h.grant_creator(user_id).await;
131 h.client.post_form("/logout", "").await;
132 h.login("rsstest", "password123").await;
133
134 let resp = h
135 .client
136 .post_form("/api/projects", "slug=rss-proj&title=RSS+Project")
137 .await;
138 let project: Value = resp.json();
139 let project_id = project["id"].as_str().unwrap();
140
141 // Make project public
142 h.client
143 .put_json(
144 &format!("/api/projects/{}", project_id),
145 r#"{"is_public": true}"#,
146 )
147 .await;
148
149 // Create a public item
150 let resp = h
151 .client
152 .post_form(
153 &format!("/api/projects/{}/items", project_id),
154 "title=RSS+Item&item_type=text&is_public=true&price_cents=0",
155 )
156 .await;
157 assert!(resp.status.is_success(), "Create item failed: {}", resp.text);
158
159 h.client.post_form("/logout", "").await;
160
161 // ── Creator RSS feed ──
162 let resp = h.client.get("/u/rsstest/rss").await;
163 assert_eq!(resp.status, 200, "Creator RSS feed should return 200");
164 assert!(
165 resp.text.contains("<rss") || resp.text.contains("<?xml"),
166 "Creator feed should be valid XML"
167 );
168 assert!(
169 resp.text.contains("RSS Item"),
170 "Creator feed should contain item title"
171 );
172
173 // ── Project RSS feed ──
174 let resp = h.client.get("/p/rss-proj/rss").await;
175 assert_eq!(resp.status, 200, "Project RSS feed should return 200");
176 assert!(
177 resp.text.contains("RSS Item"),
178 "Project feed should contain item title"
179 );
180 }
181
182 #[tokio::test]
183 async fn unpublished_item_visibility() {
184 let mut h = TestHarness::new().await;
185
186 // Setup: creator with project
187 let user_id = h.signup("vistest", "vistest@example.com", "password123").await;
188 h.grant_creator(user_id).await;
189 h.client.post_form("/logout", "").await;
190 h.login("vistest", "password123").await;
191
192 let resp = h
193 .client
194 .post_form("/api/projects", "slug=vis-proj&title=Vis+Project")
195 .await;
196 let project: Value = resp.json();
197 let project_id = project["id"].as_str().unwrap();
198
199 // Create an item (defaults to public) then make it private
200 let resp = h
201 .client
202 .post_form(
203 &format!("/api/projects/{}/items", project_id),
204 "title=Private+Item&item_type=text",
205 )
206 .await;
207 let item: Value = resp.json();
208 let item_id = item["id"].as_str().unwrap();
209
210 // Make it non-public
211 h.client
212 .put_form(
213 &format!("/api/items/{}", item_id),
214 "is_public=false",
215 )
216 .await;
217
218 // Owner can see it
219 let resp = h.client.get(&format!("/i/{}", item_id)).await;
220 assert_eq!(
221 resp.status, 200,
222 "Owner should see unpublished item"
223 );
224
225 // Log out — anonymous user should get 404
226 h.client.post_form("/logout", "").await;
227
228 let resp = h.client.get(&format!("/i/{}", item_id)).await;
229 assert_eq!(
230 resp.status, 404,
231 "Anonymous user should not see unpublished item"
232 );
233
234 // Different user should also get 404
235 h.signup("other", "other@example.com", "password123").await;
236 h.client.post_form("/logout", "").await;
237 h.login("other", "password123").await;
238
239 let resp = h.client.get(&format!("/i/{}", item_id)).await;
240 assert_eq!(
241 resp.status, 404,
242 "Non-owner should not see unpublished item"
243 );
244 }
245
246 #[tokio::test]
247 async fn discover_htmx_partial() {
248 let mut h = TestHarness::new().await;
249
250 let resp = h.client.get("/discover/results").await;
251 assert_eq!(resp.status, 200, "Discover results partial should return 200");
252
253 let resp = h.client.get("/discover/results?mode=projects").await;
254 assert_eq!(resp.status, 200, "Discover results projects mode should return 200");
255
256 let resp = h.client.get("/discover/results?item_type=audio&sort=newest").await;
257 assert_eq!(resp.status, 200, "Discover results with filters should return 200");
258 }
259