stemedb/sdk/go/steme/integration_trace_test.go
jordan b3e8a9a058 feat: Multi-application expansion with chaos testing and community UI
Major additions:
- Community Next.js app (port 18187) for browsing claims with API docs
- stemedb-chaos crate: Fault injection, chaos testing, CRDT properties
- Latent ingestion system: Reddit/FDA ingesters with ADK-Go agents
- Disputed claims handling: Manual review workflows and validation
- Aphoria security scanner: New extractors (SQL injection, command
  injection, weak crypto, TLS version), policy-based ignores, UAT reports
- Docker infrastructure: Dockerfile, docker-compose.yml for full stack
- VulnBank demo: Intentionally vulnerable multi-language test corpus

SDK & API enhancements:
- Source registry handlers for tracking data provenance
- Metrics endpoint
- Skeptic filtering improvements

Code quality:
- Split 14 large files (>500 lines) into focused modules
- All files now under 500-line limit per project guidelines

Documentation:
- Chaos testing guide, circuit breakers, observability docs
- Phase 7 UAT documentation updates
- Martin Kleppmann technical writer agent

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

204 lines
5.4 KiB
Go

//go:build integration
package steme
import (
"context"
"fmt"
"testing"
"time"
)
// Integration tests for Trace/audit operations.
// TestIntegration_Trace tests the trace endpoint for audit trail querying.
func TestIntegration_Trace(t *testing.T) {
client := getTestClient(t)
ctx := context.Background()
// Create a unique subject for this test
subject := uniqueSubject("IntegrationTest_Trace")
predicate := "has_trace_value"
// Create an assertion to generate query activity
assertion := NewAssertion(subject, predicate).
WithText("test_trace_value").
WithConfidence(0.95).
WithLifecycle(LifecycleApproved).
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
Build()
hash, err := client.Assert(ctx, assertion)
if err != nil {
t.Fatalf("Assert() failed: %v", err)
}
t.Logf("Created assertion with hash: %s", hash)
// Small delay for indexing
time.Sleep(100 * time.Millisecond)
// Perform a query to create audit trail
params := NewQuery().
WithSubject(subject).
WithPredicate(predicate).
Build()
queryResult, err := client.Query(ctx, params)
if err != nil {
t.Fatalf("Query() failed: %v", err)
}
if queryResult.TotalCount == 0 {
t.Fatal("Query() returned no results")
}
t.Logf("Query returned %d results", queryResult.TotalCount)
// Small delay for audit to be written
time.Sleep(100 * time.Millisecond)
// Get the agent ID from the client's signer
agentID := client.signer.PublicKey()
// Calculate time range (last 1 hour to now)
now := time.Now().Unix()
from := now - 3600
// Trace the agent's queries
traceParams := TraceParams{
AgentID: agentID,
From: fmt.Sprintf("%d", from),
To: fmt.Sprintf("%d", now),
Subject: subject, // Filter to our test subject
Limit: 100,
}
traceResult, err := client.Trace(ctx, traceParams)
if err != nil {
t.Fatalf("Trace() failed: %v", err)
}
t.Logf("Trace returned %d audit records", traceResult.TotalCount)
// Verify we got at least one audit record
if traceResult.TotalCount == 0 {
t.Error("Trace() returned no audit records")
}
// Verify the trace result has the correct agent ID
if traceResult.AgentID != agentID {
t.Errorf("TraceResult.AgentID = %s, want %s", traceResult.AgentID, agentID)
}
// Verify the trace result has the correct time range
if traceResult.FromTimestamp != uint64(from) {
t.Errorf("TraceResult.FromTimestamp = %d, want %d", traceResult.FromTimestamp, from)
}
// Verify audit records contain our query
foundOurQuery := false
for _, audit := range traceResult.Audits {
t.Logf("Audit record: query_id=%s, timestamp=%d, subject=%v, predicate=%v",
audit.QueryID, audit.Timestamp, audit.Params.Subject, audit.Params.Predicate)
// Check if this audit is for our query
if audit.Params.Subject != nil && *audit.Params.Subject == subject &&
audit.Params.Predicate != nil && *audit.Params.Predicate == predicate {
foundOurQuery = true
// Verify agent ID
if audit.AgentID == nil || *audit.AgentID != agentID {
t.Errorf("Audit.AgentID = %v, want %s", audit.AgentID, agentID)
}
// Verify result hash is present
if audit.ResultHash == nil {
t.Error("Audit.ResultHash is nil, expected a result")
}
// Verify contributing assertions
if len(audit.ContributingAssertions) == 0 {
t.Error("Audit.ContributingAssertions is empty, expected at least one")
}
}
}
if !foundOurQuery {
t.Errorf("Did not find audit record for our query (subject=%s, predicate=%s)", subject, predicate)
}
}
// TestIntegration_Trace_WithPattern tests trace with subject pattern filtering.
func TestIntegration_Trace_WithPattern(t *testing.T) {
client := getTestClient(t)
ctx := context.Background()
// Create unique subjects with a common prefix
prefix := uniqueSubject("IntegrationTest_TracePattern")
subject1 := prefix + "_A"
subject2 := prefix + "_B"
predicate := "has_value"
// Create assertions and query them
for _, subj := range []string{subject1, subject2} {
assertion := NewAssertion(subj, predicate).
WithText("test_value").
WithConfidence(0.9).
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
Build()
_, err := client.Assert(ctx, assertion)
if err != nil {
t.Fatalf("Assert() for %s failed: %v", subj, err)
}
time.Sleep(50 * time.Millisecond)
// Query to create audit trail
params := NewQuery().WithSubject(subj).WithPredicate(predicate).Build()
_, err = client.Query(ctx, params)
if err != nil {
t.Fatalf("Query() for %s failed: %v", subj, err)
}
}
time.Sleep(100 * time.Millisecond)
// Trace with wildcard pattern
agentID := client.signer.PublicKey()
now := time.Now().Unix()
from := now - 3600
traceParams := TraceParams{
AgentID: agentID,
From: fmt.Sprintf("%d", from),
To: fmt.Sprintf("%d", now),
Subject: prefix + "*", // Match both subjects
Limit: 100,
}
traceResult, err := client.Trace(ctx, traceParams)
if err != nil {
t.Fatalf("Trace() failed: %v", err)
}
t.Logf("Trace with pattern returned %d audit records", traceResult.TotalCount)
// Should find both queries
foundSubjects := make(map[string]bool)
for _, audit := range traceResult.Audits {
if audit.Params.Subject != nil {
foundSubjects[*audit.Params.Subject] = true
}
}
if !foundSubjects[subject1] {
t.Errorf("Did not find audit for subject %s", subject1)
}
if !foundSubjects[subject2] {
t.Errorf("Did not find audit for subject %s", subject2)
}
}