test(nativeapi): pin the artwork explain wire format

Extend the admin report spec to assert the full stored object and step
fields, and add a spec covering the queued object and lastAttemptFailed,
so a wrong JSON tag in toExplainDTO fails a test instead of shipping.
This commit is contained in:
Deluan 2026-09-03 23:25:01 -04:00
commit 395f2f9ae5

View file

@ -73,10 +73,11 @@ var _ = Describe("GET /artwork/explain", func() {
It("returns the report for an admin", func() {
// Storage shape from core/artwork/trace.go's storedStep: single-letter keys, "d" optional.
trace := `[{"c":"external:deezer","o":"hit","d":"https://cdn/x.jpg"}]`
attemptedAt := time.Date(2026, 9, 1, 10, 0, 0, 0, time.UTC)
Expect(artRepo.PutItemArtwork(&model.ItemArtwork{
ItemKind: model.KindArtistArtwork.Prefix(), ItemID: "ar-1", ImageType: model.ImageTypePrimary,
Hash: "abc", Source: "external:deezer", Trace: trace,
AttemptedAt: time.Date(2026, 9, 1, 10, 0, 0, 0, time.UTC),
Hash: "abc", Source: "external:deezer", SourcePath: "/music/Radiohead/folder.jpg", Trace: trace,
AttemptedAt: attemptedAt,
})).To(Succeed())
req := createAuthenticatedRequest("GET", "/artwork/explain?kind=ar&id=ar-1", nil, adminToken)
@ -89,8 +90,47 @@ var _ = Describe("GET /artwork/explain", func() {
Expect(got["name"]).To(Equal("Radiohead"))
Expect(got["result"]).To(Equal("resolved from external:deezer"))
Expect(got["chainOrigin"]).To(ContainSubstring("recorded"))
Expect(got["steps"]).To(HaveLen(1))
Expect(got["config"]).To(HaveKeyWithValue("setting", "ArtistArtPriority"))
Expect(got["stored"]).To(Equal(map[string]any{
"source": "external:deezer",
"hash": "abc",
"sourcePath": "/music/Radiohead/folder.jpg",
"attemptedAt": attemptedAt.Format(time.RFC3339),
}))
Expect(got["steps"]).To(Equal([]any{
map[string]any{"candidate": "external:deezer", "outcome": "hit", "detail": "https://cdn/x.jpg"},
}))
})
It("returns the queue state and the last-attempt trace", func() {
Expect(queueRepo.Enqueue(model.ArtworkQueueItem{
ItemKind: model.KindArtistArtwork.Prefix(), ItemID: "ar-1", ImageType: model.ImageTypePrimary,
Priority: model.ArtworkPriorityScan,
})).To(Succeed())
seen, err := queueRepo.Get(model.KindArtistArtwork, "ar-1", model.ImageTypePrimary)
Expect(err).ToNot(HaveOccurred())
retryAt := time.Date(2026, 9, 2, 8, 0, 0, 0, time.UTC)
failTrace := `[{"c":"external:deezer","o":"error","d":"timeout"}]`
Expect(queueRepo.MarkFailedIfUnchanged(model.KindArtistArtwork.Prefix(), "ar-1", model.ImageTypePrimary,
seen.RetryAt, retryAt, failTrace)).To(Succeed())
req := createAuthenticatedRequest("GET", "/artwork/explain?kind=ar&id=ar-1", nil, adminToken)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
Expect(w.Code).To(Equal(http.StatusOK))
var got map[string]any
Expect(json.Unmarshal(w.Body.Bytes(), &got)).To(Succeed())
Expect(got["queued"]).To(Equal(map[string]any{
"priority": float64(model.ArtworkPriorityScan),
"attempts": float64(1),
"retryAt": retryAt.Format(time.RFC3339),
}))
Expect(got["lastAttemptFailed"]).To(Equal([]any{
map[string]any{"candidate": "external:deezer", "outcome": "error", "detail": "timeout"},
}))
})
It("omits empty sections", func() {