package store import ( "context" "errors" "testing" "time" "basicstack.de/proofedmesh/services/admin-api/internal/model" ) func d(s string) time.Time { t, err := time.Parse("2006-01-02", s) if err != nil { panic(err) } return t } func rateDE(validFrom, validTo string) model.NewRateInput { in := model.NewRateInput{ Country: "DE", Currency: "EUR", FullDay: "28.00", PartialDayMin8h: "14.00", ArrivalDay: "14.00", DepartureDay: "14.00", ValidFrom: d(validFrom), SourceRegulation: "test", } if validTo != "" { t := d(validTo) in.ValidTo = &t } return in } func TestMemory_CreateAndGet(t *testing.T) { m := NewMemory() ctx := context.Background() r, err := m.Create(ctx, rateDE("2026-01-01", ""), "admin", time.Now()) if err != nil { t.Fatalf("create: %v", err) } if r.RateID != "PDR-1" { t.Errorf("expected PDR-1, got %s", r.RateID) } got, err := m.Get(ctx, "PDR-1") if err != nil { t.Fatalf("get: %v", err) } if got.FullDay != "28.00" { t.Errorf("roundtrip full_day: %s", got.FullDay) } } func TestMemory_Get_NotFound(t *testing.T) { m := NewMemory() _, err := m.Get(context.Background(), "PDR-999") if !errors.Is(err, ErrNotFound) { t.Fatalf("expected ErrNotFound, got %v", err) } } func TestMemory_OverlapRejected(t *testing.T) { m := NewMemory() ctx := context.Background() if _, err := m.Create(ctx, rateDE("2026-01-01", "2026-12-31"), "admin", time.Now()); err != nil { t.Fatalf("first create: %v", err) } // Overlap on the last day of 2026 _, err := m.Create(ctx, rateDE("2026-12-31", ""), "admin", time.Now()) if !errors.Is(err, ErrOverlap) { t.Fatalf("expected ErrOverlap, got %v", err) } } func TestMemory_RetireChangesValidTo(t *testing.T) { m := NewMemory() ctx := context.Background() r, _ := m.Create(ctx, rateDE("2026-01-01", ""), "admin", time.Now()) updated, err := m.Retire(ctx, r.RateID, d("2026-06-30"), "admin2", time.Now()) if err != nil { t.Fatalf("retire: %v", err) } if updated.ValidTo == nil || !updated.ValidTo.Equal(d("2026-06-30")) { t.Errorf("valid_to not set to 2026-06-30: %v", updated.ValidTo) } } func TestMemory_Retire_BeforeValidFrom_Rejected(t *testing.T) { m := NewMemory() ctx := context.Background() r, _ := m.Create(ctx, rateDE("2026-01-01", ""), "admin", time.Now()) _, err := m.Retire(ctx, r.RateID, d("2025-12-01"), "admin2", time.Now()) if err == nil { t.Fatal("expected error for valid_to before valid_from") } } func TestMemory_AttachAttestation(t *testing.T) { m := NewMemory() ctx := context.Background() r, _ := m.Create(ctx, rateDE("2026-01-01", ""), "admin", time.Now()) err := m.AttachAttestation(ctx, r.RateID, "sha256:0000000000000000000000000000000000000000000000000000000000000001") if err != nil { t.Fatalf("attach: %v", err) } got, _ := m.Get(ctx, r.RateID) if got.RateChangeAttestationRef == nil { t.Fatal("expected attestation ref to be set") } } func TestMemory_AttachAttestation_BadRef_Rejected(t *testing.T) { m := NewMemory() ctx := context.Background() r, _ := m.Create(ctx, rateDE("2026-01-01", ""), "admin", time.Now()) err := m.AttachAttestation(ctx, r.RateID, "not-a-hash") if err == nil { t.Fatal("expected error for malformed attestation ref") } } func TestMemory_List_FilterByCountry(t *testing.T) { m := NewMemory() ctx := context.Background() _, _ = m.Create(ctx, rateDE("2026-01-01", ""), "admin", time.Now()) fr := rateDE("2026-01-01", "") fr.Country = "FR" _, _ = m.Create(ctx, fr, "admin", time.Now()) all, _ := m.List(ctx, ListFilter{}) if len(all) != 2 { t.Fatalf("expected 2 total, got %d", len(all)) } de, _ := m.List(ctx, ListFilter{Country: "DE"}) if len(de) != 1 || de[0].Country != "DE" { t.Fatalf("country filter failed: %+v", de) } }