summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-07 00:37:28 +0300
committerPaul Buetow <paul@buetow.org>2026-05-07 00:37:28 +0300
commita101f6699ccfe8bf40de8bd2ceb58423f5414652 (patch)
tree2a123133f04c86fa7e74e1a283e354b9dda3c753
parent1898db985f708f0ff54d04d2d77303c633bc1ba2 (diff)
Fix scan cancellation progress error for q0
-rw-r--r--internal/service/scan.go6
-rw-r--r--internal/service/scan_test.go55
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)
+ }
+}