diff options
Diffstat (limited to 'internal/admin/api_colors_test.go')
| -rw-r--r-- | internal/admin/api_colors_test.go | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/internal/admin/api_colors_test.go b/internal/admin/api_colors_test.go new file mode 100644 index 0000000..d1e5e9a --- /dev/null +++ b/internal/admin/api_colors_test.go @@ -0,0 +1,159 @@ +package admin + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + + "thehouseoficarus/internal/config" +) + +func newColorsTestServer(t *testing.T) (*AdminServer, string) { + t.Helper() + dir := t.TempDir() + path := filepath.Join(dir, "config.yaml") + if err := os.WriteFile(path, []byte("tick_length: 600\ndefault_colors:\n room_name: \"74 bold\"\n"), 0644); err != nil { + t.Fatal(err) + } + loaded, err := config.Load(path) + if err != nil { + t.Fatal(err) + } + return &AdminServer{cfg: loaded, configPath: path}, path +} + +func TestColorsGet(t *testing.T) { + s, _ := newColorsTestServer(t) + req := httptest.NewRequest(http.MethodGet, "/api/colors", nil) + w := httptest.NewRecorder() + s.getColors(w, req) + if w.Code != http.StatusOK { + t.Fatalf("status %d", w.Code) + } + var out map[string]any + if err := json.Unmarshal(w.Body.Bytes(), &out); err != nil { + t.Fatal(err) + } + cats, _ := out["categories"].([]any) + if len(cats) != len(colorCategories) { + t.Fatalf("categories count: got %d want %d", len(cats), len(colorCategories)) + } + preview, _ := out["preview"].([]any) + if len(preview) < 8 { + t.Fatalf("preview sections too few: %d", len(preview)) + } +} + +func TestColorsSaveAndPersist(t *testing.T) { + s, path := newColorsTestServer(t) + before := s.cfg.DefaultColors["room_name"] + + body := map[string]any{"colors": map[string]string{"room_name": "FF bold"}} + b, _ := json.Marshal(body) + req := httptest.NewRequest(http.MethodPost, "/api/colors", bytes.NewReader(b)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + s.saveColors(w, req) + if w.Code != http.StatusOK { + t.Fatalf("save status %d: %s", w.Code, w.Body.String()) + } + if s.cfg.DefaultColors["room_name"] == before { + t.Fatalf("live map not updated") + } + if s.cfg.DefaultColors["room_name"] != "FF bold" { + t.Fatalf("live value = %q", s.cfg.DefaultColors["room_name"]) + } + raw, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + if !bytes.Contains(raw, []byte("FF bold")) { + t.Fatalf("config.yaml not persisted with new value; got:\n%s", string(raw)) + } +} + +func TestColorsSaveRejectsBadSpec(t *testing.T) { + s, _ := newColorsTestServer(t) + body := map[string]any{"colors": map[string]string{"room_name": "zzzzz"}} + b, _ := json.Marshal(body) + req := httptest.NewRequest(http.MethodPost, "/api/colors", bytes.NewReader(b)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + s.saveColors(w, req) + if w.Code != http.StatusBadRequest { + t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String()) + } +} + +func TestColorsSaveRejectsUnknownKey(t *testing.T) { + s, _ := newColorsTestServer(t) + body := map[string]any{"colors": map[string]string{"nope": "FF"}} + b, _ := json.Marshal(body) + req := httptest.NewRequest(http.MethodPost, "/api/colors", bytes.NewReader(b)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + s.saveColors(w, req) + if w.Code != http.StatusBadRequest { + t.Fatalf("expected 400, got %d", w.Code) + } +} + +func TestColorsOffAllowed(t *testing.T) { + s, _ := newColorsTestServer(t) + body := map[string]any{"colors": map[string]string{"mob": "off"}} + b, _ := json.Marshal(body) + req := httptest.NewRequest(http.MethodPost, "/api/colors", bytes.NewReader(b)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + s.saveColors(w, req) + if w.Code != http.StatusOK { + t.Fatalf("expected 200 for 'off', got %d: %s", w.Code, w.Body.String()) + } +} + +func TestColorsReset(t *testing.T) { + s, _ := newColorsTestServer(t) + s.cfg.DefaultColors["room_name"] = "FF" + req := httptest.NewRequest(http.MethodPost, "/api/colors/reset", nil) + w := httptest.NewRecorder() + s.handleColorsReset(w, req) + if w.Code != http.StatusOK { + t.Fatalf("status %d: %s", w.Code, w.Body.String()) + } + if s.cfg.DefaultColors["room_name"] != "74 bold" { + t.Fatalf("reset did not restore builtin; got %q", s.cfg.DefaultColors["room_name"]) + } +} + +func TestColorsPreviewExhaustive(t *testing.T) { + s, _ := newColorsTestServer(t) + req := httptest.NewRequest(http.MethodGet, "/api/colors", nil) + w := httptest.NewRecorder() + s.getColors(w, req) + var out map[string]any + if err := json.Unmarshal(w.Body.Bytes(), &out); err != nil { + t.Fatal(err) + } + seen := map[string]bool{} + for _, sec := range out["preview"].([]any) { + for _, ln := range sec.(map[string]any)["lines"].([]any) { + for _, seg := range ln.(map[string]any)["segments"].([]any) { + c := seg.(map[string]any)["c"].(string) + if c != "" { + seen[c] = true + } + } + } + } + for _, cat := range colorCategories { + if !seen[cat.Name] { + t.Errorf("preview missing category: %s", cat.Name) + } + } + _ = io.Discard +}
\ No newline at end of file |
