You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ai_synth/backend/tests/api_sources_preferred_test.rs

189 lines
5.6 KiB
Rust

//! Integration tests for the `PUT /api/v1/sources/preferred` endpoint.
//!
//! Tests:
//! - Setting preferred sources
//! - Clearing all preferred sources
//! - Authentication requirement
//!
//! Requires a running Postgres instance. Set `TEST_DATABASE_URL` to run.
mod common;
use axum::http::StatusCode;
fn require_test_db() -> bool {
std::env::var("TEST_DATABASE_URL").is_ok()
}
#[tokio::test]
async fn update_preferred_sets_sources() {
if !require_test_db() {
eprintln!("SKIPPED: TEST_DATABASE_URL not set");
return;
}
let app = common::TestApp::new().await;
let (_user_id, session) = app
.create_authenticated_user("pref-set@example.com")
.await;
// Create a theme first
let theme_body = serde_json::json!({
"name": "Pref Theme",
"theme": "Test",
"categories": ["Cat"]
});
let (theme_status, theme_resp) = app
.post_with_session("/api/v1/themes", &theme_body, &session)
.await;
assert_eq!(theme_status, StatusCode::CREATED);
let theme_id = theme_resp["id"].as_str().unwrap();
// Create 3 sources
let mut source_ids = Vec::new();
for i in 1..=3 {
let body = serde_json::json!({
"title": format!("Source {}", i),
"url": format!("https://source{}.example.com", i),
"theme_id": theme_id
});
let (status, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
.await;
assert_eq!(status, StatusCode::CREATED);
source_ids.push(resp["id"].as_str().unwrap().to_string());
}
// PUT /sources/preferred with [id1, id3]
let pref_body = serde_json::json!({
"source_ids": [source_ids[0], source_ids[2]],
"theme_id": theme_id
});
let (pref_status, _) = app
.put_with_session("/api/v1/sources/preferred", &pref_body, &session)
.await;
assert_eq!(pref_status, StatusCode::NO_CONTENT);
// GET /sources → verify preferred flags
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");
assert_eq!(sources.len(), 3);
for source in sources {
let id = source["id"].as_str().unwrap();
let is_preferred = source["is_preferred"].as_bool().unwrap();
if id == source_ids[0] || id == source_ids[2] {
assert!(is_preferred, "Source {} should be preferred", id);
} else {
assert!(!is_preferred, "Source {} should NOT be preferred", id);
}
}
}
#[tokio::test]
async fn update_preferred_clears_all_when_empty() {
if !require_test_db() {
eprintln!("SKIPPED: TEST_DATABASE_URL not set");
return;
}
let app = common::TestApp::new().await;
let (_user_id, session) = app
.create_authenticated_user("pref-clear@example.com")
.await;
// Create a theme
let theme_body = serde_json::json!({
"name": "Pref Clear Theme",
"theme": "Test",
"categories": ["Cat"]
});
let (_, theme_resp) = app
.post_with_session("/api/v1/themes", &theme_body, &session)
.await;
let theme_id = theme_resp["id"].as_str().unwrap();
// Create 2 sources
let mut source_ids = Vec::new();
for i in 1..=2 {
let body = serde_json::json!({
"title": format!("Source {}", i),
"url": format!("https://clear-source{}.example.com", i),
"theme_id": theme_id
});
let (_, resp) = app
.post_with_session("/api/v1/sources", &body, &session)
.await;
source_ids.push(resp["id"].as_str().unwrap().to_string());
}
// Set some as preferred
let pref_body = serde_json::json!({
"source_ids": [source_ids[0]],
"theme_id": theme_id
});
let (pref_status, _) = app
.put_with_session("/api/v1/sources/preferred", &pref_body, &session)
.await;
assert_eq!(pref_status, StatusCode::NO_CONTENT);
// Clear all preferred
let clear_body = serde_json::json!({
"source_ids": [],
"theme_id": theme_id
});
let (clear_status, _) = app
.put_with_session("/api/v1/sources/preferred", &clear_body, &session)
.await;
assert_eq!(clear_status, StatusCode::OK);
// GET /sources → all is_preferred=false
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");
for source in sources {
assert_eq!(
source["is_preferred"].as_bool().unwrap(),
false,
"All sources should be non-preferred after clearing"
);
}
}
#[tokio::test]
async fn update_preferred_without_auth_returns_401() {
if !require_test_db() {
eprintln!("SKIPPED: TEST_DATABASE_URL not set");
return;
}
let app = common::TestApp::new().await;
let body = serde_json::json!({
"source_ids": [],
"theme_id": "00000000-0000-0000-0000-000000000000"
});
let (status, resp) = app
.put_with_session("/api/v1/sources/preferred", &body, "invalid-session-token")
.await;
assert_eq!(
status,
StatusCode::UNAUTHORIZED,
"PUT /sources/preferred without auth should return 401"
);
assert_eq!(resp["error"], "unauthorized");
}