Break monolith source files into focused modules: - stemedb-core/types.rs → types/ directory (assertion, source, gold_standard, etc.) - stemedb-storage: audit_store, quota_store, trust_rank_store, vector_index, vote_store → module directories - stemedb-ingest/worker.rs → worker/ with separate test modules - stemedb-query: engine, materializer, query → module directories - stemedb-lens: epoch_aware, skeptic → module directories - stemedb-sim/lib.rs → agent, arenas/, helpers, runner, strategy, types - stemedb-api/tests: integration_tests → http_basic, http_validation, http_epoch, http_pipeline - stemedb-api/tests: e2e_flow_test → e2e_full_pipeline, e2e_lens_resolution - stemedb-query/tests: e2e_pipeline → e2e_pipeline + e2e_decay Also adds new features: gold standard verification, escalation handlers, admin endpoints, concept hierarchy spec, arena roadmap, and Go SDK. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
212 lines
7.2 KiB
Rust
212 lines
7.2 KiB
Rust
//! HTTP integration tests for query and filtering operations.
|
|
//!
|
|
//! Coverage:
|
|
//! - GET /v1/query - Subject/predicate filtering
|
|
//! - Lens parameter (Recency, Consensus, Confidence, Authority, VoteAwareConsensus)
|
|
//! - Lifecycle filtering
|
|
//! - Pagination (limit)
|
|
//! - Response structure validation
|
|
|
|
#![allow(clippy::expect_used)]
|
|
|
|
mod common;
|
|
|
|
use axum::{
|
|
body::Body,
|
|
http::{Request, StatusCode},
|
|
};
|
|
use tower::ServiceExt;
|
|
|
|
use stemedb_api::create_router;
|
|
|
|
// ============================================================================
|
|
// GET /v1/query - Query Assertions Tests
|
|
// ============================================================================
|
|
|
|
#[tokio::test]
|
|
async fn test_query_basic_with_subject_predicate() {
|
|
let env = common::create_test_env().await;
|
|
let app = create_router(env.state);
|
|
|
|
let request = Request::builder()
|
|
.uri("/v1/query?subject=Test_Entity&predicate=test_property")
|
|
.method("GET")
|
|
.body(Body::empty())
|
|
.expect("Request");
|
|
|
|
let response = app.oneshot(request).await.expect("Request");
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
|
|
let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
|
|
|
|
// Verify response structure (empty since DB is empty)
|
|
assert!(json.get("assertions").is_some());
|
|
assert!(json.get("total_count").is_some());
|
|
assert!(json.get("has_more").is_some());
|
|
assert_eq!(json["assertions"], serde_json::json!([]));
|
|
assert_eq!(json["total_count"], 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_query_with_lens_recency() {
|
|
let env = common::create_test_env().await;
|
|
let app = create_router(env.state);
|
|
|
|
let request = Request::builder()
|
|
.uri("/v1/query?subject=Test&predicate=prop&lens=Recency")
|
|
.method("GET")
|
|
.body(Body::empty())
|
|
.expect("Request");
|
|
|
|
let response = app.oneshot(request).await.expect("Request");
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
|
|
let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
|
|
|
|
// When a lens is applied (even with empty results), conflict_score and
|
|
// resolution_confidence should be None (empty result set)
|
|
assert_eq!(json["assertions"], serde_json::json!([]));
|
|
assert_eq!(json["total_count"], 0);
|
|
assert!(json.get("conflict_score").is_none() || json["conflict_score"].is_null());
|
|
assert!(json.get("resolution_confidence").is_none() || json["resolution_confidence"].is_null());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_query_lifecycle_filtering() {
|
|
let env = common::create_test_env().await;
|
|
let app = create_router(env.state);
|
|
|
|
let request = Request::builder()
|
|
.uri("/v1/query?subject=Test&lifecycle=Approved")
|
|
.method("GET")
|
|
.body(Body::empty())
|
|
.expect("Request");
|
|
|
|
let response = app.oneshot(request).await.expect("Request");
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
|
|
let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
|
|
|
|
assert_eq!(json["assertions"], serde_json::json!([]));
|
|
assert_eq!(json["total_count"], 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_query_with_limit() {
|
|
let env = common::create_test_env().await;
|
|
let app = create_router(env.state);
|
|
|
|
let request = Request::builder()
|
|
.uri("/v1/query?subject=Test&limit=10")
|
|
.method("GET")
|
|
.body(Body::empty())
|
|
.expect("Request");
|
|
|
|
let response = app.oneshot(request).await.expect("Request");
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
|
|
let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
|
|
|
|
// Verify query executes successfully (limit parameter is accepted)
|
|
assert!(json.get("assertions").is_some());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_query_with_all_lenses() {
|
|
let env = common::create_test_env().await;
|
|
let app = create_router(env.state);
|
|
|
|
// Test that all lens values are accepted
|
|
let lenses = vec!["Recency", "Consensus", "Confidence", "Authority", "VoteAwareConsensus"];
|
|
|
|
for lens in lenses {
|
|
let request = Request::builder()
|
|
.uri(format!("/v1/query?subject=Test&predicate=prop&lens={}", lens))
|
|
.method("GET")
|
|
.body(Body::empty())
|
|
.expect("Request");
|
|
|
|
let response = app.clone().oneshot(request).await.expect("Request");
|
|
assert_eq!(response.status(), StatusCode::OK, "Lens {} should be accepted", lens);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_query_response_structure() {
|
|
let env = common::create_test_env().await;
|
|
let app = create_router(env.state);
|
|
|
|
let request = Request::builder()
|
|
.uri("/v1/query?subject=Test")
|
|
.method("GET")
|
|
.body(Body::empty())
|
|
.expect("Request");
|
|
|
|
let response = app.oneshot(request).await.expect("Request");
|
|
let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
|
|
let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
|
|
|
|
// Verify expected fields are present
|
|
assert!(json.get("assertions").is_some());
|
|
assert!(json.get("total_count").is_some());
|
|
assert!(json.get("has_more").is_some());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_query_subject_only() {
|
|
let env = common::create_test_env().await;
|
|
let app = create_router(env.state);
|
|
|
|
let request = Request::builder()
|
|
.uri("/v1/query?subject=Entity123")
|
|
.method("GET")
|
|
.body(Body::empty())
|
|
.expect("Request");
|
|
|
|
let response = app.oneshot(request).await.expect("Request");
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
|
|
let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
|
|
assert!(json.get("assertions").is_some());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_query_predicate_only() {
|
|
let env = common::create_test_env().await;
|
|
let app = create_router(env.state);
|
|
|
|
let request = Request::builder()
|
|
.uri("/v1/query?predicate=has_property")
|
|
.method("GET")
|
|
.body(Body::empty())
|
|
.expect("Request");
|
|
|
|
let response = app.oneshot(request).await.expect("Request");
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
|
|
let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
|
|
assert!(json.get("assertions").is_some());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_query_empty_params() {
|
|
let env = common::create_test_env().await;
|
|
let app = create_router(env.state);
|
|
|
|
let request =
|
|
Request::builder().uri("/v1/query").method("GET").body(Body::empty()).expect("Request");
|
|
|
|
let response = app.oneshot(request).await.expect("Request");
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
|
|
let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
|
|
assert!(json.get("assertions").is_some());
|
|
}
|