fix(test): stop the Windows test job from failing at random (#6182)

* fix(persistence): don't format a nil-model row in wrapCursor

* test(plugins): assert task queue delay against the first dispatch, not consecutive gaps

* test(artwork): let the e2e worker wait outlast one retry

* chore: trim comments

* fix(persistence): guard dbFolder and dbMediaFile String() against a nil model
This commit is contained in:
Deluan Quintão 2026-09-20 15:27:20 -04:00 • committed by GitHub
commit 0429fb3d40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 39 additions and 6 deletions

View file

@ -56,7 +56,8 @@ func runWorkerUntil(ctx context.Context, worker *artwork.Worker, until func() bo
runCtx, cancel := context.WithCancel(ctx)
done := make(chan error, 1)
go func() { done <- worker.Run(runCtx) }()
Eventually(until, 5*time.Second, 10*time.Millisecond).Should(BeTrue())
// Long enough for one retry (3-7s backoff, 5s poll tick).
Eventually(until, 15*time.Second, 10*time.Millisecond).Should(BeTrue())
cancel()
Eventually(done, 2*time.Second).Should(Receive(BeNil()))
}

View file

@ -27,6 +27,14 @@ type dbFolder struct {
ImageFiles string `structs:"-" json:"-"`
}
// String guards the promoted Folder.String(), which would dereference a nil Folder.
func (f dbFolder) String() string {
if f.Folder == nil {
return "<nil>"
}
return f.Folder.String()
}
func (f *dbFolder) PostScan() error {
var err error
if f.ImageFiles != "" {

View file

@ -310,6 +310,13 @@ var _ = Describe("FolderRepository", func() {
})
})
Describe("dbFolder.String", func() {
It("does not dereference a nil Folder", func() {
Expect(fmt.Sprint(dbFolder{})).To(Equal("<nil>"))
Expect(fmt.Sprint(&dbFolder{})).To(Equal("<nil>"))
})
})
Describe("wrapFolderCursor", func() {
It("does not panic when the cursor yields a dbFolder with nil Folder", func() {
// Simulate what queryWithStableResults does on the rows.Err() path:

View file

@ -38,6 +38,14 @@ type dbMediaFile struct {
RgTrackPeak *float64 `structs:"-" json:"-"`
}
// String guards the promoted MediaFile.String(), which would dereference a nil MediaFile.
func (m dbMediaFile) String() string {
if m.MediaFile == nil {
return "<nil>"
}
return m.MediaFile.String()
}
func (m *dbMediaFile) PostScan() error {
m.RGTrackGain = m.RgTrackGain
m.RGTrackPeak = m.RgTrackPeak

View file

@ -1203,6 +1203,13 @@ var _ = Describe("MediaRepository", func() {
})
})
Describe("dbMediaFile.String", func() {
It("does not dereference a nil MediaFile", func() {
Expect(fmt.Sprint(dbMediaFile{})).To(Equal("<nil>"))
Expect(fmt.Sprint(&dbMediaFile{})).To(Equal("<nil>"))
})
})
Describe("wrapMediaFileCursor", func() {
It("does not panic when the cursor yields a dbMediaFile with nil MediaFile", func() {
// Simulate what queryWithStableResults does on the rows.Err() path:

View file

@ -378,8 +378,9 @@ func wrapCursor[D, T any](cursor iter.Seq2[D, error], toModel func(D) *T) iter.S
for row, err := range cursor {
m := toModel(row)
if m == nil {
// Don't format row: its String() derefs the nil model (golang/go#81238).
var zero T
yield(zero, fmt.Errorf("unexpected nil %T (%v): %w", zero, row, err))
yield(zero, fmt.Errorf("unexpected nil %T: %w", zero, err))
return
}
if !yield(*m, err) || err != nil {

View file

@ -689,11 +689,12 @@ var _ = Describe("TaskQueueService", func() {
copy(times, dispatchTimes)
mu.Unlock()
// Consecutive dispatches should have at least ~160ms gap (80% of 200ms)
// Wake-up latency varies per worker, so check offsets from the first dispatch, not gaps.
for i := 1; i < len(times); i++ {
gap := times[i].Sub(times[i-1])
Expect(gap).To(BeNumerically(">=", 160*time.Millisecond),
fmt.Sprintf("gap between dispatch %d and %d was %v, expected >= 160ms", i-1, i, gap))
offset := times[i].Sub(times[0])
minOffset := time.Duration(i)*200*time.Millisecond - 50*time.Millisecond
Expect(offset).To(BeNumerically(">=", minOffset),
fmt.Sprintf("dispatch %d ran %v after the first, expected >= %v", i, offset, minOffset))
}
})
})