From a101f6699ccfe8bf40de8bd2ceb58423f5414652 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 7 May 2026 00:37:28 +0300 Subject: Fix scan cancellation progress error for q0 --- internal/service/scan.go | 6 ++++- internal/service/scan_test.go | 55 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/internal/service/scan.go b/internal/service/scan.go index 52bc5c5..2a56f93 100644 --- a/internal/service/scan.go +++ b/internal/service/scan.go @@ -58,7 +58,11 @@ func (s *scanService) TriggerRescan(ctx context.Context) error { go func() { defer cancel() - if err := s.scanner.Scan(scanCtx, s.mediaRoot, progress); err != nil { + err := s.scanner.Scan(scanCtx, s.mediaRoot, progress) + if err == nil { + err = scanCtx.Err() + } + if err != nil { progress.Done(err) s.logger.Error("rescan failed", "err", err) } else { diff --git a/internal/service/scan_test.go b/internal/service/scan_test.go index aba647f..fcd7e0a 100644 --- a/internal/service/scan_test.go +++ b/internal/service/scan_test.go @@ -14,17 +14,17 @@ func TestScanService_ScanLibrary(t *testing.T) { ctx := context.Background() tests := []struct { - name string - scanErr error - wantErr bool + name string + scanErr error + wantErr bool }{ { name: "ok", }, { - name: "scan error", - scanErr: errors.New("boom"), - wantErr: false, // TriggerRescan returns nil immediately; background goroutine logs error + name: "scan error", + scanErr: errors.New("boom"), + wantErr: false, // TriggerRescan returns nil immediately; background goroutine logs error }, } @@ -62,18 +62,18 @@ func TestScanService_CancelledByAppContext(t *testing.T) { appCtx, appCancel := context.WithCancel(context.Background()) defer appCancel() - done := make(chan struct{}) + done := make(chan struct{}, 1) started := make(chan struct{}) sc := &fakeScanner{ scanFunc: func(scanCtx context.Context, _ string, progress *model.ScanProgress) error { close(started) <-scanCtx.Done() - close(done) return scanCtx.Err() }, } svc := NewScanService(appCtx, sc, "/media", clock.RealClock{}, nil) + svc.doneCh = done if err := svc.TriggerRescan(context.Background()); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -95,3 +95,42 @@ func TestScanService_CancelledByAppContext(t *testing.T) { t.Fatal("expected a last error after cancellation") } } + +func TestScanService_CancelledContextRecordedWhenScannerReturnsNil(t *testing.T) { + appCtx, appCancel := context.WithCancel(context.Background()) + defer appCancel() + + done := make(chan struct{}, 1) + started := make(chan struct{}) + sc := &fakeScanner{ + scanFunc: func(scanCtx context.Context, _ string, progress *model.ScanProgress) error { + progress.Start(1) + close(started) + <-scanCtx.Done() + return nil + }, + } + + svc := NewScanService(appCtx, sc, "/media", clock.RealClock{}, nil) + svc.doneCh = done + if err := svc.TriggerRescan(context.Background()); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + <-started + appCancel() + + select { + case <-done: + case <-time.After(5 * time.Second): + t.Fatal("timeout waiting for scan goroutine to exit after app context cancellation") + } + + p := svc.ScanProgress(context.Background()) + if p.Running { + t.Fatal("expected scan to be stopped after app context cancellation") + } + if p.LastError != context.Canceled.Error() { + t.Fatalf("expected last error %q, got %q", context.Canceled.Error(), p.LastError) + } +} -- cgit v1.2.3