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>
92 lines
3.0 KiB
Go
92 lines
3.0 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
|
|
)
|
|
|
|
func getAdminSummary(t *testing.T, h *handlers, user dbq.User) (int, adminSummaryView) {
|
|
t.Helper()
|
|
r := chi.NewRouter()
|
|
r.Route("/api/admin", func(admin chi.Router) {
|
|
admin.Use(auth.RequireAdmin())
|
|
admin.Get("/summary", h.handleGetAdminSummary)
|
|
})
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, withUser(httptest.NewRequest(http.MethodGet, "/api/admin/summary", nil), user))
|
|
var out adminSummaryView
|
|
if w.Code == http.StatusOK {
|
|
if err := json.Unmarshal(w.Body.Bytes(), &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
return w.Code, out
|
|
}
|
|
|
|
// The summary counts each queue an admin acts on and says since when the
|
|
// oldest item has waited; an empty queue has no since.
|
|
func TestAdminSummary_CountsTheQueuesAndSinceWhen(t *testing.T) {
|
|
h, _ := testHandlers(t)
|
|
admin := seedUser(t, h.pool, "admin", "pw", true)
|
|
listener := seedUser(t, h.pool, "listener", "pw", false)
|
|
ctx := context.Background()
|
|
|
|
code, empty := getAdminSummary(t, h, admin)
|
|
if code != http.StatusOK {
|
|
t.Fatalf("status = %d", code)
|
|
}
|
|
if empty.Requests.Count != 0 || empty.Requests.Since != nil || empty.Missing.Count != 0 {
|
|
t.Errorf("empty summary = %+v", empty)
|
|
}
|
|
|
|
if _, err := dbq.New(h.pool).CreateRequest(ctx, dbq.CreateRequestParams{
|
|
UserID: listener.ID, Kind: dbq.RequestKindArtist, ArtistMbid: "a0000000-0000-4000-8000-000000000001",
|
|
ArtistName: "Annadale",
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, got := getAdminSummary(t, h, admin)
|
|
if got.Requests.Count != 1 || got.Requests.Since == nil {
|
|
t.Errorf("requests = %+v, want 1 with a since", got.Requests)
|
|
}
|
|
|
|
if code, _ := getAdminSummary(t, h, listener); code != http.StatusForbidden {
|
|
t.Errorf("a listener got %d, want 403", code)
|
|
}
|
|
}
|
|
|
|
// Duplicates count what needs review: a cross-release group is kept and
|
|
// linked by the resolver and waits on nobody.
|
|
func TestAdminSummary_DuplicatesCountOnlyWhatNeedsReview(t *testing.T) {
|
|
h, _ := testHandlers(t)
|
|
truncateLibrary(t, h.pool)
|
|
admin := seedUser(t, h.pool, "dupsum-admin", "pw", true)
|
|
ctx := context.Background()
|
|
a := seedQuarantineTrack(t, h, "dupsum-a")
|
|
b := seedQuarantineTrack(t, h, "dupsum-b")
|
|
for _, g := range []struct{ key, class string }{{"dupsum-review", "review"}, {"dupsum-cross", "cross_release"}} {
|
|
var id [16]byte
|
|
if err := h.pool.QueryRow(ctx,
|
|
`INSERT INTO duplicate_groups (member_key, tier, class) VALUES ($1, 'acoustic', $2) RETURNING id`,
|
|
g.key, g.class).Scan(&id); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := h.pool.Exec(ctx, `INSERT INTO duplicate_group_members (group_id, track_id) VALUES ($1, $2), ($1, $3)`,
|
|
id, a.ID, b.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
_, got := getAdminSummary(t, h, admin)
|
|
if got.Duplicates.Count != 1 {
|
|
t.Errorf("duplicates = %d, want 1: the cross-release group needs nobody", got.Duplicates.Count)
|
|
}
|
|
}
|