diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-07 00:37:28 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-07 00:37:28 +0300 |
| commit | a101f6699ccfe8bf40de8bd2ceb58423f5414652 (patch) | |
| tree | 2a123133f04c86fa7e74e1a283e354b9dda3c753 /internal | |
| parent | 1898db985f708f0ff54d04d2d77303c633bc1ba2 (diff) | |
Fix scan cancellation progress error for q0
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/service/scan.go | 6 | ||||
| -rw-r--r-- | 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) + } +} |
