Files
minstrel/internal/api/me_timezone_test.go
bvandeusen 53a02322fb fix: A2 (relax art-source CHECK) + D + E integration residual
A2 — migration 0030: the albums/artists art `*_source` CHECK was a
fixed provider-ID allowlist fighting the extensible coverart.Register
registry (per-provider migration churn at 0016/0018/0020; rejected
test stub providers → ~11 enricher tests failed). Relax to "NULL or
non-empty"; the registry is the source of truth. Same brittleness
class as the #433 discovery-mix CHECK.

D — lidarr Approve resolves metadata/quality profiles (JSON arrays)
before the add; the test stubs returned {"id":1} for every path, so
ListMetadataProfiles failed to unmarshal → 500/Approve errors. Make
the lidarrrequests + admin_requests stubs path-aware (arrays for
/metadataprofile, /qualityprofile).

E:
- auth: requireUser (prelude.go) emitted code "auth_required"; the
  canonical code is "unauthenticated" (operator decision). Change the
  code; drop the now-dead "auth_required" web error-copy key
  ("unauthenticated" already has copy).
- playlists_system_test wrapped the real auth.RequireUser middleware
  but only injected withUser() context → 401. Like every other api
  handler test, drop the middleware and rely on requireUser().
- admin_users dup-username test seeded "test-existing" (seedUser
  prefixes) but POSTed "existing" → no collision; POST the prefixed
  name.
- me_timezone test decoded a top-level {"code"} but the envelope is
  {"error":{"code"}}; decode the nested shape.
- audit test asserted compact JSON; Postgres jsonb::text is spaced.
- put-lidarr-config tests predated the missing_defaults gate (correct
  handler behavior); supply the required defaults in the bodies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 20:22:30 -04:00

105 lines
3.0 KiB
Go

package api
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/go-chi/chi/v5"
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
)
func newMeTimezoneRouter(h *handlers) chi.Router {
r := chi.NewRouter()
r.Put("/api/me/timezone", h.handlePutTimezone)
return r
}
func TestPutTimezone_ValidIANA(t *testing.T) {
if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
t.Skip("MINSTREL_TEST_DATABASE_URL not set")
}
h, pool := testHandlers(t)
user := seedUser(t, pool, "tz1", "irrelevant1", false)
body := bytes.NewBufferString(`{"timezone":"America/New_York"}`)
req := httptest.NewRequest(http.MethodPut, "/api/me/timezone", body)
req.Header.Set("Content-Type", "application/json")
req = withUser(req, user)
rec := httptest.NewRecorder()
newMeTimezoneRouter(h).ServeHTTP(rec, req)
if rec.Code != http.StatusNoContent {
t.Fatalf("status = %d, want 204; body=%s", rec.Code, rec.Body.String())
}
// Verify the DB row was updated.
got, err := dbq.New(pool).GetUserByID(context.Background(), user.ID)
if err != nil {
t.Fatalf("GetUserByID: %v", err)
}
if got.Timezone != "America/New_York" {
t.Errorf("timezone = %q, want %q", got.Timezone, "America/New_York")
}
if !got.TimezoneUpdatedAt.Valid {
t.Errorf("expected timezone_updated_at to be set after PUT")
}
}
func TestPutTimezone_InvalidIANA(t *testing.T) {
if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
t.Skip("MINSTREL_TEST_DATABASE_URL not set")
}
h, pool := testHandlers(t)
user := seedUser(t, pool, "tz2", "irrelevant1", false)
body := bytes.NewBufferString(`{"timezone":"Not/A/Real/Zone"}`)
req := httptest.NewRequest(http.MethodPut, "/api/me/timezone", body)
req.Header.Set("Content-Type", "application/json")
req = withUser(req, user)
rec := httptest.NewRecorder()
newMeTimezoneRouter(h).ServeHTTP(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want 400; body=%s", rec.Code, rec.Body.String())
}
// Error envelope is nested: {"error":{"code":...}} (matches the
// rest of /api/*), not a top-level {"code":...}.
var errResp struct {
Error struct {
Code string `json:"code"`
} `json:"error"`
}
if err := json.NewDecoder(rec.Body).Decode(&errResp); err != nil {
t.Fatalf("decode err response: %v", err)
}
if errResp.Error.Code != "invalid_timezone" {
t.Errorf("error code = %q, want invalid_timezone", errResp.Error.Code)
}
}
func TestPutTimezone_EmptyTimezone(t *testing.T) {
if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
t.Skip("MINSTREL_TEST_DATABASE_URL not set")
}
h, pool := testHandlers(t)
user := seedUser(t, pool, "tz3", "irrelevant1", false)
body := bytes.NewBufferString(`{"timezone":""}`)
req := httptest.NewRequest(http.MethodPut, "/api/me/timezone", body)
req.Header.Set("Content-Type", "application/json")
req = withUser(req, user)
rec := httptest.NewRecorder()
newMeTimezoneRouter(h).ServeHTTP(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("status = %d, want 400", rec.Code)
}
}