From 0111135e783f68f5fd3f5ea779ac3a63925b152a Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Wed, 6 May 2026 07:33:40 +0800 Subject: [PATCH] test(jobs/runners): cover CAS-lost, MarkSyncing-error, MarkFetchResult-error branches --- internal/jobs/runners/workspace_fetch_test.go | 83 ++++++++++++++++++- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/internal/jobs/runners/workspace_fetch_test.go b/internal/jobs/runners/workspace_fetch_test.go index 0093fd1..5b26416 100644 --- a/internal/jobs/runners/workspace_fetch_test.go +++ b/internal/jobs/runners/workspace_fetch_test.go @@ -18,6 +18,13 @@ type fakeWorkspaceFetchRepo struct { listed []runners.WorkspaceFetchTarget startCAS map[uuid.UUID]bool finished map[uuid.UUID]string + + // markSyncingErrFor: if an entry exists for ID, MarkSyncing returns (false, err). + markSyncingErrFor map[uuid.UUID]error + // markSyncingLost: if true (and no error matched), returns (false, nil) to simulate CAS lost. + markSyncingLost bool + // markFetchResultErr: if non-nil, MarkFetchResult returns it (after recording the call). + markFetchResultErr error } func (f *fakeWorkspaceFetchRepo) ListFetchTargets(_ context.Context) ([]runners.WorkspaceFetchTarget, error) { @@ -28,6 +35,12 @@ func (f *fakeWorkspaceFetchRepo) MarkSyncing(_ context.Context, id uuid.UUID) (b if f.startCAS == nil { f.startCAS = map[uuid.UUID]bool{} } + if err, ok := f.markSyncingErrFor[id]; ok { + return false, err + } + if f.markSyncingLost { + return false, nil + } f.startCAS[id] = true return true, nil } @@ -41,12 +54,23 @@ func (f *fakeWorkspaceFetchRepo) MarkFetchResult(_ context.Context, id uuid.UUID } else { f.finished[id] = errMsg } - return nil + return f.markFetchResultErr } -type fakeFetcher struct{ err error } +type fakeFetcher struct { + err error + calls int + fetched map[uuid.UUID]bool +} -func (f *fakeFetcher) Fetch(_ context.Context, _ string, _ uuid.UUID) error { return f.err } +func (f *fakeFetcher) Fetch(_ context.Context, _ string, id uuid.UUID) error { + f.calls++ + if f.fetched == nil { + f.fetched = map[uuid.UUID]bool{} + } + f.fetched[id] = true + return f.err +} type recordingDispatcher struct{ msgs []notify.Message } @@ -92,3 +116,56 @@ func TestWorkspaceFetch_NoTargetsNoOp(t *testing.T) { r.Run(context.Background()) assert.Empty(t, repo.startCAS) } + +func TestWorkspaceFetch_CASLostSkipsSilently(t *testing.T) { + t.Parallel() + id := uuid.New() + repo := &fakeWorkspaceFetchRepo{ + listed: []runners.WorkspaceFetchTarget{ + {ID: id, OwnerID: uuid.New(), Name: "ws", MainPath: "/x"}, + }, + markSyncingLost: true, + } + fetcher := &fakeFetcher{} + disp := &recordingDispatcher{} + r := runners.NewWorkspaceFetch(repo, fetcher, disp, discardLogger()) + r.Run(context.Background()) + assert.Zero(t, fetcher.calls, "Fetch must not be called when CAS lost") + assert.NotContains(t, repo.finished, id, "MarkFetchResult must not be called when CAS lost") + assert.Empty(t, disp.msgs, "no dispatch when CAS lost") +} + +func TestWorkspaceFetch_MarkSyncingErrorContinuesLoop(t *testing.T) { + t.Parallel() + bad := uuid.New() + good := uuid.New() + repo := &fakeWorkspaceFetchRepo{ + listed: []runners.WorkspaceFetchTarget{ + {ID: bad, OwnerID: uuid.New(), Name: "bad", MainPath: "/b"}, + {ID: good, OwnerID: uuid.New(), Name: "good", MainPath: "/g"}, + }, + markSyncingErrFor: map[uuid.UUID]error{bad: errors.New("db down")}, + } + fetcher := &fakeFetcher{} + r := runners.NewWorkspaceFetch(repo, fetcher, &recordingDispatcher{}, discardLogger()) + r.Run(context.Background()) + assert.False(t, fetcher.fetched[bad], "Fetch must not run when MarkSyncing errors") + assert.True(t, fetcher.fetched[good], "loop must continue to next target") + assert.Equal(t, "ok", repo.finished[good]) +} + +func TestWorkspaceFetch_MarkFetchResultErrorOnSuccessDoesNotDispatch(t *testing.T) { + t.Parallel() + id := uuid.New() + repo := &fakeWorkspaceFetchRepo{ + listed: []runners.WorkspaceFetchTarget{ + {ID: id, OwnerID: uuid.New(), Name: "ws", MainPath: "/x"}, + }, + markFetchResultErr: errors.New("db blip"), + } + fetcher := &fakeFetcher{} + disp := &recordingDispatcher{} + r := runners.NewWorkspaceFetch(repo, fetcher, disp, discardLogger()) + r.Run(context.Background()) + assert.Empty(t, disp.msgs, "success path must not dispatch even if MarkFetchResult errors") +}