package main import ( "os" "path/filepath" "slices" "testing" "github.com/sophotechlabs/spinoza/internal/api" "github.com/sophotechlabs/spinoza/internal/checks" settingsstore "github.com/sophotechlabs/spinoza/internal/settings" pastore "github.com/sophotechlabs/spinoza/internal/store" ) func TestTheFlagAloneAllowsANodeShell(t *testing.T) { allow := allowNodeShell(true, settingsstore.Memory()) if !allow() { t.Fatal("--node-shell did not allow a node shell") } } func TestWithoutTheFlagTheStoredSettingDecides(t *testing.T) { store := settingsstore.Memory() allow := allowNodeShell(false, store) if allow() { t.Fatal("a node shell was allowed before anything turned it on") } err := store.Merge(map[string]string{settingsstore.NodeShellKey: "on"}) if err != nil { t.Fatalf("merge: %v", err) } if !allow() { t.Fatal("turning the setting on did not reach a shell asked for afterwards") } off := store.Merge(map[string]string{settingsstore.NodeShellKey: "off"}) if off != nil { t.Fatalf("merge: %v", off) } if allow() { t.Fatal("turning the setting off left node shells allowed") } } func TestCustomColumnsFollowStoredSettings(t *testing.T) { store := settingsstore.Memory() columns := customColumns(store) if got := columns(); len(got) != 0 { t.Fatalf("columns before configuration = %v", got) } err := store.Merge(map[string]string{ settingsstore.ColumnsKey: `{"/v1/pods":[{"name":"Node name","path":".spec.nodeName"}]}`, }) if err != nil { t.Fatalf("store columns: %v", err) } got := columns() want := api.CustomColumn{Name: "Node name", Path: ".spec.nodeName"} if len(got["/v1/pods"]) != 1 || got["/v1/pods"][0] != want { t.Fatalf("columns after configuration = %v, want %v", got, want) } err = store.Merge(map[string]string{settingsstore.ColumnsKey: "not json"}) if err != nil { t.Fatalf("store malformed columns: %v", err) } if got := columns(); len(got) != 0 { t.Fatalf("malformed columns = %v, want none", got) } } func TestReadableNamespacesFollowStoredSettings(t *testing.T) { store := settingsstore.Memory() named := readableNamespaces(store) if got := named(); len(got) != 0 { t.Fatalf("namespaces before configuration = %v", got) } err := store.Merge(map[string]string{ settingsstore.NamespacesKey: `{"spinoza-eks-editor":["payments"]}`, }) if err != nil { t.Fatalf("store namespaces: %v", err) } got := named() if len(got["spinoza-eks-editor"]) != 1 || got["spinoza-eks-editor"][0] != "payments" { t.Fatalf("namespaces after configuration = %v, want payments", got) } err = store.Merge(map[string]string{settingsstore.NamespacesKey: "not json"}) if err != nil { t.Fatalf("store malformed namespaces: %v", err) } if got := named(); len(got) != 0 { t.Fatalf("malformed namespaces = %v, want none", got) } } func TestTheStoreIsReadFromTheUsualPlace(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) t.Setenv("XDG_CONFIG_HOME", dir) path, err := settingsstore.DefaultPath() if err != nil { t.Fatalf("path: %v", err) } mkdirErr := os.MkdirAll(filepath.Dir(path), 0o700) if mkdirErr != nil { t.Fatalf("mkdir: %v", mkdirErr) } body := `{"values":{"` + settingsstore.NodeShellKey + `":"on"}}` writeErr := os.WriteFile(path, []byte(body), 0o600) if writeErr != nil { t.Fatalf("write: %v", writeErr) } store := settingsStore(t.Context()) if !store.On(settingsstore.NodeShellKey) { t.Fatal("the settings written on disk were not read back") } } func TestNowhereToKeepSettingsStillLeavesAStore(t *testing.T) { t.Setenv("HOME", "") t.Setenv("XDG_CONFIG_HOME", "") store := settingsStore(t.Context()) if store == nil { t.Fatal("settings with nowhere to live left no store at all") } err := store.Merge(map[string]string{settingsstore.NodeShellKey: "on"}) if err != nil { t.Fatalf("merge: %v", err) } if !store.On(settingsstore.NodeShellKey) { t.Fatal("a store with nowhere to write forgot what it was told") } } func TestUnreadableSettingsStillLeaveAStore(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) t.Setenv("XDG_CONFIG_HOME", dir) path, err := settingsstore.DefaultPath() if err != nil { t.Fatalf("path: %v", err) } mkdirErr := os.MkdirAll(filepath.Dir(path), 0o700) if mkdirErr != nil { t.Fatalf("mkdir: %v", mkdirErr) } writeErr := os.WriteFile(path, []byte("{not json"), 0o600) if writeErr != nil { t.Fatalf("write: %v", writeErr) } store := settingsStore(t.Context()) if store == nil { t.Fatal("settings that cannot be read left no store at all") } if store.On(settingsstore.NodeShellKey) { t.Fatal("settings that cannot be read turned a node shell on") } } func TestBaselinesAreKeptBesideTheSettings(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) t.Setenv("XDG_CONFIG_HOME", dir) store := baselineStore() taken := checks.Baseline{ TakenAt: "2026-08-30T00:00:00Z", Checks: []string{"privileged-containers"}, } if err := store.Save("https://one.example", taken); err != nil { t.Fatalf("save: %v", err) } if _, ok := store.Load("https://one.example"); !ok { t.Fatal("a baseline saved through the store could not be read back") } } func TestNowhereToKeepBaselinesStillLeavesAStore(t *testing.T) { t.Setenv("HOME", "") t.Setenv("XDG_CONFIG_HOME", "") store := baselineStore() if _, ok := store.Load("https://one.example"); ok { t.Fatal("a store with nowhere to write produced a baseline") } } func TestHistoryIsKeptInTheUsualPlace(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) t.Setenv("XDG_CONFIG_HOME", dir) store := historyStore(t.Context()) t.Cleanup(func() { _ = store.Close() }) if store.Reason() != "" { t.Fatalf("reason = %q, want a store that records", store.Reason()) } path, err := pastore.DefaultPath() if err != nil { t.Fatalf("path: %v", err) } if _, statErr := os.Stat(path); statErr != nil { t.Fatalf("the history file was not created at %s: %v", path, statErr) } } func TestNowhereToKeepHistoryStillLeavesAStore(t *testing.T) { t.Setenv("HOME", "") t.Setenv("XDG_CONFIG_HOME", "") store := historyStore(t.Context()) t.Cleanup(func() { _ = store.Close() }) if store == nil { t.Fatal("history with nowhere to live left no store at all") } if store.Reason() == "" { t.Skip("this platform still names a config directory without HOME") } if err := store.For("https://p-mk1:6443").Record(t.Context(), pastore.Entry{Name: "web"}); err != nil { t.Fatalf("record: %v, want a quiet no-op", err) } } func TestAHistoryFileThatCannotBeReadStillLeavesAStore(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) t.Setenv("XDG_CONFIG_HOME", dir) path, err := pastore.DefaultPath() if err != nil { t.Fatalf("path: %v", err) } if mkdirErr := os.MkdirAll(filepath.Dir(path), 0o700); mkdirErr != nil { t.Fatalf("mkdir: %v", mkdirErr) } if writeErr := os.WriteFile(path, []byte("not a database"), 0o600); writeErr != nil { t.Fatalf("write: %v", writeErr) } store := historyStore(t.Context()) t.Cleanup(func() { _ = store.Close() }) if store == nil { t.Fatal("a broken history file left no store at all") } if store.Reason() == "" { t.Fatal("a broken history file was reported as recording") } } func TestNamespacesComeBackFromTheirJSON(t *testing.T) { held := parseNamespaces(`{"spinoza-eks-editor":["payments","storefront"]}`) if !slices.Equal(held["spinoza-eks-editor"], []string{"payments", "storefront"}) { t.Fatalf("read %v", held) } } func TestAnythingThatIsNotNamespacesReadsAsNone(t *testing.T) { for _, raw := range []string{"", "not json", "[]", "null", "42", `{"ctx":"payments"}`} { if got := parseNamespaces(raw); len(got) != 0 { t.Fatalf("%q read as %v", raw, got) } } }