package api import ( "log/slog" "net/http" "net/http/httptest" "strconv" "testing" "github.com/ponzischeme89/memby/server/internal/store" ) func TestRequestLogLevel(t *testing.T) { tests := []struct { path string status int want slog.Level }{ {"/healthz", http.StatusOK, slog.LevelDebug}, {"/v1/status", http.StatusOK, slog.LevelDebug}, {"/v1/images/123/primary", http.StatusOK, slog.LevelDebug}, {"/admin/api/status", http.StatusOK, slog.LevelDebug}, {"/admin/api/status", http.StatusUnauthorized, slog.LevelWarn}, {"/v1/home", http.StatusOK, slog.LevelInfo}, {"/v1/images/123/primary", http.StatusNotFound, slog.LevelWarn}, {"/v1/home", http.StatusServiceUnavailable, slog.LevelError}, } for _, test := range tests { if got := requestLogLevel(test.path, test.status); got != test.want { t.Errorf("requestLogLevel(%q, %d) = %v, want %v", test.path, test.status, got, test.want) } } } func TestComponentNamesThePartOfTheAppARouteBelongsTo(t *testing.T) { tests := map[string]string{ "/healthz": "health", "/v1/status": "status", "/v1/home": "home", "/v1/preferences": "settings", "/v1/screensaver": "screensaver", "/v1/auth/login": "auth", "/v1/auth/devices/tv-1": "devices", "/v1/search": "search", "/v1/genres/Comedy/items": "search", "/v1/items/42": "details", "/v1/items/42/related": "details", "/v1/items/42/playback": "playback", "/v1/items/42/next": "playback", "/v1/items/42/subtitles/search": "playback", "/v1/items/42/trickplay": "playback", "/v1/items/42/trickplay/12.jpg": "playback", "/v1/playback/started": "playback", "/v1/images/42/primary": "artwork", "/v1/recommendations": "recommendations", "/v1/for-you": "recommendations", "/v1/my-shows": "my-shows", "/admin/api/status": "admin", "/hooks/radarr": "webhooks", "/install": "installer", "/updates/latest.apk": "updates", "/something-nobody-has-written": "api", } for path, want := range tests { if got := componentFor(path); got != want { t.Errorf("componentFor(%q) = %q, want %q", path, got, want) } } } func TestIdentifyNamesTheViewerAndTelevision(t *testing.T) { request := httptest.NewRequest(http.MethodGet, "/v1/home", nil) request.Header.Set("X-Memby-Version", "0.1.60") request, identity := withRequestIdentity(request) identify(request.Context(), store.Session{ Username: "matt", DeviceName: "Living room", ClientProtocol: "1", }) if identity.component != "home" || identity.user != "matt" || identity.device != "Living room" { t.Fatalf("identity was not filled in: %+v", identity) } // The header the TV actually sent must survive a session that has no version yet. if identity.client != "0.1.60" || identity.protocol != "1" { t.Fatalf("client identity was lost: %+v", identity) } } // A device with no name at all must still be identifiable, or an old APK's traffic // becomes anonymous exactly when someone is trying to work out which television it is. func TestIdentifyFallsBackToTheDeviceID(t *testing.T) { request, identity := withRequestIdentity(httptest.NewRequest(http.MethodGet, "/v1/home", nil)) identify(request.Context(), store.Session{DeviceID: "tv-1"}) if identity.device != "tv-1" { t.Fatalf("device = %q", identity.device) } } func TestPlaybackTitlesNamesReportsAndStaysBounded(t *testing.T) { var titles playbackTitles titles.remember("42", "Dune") if got := titles.name("42"); got != "Dune" { t.Fatalf("remembered title = %q", got) } // An unknown item reports as itself rather than as an empty field. if got := titles.name("99"); got != "99" { t.Fatalf("unknown title = %q", got) } for i := range rememberedTitles + 10 { titles.remember(strconv.Itoa(1000+i), "Title") } if len(titles.titles) > rememberedTitles { t.Fatalf("title memory grew to %d entries", len(titles.titles)) } } func TestWatchedPercentReadsAsProgress(t *testing.T) { if got := watchedPercent(30_000, 60_000); got != "50%" { t.Errorf("half watched = %q", got) } // Emby's reported position can overshoot a runtime by a frame or two. if got := watchedPercent(61_000, 60_000); got != "100%" { t.Errorf("overshoot = %q", got) } if got := watchedPercent(30_000, 0); got != "unknown" { t.Errorf("unknown runtime = %q", got) } } func TestClientLogValueIsNeverBlank(t *testing.T) { if got := clientLogValue(""); got != "unknown" { t.Fatalf("blank identity logged as %q", got) } if got := clientLogValue("0.1.60"); got != "0.1.60" { t.Fatalf("reported identity changed to %q", got) } }