fix: update sources integration tests for multi-theme (add theme_id everywhere)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
master
oabrivard 2 months ago
parent 0a0684e42e
commit 6cf2b9f5a4

@ -22,6 +22,20 @@ fn require_test_db() -> bool {
std::env::var("TEST_DATABASE_URL").is_ok()
}
/// Helper: create a theme and return its id.
async fn create_theme(app: &common::TestApp, session: &str) -> String {
let body = serde_json::json!({
"name": "Test Theme",
"theme": "Test",
"categories": ["Cat"]
});
let (status, resp) = app
.post_with_session("/api/v1/themes", &body, session)
.await;
assert_eq!(status.as_u16(), 201, "Theme creation should succeed");
resp["id"].as_str().unwrap().to_string()
}
// ═══════════════════════════════════════════════════════════════════════════
// Authentication
// ═══════════════════════════════════════════════════════════════════════════
@ -107,8 +121,11 @@ async fn get_sources_empty_list() {
let (_user_id, session) = app
.create_authenticated_user("sources-empty@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let (status, body) = app.get_with_session("/api/v1/sources", &session).await;
let (status, body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
assert_eq!(status, StatusCode::OK, "GET /sources should return 200");
let sources = body.as_array().expect("Response should be an array");
@ -126,10 +143,12 @@ async fn create_source_with_valid_data_returns_201() {
let (_user_id, session) = app
.create_authenticated_user("sources-create@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let body = serde_json::json!({
"title": "My Blog",
"url": "https://blog.example.com"
"url": "https://blog.example.com",
"theme_id": theme_id
});
let (status, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -163,11 +182,13 @@ async fn create_source_then_list_shows_it() {
let (_user_id, session) = app
.create_authenticated_user("sources-create-list@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
// Create a source
let body = serde_json::json!({
"title": "Tech News",
"url": "https://technews.example.com"
"url": "https://technews.example.com",
"theme_id": theme_id
});
let (create_status, create_resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -176,7 +197,9 @@ async fn create_source_then_list_shows_it() {
let created_id = create_resp["id"].as_str().unwrap();
// List sources
let (list_status, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (list_status, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
assert_eq!(list_status, StatusCode::OK);
let sources = list_body.as_array().expect("Should be an array");
@ -197,12 +220,14 @@ async fn create_multiple_sources_list_returns_all() {
let (_user_id, session) = app
.create_authenticated_user("sources-multi@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
// Create three sources
for i in 1..=3 {
let body = serde_json::json!({
"title": format!("Source {}", i),
"url": format!("https://source{}.example.com", i)
"url": format!("https://source{}.example.com", i),
"theme_id": theme_id
});
let (status, _) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -211,7 +236,9 @@ async fn create_multiple_sources_list_returns_all() {
}
// List should have 3 sources
let (status, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (status, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
assert_eq!(status, StatusCode::OK);
let sources = list_body.as_array().unwrap();
@ -233,10 +260,12 @@ async fn create_source_invalid_url_returns_422() {
let (_user_id, session) = app
.create_authenticated_user("sources-invalid-url@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let body = serde_json::json!({
"title": "My Blog",
"url": "not-a-valid-url"
"url": "not-a-valid-url",
"theme_id": theme_id
});
let (status, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -261,10 +290,12 @@ async fn create_source_ftp_url_returns_422() {
let (_user_id, session) = app
.create_authenticated_user("sources-ftp-url@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let body = serde_json::json!({
"title": "FTP Source",
"url": "ftp://files.example.com"
"url": "ftp://files.example.com",
"theme_id": theme_id
});
let (status, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -289,10 +320,12 @@ async fn create_source_empty_title_returns_422() {
let (_user_id, session) = app
.create_authenticated_user("sources-empty-title@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let body = serde_json::json!({
"title": " ",
"url": "https://example.com"
"url": "https://example.com",
"theme_id": theme_id
});
let (status, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -317,11 +350,13 @@ async fn create_source_title_too_long_returns_422() {
let (_user_id, session) = app
.create_authenticated_user("sources-title-long@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let long_title = "a".repeat(201);
let body = serde_json::json!({
"title": long_title,
"url": "https://example.com"
"url": "https://example.com",
"theme_id": theme_id
});
let (status, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -346,12 +381,14 @@ async fn create_source_url_too_long_returns_422() {
let (_user_id, session) = app
.create_authenticated_user("sources-url-long@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let long_url = format!("https://example.com/{}", "a".repeat(990));
assert!(long_url.len() > 1000);
let body = serde_json::json!({
"title": "My Blog",
"url": long_url
"url": long_url,
"theme_id": theme_id
});
let (status, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -376,10 +413,12 @@ async fn create_source_empty_url_returns_422() {
let (_user_id, session) = app
.create_authenticated_user("sources-empty-url@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let body = serde_json::json!({
"title": "My Blog",
"url": ""
"url": "",
"theme_id": theme_id
});
let (status, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -408,10 +447,12 @@ async fn create_source_duplicate_url_returns_error() {
let (_user_id, session) = app
.create_authenticated_user("sources-dup@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let body = serde_json::json!({
"title": "My Blog",
"url": "https://duplicate.example.com"
"url": "https://duplicate.example.com",
"theme_id": theme_id
});
// First creation should succeed
@ -423,7 +464,8 @@ async fn create_source_duplicate_url_returns_error() {
// Second creation with same URL should fail (DB unique constraint)
let body2 = serde_json::json!({
"title": "Different Title",
"url": "https://duplicate.example.com"
"url": "https://duplicate.example.com",
"theme_id": theme_id
});
let (status2, _) = app
.post_with_session("/api/v1/sources", &body2, &session)
@ -457,11 +499,13 @@ async fn delete_source_valid_id_returns_204() {
let (_user_id, session) = app
.create_authenticated_user("sources-delete@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
// Create a source
let body = serde_json::json!({
"title": "To Delete",
"url": "https://delete.example.com"
"url": "https://delete.example.com",
"theme_id": theme_id
});
let (create_status, create_resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -491,11 +535,13 @@ async fn delete_source_then_list_no_longer_shows_it() {
let (_user_id, session) = app
.create_authenticated_user("sources-delete-list@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
// Create two sources
let body1 = serde_json::json!({
"title": "Keep This",
"url": "https://keep.example.com"
"url": "https://keep.example.com",
"theme_id": theme_id
});
let (_, resp1) = app
.post_with_session("/api/v1/sources", &body1, &session)
@ -504,7 +550,8 @@ async fn delete_source_then_list_no_longer_shows_it() {
let body2 = serde_json::json!({
"title": "Delete This",
"url": "https://delete-me.example.com"
"url": "https://delete-me.example.com",
"theme_id": theme_id
});
let (_, resp2) = app
.post_with_session("/api/v1/sources", &body2, &session)
@ -518,7 +565,9 @@ async fn delete_source_then_list_no_longer_shows_it() {
assert_eq!(del_status, StatusCode::NO_CONTENT);
// List should only show the first source
let (_, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (_, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
let sources = list_body.as_array().unwrap();
assert_eq!(sources.len(), 1, "Should have exactly 1 source after delete");
assert_eq!(sources[0]["id"], keep_id);
@ -568,11 +617,14 @@ async fn user_a_sources_not_visible_to_user_b() {
let (_user_b_id, session_b) = app
.create_authenticated_user("user-b-sources@example.com")
.await;
let theme_id_a = create_theme(&app, &session_a).await;
let theme_id_b = create_theme(&app, &session_b).await;
// User A creates a source
let body = serde_json::json!({
"title": "User A Blog",
"url": "https://usera.example.com"
"url": "https://usera.example.com",
"theme_id": theme_id_a
});
let (status, _) = app
.post_with_session("/api/v1/sources", &body, &session_a)
@ -581,7 +633,7 @@ async fn user_a_sources_not_visible_to_user_b() {
// User B lists sources -> should be empty
let (list_status, list_body) = app
.get_with_session("/api/v1/sources", &session_b)
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id_b), &session_b)
.await;
assert_eq!(list_status, StatusCode::OK);
let sources = list_body.as_array().unwrap();
@ -592,7 +644,7 @@ async fn user_a_sources_not_visible_to_user_b() {
// User A lists sources -> should see their source
let (_, list_body_a) = app
.get_with_session("/api/v1/sources", &session_a)
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id_a), &session_a)
.await;
let sources_a = list_body_a.as_array().unwrap();
assert_eq!(sources_a.len(), 1, "User A should see their own source");
@ -613,11 +665,13 @@ async fn user_b_cannot_delete_user_a_source_returns_404() {
let (_user_b_id, session_b) = app
.create_authenticated_user("attacker-b@example.com")
.await;
let theme_id_a = create_theme(&app, &session_a).await;
// User A creates a source
let body = serde_json::json!({
"title": "User A Private",
"url": "https://private-a.example.com"
"url": "https://private-a.example.com",
"theme_id": theme_id_a
});
let (_, create_resp) = app
.post_with_session("/api/v1/sources", &body, &session_a)
@ -638,7 +692,7 @@ async fn user_b_cannot_delete_user_a_source_returns_404() {
// Verify User A's source is still there
let (_, list_body) = app
.get_with_session("/api/v1/sources", &session_a)
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id_a), &session_a)
.await;
let sources = list_body.as_array().unwrap();
assert_eq!(
@ -663,13 +717,15 @@ async fn bulk_import_valid_sources_succeeds() {
let (_user_id, session) = app
.create_authenticated_user("bulk-import@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let body = serde_json::json!({
"sources": [
{ "title": "Blog 1", "url": "https://blog1.example.com" },
{ "title": "Blog 2", "url": "https://blog2.example.com" },
{ "title": "Blog 3", "url": "https://blog3.example.com" }
]
],
"theme_id": theme_id
});
let (status, resp) = app
@ -685,7 +741,9 @@ async fn bulk_import_valid_sources_succeeds() {
assert_eq!(resp["skipped"], 0, "Should have skipped 0 sources");
// Verify they appear in the list
let (_, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (_, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
let sources = list_body.as_array().unwrap();
assert_eq!(sources.len(), 3);
}
@ -701,11 +759,13 @@ async fn bulk_import_with_duplicates_skips_them() {
let (_user_id, session) = app
.create_authenticated_user("bulk-dup@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
// First, create one source normally
let body = serde_json::json!({
"title": "Existing",
"url": "https://existing.example.com"
"url": "https://existing.example.com",
"theme_id": theme_id
});
let (status, _) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -717,7 +777,8 @@ async fn bulk_import_with_duplicates_skips_them() {
"sources": [
{ "title": "Existing Dup", "url": "https://existing.example.com" },
{ "title": "New One", "url": "https://new.example.com" }
]
],
"theme_id": theme_id
});
let (bulk_status, resp) = app
@ -735,7 +796,9 @@ async fn bulk_import_with_duplicates_skips_them() {
);
// Total should be 2 (original + new)
let (_, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (_, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
let sources = list_body.as_array().unwrap();
assert_eq!(sources.len(), 2);
}
@ -751,9 +814,11 @@ async fn bulk_import_empty_array_returns_error() {
let (_user_id, session) = app
.create_authenticated_user("bulk-empty@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let body = serde_json::json!({
"sources": []
"sources": [],
"theme_id": theme_id
});
let (status, resp) = app
@ -779,6 +844,7 @@ async fn bulk_import_with_invalid_entries_reports_errors() {
let (_user_id, session) = app
.create_authenticated_user("bulk-invalid@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let body = serde_json::json!({
"sources": [
@ -786,7 +852,8 @@ async fn bulk_import_with_invalid_entries_reports_errors() {
{ "title": "", "url": "https://empty-title.example.com" },
{ "title": "Bad URL", "url": "not-a-url" },
{ "title": "Also Valid", "url": "https://alsovalid.example.com" }
]
],
"theme_id": theme_id
});
let (status, resp) = app
@ -806,7 +873,9 @@ async fn bulk_import_with_invalid_entries_reports_errors() {
);
// Verify only 2 sources exist
let (_, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (_, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
let sources = list_body.as_array().unwrap();
assert_eq!(sources.len(), 2);
}
@ -850,12 +919,14 @@ async fn export_csv_with_sources_returns_csv() {
let (_user_id, session) = app
.create_authenticated_user("csv-export@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
// Create some sources
for i in 1..=2 {
let body = serde_json::json!({
"title": format!("Source {}", i),
"url": format!("https://source{}.example.com", i)
"url": format!("https://source{}.example.com", i),
"theme_id": theme_id
});
let (s, _) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -866,7 +937,7 @@ async fn export_csv_with_sources_returns_csv() {
// Export CSV
let req = Request::builder()
.method(Method::GET)
.uri("/api/v1/sources/export-csv")
.uri(&format!("/api/v1/sources/export-csv?theme_id={}", theme_id))
.header(
"Cookie",
format!("ai_synth_session={}", session),
@ -927,10 +998,11 @@ async fn export_csv_with_no_sources_returns_header_only() {
let (_user_id, session) = app
.create_authenticated_user("csv-export-empty@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let req = Request::builder()
.method(Method::GET)
.uri("/api/v1/sources/export-csv")
.uri(&format!("/api/v1/sources/export-csv?theme_id={}", theme_id))
.header(
"Cookie",
format!("ai_synth_session={}", session),
@ -977,6 +1049,7 @@ async fn export_csv_without_auth_returns_401() {
fn build_csv_multipart_request(
csv_content: &str,
session_cookie: &str,
theme_id: &str,
) -> Request<Body> {
let boundary = "----TestBoundary12345";
let body = format!(
@ -992,7 +1065,7 @@ fn build_csv_multipart_request(
Request::builder()
.method(Method::POST)
.uri("/api/v1/sources/import-csv")
.uri(&format!("/api/v1/sources/import-csv?theme_id={}", theme_id))
.header(
"Content-Type",
format!("multipart/form-data; boundary={}", boundary),
@ -1017,9 +1090,10 @@ async fn import_csv_with_valid_data_succeeds() {
let (_user_id, session) = app
.create_authenticated_user("csv-import@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let csv_content = "title,url\nBlog One,https://blog1.example.com\nBlog Two,https://blog2.example.com";
let req = build_csv_multipart_request(csv_content, &session);
let req = build_csv_multipart_request(csv_content, &session, &theme_id);
let response = app.raw_request(req).await;
let status = response.status();
@ -1037,7 +1111,9 @@ async fn import_csv_with_valid_data_succeeds() {
assert_eq!(resp["skipped"], 0);
// Verify via list
let (_, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (_, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
let sources = list_body.as_array().unwrap();
assert_eq!(sources.len(), 2);
}
@ -1053,9 +1129,10 @@ async fn import_csv_semicolon_separated_succeeds() {
let (_user_id, session) = app
.create_authenticated_user("csv-semicolon@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let csv_content = "titre;lien\nMon Blog;https://monblog.example.com\nActus;https://actus.example.com";
let req = build_csv_multipart_request(csv_content, &session);
let req = build_csv_multipart_request(csv_content, &session, &theme_id);
let response = app.raw_request(req).await;
let status = response.status();
@ -1078,7 +1155,9 @@ async fn import_csv_semicolon_separated_succeeds() {
);
// Verify the actual titles
let (_, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (_, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
let sources = list_body.as_array().unwrap();
assert_eq!(sources.len(), 2);
let titles: Vec<&str> = sources
@ -1143,6 +1222,7 @@ async fn csv_export_roundtrip() {
let (_user_id, session) = app
.create_authenticated_user("csv-roundtrip@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
// Create sources
let source_data = vec![
@ -1152,7 +1232,7 @@ async fn csv_export_roundtrip() {
];
for (title, url) in &source_data {
let body = serde_json::json!({ "title": title, "url": url });
let body = serde_json::json!({ "title": title, "url": url, "theme_id": theme_id });
let (s, _) = app
.post_with_session("/api/v1/sources", &body, &session)
.await;
@ -1162,7 +1242,7 @@ async fn csv_export_roundtrip() {
// Export CSV
let req = Request::builder()
.method(Method::GET)
.uri("/api/v1/sources/export-csv")
.uri(&format!("/api/v1/sources/export-csv?theme_id={}", theme_id))
.header("Cookie", format!("ai_synth_session={}", session))
.body(Body::empty())
.unwrap();
@ -1210,29 +1290,35 @@ async fn max_sources_limit_enforced() {
let (user_id, session) = app
.create_authenticated_user("max-sources@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let theme_uuid: uuid::Uuid = theme_id.parse().unwrap();
// Insert 100 sources directly into the database (faster than 100 API calls)
for i in 0..100 {
sqlx::query(
"INSERT INTO sources (user_id, title, url) VALUES ($1, $2, $3)",
"INSERT INTO sources (user_id, title, url, theme_id) VALUES ($1, $2, $3, $4)",
)
.bind(user_id)
.bind(format!("Source {}", i))
.bind(format!("https://source{}.example.com", i))
.bind(theme_uuid)
.execute(&app.pool)
.await
.expect("Failed to insert source");
}
// Verify we have 100
let (_, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (_, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
let sources = list_body.as_array().unwrap();
assert_eq!(sources.len(), 100, "Should have 100 sources");
// Attempt to create the 101st source via API
let body = serde_json::json!({
"title": "One Too Many",
"url": "https://toomany.example.com"
"url": "https://toomany.example.com",
"theme_id": theme_id
});
let (status, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -1264,15 +1350,18 @@ async fn bulk_import_respects_max_sources_limit() {
let (user_id, session) = app
.create_authenticated_user("bulk-max@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let theme_uuid: uuid::Uuid = theme_id.parse().unwrap();
// Insert 98 sources directly
for i in 0..98 {
sqlx::query(
"INSERT INTO sources (user_id, title, url) VALUES ($1, $2, $3)",
"INSERT INTO sources (user_id, title, url, theme_id) VALUES ($1, $2, $3, $4)",
)
.bind(user_id)
.bind(format!("Source {}", i))
.bind(format!("https://source{}.example.com", i))
.bind(theme_uuid)
.execute(&app.pool)
.await
.expect("Failed to insert source");
@ -1288,7 +1377,7 @@ async fn bulk_import_respects_max_sources_limit() {
})
.collect();
let body = serde_json::json!({ "sources": sources });
let body = serde_json::json!({ "sources": sources, "theme_id": theme_id });
let (status, resp) = app
.post_with_session("/api/v1/sources/bulk", &body, &session)
.await;
@ -1306,7 +1395,9 @@ async fn bulk_import_respects_max_sources_limit() {
);
// Verify total is exactly 100
let (_, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (_, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
let all_sources = list_body.as_array().unwrap();
assert_eq!(all_sources.len(), 100, "Should have exactly 100 sources");
}
@ -1326,12 +1417,14 @@ async fn create_source_with_boundary_values_succeeds() {
let (_user_id, session) = app
.create_authenticated_user("sources-boundary@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
// Title exactly 200 chars
let title_200 = "a".repeat(200);
let body = serde_json::json!({
"title": title_200,
"url": "https://boundary-title.example.com"
"url": "https://boundary-title.example.com",
"theme_id": theme_id
});
let (status, _) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -1347,7 +1440,8 @@ async fn create_source_with_boundary_values_succeeds() {
assert_eq!(url_1000.len(), 1000);
let body2 = serde_json::json!({
"title": "Boundary URL",
"url": url_1000
"url": url_1000,
"theme_id": theme_id
});
let (status2, _) = app
.post_with_session("/api/v1/sources", &body2, &session)
@ -1361,7 +1455,8 @@ async fn create_source_with_boundary_values_succeeds() {
// Minimal valid source
let body3 = serde_json::json!({
"title": "A",
"url": "http://x.co"
"url": "http://x.co",
"theme_id": theme_id
});
let (status3, _) = app
.post_with_session("/api/v1/sources", &body3, &session)
@ -1384,10 +1479,12 @@ async fn create_source_with_http_url_succeeds() {
let (_user_id, session) = app
.create_authenticated_user("sources-http@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
let body = serde_json::json!({
"title": "HTTP Source",
"url": "http://insecure.example.com"
"url": "http://insecure.example.com",
"theme_id": theme_id
});
let (status, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
@ -1412,13 +1509,15 @@ async fn bulk_import_all_duplicates_within_batch() {
let (_user_id, session) = app
.create_authenticated_user("bulk-inner-dup@example.com")
.await;
let theme_id = create_theme(&app, &session).await;
// Same URL appearing twice in one batch
let body = serde_json::json!({
"sources": [
{ "title": "First", "url": "https://same.example.com" },
{ "title": "Second", "url": "https://same.example.com" }
]
],
"theme_id": theme_id
});
let (status, resp) = app
@ -1431,7 +1530,9 @@ async fn bulk_import_all_duplicates_within_batch() {
assert_eq!(resp["skipped"], 1, "The duplicate should be counted as skipped");
// Verify only 1 source exists
let (_, list_body) = app.get_with_session("/api/v1/sources", &session).await;
let (_, list_body) = app
.get_with_session(&format!("/api/v1/sources?theme_id={}", theme_id), &session)
.await;
let sources = list_body.as_array().unwrap();
assert_eq!(sources.len(), 1);
}

Loading…
Cancel
Save