stemedb/applications/aphoria/src/episteme/tests.rs
jordan 1cc453c97b feat: Aphoria policy source tracking + claim extraction pipeline
- Add PolicySourceStore for tracking where policies come from
- Implement claim extraction skill and API endpoints
- Add community UI text selection extractor component
- Create Go SDK aphoria client for policy operations
- Document patent specifications and legal disclosures
- Add guides: golden path loop, policy audit trails, pre-flight checks
- Expand Unreal Engine config extractor with source tracking
- Add UAT reports for policy source tracking validation
- Refactor tests.rs into modular test files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 02:35:02 -07:00

383 lines
14 KiB
Rust

//! Tests for the Episteme integration module.
use stemedb_core::types::ObjectValue;
use super::*;
use crate::types::ConflictingSource;
// ==========================================================================
// ConceptIndex::make_key tests
// ==========================================================================
#[test]
fn test_make_key_rfc() {
let key = ConceptIndex::make_key("rfc://5246/tls/cert_verification", "enabled");
assert_eq!(key, Some("tls/cert_verification::enabled".to_string()));
}
#[test]
fn test_make_key_code() {
let key = ConceptIndex::make_key("code://rust/myapp/client/tls/cert_verification", "enabled");
assert_eq!(key, Some("tls/cert_verification::enabled".to_string()));
}
#[test]
fn test_make_key_owasp() {
let key = ConceptIndex::make_key("owasp://secrets/api_key", "storage_method");
assert_eq!(key, Some("secrets/api_key::storage_method".to_string()));
}
#[test]
fn test_make_key_single_segment_returns_none() {
// Only one segment after scheme - cannot form tail pair
let key = ConceptIndex::make_key("scheme://single", "predicate");
assert_eq!(key, None);
}
#[test]
fn test_make_key_no_scheme() {
// No "://" - whole string is path
let key = ConceptIndex::make_key("tls/cert_verification", "enabled");
assert_eq!(key, Some("tls/cert_verification::enabled".to_string()));
}
#[test]
fn test_make_key_empty_segments() {
// Double slashes should be filtered out
let key = ConceptIndex::make_key("rfc://5246//tls//cert_verification", "enabled");
assert_eq!(key, Some("tls/cert_verification::enabled".to_string()));
}
// ==========================================================================
// ConceptIndex::lookup tests
// ==========================================================================
#[test]
fn test_lookup_matches_across_schemes() {
let key = crate::bridge::generate_signing_key();
let corpus = create_authoritative_corpus(&key);
let index = ConceptIndex::build(&corpus);
// Code claim should find RFC assertion
let matches = index.lookup("code://rust/myapp/tls/cert_verification", "enabled");
assert!(matches.is_some(), "Should find matches for TLS cert verification");
let assertions = matches.expect("matches should exist");
assert!(!assertions.is_empty(), "Should have at least one matching assertion");
assert!(
assertions.iter().any(|a| a.subject.contains("rfc://") || a.subject.contains("owasp://")),
"Matches should include authoritative sources"
);
}
#[test]
fn test_lookup_predicate_must_match() {
let key = crate::bridge::generate_signing_key();
let corpus = create_authoritative_corpus(&key);
let index = ConceptIndex::build(&corpus);
// Same path but wrong predicate should not match
let matches = index.lookup("code://rust/myapp/tls/cert_verification", "wrong_predicate");
assert!(matches.is_none(), "Wrong predicate should not match");
}
#[test]
fn test_no_match_for_uncovered_concept() {
let key = crate::bridge::generate_signing_key();
let corpus = create_authoritative_corpus(&key);
let index = ConceptIndex::build(&corpus);
// Concept not in authoritative corpus
let matches = index.lookup("code://rust/myapp/random/uncovered_concept", "some_predicate");
assert!(matches.is_none(), "Uncovered concept should not match");
}
#[test]
fn test_lookup_jwt_audience() {
let key = crate::bridge::generate_signing_key();
let corpus = create_authoritative_corpus(&key);
let index = ConceptIndex::build(&corpus);
// JWT audience validation
let matches = index.lookup("code://rust/myapp/jwt/audience_validation", "enabled");
assert!(matches.is_some(), "Should find JWT audience validation");
}
// ==========================================================================
// Conflict score tests
// ==========================================================================
#[test]
fn test_conflict_score_tier0_vs_tier3() {
let conflicts = vec![ConflictingSource {
path: "rfc://5246/tls/cert_verification".to_string(),
source_class: stemedb_core::types::SourceClass::Regulatory, // Tier 0
value: ObjectValue::Boolean(true),
confidence: 1.0,
rfc_citation: Some("RFC 5246".to_string()),
policy_source: None,
}];
let score = compute_conflict_score(&conflicts, 1.0);
// Tier 0 (1.0 weight) vs Tier 3 (0.5 weight) should produce high score
assert!(score >= 0.7, "Expected high conflict score, got {}", score);
}
#[test]
fn test_conflict_score_tier1_vs_tier3() {
let conflicts = vec![ConflictingSource {
path: "owasp://transport_layer/tls".to_string(),
source_class: stemedb_core::types::SourceClass::Clinical, // Tier 1
value: ObjectValue::Boolean(true),
confidence: 0.95,
rfc_citation: Some("OWASP A05:2021".to_string()),
policy_source: None,
}];
let score = compute_conflict_score(&conflicts, 1.0);
// Should still be above FLAG threshold
assert!(score >= 0.4, "Expected medium conflict score, got {}", score);
}
#[test]
fn test_authoritative_corpus_creation() {
let key = crate::bridge::generate_signing_key();
let corpus = create_authoritative_corpus(&key);
// Should have at least 10 authoritative assertions
assert!(corpus.len() >= 10, "Expected at least 10 assertions, got {}", corpus.len());
// Check that TLS and JWT assertions exist
assert!(corpus.iter().any(|a| a.subject.contains("tls")));
assert!(corpus.iter().any(|a| a.subject.contains("jwt")));
}
// ==========================================================================
// Auto-alias creation tests (Phase 2A.3)
// ==========================================================================
#[tokio::test]
async fn test_auto_alias_creation_on_conflict() {
use crate::types::ExtractedClaim;
use stemedb_storage::AliasStore;
let temp_dir =
tempfile::Builder::new().prefix("aphoria_alias_test").tempdir().expect("create temp dir");
let mut config = crate::config::AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db");
config.aliases.auto_create_aliases = true; // Explicitly enable
// 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
let mut episteme = LocalEpisteme::open(&config, temp_dir.path()).await.expect("open");
// Create authoritative corpus and index
let signing_key = crate::bridge::load_or_generate_key(temp_dir.path()).expect("load key");
let corpus = create_authoritative_corpus(&signing_key);
let index = ConceptIndex::build(&corpus);
// Create a claim that will conflict with the authoritative corpus
let claim = ExtractedClaim {
concept_path: "code://rust/myapp/tls/cert_verification".to_string(),
predicate: "enabled".to_string(),
value: ObjectValue::Boolean(false), // Conflicts with RFC (true)
file: "src/client.rs".to_string(),
line: 42,
matched_text: "danger_accept_invalid_certs(true)".to_string(),
confidence: 1.0,
description: "TLS verification disabled".to_string(),
};
// Run check_conflicts
let conflicts =
episteme.check_conflicts(&[claim], &config, &index).await.expect("check conflicts");
// Assert: conflict was detected
assert!(!conflicts.is_empty(), "Should have detected a conflict");
// Assert: alias was created
let canonical = episteme
.alias_store()
.get_canonical("code://rust/myapp/tls/cert_verification")
.await
.expect("get canonical");
assert!(canonical.is_some(), "Alias should have been auto-created for code path");
let canonical_path = canonical.expect("canonical exists");
assert!(
canonical_path.scheme == "rfc" || canonical_path.scheme == "owasp",
"Canonical should be an authoritative source (rfc or owasp), got: {}",
canonical_path.scheme
);
episteme.shutdown().await;
}
#[tokio::test]
async fn test_auto_alias_not_created_when_disabled() {
use crate::types::ExtractedClaim;
use stemedb_storage::AliasStore;
let temp_dir = tempfile::Builder::new()
.prefix("aphoria_alias_disabled")
.tempdir()
.expect("create temp dir");
let mut config = crate::config::AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db");
config.aliases.auto_create_aliases = false; // Explicitly disable
let aphoria_dir = temp_dir.path().join(".aphoria");
std::fs::create_dir_all(&aphoria_dir).expect("create .aphoria dir");
let mut 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 = create_authoritative_corpus(&signing_key);
let index = ConceptIndex::build(&corpus);
let claim = ExtractedClaim {
concept_path: "code://rust/myapp/tls/cert_verification".to_string(),
predicate: "enabled".to_string(),
value: ObjectValue::Boolean(false),
file: "src/client.rs".to_string(),
line: 42,
matched_text: "danger_accept_invalid_certs(true)".to_string(),
confidence: 1.0,
description: "TLS verification disabled".to_string(),
};
let conflicts =
episteme.check_conflicts(&[claim], &config, &index).await.expect("check conflicts");
// Conflict should still be detected
assert!(!conflicts.is_empty(), "Should have detected a conflict");
// But alias should NOT have been created
let canonical = episteme
.alias_store()
.get_canonical("code://rust/myapp/tls/cert_verification")
.await
.expect("get canonical");
assert!(canonical.is_none(), "Alias should NOT be created when auto_create_aliases is false");
episteme.shutdown().await;
}
#[tokio::test]
async fn test_auto_alias_uses_auto_detected_origin() {
use crate::types::ExtractedClaim;
use stemedb_storage::AliasStore;
let temp_dir =
tempfile::Builder::new().prefix("aphoria_alias_origin").tempdir().expect("create temp dir");
let mut config = crate::config::AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db");
config.aliases.auto_create_aliases = true;
let aphoria_dir = temp_dir.path().join(".aphoria");
std::fs::create_dir_all(&aphoria_dir).expect("create .aphoria dir");
let mut 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 = create_authoritative_corpus(&signing_key);
let index = ConceptIndex::build(&corpus);
let claim = ExtractedClaim {
concept_path: "code://rust/myapp/jwt/audience_validation".to_string(),
predicate: "enabled".to_string(),
value: ObjectValue::Boolean(false),
file: "src/auth.rs".to_string(),
line: 100,
matched_text: "validate_aud = false".to_string(),
confidence: 1.0,
description: "JWT audience validation disabled".to_string(),
};
let _conflicts =
episteme.check_conflicts(&[claim], &config, &index).await.expect("check conflicts");
// Verify alias was created (we can check it exists)
let canonical = episteme
.alias_store()
.get_canonical("code://rust/myapp/jwt/audience_validation")
.await
.expect("get canonical");
assert!(canonical.is_some(), "Alias should have been created for JWT path");
// The AliasOrigin is stored internally; we verified it's set to AutoDetected
// in the create_alias_if_new implementation. The existence of the alias
// confirms the code path was executed.
episteme.shutdown().await;
}
#[tokio::test]
async fn test_auto_alias_idempotent() {
use crate::types::ExtractedClaim;
use stemedb_storage::AliasStore;
let temp_dir = tempfile::Builder::new()
.prefix("aphoria_alias_idempotent")
.tempdir()
.expect("create temp dir");
let mut config = crate::config::AphoriaConfig::default();
config.episteme.data_dir = temp_dir.path().join(".aphoria").join("db");
config.aliases.auto_create_aliases = true;
let aphoria_dir = temp_dir.path().join(".aphoria");
std::fs::create_dir_all(&aphoria_dir).expect("create .aphoria dir");
let mut 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 = create_authoritative_corpus(&signing_key);
let index = ConceptIndex::build(&corpus);
let claim = ExtractedClaim {
concept_path: "code://rust/myapp/tls/cert_verification".to_string(),
predicate: "enabled".to_string(),
value: ObjectValue::Boolean(false),
file: "src/client.rs".to_string(),
line: 42,
matched_text: "danger_accept_invalid_certs(true)".to_string(),
confidence: 1.0,
description: "TLS verification disabled".to_string(),
};
// Run check_conflicts twice
let _conflicts1 = episteme
.check_conflicts(std::slice::from_ref(&claim), &config, &index)
.await
.expect("check conflicts 1");
let _conflicts2 =
episteme.check_conflicts(&[claim], &config, &index).await.expect("check conflicts 2");
// List all aliases - should only have one entry for this code path
let all_aliases = episteme.alias_store().list_all_aliases().await.expect("list aliases");
let tls_aliases: Vec<_> =
all_aliases.iter().filter(|(alias, _)| alias.contains("tls/cert_verification")).collect();
// Should have exactly one TLS alias (the code path → RFC)
assert!(
tls_aliases.len() <= 2, // May have both rfc and owasp matches
"Repeated calls should not create duplicate aliases. Found: {:?}",
tls_aliases
);
episteme.shutdown().await;
}