You've already forked agentic-coding-workflow
test(jobs/runners): cover CAS-lost, MarkSyncing-error, MarkFetchResult-error branches
This commit is contained in:
@@ -18,6 +18,13 @@ type fakeWorkspaceFetchRepo struct {
|
|||||||
listed []runners.WorkspaceFetchTarget
|
listed []runners.WorkspaceFetchTarget
|
||||||
startCAS map[uuid.UUID]bool
|
startCAS map[uuid.UUID]bool
|
||||||
finished map[uuid.UUID]string
|
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) {
|
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 {
|
if f.startCAS == nil {
|
||||||
f.startCAS = map[uuid.UUID]bool{}
|
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
|
f.startCAS[id] = true
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
@@ -41,12 +54,23 @@ func (f *fakeWorkspaceFetchRepo) MarkFetchResult(_ context.Context, id uuid.UUID
|
|||||||
} else {
|
} else {
|
||||||
f.finished[id] = errMsg
|
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 }
|
type recordingDispatcher struct{ msgs []notify.Message }
|
||||||
|
|
||||||
@@ -92,3 +116,56 @@ func TestWorkspaceFetch_NoTargetsNoOp(t *testing.T) {
|
|||||||
r.Run(context.Background())
|
r.Run(context.Background())
|
||||||
assert.Empty(t, repo.startCAS)
|
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")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user