diff --git a/core/artwork/explain.go b/core/artwork/explain.go index c6bba8553..9001c5cb6 100644 --- a/core/artwork/explain.go +++ b/core/artwork/explain.go @@ -131,6 +131,9 @@ func Explain(ctx context.Context, ds model.DataStore, ag *agents.Agents, kind mo return ExplainReport{}, fmt.Errorf("reading the artwork queue: %w", err) } } + // Set even for a kind the chain never walks, so a caller that asked to walk (e.g. --live on a + // playlist) is reported as having tried, matching the pre-refactor CLI's unconditional flag. + rep.Walked = opts.Walk != nil if !Explainable(kind) { return rep, nil } @@ -138,7 +141,6 @@ func Explain(ctx context.Context, ds model.DataStore, ag *agents.Agents, kind mo rep.Agents = FormatAgents(conf.Server.Agents, ImageAgentNames(ag, kind), opts.UnavailableNote) } - rep.Walked = opts.Walk != nil switch { case rep.Walked: trace := &ChainTrace{} @@ -161,11 +163,19 @@ func (r ExplainReport) ChainOrigin() string { return "walked now" } if r.Stored != nil { - return "recorded " + r.Stored.AttemptedAt.Format(time.RFC3339) + return "recorded " + formatAttemptedAt(r.Stored.AttemptedAt) } return "not recorded" } +// formatAttemptedAt matches the CLI's formatTime: a zero time reads as unset, not as year 1. +func formatAttemptedAt(t time.Time) string { + if t.IsZero() { + return "-" + } + return t.Format(time.RFC3339) +} + // LastAttemptFailed decodes why the queued row's last attempt failed, if there is one queued. func (r ExplainReport) LastAttemptFailed() []TraceStep { if r.Queued == nil { diff --git a/core/artwork/explain_test.go b/core/artwork/explain_test.go index 8f9abf224..676f626d0 100644 --- a/core/artwork/explain_test.go +++ b/core/artwork/explain_test.go @@ -133,6 +133,11 @@ var _ = Describe("Explain", func() { Expect(rep.ChainOrigin()).To(ContainSubstring("recorded")) }) + It("renders a zero attempted-at the same way the CLI's formatTime does", func() { + rep := artwork.ExplainReport{Stored: &model.ItemArtwork{Source: "folder"}} + Expect(rep.ChainOrigin()).To(Equal("recorded -")) + }) + It("reports nothing recorded when there is no stored state", func() { rep, err := artwork.Explain(ctx, ds, nil, model.KindArtistArtwork, "ar-1", artwork.ExplainOptions{}) Expect(err).ToNot(HaveOccurred()) @@ -166,4 +171,28 @@ var _ = Describe("Explain", func() { Expect(rep.Stored).To(BeNil()) Expect(rep.Steps).To(BeEmpty()) }) + + It("performs a fresh walk and reports its outcome, including a failed one, when a walker is supplied", func() { + // GetAll erroring mid-walk is a deterministic way to force ResolveErr without a real library. + ds.Album(ctx).(*tests.MockAlbumRepo).SetError(true) + opts := artwork.ExplainOptions{Walk: func(t *artwork.ChainTrace) *artwork.TracingResolver { + return artwork.NewTracingResolver(ds, nil, nil, t, false) + }} + + rep, err := artwork.Explain(ctx, ds, nil, model.KindArtistArtwork, "ar-1", opts) + Expect(err).ToNot(HaveOccurred()) + Expect(rep.Walked).To(BeTrue()) + Expect(rep.ResolveErr).To(HaveOccurred()) + }) + + It("still reports Walked for a kind with no chain to walk, when a walker is supplied", func() { + Expect(ds.Playlist(ctx).Put(&model.Playlist{ID: "pl-1", Name: "Favorites"})).To(Succeed()) + opts := artwork.ExplainOptions{Walk: func(*artwork.ChainTrace) *artwork.TracingResolver { + panic("a kind that cannot walk must never be handed to the walker") + }} + + rep, err := artwork.Explain(ctx, ds, nil, model.KindPlaylistArtwork, "pl-1", opts) + Expect(err).ToNot(HaveOccurred()) + Expect(rep.Walked).To(BeTrue()) + }) })