Rewrites the internal/api comment blocks to rule 32: task, milestone and rule ids gone, the stories behind them restated as the constraint they left, stale references corrected (the stream secret is wired from config, the timezone handler already refreshes the scheduler, the Android app is the native client, the design docs cited are not in the repo). Also rewraps comment lines over 80 columns left by the earlier batches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
3.0 KiB
Go
90 lines
3.0 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/buildinfo"
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/logging"
|
|
)
|
|
|
|
// openPlay records a play by user of track that started ago and has not
|
|
// ended.
|
|
func openPlay(t *testing.T, h *handlers, user, track pgtype.UUID, ago string) {
|
|
t.Helper()
|
|
ctx := context.Background()
|
|
var session pgtype.UUID
|
|
if err := h.pool.QueryRow(ctx,
|
|
`INSERT INTO play_sessions (user_id, started_at, last_event_at) VALUES ($1, now(), now()) RETURNING id`,
|
|
user).Scan(&session); err != nil {
|
|
t.Fatalf("session: %v", err)
|
|
}
|
|
if _, err := h.pool.Exec(ctx,
|
|
`INSERT INTO play_events (user_id, track_id, session_id, started_at, client_id)
|
|
VALUES ($1, $2, $3, now() - $4::interval, 'web')`, user, track, session, ago); err != nil {
|
|
t.Fatalf("play: %v", err)
|
|
}
|
|
}
|
|
|
|
// The Overview's status: a play still inside its track's length is
|
|
// listening now; one long past it, never closed by its client, is not.
|
|
func TestAdminStatus_ListeningAndServer(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
truncateLibrary(t, pool)
|
|
admin := seedUser(t, pool, "admin", "pw", true)
|
|
|
|
fresh, _ := seedTrackOnDisk(t, h, "statusfresh") // 30 s long
|
|
stale, _ := seedTrackOnDisk(t, h, "statusstale")
|
|
openPlay(t, h, admin.ID, fresh.ID, "10 seconds")
|
|
openPlay(t, h, admin.ID, stale.ID, "1 hour")
|
|
|
|
req := withUser(httptest.NewRequest(http.MethodGet, "/api/admin/status", nil), admin)
|
|
w := httptest.NewRecorder()
|
|
h.handleGetAdminStatus(w, req)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("status %d: %s", w.Code, w.Body.String())
|
|
}
|
|
var got adminStatusView
|
|
if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
|
|
t.Fatalf("decode: %v", err)
|
|
}
|
|
|
|
var sawFresh, sawStale bool
|
|
for _, p := range got.Listening.Now {
|
|
switch p.TrackID {
|
|
case uuidToString(fresh.ID):
|
|
sawFresh = true
|
|
if p.Username != admin.Username || p.Client != "web" || p.Title != "AdmQ Track statusfresh" {
|
|
t.Errorf("listening row = %+v", p)
|
|
}
|
|
case uuidToString(stale.ID):
|
|
sawStale = true
|
|
}
|
|
}
|
|
if !sawFresh || sawStale {
|
|
t.Errorf("listening now: fresh %v (want true), stale %v (want false)", sawFresh, sawStale)
|
|
}
|
|
if got.Listening.PlaysDay < 2 || got.Listening.LastPlayAt == nil {
|
|
t.Errorf("last day: plays %d, last %v", got.Listening.PlaysDay, got.Listening.LastPlayAt)
|
|
}
|
|
|
|
if got.Library.Tracks != 2 || got.Library.Albums != 2 || got.Library.Bytes != 10 {
|
|
t.Errorf("library = %+v, want 2 tracks, 2 albums, 10 bytes", got.Library)
|
|
}
|
|
if got.Downloads.Live == nil || got.Downloads.ClientsDown == nil || got.Library.Disks == nil {
|
|
t.Error("lists must be [], never null")
|
|
}
|
|
|
|
if got.Server.Version != buildinfo.Version || got.Server.StartedAt == "" || got.Server.Migration < 92 {
|
|
t.Errorf("server = %+v", got.Server)
|
|
}
|
|
if _, total := logging.Errors.Snapshot(); got.Server.ErrorCount != total {
|
|
t.Errorf("error count = %d, ring says %d", got.Server.ErrorCount, total)
|
|
}
|
|
}
|