use super::super::router;
use super::test_state;
use axum::body::{to_bytes, Body};
use axum::http::{Request, StatusCode};
use serde_json::{json, Value};
use tower::util::ServiceExt;
#[tokio::test]
async fn delete_palace_force_removes_populated_palace() {
let state = test_state();
let app = router().with_state(state.clone());
let resp = app
.clone()
.oneshot(
Request::builder()
.method("POST")
.uri("/api/v1/palaces")
.header("content-type", "application/json")
.body(Body::from(json!({"name": "force-delete"}).to_string()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let resp = app
.clone()
.oneshot(
Request::builder()
.method("POST")
.uri("/api/v1/palaces/force-delete/drawers")
.header("content-type", "application/json")
.body(Body::from(
json!({"content": "Sacrificial drawer for the force-delete path.", "tags": []})
.to_string(),
))
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let resp = app
.clone()
.oneshot(
Request::builder()
.method("DELETE")
.uri("/api/v1/palaces/force-delete?force=true")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
let resp = app
.oneshot(
Request::builder()
.uri("/api/v1/palaces/force-delete")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn delete_palace_returns_not_found_for_missing_id() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.oneshot(
Request::builder()
.method("DELETE")
.uri("/api/v1/palaces/never-existed")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn update_palace_name_renames_palace() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.clone()
.oneshot(
Request::builder()
.method("POST")
.uri("/api/v1/palaces")
.header("content-type", "application/json")
.body(Body::from(json!({"name": "rename-me"}).to_string()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let resp = app
.clone()
.oneshot(
Request::builder()
.method("PATCH")
.uri("/api/v1/palaces/rename-me")
.header("content-type", "application/json")
.body(Body::from(json!({"name": "New Display Name"}).to_string()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 4096).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(v["id"].as_str(), Some("rename-me"));
assert_eq!(v["name"].as_str(), Some("New Display Name"));
let resp = app
.oneshot(
Request::builder()
.uri("/api/v1/palaces/rename-me")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 4096).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(v["id"].as_str(), Some("rename-me"));
assert_eq!(v["name"].as_str(), Some("New Display Name"));
}
#[tokio::test]
async fn update_palace_name_rejects_empty_name() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.clone()
.oneshot(
Request::builder()
.method("POST")
.uri("/api/v1/palaces")
.header("content-type", "application/json")
.body(Body::from(json!({"name": "keep-name"}).to_string()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let resp = app
.oneshot(
Request::builder()
.method("PATCH")
.uri("/api/v1/palaces/keep-name")
.header("content-type", "application/json")
.body(Body::from(json!({"name": " "}).to_string()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
#[tokio::test]
async fn update_palace_name_returns_not_found_for_missing_id() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.oneshot(
Request::builder()
.method("PATCH")
.uri("/api/v1/palaces/no-such-palace")
.header("content-type", "application/json")
.body(Body::from(json!({"name": "irrelevant"}).to_string()))
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn palace_list_includes_graph_counts() {
let state = test_state();
let app = router().with_state(state.clone());
let body = json!({"name": "graph-counts", "description": null}).to_string();
let resp = app
.clone()
.oneshot(
Request::builder()
.method("POST")
.uri("/api/v1/palaces")
.header("content-type", "application/json")
.body(Body::from(body))
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let resp = app
.oneshot(
Request::builder()
.uri("/api/v1/palaces")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 4096).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
let arr = v.as_array().expect("array");
let row = arr
.iter()
.find(|p| p["id"] == "graph-counts")
.expect("created palace must appear in list");
assert_eq!(row["node_count"].as_u64(), Some(0));
assert_eq!(row["edge_count"].as_u64(), Some(0));
assert_eq!(row["community_count"].as_u64(), Some(0));
assert_eq!(row["is_compacting"].as_bool(), Some(false));
}
#[tokio::test]
async fn status_includes_total_counters() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.oneshot(
Request::builder()
.uri("/api/v1/status")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
let bytes = to_bytes(resp.into_body(), 4096).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(v["total_drawers"], 0);
assert_eq!(v["total_vectors"], 0);
assert_eq!(v["total_kg_triples"], 0);
}
#[tokio::test]
async fn dream_status_empty_returns_nulls() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.oneshot(
Request::builder()
.uri("/api/v1/dream/status")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 4096).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
assert!(v["last_run_at"].is_null());
assert_eq!(v["merged"], 0);
assert_eq!(v["pruned"], 0);
}