| 1 |
|
| 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.signup("linkuser", "linkuser@test.com", "password123").await; |
| 10 |
|
| 11 |
|
| 12 |
let resp = h.client.post_form( |
| 13 |
"/api/links", |
| 14 |
"url=https%3A%2F%2Fexample.com&title=My+Website&description=Personal+site", |
| 15 |
).await; |
| 16 |
assert!(resp.status.is_success(), "Create link failed: {} {}", resp.status, resp.text); |
| 17 |
let link: Value = resp.json(); |
| 18 |
assert_eq!(link["url"].as_str().unwrap(), "https://example.com"); |
| 19 |
assert_eq!(link["title"].as_str().unwrap(), "My Website"); |
| 20 |
assert_eq!(link["description"].as_str().unwrap(), "Personal site"); |
| 21 |
let link_id = link["id"].as_str().expect("link should have id"); |
| 22 |
|
| 23 |
|
| 24 |
let resp = h.client.put_json( |
| 25 |
&format!("/api/links/{}", link_id), |
| 26 |
r#"{"title": "Updated Title"}"#, |
| 27 |
).await; |
| 28 |
assert!(resp.status.is_success(), "Update link failed: {} {}", resp.status, resp.text); |
| 29 |
let updated: Value = resp.json(); |
| 30 |
assert_eq!(updated["title"].as_str().unwrap(), "Updated Title"); |
| 31 |
assert_eq!(updated["url"].as_str().unwrap(), "https://example.com", "URL should remain unchanged"); |
| 32 |
|
| 33 |
|
| 34 |
let resp = h.client.delete(&format!("/api/links/{}", link_id)).await; |
| 35 |
assert_eq!(resp.status, 204, "Delete should return 204"); |
| 36 |
} |
| 37 |
|
| 38 |
#[tokio::test] |
| 39 |
async fn custom_link_reorder() { |
| 40 |
let mut h = TestHarness::new().await; |
| 41 |
let _user_id = h.signup("linkorder", "linkorder@test.com", "password123").await; |
| 42 |
|
| 43 |
|
| 44 |
let resp = h.client.post_form("/api/links", "url=https%3A%2F%2Fa.com&title=Link+A").await; |
| 45 |
let link_a: Value = resp.json(); |
| 46 |
let id_a = link_a["id"].as_str().unwrap(); |
| 47 |
|
| 48 |
let resp = h.client.post_form("/api/links", "url=https%3A%2F%2Fb.com&title=Link+B").await; |
| 49 |
let link_b: Value = resp.json(); |
| 50 |
let id_b = link_b["id"].as_str().unwrap(); |
| 51 |
|
| 52 |
let resp = h.client.post_form("/api/links", "url=https%3A%2F%2Fc.com&title=Link+C").await; |
| 53 |
let link_c: Value = resp.json(); |
| 54 |
let id_c = link_c["id"].as_str().unwrap(); |
| 55 |
|
| 56 |
|
| 57 |
let reorder_body = format!(r#"{{"link_ids": ["{}", "{}", "{}"]}}"#, id_c, id_a, id_b); |
| 58 |
let resp = h.client.put_json("/api/links/reorder", &reorder_body).await; |
| 59 |
assert_eq!(resp.status, 204, "Reorder should return 204, got {}: {}", resp.status, resp.text); |
| 60 |
|
| 61 |
|
| 62 |
let rows: Vec<(String, i32)> = sqlx::query_as( |
| 63 |
"SELECT title, sort_order FROM custom_links WHERE user_id = (SELECT id FROM users WHERE username = 'linkorder') ORDER BY sort_order" |
| 64 |
) |
| 65 |
.fetch_all(&h.db) |
| 66 |
.await |
| 67 |
.unwrap(); |
| 68 |
|
| 69 |
assert_eq!(rows.len(), 3); |
| 70 |
assert_eq!(rows[0].0, "Link C", "First should be Link C"); |
| 71 |
assert_eq!(rows[1].0, "Link A", "Second should be Link A"); |
| 72 |
assert_eq!(rows[2].0, "Link B", "Third should be Link B"); |
| 73 |
} |
| 74 |
|
| 75 |
#[tokio::test] |
| 76 |
async fn custom_link_validation_errors() { |
| 77 |
let mut h = TestHarness::new().await; |
| 78 |
let _user_id = h.signup("linkval", "linkval@test.com", "password123").await; |
| 79 |
|
| 80 |
|
| 81 |
let resp = h.client.post_form( |
| 82 |
"/api/links", |
| 83 |
"url=ftp%3A%2F%2Fexample.com&title=Bad+Link", |
| 84 |
).await; |
| 85 |
assert_eq!(resp.status, 422, "Bad URL scheme should return 422, got {}: {}", resp.status, resp.text); |
| 86 |
|
| 87 |
|
| 88 |
let resp = h.client.post_form( |
| 89 |
"/api/links", |
| 90 |
"url=https%3A%2F%2Fexample.com&title=", |
| 91 |
).await; |
| 92 |
assert_eq!(resp.status, 422, "Empty title should return 422, got {}: {}", resp.status, resp.text); |
| 93 |
} |
| 94 |
|
| 95 |
#[tokio::test] |
| 96 |
async fn custom_link_auth_other_user() { |
| 97 |
let mut h = TestHarness::new().await; |
| 98 |
|
| 99 |
|
| 100 |
let _user_a = h.signup("linkowner", "linkowner@test.com", "password123").await; |
| 101 |
let resp = h.client.post_form( |
| 102 |
"/api/links", |
| 103 |
"url=https%3A%2F%2Fexample.com&title=Owner+Link", |
| 104 |
).await; |
| 105 |
assert!(resp.status.is_success()); |
| 106 |
let link: Value = resp.json(); |
| 107 |
let link_id = link["id"].as_str().unwrap(); |
| 108 |
|
| 109 |
|
| 110 |
h.client.post_form("/logout", "").await; |
| 111 |
let _user_b = h.signup("linkthief", "linkthief@test.com", "password123").await; |
| 112 |
|
| 113 |
|
| 114 |
let resp = h.client.put_json( |
| 115 |
&format!("/api/links/{}", link_id), |
| 116 |
r#"{"title": "Stolen"}"#, |
| 117 |
).await; |
| 118 |
assert_eq!(resp.status, 404, "Updating another user's link should return 404, got {}", resp.status); |
| 119 |
|
| 120 |
|
| 121 |
let resp = h.client.delete(&format!("/api/links/{}", link_id)).await; |
| 122 |
assert_eq!(resp.status, 404, "Deleting another user's link should return 404, got {}", resp.status); |
| 123 |
} |
| 124 |
|