stemedb/crates/stemedb-api/tests/http_pipeline.rs
jordan 55349845d0 refactor: Split all files to enforce 500-line max
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>
2026-02-02 01:13:45 -07:00

149 lines
5.9 KiB
Rust

//! HTTP integration tests for full pipeline roundtrip.
//!
//! Coverage:
//! - Full assertion roundtrip: POST → Ingest → GET
//! - Integration with Ingestor for processing WAL entries
//! - Signature verification through the pipeline
//! - Query results after ingestion
//!
//! These tests validate the complete data flow through the system:
//! 1. Client POSTs assertion to /v1/assert (writes to WAL)
//! 2. Ingestor processes WAL entries (signature verification, storage)
//! 3. Client GETs via /v1/query (reads from indexed storage)
#![allow(clippy::expect_used)]
mod common;
use axum::{
body::Body,
http::{Request, StatusCode},
};
use tower::ServiceExt;
use stemedb_api::create_router;
// ============================================================================
// Full Pipeline Integration Tests (POST → Ingest → GET roundtrip)
// ============================================================================
/// Test full assertion roundtrip: POST → Ingest → GET
///
/// This test validates the complete pipeline:
/// 1. POST an assertion to /v1/assert (writes to WAL)
/// 2. Run the ingestor to process the WAL entry
/// 3. GET /v1/query to verify the assertion is queryable
#[tokio::test]
async fn test_assertion_roundtrip_with_ingestor() {
let env = common::create_test_env_with_ingestor().await;
let app = create_router(env.state.clone());
let subject = "RoundtripTest_Entity";
let predicate = "test_property";
let value = 42.0;
// 1. POST the assertion
let assertion = common::create_signed_assertion_json(subject, predicate, value);
let request = Request::builder()
.uri("/v1/assert")
.method("POST")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_vec(&assertion).expect("JSON")))
.expect("Request");
let response = app.clone().oneshot(request).await.expect("Request");
assert_eq!(response.status(), StatusCode::CREATED, "POST should succeed with 201");
let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
let post_result: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
assert_eq!(post_result["status"], "created");
let assertion_hash = post_result["hash"].as_str().expect("hash should be present");
assert_eq!(assertion_hash.len(), 64, "hash should be 64 hex chars");
// 2. Run the ingestor to process the pending record (synchronous for testing)
let bytes_processed = env.ingestor.process_pending().await.expect("ingestor should process");
assert!(bytes_processed > 0, "Should have processed WAL bytes");
// 3. GET the assertion via query
let request = Request::builder()
.uri(format!("/v1/query?subject={}&predicate={}", subject, predicate))
.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 query_result: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
// Verify the assertion is returned correctly
assert_eq!(query_result["total_count"], 1, "Should find exactly 1 assertion");
let assertions = query_result["assertions"].as_array().expect("assertions array");
assert_eq!(assertions.len(), 1);
let found = &assertions[0];
assert_eq!(found["subject"], subject);
assert_eq!(found["predicate"], predicate);
assert_eq!(found["object"]["type"], "Number");
assert!((found["object"]["value"].as_f64().expect("value") - value).abs() < 0.001);
assert_eq!(found["confidence"], 0.95);
}
/// Test multiple assertions through the pipeline
#[tokio::test]
async fn test_multiple_assertions_roundtrip() {
let env = common::create_test_env_with_ingestor().await;
let app = create_router(env.state.clone());
let subject = "MultiTest_Entity";
// POST multiple assertions for the same subject
for i in 1..=3 {
let predicate = format!("property_{}", i);
let value = i as f64 * 10.0;
let assertion = common::create_signed_assertion_json(subject, &predicate, value);
let request = Request::builder()
.uri("/v1/assert")
.method("POST")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_vec(&assertion).expect("JSON")))
.expect("Request");
let response = app.clone().oneshot(request).await.expect("Request");
assert_eq!(response.status(), StatusCode::CREATED, "POST {} should succeed", i);
}
// Process all pending WAL entries
let bytes_processed = env.ingestor.process_pending().await.expect("ingestor should process");
assert!(bytes_processed > 0, "Should have processed WAL bytes");
// Query by subject only - should get all 3 assertions
let request = Request::builder()
.uri(format!("/v1/query?subject={}", subject))
.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 query_result: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
assert_eq!(query_result["total_count"], 3, "Should find all 3 assertions");
let assertions = query_result["assertions"].as_array().expect("assertions array");
assert_eq!(assertions.len(), 3);
// Verify all predicates are present
let predicates: Vec<String> = assertions
.iter()
.map(|a| a["predicate"].as_str().expect("predicate").to_string())
.collect();
assert!(predicates.contains(&"property_1".to_string()));
assert!(predicates.contains(&"property_2".to_string()));
assert!(predicates.contains(&"property_3".to_string()));
}