package release import ( "context" "strings" "testing" "time" ) func TestRetentionRoundTrip(t *testing.T) { ctx := context.Background() s := NewDirStore(t.TempDir()) now := time.Date(2026, 8, 10, 12, 0, 0, 0, time.UTC) // Absent is legal forever: off, not an error. got, err := GetRetention(ctx, s, "bab", "kass") if err != nil || got != nil { t.Fatalf("absent retention = %v, %v; want nil, nil", got, err) } if err := SetRetention(ctx, s, "bab", "kass", 50, "alice@keymail.dev", now); err != nil { t.Fatal(err) } got, err = GetRetention(ctx, s, "bab", "kass") if err != nil { t.Fatal(err) } if got == nil || got.Keep != 50 || got.SetBy != "alice@keymail.dev" || !got.SetAt.Equal(now) { t.Fatalf("retention = %+v, want keep 50 by alice", got) } // Overwrite: a second set replaces the first (CAS re-read, not create-only). if err := SetRetention(ctx, s, "bab", "kass", 30, "bob@keymail.dev", now.Add(time.Hour)); err != nil { t.Fatal(err) } got, _ = GetRetention(ctx, s, "bab", "kass") if got == nil || got.Keep != 30 { t.Fatalf("after overwrite = %+v, want keep 30", got) } // Clear: record gone, absent again, and clearing twice is a no-op // (Delete is idempotent by store contract). if err := ClearRetention(ctx, s, "bab", "kass"); err != nil { t.Fatal(err) } if err := ClearRetention(ctx, s, "bab", "kass"); err != nil { t.Fatal(err) } if got, err := GetRetention(ctx, s, "bab", "kass"); err != nil || got != nil { t.Fatalf("after clear = %v, %v; want nil, nil", got, err) } } func TestSetRetentionRefusesBelowFloor(t *testing.T) { s := NewDirStore(t.TempDir()) err := SetRetention(context.Background(), s, "bab", "kass", KeepFloor-1, "x", time.Now()) if err == nil || !strings.Contains(err.Error(), "20") { t.Fatalf("keep below floor = %v, want an error naming the floor", err) } } // A record that lies about identity is never guessed at (provision.Record's // rule): wrong app in the body, or an unknown schema, refuses. func TestGetRetentionRefusesLiesAndUnknownSchema(t *testing.T) { ctx := context.Background() s := NewDirStore(t.TempDir()) put := func(body string) { if _, err := s.Put(ctx, RetentionKey("bab", "kass"), []byte(body), ""); err != nil { t.Fatal(err) } t.Cleanup(func() { s.Delete(ctx, RetentionKey("bab", "kass")) }) } put(`{"schema":1,"app":"other","keep":50,"set_at":"2026-08-10T12:00:00Z"}`) if _, err := GetRetention(ctx, s, "bab", "kass"); err == nil { t.Fatal("a body naming a different app was accepted") } s.Delete(ctx, RetentionKey("bab", "kass")) put(`{"schema":2,"app":"kass","keep":50,"set_at":"2026-08-10T12:00:00Z"}`) if _, err := GetRetention(ctx, s, "bab", "kass"); err == nil { t.Fatal("an unknown schema was accepted") } s.Delete(ctx, RetentionKey("bab", "kass")) // Keep below floor in a stored record is corrupt, not a policy. put(`{"schema":1,"app":"kass","keep":3,"set_at":"2026-08-10T12:00:00Z"}`) if _, err := GetRetention(ctx, s, "bab", "kass"); err == nil { t.Fatal("a stored keep below the floor was accepted") } }