stemedb/applications/aphoria/src/tests.rs
jordan a734be3a0d feat: Phase 7 Content Defense + code structure refactoring
Content Defense (Phase 7):
- Add SimilarityIndex with MinHash/LSH for near-duplicate detection
- Add QuarantineStore for flagged assertions awaiting admin review
- Add CircuitBreakerStore for per-agent circuit breaker state
- Add ContentDefenseLayer for ingestion pipeline integration
- Add API endpoints for quarantine and circuit breaker management
- Add research module with gap detection and documentation fetching

Code Structure Improvements:
- Extract research CLI commands to research_commands.rs
- Extract API routers to routers.rs module
- Extract key_codec extraction functions to separate module
- Extract test modules to separate files across multiple crates
- All files now under 500 line limit per pre-commit hook

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 12:44:05 -07:00

306 lines
9.9 KiB
Rust

//! Integration tests for Aphoria scan functionality.
use super::*;
#[tokio::test]
async fn test_scan_returns_result() {
let temp_dir = tempfile::tempdir().expect("create temp dir");
// Create a test file with a TLS issue
let src_dir = temp_dir.path().join("src");
std::fs::create_dir_all(&src_dir).expect("create src dir");
std::fs::write(
src_dir.join("client.rs"),
r#"
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()?;
"#,
)
.expect("write file");
// Create Cargo.toml so it's detected as a Rust project
std::fs::write(
temp_dir.path().join("Cargo.toml"),
r#"
[package]
name = "testproject"
version = "0.1.0"
"#,
)
.expect("write cargo.toml");
let args = ScanArgs {
path: temp_dir.path().to_path_buf(),
format: "table".to_string(),
exit_code_enabled: false,
};
let mut config = AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db");
let result = run_scan(args, &config).await.expect("scan should succeed");
assert!(result.files_scanned > 0);
assert!(result.claims_extracted > 0);
}
#[tokio::test]
async fn test_initialize_creates_corpus() {
// Use a unique temp dir to avoid conflicts with parallel tests
let temp_dir =
tempfile::Builder::new().prefix("aphoria_test_init").tempdir().expect("create temp dir");
let mut config = AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db");
// Create .aphoria directory for the agent key
let aphoria_dir = temp_dir.path().join(".aphoria");
std::fs::create_dir_all(&aphoria_dir).expect("create .aphoria dir");
// Open LocalEpisteme directly instead of using initialize()
// which relies on current_dir()
let mut episteme =
crate::episteme::LocalEpisteme::open(&config, temp_dir.path()).await.expect("open");
let signing_key = crate::bridge::load_or_generate_key(temp_dir.path()).expect("load key");
let corpus = crate::episteme::create_authoritative_corpus(&signing_key);
let ingested = episteme.ingest_authoritative(&corpus).await.expect("ingest");
episteme.shutdown().await;
assert!(ingested > 0);
assert!(config.episteme.data_dir.exists());
assert!(temp_dir.path().join(".aphoria").join("agent.key").exists());
}
#[tokio::test]
async fn test_acknowledge_succeeds() {
let temp_dir =
tempfile::Builder::new().prefix("aphoria_test_ack").tempdir().expect("create temp dir");
let mut config = AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db");
// Create .aphoria directory for the agent key
let aphoria_dir = temp_dir.path().join(".aphoria");
std::fs::create_dir_all(&aphoria_dir).expect("create .aphoria dir");
// Open LocalEpisteme and ingest an acknowledgement claim
let mut episteme =
crate::episteme::LocalEpisteme::open(&config, temp_dir.path()).await.expect("open");
let claim = ExtractedClaim {
concept_path: "code://rust/test/jwt/audience_validation".to_string(),
predicate: "acknowledged".to_string(),
value: stemedb_core::types::ObjectValue::Text("Internal service".to_string()),
file: "aphoria_ack".to_string(),
line: 0,
matched_text: "Acknowledged: Internal service".to_string(),
confidence: 1.0,
description: "Conflict acknowledged: Internal service".to_string(),
};
let result = episteme.ingest_claims(&[claim]).await;
episteme.shutdown().await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_status_before_init() {
let temp_dir =
tempfile::Builder::new().prefix("aphoria_test_status").tempdir().expect("create temp dir");
let mut config = AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join("nonexistent");
// Manually check status logic without relying on current_dir()
let data_dir = &config.episteme.data_dir;
let status = if !data_dir.exists() { "Not initialized" } else { "Initialized" };
assert!(status.contains("Not initialized"));
}
// ==========================================================================
// Integration tests for conflict detection (Phase 2A)
// ==========================================================================
#[tokio::test]
async fn test_conflict_detection_tls_disabled() {
// Create temp project with danger_accept_invalid_certs(true)
let temp_dir =
tempfile::Builder::new().prefix("aphoria_tls_conflict").tempdir().expect("create temp dir");
let src_dir = temp_dir.path().join("src");
std::fs::create_dir_all(&src_dir).expect("create src dir");
// Write a Rust file with TLS verification disabled
std::fs::write(
src_dir.join("client.rs"),
r#"
fn create_client() -> Result<Client, Error> {
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()?;
Ok(client)
}
"#,
)
.expect("write file");
// Create Cargo.toml so it's detected as a Rust project
std::fs::write(
temp_dir.path().join("Cargo.toml"),
r#"
[package]
name = "testproject"
version = "0.1.0"
"#,
)
.expect("write cargo.toml");
let args = ScanArgs {
path: temp_dir.path().to_path_buf(),
format: "table".to_string(),
exit_code_enabled: true,
};
let mut config = AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db");
let result = run_scan(args, &config).await.expect("scan should succeed");
// Assert: conflicts not empty, has_blocks() == true
assert!(
!result.conflicts.is_empty(),
"Should detect conflicts for TLS verification disabled. \
Claims extracted: {}, Files scanned: {}",
result.claims_extracted,
result.files_scanned
);
assert!(
result.has_blocks(),
"TLS verification disabled should be a BLOCK verdict. \
Conflicts: {:?}",
result.conflicts.iter().map(|c| (&c.claim.concept_path, &c.verdict)).collect::<Vec<_>>()
);
}
#[tokio::test]
async fn test_conflict_detection_jwt_audience_disabled() {
// Create temp project with JWT audience validation disabled
let temp_dir =
tempfile::Builder::new().prefix("aphoria_jwt_conflict").tempdir().expect("create temp dir");
let src_dir = temp_dir.path().join("src");
std::fs::create_dir_all(&src_dir).expect("create src dir");
// Write a Rust file with JWT audience validation disabled
std::fs::write(
src_dir.join("auth.rs"),
r#"
fn validate_token(token: &str) -> Result<Claims, Error> {
let mut validation = Validation::default();
validation.validate_aud = false; // Disabled!
let token_data = decode::<Claims>(token, &key, &validation)?;
Ok(token_data.claims)
}
"#,
)
.expect("write file");
// Create Cargo.toml
std::fs::write(
temp_dir.path().join("Cargo.toml"),
r#"
[package]
name = "testproject"
version = "0.1.0"
"#,
)
.expect("write cargo.toml");
let args = ScanArgs {
path: temp_dir.path().to_path_buf(),
format: "table".to_string(),
exit_code_enabled: true,
};
let mut config = AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db");
let result = run_scan(args, &config).await.expect("scan should succeed");
// Assert: conflicts not empty for JWT audience validation
assert!(
!result.conflicts.is_empty(),
"Should detect conflicts for JWT audience validation disabled. \
Claims extracted: {}, Files scanned: {}",
result.claims_extracted,
result.files_scanned
);
// Check that at least one conflict is about JWT audience
let has_jwt_conflict = result
.conflicts
.iter()
.any(|c| c.claim.concept_path.contains("jwt") && c.claim.concept_path.contains("audience"));
assert!(
has_jwt_conflict,
"Should have a conflict about JWT audience validation. \
Conflicts: {:?}",
result.conflicts.iter().map(|c| &c.claim.concept_path).collect::<Vec<_>>()
);
}
#[tokio::test]
async fn test_no_conflicts_when_compliant() {
// Create temp project with compliant code (no dangerous patterns)
let temp_dir =
tempfile::Builder::new().prefix("aphoria_compliant").tempdir().expect("create temp dir");
let src_dir = temp_dir.path().join("src");
std::fs::create_dir_all(&src_dir).expect("create src dir");
// Write a Rust file with compliant code
std::fs::write(
src_dir.join("main.rs"),
r#"
fn main() {
println!("Hello, world!");
}
"#,
)
.expect("write file");
// Create Cargo.toml
std::fs::write(
temp_dir.path().join("Cargo.toml"),
r#"
[package]
name = "testproject"
version = "0.1.0"
"#,
)
.expect("write cargo.toml");
let args = ScanArgs {
path: temp_dir.path().to_path_buf(),
format: "table".to_string(),
exit_code_enabled: true,
};
let mut config = AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db");
let result = run_scan(args, &config).await.expect("scan should succeed");
// No dangerous patterns = no claims = no conflicts
assert!(
result.conflicts.is_empty(),
"Compliant code should have no conflicts. Found: {:?}",
result.conflicts.iter().map(|c| &c.claim.concept_path).collect::<Vec<_>>()
);
}