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>
187 lines
4.5 KiB
Go
187 lines
4.5 KiB
Go
package adk
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/orchard9/stemedb-go/steme"
|
|
)
|
|
|
|
func TestConstraintCheckTool(t *testing.T) {
|
|
// Mock client that returns constraint assertions
|
|
client := &mockClient{
|
|
queryFunc: func(ctx context.Context, params steme.QueryParams) (*steme.QueryResult, error) {
|
|
lifecycle := steme.LifecycleApproved
|
|
return &steme.QueryResult{
|
|
Assertions: []steme.AssertionResponse{
|
|
{
|
|
Hash: "constraint1",
|
|
Subject: "auth_jwt",
|
|
Predicate: "must_use",
|
|
Object: steme.NewTextValue("Ed25519"),
|
|
Confidence: 1.0,
|
|
SourceHash: "source",
|
|
Lifecycle: lifecycle,
|
|
},
|
|
{
|
|
Hash: "constraint2",
|
|
Subject: "auth_jwt",
|
|
Predicate: "forbidden",
|
|
Object: steme.NewTextValue("MD5"),
|
|
Confidence: 1.0,
|
|
SourceHash: "source",
|
|
Lifecycle: lifecycle,
|
|
},
|
|
},
|
|
TotalCount: 2,
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
tool := NewConstraintCheckTool(client)
|
|
|
|
input := ConstraintCheckInput{
|
|
Context: "auth_jwt",
|
|
}
|
|
|
|
inputBytes, _ := json.Marshal(input)
|
|
outputBytes, err := tool.Execute(context.Background(), inputBytes)
|
|
if err != nil {
|
|
t.Fatalf("constraint check failed: %v", err)
|
|
}
|
|
|
|
var output ConstraintCheckOutput
|
|
if err := json.Unmarshal(outputBytes, &output); err != nil {
|
|
t.Fatalf("failed to unmarshal output: %v", err)
|
|
}
|
|
|
|
// Verify constraints were returned
|
|
if len(output.Constraints) != 2 {
|
|
t.Errorf("expected 2 constraints, got %d", len(output.Constraints))
|
|
}
|
|
|
|
// Check for must-use constraint
|
|
foundMustUse := false
|
|
for _, c := range output.Constraints {
|
|
if c.MustUse == "Ed25519" {
|
|
foundMustUse = true
|
|
}
|
|
}
|
|
if !foundMustUse {
|
|
t.Error("expected to find Ed25519 must-use constraint")
|
|
}
|
|
|
|
// Check for forbidden constraint
|
|
foundForbidden := false
|
|
for _, c := range output.Constraints {
|
|
if c.Forbidden == "MD5" {
|
|
foundForbidden = true
|
|
}
|
|
}
|
|
if !foundForbidden {
|
|
t.Error("expected to find MD5 forbidden constraint")
|
|
}
|
|
}
|
|
|
|
func TestTraceTool(t *testing.T) {
|
|
// Mock client that returns audit records
|
|
agentID := "deadbeef00000000000000000000000000000000000000000000000000000000"
|
|
subject := "Tesla_Inc"
|
|
predicate := "has_revenue"
|
|
resultHash := "result_hash_123"
|
|
lens := steme.LensConsensus
|
|
|
|
client := &mockClient{
|
|
traceFunc: func(ctx context.Context, params steme.TraceParams) (*steme.TraceResult, error) {
|
|
return &steme.TraceResult{
|
|
Audits: []steme.QueryAuditRecord{
|
|
{
|
|
QueryID: "query_123",
|
|
AgentID: &agentID,
|
|
Timestamp: 1704067200,
|
|
Params: steme.QueryParamsAudit{
|
|
Subject: &subject,
|
|
Predicate: &predicate,
|
|
Lens: &lens,
|
|
},
|
|
ResultHash: &resultHash,
|
|
ResultConfidence: 0.95,
|
|
ContributingAssertions: []steme.ContributingAssertion{
|
|
{
|
|
AssertionHash: "assertion_1",
|
|
Weight: 1.0,
|
|
SourceHash: "source_1",
|
|
Lifecycle: steme.LifecycleApproved,
|
|
},
|
|
{
|
|
AssertionHash: "assertion_2",
|
|
Weight: 0.5,
|
|
SourceHash: "source_2",
|
|
Lifecycle: steme.LifecycleProposed,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalCount: 1,
|
|
AgentID: agentID,
|
|
FromTimestamp: 1704067200,
|
|
ToTimestamp: 1704153600,
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
tool := NewTraceTool(client)
|
|
|
|
input := TraceInput{
|
|
AgentID: agentID,
|
|
From: "1704067200",
|
|
To: "1704153600",
|
|
Subject: "Tesla*",
|
|
}
|
|
|
|
inputBytes, _ := json.Marshal(input)
|
|
outputBytes, err := tool.Execute(context.Background(), inputBytes)
|
|
if err != nil {
|
|
t.Fatalf("trace failed: %v", err)
|
|
}
|
|
|
|
var output TraceOutput
|
|
if err := json.Unmarshal(outputBytes, &output); err != nil {
|
|
t.Fatalf("failed to unmarshal output: %v", err)
|
|
}
|
|
|
|
// Verify output
|
|
if len(output.Queries) != 1 {
|
|
t.Errorf("expected 1 query, got %d", len(output.Queries))
|
|
}
|
|
|
|
if len(output.Queries) > 0 {
|
|
query := output.Queries[0]
|
|
|
|
if query.QueryID != "query_123" {
|
|
t.Errorf("expected query_id query_123, got %s", query.QueryID)
|
|
}
|
|
|
|
if query.Subject != "Tesla_Inc" {
|
|
t.Errorf("expected subject Tesla_Inc, got %s", query.Subject)
|
|
}
|
|
|
|
if query.Predicate != "has_revenue" {
|
|
t.Errorf("expected predicate has_revenue, got %s", query.Predicate)
|
|
}
|
|
|
|
if query.Lens != "consensus" {
|
|
t.Errorf("expected lens consensus, got %s", query.Lens)
|
|
}
|
|
|
|
if query.Confidence != 0.95 {
|
|
t.Errorf("expected confidence 0.95, got %f", query.Confidence)
|
|
}
|
|
|
|
if len(query.Contributing) != 2 {
|
|
t.Errorf("expected 2 contributing assertions, got %d", len(query.Contributing))
|
|
}
|
|
}
|
|
}
|