131 lines
3.8 KiB
Go
131 lines
3.8 KiB
Go
package attest
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"basicstack.de/proofedmesh/services/admin-api/internal/model"
|
|
)
|
|
|
|
type fakeSigner struct {
|
|
err error
|
|
wantKeyRef string
|
|
tookStmt Statement
|
|
returnEnv Envelope
|
|
returnHash string
|
|
}
|
|
|
|
func (f *fakeSigner) SignDSSE(_ context.Context, keyRef string, stmt Statement) (Envelope, string, error) {
|
|
if f.err != nil {
|
|
return Envelope{}, "", f.err
|
|
}
|
|
f.tookStmt = stmt
|
|
if f.wantKeyRef != "" && keyRef != f.wantKeyRef {
|
|
return Envelope{}, "", errors.New("unexpected keyRef")
|
|
}
|
|
return f.returnEnv, f.returnHash, nil
|
|
}
|
|
|
|
type fakeSubmitter struct {
|
|
err error
|
|
gotEnv Envelope
|
|
gotHash string
|
|
returnRct LogReceipt
|
|
}
|
|
|
|
func (f *fakeSubmitter) Submit(_ context.Context, env Envelope, ch string) (LogReceipt, error) {
|
|
if f.err != nil {
|
|
return LogReceipt{}, f.err
|
|
}
|
|
f.gotEnv = env
|
|
f.gotHash = ch
|
|
if f.returnRct.ContentHash == "" {
|
|
f.returnRct.ContentHash = ch
|
|
}
|
|
return f.returnRct, nil
|
|
}
|
|
|
|
func TestOrchestrator_HappyPath(t *testing.T) {
|
|
env := Envelope{PayloadType: "application/vnd.in-toto+json", Payload: "aGVsbG8"}
|
|
ch, _ := EnvelopeContentHash(env)
|
|
s := &fakeSigner{wantKeyRef: "k", returnEnv: env, returnHash: ch}
|
|
sub := &fakeSubmitter{returnRct: LogReceipt{LogIndex: 7, ContentHash: ch, LoggedAt: time.Now()}}
|
|
orch := Orchestrator{Signer: s, Submitter: sub, KeyRef: "k"}
|
|
|
|
gotHash, receipt, err := orch.SignAndLog(context.Background(), RateChangeEvent{
|
|
Kind: ChangeCreate,
|
|
Rate: sampleRateMinimal(),
|
|
PerformedBy: PerformedBy{Issuer: "https://auth.basicstack.de", Subject: "u", SessionID: "s"},
|
|
PerformedAt: time.Now(),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if gotHash != ch {
|
|
t.Errorf("hash mismatch: %q vs %q", gotHash, ch)
|
|
}
|
|
if receipt.LogIndex != 7 {
|
|
t.Errorf("receipt log_index: %d", receipt.LogIndex)
|
|
}
|
|
if sub.gotHash != ch {
|
|
t.Errorf("submitter saw different hash: %q vs %q", sub.gotHash, ch)
|
|
}
|
|
if s.tookStmt.PredicateType != "https://basicstack.de/attestations/rate_change/v1" {
|
|
t.Errorf("predicateType in statement: %q", s.tookStmt.PredicateType)
|
|
}
|
|
}
|
|
|
|
func TestOrchestrator_SignerErrorStops(t *testing.T) {
|
|
orch := Orchestrator{
|
|
Signer: &fakeSigner{err: errors.New("bao down")},
|
|
Submitter: &fakeSubmitter{},
|
|
KeyRef: "k",
|
|
}
|
|
_, _, err := orch.SignAndLog(context.Background(), RateChangeEvent{Rate: sampleRateMinimal()})
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestOrchestrator_SubmitterErrorPropagates(t *testing.T) {
|
|
env := Envelope{PayloadType: "application/vnd.in-toto+json"}
|
|
ch, _ := EnvelopeContentHash(env)
|
|
orch := Orchestrator{
|
|
Signer: &fakeSigner{wantKeyRef: "k", returnEnv: env, returnHash: ch},
|
|
Submitter: &fakeSubmitter{err: errors.New("trillian down")},
|
|
KeyRef: "k",
|
|
}
|
|
gotHash, _, err := orch.SignAndLog(context.Background(), RateChangeEvent{Rate: sampleRateMinimal()})
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
// The hash from the signer is still returned so a reconciler can retry.
|
|
if gotHash != ch {
|
|
t.Errorf("expected signer hash to leak through on submitter error, got %q", gotHash)
|
|
}
|
|
}
|
|
|
|
func TestOrchestrator_RejectsMisconfiguration(t *testing.T) {
|
|
cases := []Orchestrator{
|
|
{Signer: nil, Submitter: &fakeSubmitter{}, KeyRef: "k"},
|
|
{Signer: &fakeSigner{}, Submitter: nil, KeyRef: "k"},
|
|
{Signer: &fakeSigner{}, Submitter: &fakeSubmitter{}, KeyRef: ""},
|
|
}
|
|
for i, o := range cases {
|
|
if _, _, err := o.SignAndLog(context.Background(), RateChangeEvent{}); err == nil {
|
|
t.Errorf("case %d: expected error for misconfigured orchestrator", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func sampleRateMinimal() model.Rate {
|
|
t, _ := time.Parse("2006-01-02", "2026-01-01")
|
|
return model.Rate{
|
|
RateID: "PDR-1", Country: "DE", Currency: "EUR",
|
|
FullDay: "28.00", PartialDayMin8h: "14.00",
|
|
ArrivalDay: "14.00", DepartureDay: "14.00",
|
|
ValidFrom: t, SourceRegulation: "test", CreatedBy: "admin",
|
|
}
|
|
}
|