release / govulncheck (push) Successful in 18s
release / web (push) Successful in 1m30s
release / go (push) Successful in 1m46s
release / integration (push) Successful in 5m23s
release / android (push) Successful in 5m43s
release / Build signed APK (releases and dev) (push) Successful in 6m2s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 25s
release / Verify release artifacts (tag releases only) (push) Skipped
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
85 lines
3.1 KiB
Go
85 lines
3.1 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
// seedMissingWant marks a track's file missing for two days and puts its
|
|
// album on the wanted list, held at heldDaysAgo (0 for an open want) and
|
|
// last searched with nothing found.
|
|
func seedMissingWant(t *testing.T, h *handlers, trackID, albumID pgtype.UUID, group string, heldDaysAgo int) string {
|
|
t.Helper()
|
|
ctx := context.Background()
|
|
if _, err := h.pool.Exec(ctx,
|
|
`UPDATE tracks SET missing_since = now() - interval '2 days' WHERE id = $1`, trackID); err != nil {
|
|
t.Fatalf("mark missing: %v", err)
|
|
}
|
|
if _, err := h.pool.Exec(ctx, `DELETE FROM wants WHERE release_group_mbid = $1`, group); err != nil {
|
|
t.Fatalf("clear want: %v", err)
|
|
}
|
|
var id pgtype.UUID
|
|
if err := h.pool.QueryRow(ctx,
|
|
`INSERT INTO wants (release_group_mbid, artist_mbid, artist_name, title, reason, album_id,
|
|
wanted_at, fulfilled_at, searched_at, search_outcome)
|
|
VALUES ($1, 'artist-'||$1, 'Artist', 'Album '||$1, 'library', $2,
|
|
now() - interval '30 days',
|
|
CASE WHEN $3::int > 0 THEN now() - make_interval(days => $3::int) END,
|
|
now() - interval '1 hour', 'nothing')
|
|
RETURNING id`, group, albumID, heldDaysAgo).Scan(&id); err != nil {
|
|
t.Fatalf("insert want: %v", err)
|
|
}
|
|
return uuidToString(id)
|
|
}
|
|
|
|
// A folder's album that was held before its files went is not wanted back
|
|
// yet (every library album is on the list, held, since #5567); an open want
|
|
// comes with its search, so the Missing page can show it and act on it (M504).
|
|
func TestListMissingTracks_WantState(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
truncateLibrary(t, pool)
|
|
admin := seedUser(t, pool, "admin", "pw", true)
|
|
|
|
heldTrack, _ := seedTrackOnDisk(t, h, "missheld")
|
|
openTrack, _ := seedTrackOnDisk(t, h, "missopen")
|
|
seedMissingWant(t, h, heldTrack.ID, heldTrack.AlbumID, "rg-missing-held", 10)
|
|
openID := seedMissingWant(t, h, openTrack.ID, openTrack.AlbumID, "rg-missing-open", 0)
|
|
|
|
req := withUser(httptest.NewRequest(http.MethodGet, "/api/admin/library/missing", nil), admin)
|
|
w := httptest.NewRecorder()
|
|
h.handleListMissingTracks(w, req)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("status %d: %s", w.Code, w.Body.String())
|
|
}
|
|
var resp adminMissingResponse
|
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
|
t.Fatalf("decode: %v", err)
|
|
}
|
|
if len(resp.Groups) != 2 {
|
|
t.Fatalf("groups = %d, want 2", len(resp.Groups))
|
|
}
|
|
byTitle := map[string]missingGroupView{}
|
|
for _, g := range resp.Groups {
|
|
byTitle[g.Tracks[0].Title] = g
|
|
}
|
|
|
|
if r := byTitle["AdmQ Track missheld"].Reacquisition; r != nil {
|
|
t.Errorf("held before the loss: reacquisition = %+v, want none", r)
|
|
}
|
|
r := byTitle["AdmQ Track missopen"].Reacquisition
|
|
if r == nil || r.WantedAt == nil || r.HeldAt != nil {
|
|
t.Fatalf("open want: reacquisition = %+v, want wanted and not held", r)
|
|
}
|
|
if r.Want == nil || r.Want.ID != openID {
|
|
t.Fatalf("open want: want = %+v, want id %s", r.Want, openID)
|
|
}
|
|
if r.Want.Search == nil || r.Want.Search.Outcome != "nothing" {
|
|
t.Errorf("open want: search = %+v, want outcome nothing", r.Want.Search)
|
|
}
|
|
}
|