package baseline import ( "encoding/json" "errors" "fmt" "os" "path/filepath" "reflect" "strconv" "strings" "sync" "testing" "github.com/sophotechlabs/spinoza/internal/checks" ) const cluster = "https://one.example:6443" func taken() checks.Baseline { return checks.Baseline{ TakenAt: "2026-08-30T00:00:00Z", Scanned: 7, Checks: []string{"privileged-containers"}, Counts: map[string]int{"privileged-containers": 2}, Keys: map[string]string{ "privileged-containers\x00aaaaaaaaaaa": "Deployment apps/api", "privileged-containers\x00bbbbbbbbbbb": "Deployment apps/web", }, } } func store(t *testing.T) *Store { t.Helper() return Open(t.TempDir()) } func TestABaselineComesBackAsItWasSaved(t *testing.T) { held := store(t) if err := held.Save(cluster, taken()); err != nil { t.Fatalf("save: %v", err) } back, ok := held.Load(cluster) if !ok { t.Fatal("a baseline that was just saved could not be read") } if back.TakenAt != taken().TakenAt || back.Scanned != 7 || back.Counts["privileged-containers"] != 2 { t.Fatalf("read back %+v", back) } if back.Keys["privileged-containers\x00aaaaaaaaaaa"] != "Deployment apps/api" || len(back.Keys) != 2 { t.Fatalf("read back %d keys: %v", len(back.Keys), back.Keys) } } func TestAnOlderStoredBaselineWithNoFingerprintsStillLoads(t *testing.T) { held := store(t) body := []byte(`{"takenAt":"2026-08-30T00:00:00Z","checks":["privileged-containers"]}`) if err := os.WriteFile(held.fileFor(cluster), body, 0o600); err != nil { t.Fatalf("write: %v", err) } back, ok := held.Load(cluster) if !ok { t.Fatal("an older baseline without fingerprints could not be read") } if back.Keys == nil { t.Fatal("an older baseline came back with a nil set to look fingerprints up in") } } func TestOneClusterCannotReadAnotherClusterBaseline(t *testing.T) { held := store(t) if err := held.Save(cluster, taken()); err != nil { t.Fatalf("save: %v", err) } if _, ok := held.Load("https://two.example:6443"); ok { t.Fatal("a second cluster was given the first one's baseline") } } func TestConcurrentClustersKeepTheirOwnBaselines(t *testing.T) { held := store(t) const clusters = 24 var group sync.WaitGroup for index := range clusters { server := fmt.Sprintf("https://cluster-%02d.example:6443", index) baseline := taken() baseline.Cluster = server baseline.TakenAt = fmt.Sprintf("2026-09-01T%02d:00:00Z", index) group.Go(func() { if err := held.Save(server, baseline); err != nil { t.Errorf("save %s: %v", server, err) } }) } group.Wait() for index := range clusters { server := fmt.Sprintf("https://cluster-%02d.example:6443", index) wantTakenAt := fmt.Sprintf("2026-09-01T%02d:00:00Z", index) baseline, ok := held.Load(server) if !ok { t.Fatalf("%s has no baseline", server) } if baseline.Cluster != server || baseline.TakenAt != wantTakenAt { t.Fatalf("%s loaded %+v", server, baseline) } } } func TestConcurrentReadsAndReplacementsNeverExposeAPartialBaseline(t *testing.T) { held := store(t) initial := taken() initial.Cluster = cluster if err := held.Save(cluster, initial); err != nil { t.Fatalf("initial save: %v", err) } const workers = 24 var group sync.WaitGroup for index := range workers { group.Go(func() { next := taken() next.Cluster = cluster next.TakenAt = fmt.Sprintf("2026-09-01T%02d:00:00Z", index) if err := held.Save(cluster, next); err != nil { t.Errorf("save %d: %v", index, err) } }) group.Go(func() { loaded, ok := held.Load(cluster) if !ok { t.Errorf("read %d saw no baseline", index) return } if loaded.Cluster != cluster || len(loaded.Checks) != 1 || len(loaded.Keys) != 2 { t.Errorf("read %d saw a partial baseline: %+v", index, loaded) } }) } group.Wait() } func TestStoredBaselineAndDirectoryArePrivate(t *testing.T) { dir := filepath.Join(t.TempDir(), "baselines") held := Open(dir) if err := held.Save(cluster, taken()); err != nil { t.Fatalf("save: %v", err) } dirInfo, err := os.Stat(dir) if err != nil { t.Fatalf("stat directory: %v", err) } if dirInfo.Mode().Perm() != 0o700 { t.Fatalf("directory mode = %v, want 0700", dirInfo.Mode().Perm()) } fileInfo, err := os.Stat(held.fileFor(cluster)) if err != nil { t.Fatalf("stat baseline: %v", err) } if fileInfo.Mode().Perm() != 0o600 { t.Fatalf("baseline mode = %v, want 0600", fileInfo.Mode().Perm()) } } func TestTakingANewBaselineReplacesTheOldOne(t *testing.T) { held := store(t) if err := held.Save(cluster, taken()); err != nil { t.Fatalf("save: %v", err) } next := taken() next.TakenAt = "2026-09-01T00:00:00Z" if err := held.Save(cluster, next); err != nil { t.Fatalf("save again: %v", err) } back, _ := held.Load(cluster) if back.TakenAt != next.TakenAt { t.Fatalf("the older baseline survived: %q", back.TakenAt) } } func TestForgettingABaselineLeavesNothingToCompareAgainst(t *testing.T) { held := store(t) if err := held.Save(cluster, taken()); err != nil { t.Fatalf("save: %v", err) } if err := held.Clear(cluster); err != nil { t.Fatalf("clear: %v", err) } if _, ok := held.Load(cluster); ok { t.Fatal("a forgotten baseline was still there") } } func TestForgettingABaselineThatWasNeverTakenIsNotAnError(t *testing.T) { if err := store(t).Clear(cluster); err != nil { t.Fatalf("clear: %v", err) } } func TestAClusterWithNoBaselineSaysSo(t *testing.T) { if _, ok := store(t).Load(cluster); ok { t.Fatal("a cluster with no baseline was given one") } } func TestAFileThatIsNotABaselineIsIgnoredRatherThanObeyed(t *testing.T) { dir := t.TempDir() held := Open(dir) if err := held.Save(cluster, taken()); err != nil { t.Fatalf("save: %v", err) } name, err := os.ReadDir(dir) if err != nil || len(name) != 1 { t.Fatalf("the store wrote %v (%v)", name, err) } if writeErr := os.WriteFile(filepath.Join(dir, name[0].Name()), []byte("{{{"), 0o600); writeErr != nil { t.Fatalf("write: %v", writeErr) } if _, ok := held.Load(cluster); ok { t.Fatal("an unreadable file was read as a baseline") } } func TestMoreFindingsThanOneBaselineHoldsIsRefused(t *testing.T) { huge := taken() huge.Keys = make(map[string]string, maxKeys+1) for at := range maxKeys + 1 { huge.Keys[strconv.Itoa(at)] = "Deployment apps/api" } if err := store(t).Save(cluster, huge); err == nil { t.Fatal("a baseline past the cap was written anyway") } } func TestABaselineTooLargeToReadBackIsNotSaved(t *testing.T) { held := store(t) huge := taken() huge.Keys = map[string]string{"finding": strings.Repeat("x", maxBytes)} if err := held.Save(cluster, huge); err == nil { t.Fatal("a baseline past the byte cap was written anyway") } if _, ok := held.Load(cluster); ok { t.Fatal("a refused baseline was left behind") } } func TestAnOversizedBaselineFileIsNotLoaded(t *testing.T) { held := store(t) path := held.fileFor(cluster) if err := os.WriteFile(path, []byte("{}"), 0o600); err != nil { t.Fatalf("write: %v", err) } if err := os.Truncate(path, maxBytes+1); err != nil { t.Fatalf("enlarge: %v", err) } if _, ok := held.Load(cluster); ok { t.Fatal("an oversized baseline file was loaded") } } func TestAStoreWithNoDirectoryKeepsNothingAndSaysNothing(t *testing.T) { held := Open("") if err := held.Save(cluster, taken()); err != nil { t.Fatalf("save: %v", err) } if err := held.Clear(cluster); err != nil { t.Fatalf("clear: %v", err) } if _, ok := held.Load(cluster); ok { t.Fatal("a store with nowhere to write produced a baseline") } } func TestADirectoryThatCannotBeMadeIsReported(t *testing.T) { blocked := filepath.Join(t.TempDir(), "a-file") if err := os.WriteFile(blocked, []byte("not a directory"), 0o600); err != nil { t.Fatalf("write: %v", err) } if err := Open(filepath.Join(blocked, "baselines")).Save(cluster, taken()); err == nil { t.Fatal("saving under a file succeeded") } } func TestADirectoryThatCannotBeWrittenToIsReported(t *testing.T) { dir := filepath.Join(t.TempDir(), "baselines") if err := os.MkdirAll(dir, 0o500); err != nil { t.Fatalf("mkdir: %v", err) } if err := Open(dir).Save(cluster, taken()); err == nil { t.Fatal("saving into a directory that refuses writes succeeded") } } func TestABaselineThatCannotBeRemovedIsReported(t *testing.T) { dir := t.TempDir() held := Open(dir) if err := os.MkdirAll(held.fileFor(cluster), 0o700); err != nil { t.Fatalf("mkdir: %v", err) } if err := os.WriteFile(filepath.Join(held.fileFor(cluster), "in-the-way"), []byte("x"), 0o600); err != nil { t.Fatalf("write: %v", err) } if err := held.Clear(cluster); err == nil { t.Fatal("clearing something that cannot be removed succeeded") } } func TestNoHomeMeansNoDefaultDirectory(t *testing.T) { t.Setenv("HOME", "") t.Setenv("XDG_CONFIG_HOME", "") if _, err := DefaultDir(); err == nil { t.Fatal("a machine with no config directory named one anyway") } } func TestTheDefaultDirectorySitsBesideTheSettings(t *testing.T) { dir, err := DefaultDir() if err != nil { t.Skipf("no user config directory here: %v", err) } if filepath.Base(dir) != "baselines" { t.Fatalf("the default directory is %q", dir) } } func TestABaselineComesBackFromTheFileItWasWrittenTo(t *testing.T) { body, err := Encode(taken()) if err != nil { t.Fatalf("encode: %v", err) } back, decodeErr := Decode(body) if decodeErr != nil { t.Fatalf("decode: %v", decodeErr) } if back.TakenAt != taken().TakenAt || back.Scanned != 7 || len(back.Keys) != 2 { t.Fatalf("read back %+v", back) } } func TestABaselineRemembersWhichClusterItWasTakenOn(t *testing.T) { mine := taken() mine.Cluster = cluster body, err := Encode(mine) if err != nil { t.Fatalf("encode: %v", err) } back, decodeErr := Decode(body) if decodeErr != nil { t.Fatalf("decode: %v", decodeErr) } if back.Cluster != cluster { t.Fatalf("the baseline came back naming %q", back.Cluster) } } func TestSomethingThatIsNotABaselineIsRefusedRatherThanStored(t *testing.T) { cases := []struct{ name, body string }{ {"not json", "{{{"}, {"an empty object", "{}"}, {"a baseline with no checks", `{"takenAt":"2026-08-30T00:00:00Z","checks":[]}`}, {"a baseline with no day", `{"checks":["a"]}`}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { if _, err := Decode([]byte(tc.body)); err == nil { t.Fatal("it was read as a baseline") } }) } } func TestMalformedBaselineShapesAreRefused(t *testing.T) { cases := []struct { name string body string want string alternate string }{ {name: "empty", body: "", want: "EOF"}, {name: "top level list", body: `[]`, want: "top-level value is not an object"}, {name: "unknown field", body: `{"unknown":1}`, want: `unknown field "unknown"`}, {name: "cluster type", body: `{"cluster":1}`, want: "cannot unmarshal number"}, {name: "trailing value", body: `{} {}`, want: "baseline has a trailing value"}, {name: "broken trailing value", body: `{} {`, want: "unexpected EOF"}, {name: "checks opening", body: `{"checks":`, want: "EOF"}, {name: "checks shape", body: `{"checks":{}}`, want: "baseline checks are not a list"}, {name: "check type", body: `{"checks":[{}]}`, want: "cannot unmarshal object"}, {name: "checks closing", body: `{"checks":["one"`, want: "EOF", alternate: "unexpected end of JSON input"}, {name: "counts opening", body: `{"counts":`, want: "EOF"}, {name: "counts shape", body: `{"counts":[]}`, want: "baseline counts are not an object"}, {name: "count type", body: `{"counts":{"one":"bad"}}`, want: "cannot unmarshal string"}, {name: "counts closing", body: `{"counts":{"one":1`, want: "EOF", alternate: "unexpected end of JSON input"}, {name: "keys opening", body: `{"keys":`, want: "EOF"}, {name: "keys shape", body: `{"keys":[]}`, want: "baseline keys are not an object"}, {name: "key value type", body: `{"keys":{"one":1}}`, want: "cannot unmarshal number"}, {name: "keys closing", body: `{"keys":{"one":"value"`, want: "EOF", alternate: "unexpected end of JSON input"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { found, err := Decode([]byte(tc.body)) if err == nil { t.Fatal("malformed baseline was accepted") } if !reflect.DeepEqual(found, checks.Baseline{}) { t.Fatalf("baseline = %+v, want no partial document after refusal", found) } if errors.Is(err, ErrRead) { t.Fatalf("error = %v, want malformed input distinguished from an interrupted read", err) } if strings.Contains(err.Error(), tc.want) { return } if tc.alternate != "" { if strings.Contains(err.Error(), tc.alternate) { return } } t.Fatalf("error = %q, want %q or %q", err, tc.want, tc.alternate) }) } } func TestNullOptionalBaselineMapsBecomeEmptyMaps(t *testing.T) { body := []byte(`{"takenAt":"2026-09-03T12:00:00Z","checks":["one"],"counts":null,"keys":null}`) baseline, err := Decode(body) if err != nil { t.Fatalf("decode: %v", err) } if baseline.Counts == nil { t.Fatal("counts remained nil") } if baseline.Keys == nil { t.Fatal("keys remained nil") } } func TestABaselineTooLargeToBeOneIsRefused(t *testing.T) { if _, err := Decode(make([]byte, maxBytes+1)); err == nil { t.Fatal("a body past the cap was read as a baseline") } } func TestAnImportedBaselineCannotExceedTheFindingCap(t *testing.T) { huge := taken() huge.Keys = make(map[string]string, maxKeys+1) for at := range maxKeys + 1 { huge.Keys[strconv.Itoa(at)] = "Deployment apps/api" } body, err := json.Marshal(flatten(huge)) if err != nil { t.Fatalf("marshal: %v", err) } if _, err := Decode(body); err == nil { t.Fatal("a baseline past the finding cap was imported") } } func TestAnImportedBaselineAcceptsTheFindingCap(t *testing.T) { held := taken() held.Keys = make(map[string]string, maxKeys) for at := range maxKeys { held.Keys[strconv.Itoa(at)] = "Deployment apps/api" } body, err := json.Marshal(flatten(held)) if err != nil { t.Fatalf("marshal: %v", err) } back, err := Decode(body) if err != nil { t.Fatalf("decode the maximum finding set: %v", err) } if len(back.Keys) != maxKeys { t.Fatalf("findings = %d, want %d", len(back.Keys), maxKeys) } } func TestImportedBaselineKeyContentHasAnExactByteLimit(t *testing.T) { accepted := taken() accepted.Keys = map[string]string{"k": strings.Repeat("x", maxKeyValueBytes-1)} body, err := json.Marshal(flatten(accepted)) if err != nil { t.Fatalf("marshal accepted baseline: %v", err) } if _, decodeErr := Decode(body); decodeErr != nil { t.Fatalf("decode the maximum key content: %v", decodeErr) } rejected := taken() rejected.Keys = map[string]string{"k": strings.Repeat("x", maxKeyValueBytes)} body, err = json.Marshal(flatten(rejected)) if err != nil { t.Fatalf("marshal rejected baseline: %v", err) } if _, err := Decode(body); err == nil || !strings.Contains(err.Error(), "keys and values exceed") { t.Fatalf("one extra content byte returned %v", err) } } func TestImportedBaselineAcceptsTheExactFileByteLimit(t *testing.T) { body, err := json.Marshal(flatten(taken())) if err != nil { t.Fatalf("marshal baseline: %v", err) } body = append(body, []byte(strings.Repeat(" ", maxBytes-len(body)))...) if _, err := Decode(body); err != nil { t.Fatalf("decode the maximum file size: %v", err) } } func TestImportedBaselineChecksAndCountsHaveExactEntryLimits(t *testing.T) { for _, tc := range []struct { name string accepted stored rejected stored }{ { name: "checks", accepted: stored{ TakenAt: "2026-09-03T12:00:00Z", Checks: numberedStrings(maxChecks), }, rejected: stored{ TakenAt: "2026-09-03T12:00:00Z", Checks: numberedStrings(maxChecks + 1), }, }, { name: "counts", accepted: stored{ TakenAt: "2026-09-03T12:00:00Z", Checks: []string{"one"}, Counts: numberedCounts(maxCounts), }, rejected: stored{ TakenAt: "2026-09-03T12:00:00Z", Checks: []string{"one"}, Counts: numberedCounts(maxCounts + 1), }, }, } { t.Run(tc.name, func(t *testing.T) { accepted, err := json.Marshal(tc.accepted) if err != nil { t.Fatalf("marshal accepted shape: %v", err) } if _, decodeErr := Decode(accepted); decodeErr != nil { t.Fatalf("decode accepted shape: %v", decodeErr) } rejected, err := json.Marshal(tc.rejected) if err != nil { t.Fatalf("marshal rejected shape: %v", err) } if _, err := Decode(rejected); err == nil { t.Fatal("one extra entry was accepted") } }) } } func numberedStrings(count int) []string { out := make([]string, count) for at := range out { out[at] = strconv.Itoa(at) } return out } func numberedCounts(count int) map[string]int { out := make(map[string]int, count) for at := range count { out[strconv.Itoa(at)] = at } return out } func TestABaselineWithNoFingerprintsStillReads(t *testing.T) { back, err := Decode([]byte(`{"takenAt":"2026-08-30T00:00:00Z","checks":["a"]}`)) if err != nil { t.Fatalf("decode: %v", err) } if back.Keys == nil { t.Fatal("a baseline with no fingerprints came back with a nil set to look them up in") } }