Files
notarius/internal/framework/semanticreconcile/identity_test.go

94 lines
4.0 KiB
Go

package semanticreconcile
import (
"testing"
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
)
func TestEngineMetadataAndFingerprintsCoverCoreIdentity(t *testing.T) {
base := newTestEngine(t, &recordingReconciliationClient{}, DefaultLimits())
metadata := base.ManifestMetadata()
for _, key := range []string{
"prompt_id", "prompt_version", "prompt_sha256",
"response_schema_key", "response_schema_id", "response_schema_name", "response_schema_version", "response_schema_sha256",
"semantic_reconciliation_policy", "semantic_reconciliation_limits",
} {
if metadata[key] == nil || metadata[key] == "" {
t.Fatalf("metadata[%q] = %#v, want populated core identity", key, metadata[key])
}
}
limits, ok := metadata["semantic_reconciliation_limits"].(map[string]any)
if !ok || len(limits) != 3 || limits["context_radius"] == nil || limits["maximum_candidates"] == nil || limits["maximum_material_bytes"] == nil {
t.Fatalf("limit metadata = %#v, want complete limits", metadata["semantic_reconciliation_limits"])
}
fingerprints := base.CheckpointFingerprints()
wantNames := []string{"prompt", "response_schema", "semantic_reconciliation_policy", "semantic_reconciliation_limits"}
if len(fingerprints) != len(wantNames) {
t.Fatalf("fingerprints = %#v, want required categories", fingerprints)
}
for index, want := range wantNames {
if fingerprints[index].Name != want || fingerprints[index].Value == "" {
t.Fatalf("fingerprint %d = %#v, want %q with value", index, fingerprints[index], want)
}
}
metadata["prompt_id"] = "changed"
limits["context_radius"] = -1
fingerprints[0].Name = "changed"
if got := base.ManifestMetadata(); got["prompt_id"] == "changed" || got["semantic_reconciliation_limits"].(map[string]any)["context_radius"] == -1 {
t.Fatalf("ManifestMetadata() exposed retained state: %#v", got)
}
if got := base.CheckpointFingerprints(); got[0].Name == "changed" {
t.Fatalf("CheckpointFingerprints() exposed retained state: %#v", got)
}
}
func TestCoreFingerprintsChangeWithBehavioralIdentity(t *testing.T) {
base := newTestEngine(t, &recordingReconciliationClient{}, DefaultLimits())
baseFingerprints := base.CheckpointFingerprints()
promptChanged, err := NewEngine(&recordingReconciliationClient{}, testPromptSpec("b"), DefaultLimits())
if err != nil {
t.Fatal(err)
}
assertOnlyFingerprintChanged(t, baseFingerprints, promptChanged.CheckpointFingerprints(), "prompt")
schema, err := LoadResponseSchema()
if err != nil {
t.Fatal(err)
}
schema.SHA256 = testDigest("b")
schemaChanged := newEngine(&recordingReconciliationClient{}, base.prompt, schema, DefaultLimits())
assertOnlyFingerprintChanged(t, baseFingerprints, schemaChanged.CheckpointFingerprints(), "response_schema")
limits := DefaultLimits()
limits.ContextRadius++
limitsChanged, err := NewEngine(&recordingReconciliationClient{}, base.prompt, limits)
if err != nil {
t.Fatal(err)
}
assertOnlyFingerprintChanged(t, baseFingerprints, limitsChanged.CheckpointFingerprints(), "semantic_reconciliation_limits")
}
func TestInvalidEngineIdentityHasNoMetadataOrFingerprints(t *testing.T) {
invalid := newEngine(&recordingReconciliationClient{}, testPromptSpec("a"), llm.ResponseSchema{}, DefaultLimits())
if invalid.ManifestMetadata() != nil || invalid.CheckpointFingerprints() != nil {
t.Fatalf("invalid engine exposed identity: metadata %#v fingerprints %#v", invalid.ManifestMetadata(), invalid.CheckpointFingerprints())
}
}
func assertOnlyFingerprintChanged(t *testing.T, before, after []pipeline.CheckpointFingerprint, changedName string) {
t.Helper()
if len(before) != len(after) {
t.Fatalf("fingerprint counts differ: %#v %#v", before, after)
}
for index := range before {
changed := before[index] != after[index]
if changed != (before[index].Name == changedName) {
t.Fatalf("fingerprint %q change = %t, want only %q changed\nbefore: %#v\nafter: %#v", before[index].Name, changed, changedName, before, after)
}
}
}