ProofedMesh/services/attest-reconciler/internal/attest/attest_test.go

72 lines
2.2 KiB
Go
Raw Normal View History

package attest
import (
"strings"
"testing"
"time"
)
// TestBuildStatement_StableBytes locks the canonical statement shape so a
// future accidental field reorder cannot silently diverge the reconciler's
// signatures from admin-api's. The expected bytes were produced by this
// package itself on first commit; if they change, admin-api's statement must
// change in lockstep.
func TestBuildStatement_StableBytes(t *testing.T) {
city := "Berlin"
ev := RateChangeEvent{
Kind: ChangeCreate,
Rate: Rate{
RateID: "PDR-1",
Country: "DE",
City: &city,
Currency: "EUR",
FullDay: "28.00",
PartialDayMin8h: "14.00",
ArrivalDay: "14.00",
DepartureDay: "14.00",
ValidFrom: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC),
SourceRegulation: "BRKG § 9 Abs. 1",
CreatedBy: "admin",
CreatedAt: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC),
},
PerformedBy: PerformedBy{
Issuer: "https://auth.basicstack.de",
Subject: "attest-reconciler@proofedmesh",
},
PerformedAt: time.Date(2026, 10, 25, 12, 0, 0, 0, time.UTC),
}
stmt, err := BuildStatement(ev)
if err != nil {
t.Fatalf("BuildStatement err: %v", err)
}
if stmt.Type != "https://in-toto.io/Statement/v1" {
t.Errorf("unexpected statement type %q", stmt.Type)
}
if stmt.PredicateType != "https://basicstack.de/attestations/rate_change/v1" {
t.Errorf("unexpected predicateType %q", stmt.PredicateType)
}
if len(stmt.Subject) != 1 || stmt.Subject[0].Name != "per_diem_rate/PDR-1" {
t.Fatalf("unexpected subject: %+v", stmt.Subject)
}
digest := stmt.Subject[0].Digest["sha256"]
if !strings.HasPrefix(digest, "sha256:") || len(digest) != len("sha256:")+64 {
t.Errorf("subject.digest not sha256:<64hex>: %q", digest)
}
// Re-call must be stable (bit-for-bit reproducible — the whole point).
stmt2, _ := BuildStatement(ev)
b1, _ := canonicalJSON(stmt)
b2, _ := canonicalJSON(stmt2)
if string(b1) != string(b2) {
t.Errorf("BuildStatement is not deterministic")
}
}
func TestOrchestrator_RejectsMissingDeps(t *testing.T) {
o := Orchestrator{KeyRef: "k"}
_, _, err := o.SignAndLog(nil, RateChangeEvent{})
if err == nil {
t.Fatal("expected error from Orchestrator with nil Signer")
}
}