From 30c2b0fe8232cc748ab2bded6ab4d76febe32425 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 3 May 2026 19:17:04 +0300 Subject: fix(admin): fix rescan goroutine lifecycle and race on shared ScanProgress - Protect adminService scan state (cancel func + progress pointer) with sync.Mutex. - Allocate fresh ScanProgress per trigger and pass it to the scanner, eliminating races on the previously shared progress struct. - Cancel previous scan context before starting a new one. - Add tests for cancellation, fresh progress per scan, concurrent triggers, and empty progress when never started. - Fix race-prone tests by polling Running==true before waiting for completion. --- internal/repository/sqlite_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'internal/repository/sqlite_test.go') diff --git a/internal/repository/sqlite_test.go b/internal/repository/sqlite_test.go index f2382f5..fa15a80 100644 --- a/internal/repository/sqlite_test.go +++ b/internal/repository/sqlite_test.go @@ -919,6 +919,23 @@ func TestSQLite_MediaFilters(t *testing.T) { } }, }, + { + name: "max duration filter", + run: func(t *testing.T, ctx context.Context, s *SQLite) { + now := time.Now().Truncate(time.Second) + sid, _ := s.CreateSet(ctx, &model.Set{Name: "s", RootPath: "/s", CreatedAt: now}) + _, _ = s.CreateMedia(ctx, &model.Media{SetID: sid, RelPath: "a.mp4", FileName: "a.mp4", AbsPath: "/s/a.mp4", Type: model.MediaTypeVideo, Duration: 100, CreatedAt: now}) + _, _ = s.CreateMedia(ctx, &model.Media{SetID: sid, RelPath: "b.mp4", FileName: "b.mp4", AbsPath: "/s/b.mp4", Type: model.MediaTypeVideo, Duration: 200, CreatedAt: now}) + maxDur := 150.0 + res, err := s.ListMedia(ctx, MediaFilter{MaxDuration: &maxDur, Sort: "duration"}) + if err != nil { + t.Fatalf("list: %v", err) + } + if len(res) != 1 || res[0].FileName != "a.mp4" { + t.Fatalf("unexpected result: %+v", res) + } + }, + }, { name: "limit offset", run: func(t *testing.T, ctx context.Context, s *SQLite) { -- cgit v1.2.3