package release import ( "context" "encoding/json" "errors" "testing" "time" ) func TestSystemKeyShape(t *testing.T) { if got := SystemKey("antivirus"); got != "system/antivirus.json" { t.Fatalf("SystemKey = %q", got) } } // Absent is a state, not an error — and it means NOT ENABLED. A deployment // that has never enabled scanning does not scan. func TestGetAntivirusAbsentIsNotEnabled(t *testing.T) { ctx := context.Background() s := NewDirStore(t.TempDir()) av, etag, err := GetAntivirus(ctx, s) if err != nil || av != nil || etag != "" { t.Fatalf("absent record = %+v, %q, %v", av, etag, err) } on, err := AntivirusEnabled(ctx, s) if err != nil || on { t.Fatalf("AntivirusEnabled with no record = %v, %v", on, err) } } // An unknown schema refuses rather than guessing. A future schema 2 could mean // anything, and the thing this record drives is whether the deployment reads // tenant object bytes. func TestAntivirusSchemaRefusesUnknown(t *testing.T) { ctx := context.Background() s := NewDirStore(t.TempDir()) body, _ := json.Marshal(map[string]any{"schema": 2, "enabled": true}) if _, err := s.Put(ctx, SystemKey(AntivirusName), body, ""); err != nil { t.Fatal(err) } if _, _, err := GetAntivirus(ctx, s); err == nil { t.Fatal("an unknown schema was read as if it were understood") } // And the folded helper must not turn that refusal into "disabled" — a // caller acting on false would silently stop scanning. if _, err := AntivirusEnabled(ctx, s); err == nil { t.Fatal("AntivirusEnabled swallowed a schema refusal as disabled") } } // A read error is not "disabled" either, for the same reason. func TestAntivirusEnabledDoesNotSwallowErrors(t *testing.T) { ctx := context.Background() s := NewDirStore(t.TempDir()) body, _ := json.Marshal(map[string]any{"schema": 1, "enabled": true}) if _, err := s.Put(ctx, SystemKey(AntivirusName), []byte(string(body)[:len(body)-3]), ""); err != nil { t.Fatal(err) } if _, err := AntivirusEnabled(ctx, s); err == nil { t.Fatal("malformed JSON was read as disabled rather than as an error") } } func TestPutAntivirusCreateOnlyThenReplace(t *testing.T) { ctx := context.Background() s := NewDirStore(t.TempDir()) now := time.Date(2026, 9, 2, 8, 0, 0, 0, time.UTC) etag, err := PutAntivirus(ctx, s, &AntivirusSettings{ Enabled: true, Engine: "clamav", At: now, By: "op@example.com", }, "") if err != nil { t.Fatal(err) } // Create-only against an existing key conflicts — this is what makes the // boot seed idempotent across two consoles and a restart loop. if _, err := PutAntivirus(ctx, s, &AntivirusSettings{Enabled: false}, ""); !errors.Is(err, ErrCASConflict) { t.Fatalf("second create-only write = %v, want ErrCASConflict", err) } if _, err := PutAntivirus(ctx, s, &AntivirusSettings{ Enabled: false, Engine: "clamav", At: now, By: "op@example.com", }, etag); err != nil { t.Fatalf("replace with the current etag: %v", err) } av, _, err := GetAntivirus(ctx, s) if err != nil || av == nil || av.Enabled { t.Fatalf("after disable: %+v, %v", av, err) } if av.Schema != AntivirusSchema { t.Fatalf("schema not stamped on write: %+v", av) } }