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>
158 lines
4.3 KiB
Go
158 lines
4.3 KiB
Go
package steme
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// TestCanonicalMessage tests the canonical message generation for signing.
|
|
func TestCanonicalMessage(t *testing.T) {
|
|
assertion := NewAssertion("Tesla_Inc", "has_revenue").
|
|
WithNumber(96.7).
|
|
WithConfidence(0.95).
|
|
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
|
|
Build()
|
|
|
|
msg1, err := canonicalAssertionMessage(&assertion)
|
|
if err != nil {
|
|
t.Fatalf("canonicalAssertionMessage() failed: %v", err)
|
|
}
|
|
|
|
// Verify format matches server expectation: "{subject}:{predicate}"
|
|
expected := "Tesla_Inc:has_revenue"
|
|
if string(msg1) != expected {
|
|
t.Errorf("canonicalAssertionMessage() = %q, want %q", string(msg1), expected)
|
|
}
|
|
|
|
// Same assertion should produce same message
|
|
msg2, err := canonicalAssertionMessage(&assertion)
|
|
if err != nil {
|
|
t.Fatalf("canonicalAssertionMessage() failed: %v", err)
|
|
}
|
|
|
|
if string(msg1) != string(msg2) {
|
|
t.Errorf("Canonical message not deterministic")
|
|
}
|
|
|
|
// Different subject/predicate should produce different message
|
|
assertion2 := NewAssertion("Tesla_Inc", "has_employees"). // Different predicate
|
|
WithNumber(97.0).
|
|
WithConfidence(0.95).
|
|
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
|
|
Build()
|
|
|
|
msg3, err := canonicalAssertionMessage(&assertion2)
|
|
if err != nil {
|
|
t.Fatalf("canonicalAssertionMessage() failed: %v", err)
|
|
}
|
|
|
|
if string(msg1) == string(msg3) {
|
|
t.Errorf("Different assertions produced same canonical message")
|
|
}
|
|
}
|
|
|
|
// TestCanonicalMessageAllObjectTypes tests canonical message with all object types.
|
|
// The canonical message is just "{subject}:{predicate}" - object type doesn't affect it.
|
|
func TestCanonicalMessageAllObjectTypes(t *testing.T) {
|
|
types := []struct {
|
|
name string
|
|
obj ObjectValue
|
|
}{
|
|
{"text", NewTextValue("hello")},
|
|
{"number", NewNumberValue(42.5)},
|
|
{"boolean", NewBooleanValue(true)},
|
|
{"reference", NewReferenceValue("Entity_123")},
|
|
}
|
|
|
|
for _, tt := range types {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
a := NewAssertion("Subject", "Predicate").
|
|
WithConfidence(0.5).
|
|
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
|
|
Build()
|
|
a.Object = tt.obj
|
|
|
|
msg, err := canonicalAssertionMessage(&a)
|
|
if err != nil {
|
|
t.Errorf("canonicalAssertionMessage() failed for %s: %v", tt.name, err)
|
|
}
|
|
// Canonical message is "{subject}:{predicate}" regardless of object type
|
|
expected := "Subject:Predicate"
|
|
if string(msg) != expected {
|
|
t.Errorf("Expected %q, got %q", expected, string(msg))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestCanonicalMessageEdgeCases tests edge cases in canonical message generation.
|
|
func TestCanonicalMessageEdgeCases(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
build func() Assertion
|
|
wantErr bool
|
|
expected string
|
|
}{
|
|
{
|
|
name: "zero confidence",
|
|
build: func() Assertion {
|
|
return NewAssertion("S", "P").
|
|
WithText("v").
|
|
WithConfidence(0.0).
|
|
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
|
|
Build()
|
|
},
|
|
wantErr: false,
|
|
expected: "S:P",
|
|
},
|
|
{
|
|
name: "max confidence",
|
|
build: func() Assertion {
|
|
return NewAssertion("S", "P").
|
|
WithText("v").
|
|
WithConfidence(1.0).
|
|
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
|
|
Build()
|
|
},
|
|
wantErr: false,
|
|
expected: "S:P",
|
|
},
|
|
{
|
|
name: "empty strings",
|
|
build: func() Assertion {
|
|
return NewAssertion("", "").
|
|
WithText("v").
|
|
WithConfidence(0.5).
|
|
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
|
|
Build()
|
|
},
|
|
wantErr: false,
|
|
expected: ":",
|
|
},
|
|
{
|
|
name: "special characters",
|
|
build: func() Assertion {
|
|
return NewAssertion("Subject:With:Colons", "Predicate").
|
|
WithText("v").
|
|
WithConfidence(0.5).
|
|
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
|
|
Build()
|
|
},
|
|
wantErr: false,
|
|
expected: "Subject:With:Colons:Predicate",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
a := tt.build()
|
|
msg, err := canonicalAssertionMessage(&a)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
if !tt.wantErr && string(msg) != tt.expected {
|
|
t.Errorf("got %q, want %q", string(msg), tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|