Skip to main content

max / makenotwork

5.2 KB · 183 lines History Blame Raw
1 //! Custom profile link CRUD and reorder workflow tests.
2
3 use crate::harness::TestHarness;
4 use serde_json::Value;
5
6 #[tokio::test]
7 async fn custom_link_crud_lifecycle() {
8 let mut h = TestHarness::new().await;
9 let _user_id = h
10 .signup("linkuser", "linkuser@test.com", "password123")
11 .await;
12
13 // Create a link
14 let resp = h
15 .client
16 .post_form(
17 "/api/links",
18 "url=https%3A%2F%2Fexample.com&title=My+Website&description=Personal+site",
19 )
20 .await;
21 assert!(
22 resp.status.is_success(),
23 "Create link failed: {} {}",
24 resp.status,
25 resp.text
26 );
27 let link: Value = resp.json();
28 assert_eq!(link["url"].as_str().unwrap(), "https://example.com");
29 assert_eq!(link["title"].as_str().unwrap(), "My Website");
30 assert_eq!(link["description"].as_str().unwrap(), "Personal site");
31 let link_id = link["id"].as_str().expect("link should have id");
32
33 // Update only the title (partial update via JSON)
34 let resp = h
35 .client
36 .put_json(
37 &format!("/api/links/{link_id}"),
38 r#"{"title": "Updated Title"}"#,
39 )
40 .await;
41 assert!(
42 resp.status.is_success(),
43 "Update link failed: {} {}",
44 resp.status,
45 resp.text
46 );
47 let updated: Value = resp.json();
48 assert_eq!(updated["title"].as_str().unwrap(), "Updated Title");
49 assert_eq!(
50 updated["url"].as_str().unwrap(),
51 "https://example.com",
52 "URL should remain unchanged"
53 );
54
55 let resp = h.client.delete(&format!("/api/links/{link_id}")).await;
56 assert_eq!(resp.status, 204, "Delete should return 204");
57 }
58
59 #[tokio::test]
60 async fn custom_link_reorder() {
61 let mut h = TestHarness::new().await;
62 let _user_id = h
63 .signup("linkorder", "linkorder@test.com", "password123")
64 .await;
65
66 // Create 3 links
67 let resp = h
68 .client
69 .post_form("/api/links", "url=https%3A%2F%2Fa.com&title=Link+A")
70 .await;
71 let link_a: Value = resp.json();
72 let id_a = link_a["id"].as_str().unwrap();
73
74 let resp = h
75 .client
76 .post_form("/api/links", "url=https%3A%2F%2Fb.com&title=Link+B")
77 .await;
78 let link_b: Value = resp.json();
79 let id_b = link_b["id"].as_str().unwrap();
80
81 let resp = h
82 .client
83 .post_form("/api/links", "url=https%3A%2F%2Fc.com&title=Link+C")
84 .await;
85 let link_c: Value = resp.json();
86 let id_c = link_c["id"].as_str().unwrap();
87
88 // Reorder: C, A, B
89 let reorder_body = format!(r#"{{"link_ids": ["{id_c}", "{id_a}", "{id_b}"]}}"#);
90 let resp = h.client.put_json("/api/links/reorder", &reorder_body).await;
91 assert_eq!(
92 resp.status, 204,
93 "Reorder should return 204, got {}: {}",
94 resp.status, resp.text
95 );
96
97 // Verify order via direct SQL
98 let rows: Vec<(String, i32)> = sqlx::query_as(
99 "SELECT title, sort_order FROM custom_links WHERE user_id = (SELECT id FROM users WHERE username = 'linkorder') ORDER BY sort_order"
100 )
101 .fetch_all(&h.db)
102 .await
103 .unwrap();
104
105 assert_eq!(rows.len(), 3);
106 assert_eq!(rows[0].0, "Link C", "First should be Link C");
107 assert_eq!(rows[1].0, "Link A", "Second should be Link A");
108 assert_eq!(rows[2].0, "Link B", "Third should be Link B");
109 }
110
111 #[tokio::test]
112 async fn custom_link_validation_errors() {
113 let mut h = TestHarness::new().await;
114 let _user_id = h.signup("linkval", "linkval@test.com", "password123").await;
115
116 // Bad URL scheme (ftp)
117 let resp = h
118 .client
119 .post_form("/api/links", "url=ftp%3A%2F%2Fexample.com&title=Bad+Link")
120 .await;
121 assert_eq!(
122 resp.status, 422,
123 "Bad URL scheme should return 422, got {}: {}",
124 resp.status, resp.text
125 );
126
127 // Empty title
128 let resp = h
129 .client
130 .post_form("/api/links", "url=https%3A%2F%2Fexample.com&title=")
131 .await;
132 assert_eq!(
133 resp.status, 422,
134 "Empty title should return 422, got {}: {}",
135 resp.status, resp.text
136 );
137 }
138
139 #[tokio::test]
140 async fn custom_link_auth_other_user() {
141 let mut h = TestHarness::new().await;
142
143 // User A creates a link
144 let _user_a = h
145 .signup("linkowner", "linkowner@test.com", "password123")
146 .await;
147 let resp = h
148 .client
149 .post_form(
150 "/api/links",
151 "url=https%3A%2F%2Fexample.com&title=Owner+Link",
152 )
153 .await;
154 assert!(resp.status.is_success());
155 let link: Value = resp.json();
156 let link_id = link["id"].as_str().unwrap();
157
158 // Switch to User B
159 h.client.post_form("/logout", "").await;
160 let _user_b = h
161 .signup("linkthief", "linkthief@test.com", "password123")
162 .await;
163
164 // User B tries to update User A's link
165 let resp = h
166 .client
167 .put_json(&format!("/api/links/{link_id}"), r#"{"title": "Stolen"}"#)
168 .await;
169 assert_eq!(
170 resp.status, 404,
171 "Updating another user's link should return 404, got {}",
172 resp.status
173 );
174
175 // User B tries to delete User A's link
176 let resp = h.client.delete(&format!("/api/links/{link_id}")).await;
177 assert_eq!(
178 resp.status, 404,
179 "Deleting another user's link should return 404, got {}",
180 resp.status
181 );
182 }
183