package runners_test import ( "context" "errors" "testing" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/yan1h/agent-coding-workflow/internal/infra/notify" "github.com/yan1h/agent-coding-workflow/internal/jobs/runners" ) // fakeWorkspaceFetchRepo: inline mock holding a few workspaces + recorded calls. type fakeWorkspaceFetchRepo struct { listed []runners.WorkspaceFetchTarget startCAS map[uuid.UUID]bool finished map[uuid.UUID]string } func (f *fakeWorkspaceFetchRepo) ListFetchTargets(_ context.Context) ([]runners.WorkspaceFetchTarget, error) { return f.listed, nil } func (f *fakeWorkspaceFetchRepo) MarkSyncing(_ context.Context, id uuid.UUID) (bool, error) { if f.startCAS == nil { f.startCAS = map[uuid.UUID]bool{} } f.startCAS[id] = true return true, nil } func (f *fakeWorkspaceFetchRepo) MarkFetchResult(_ context.Context, id uuid.UUID, success bool, errMsg string) error { if f.finished == nil { f.finished = map[uuid.UUID]string{} } if success { f.finished[id] = "ok" } else { f.finished[id] = errMsg } return nil } type fakeFetcher struct{ err error } func (f *fakeFetcher) Fetch(_ context.Context, _ string, _ uuid.UUID) error { return f.err } type recordingDispatcher struct{ msgs []notify.Message } func (r *recordingDispatcher) Dispatch(_ context.Context, m notify.Message) error { r.msgs = append(r.msgs, m) return nil } func TestWorkspaceFetch_SuccessUpdatesIdle(t *testing.T) { t.Parallel() id := uuid.New() ownerID := uuid.New() repo := &fakeWorkspaceFetchRepo{listed: []runners.WorkspaceFetchTarget{ {ID: id, OwnerID: ownerID, Name: "ws", MainPath: "/x"}, }} r := runners.NewWorkspaceFetch(repo, &fakeFetcher{}, &recordingDispatcher{}, discardLogger()) r.Run(context.Background()) assert.True(t, repo.startCAS[id]) assert.Equal(t, "ok", repo.finished[id]) } func TestWorkspaceFetch_FailureMarksErrorAndDispatches(t *testing.T) { t.Parallel() id := uuid.New() ownerID := uuid.New() repo := &fakeWorkspaceFetchRepo{listed: []runners.WorkspaceFetchTarget{ {ID: id, OwnerID: ownerID, Name: "ws", MainPath: "/x"}, }} disp := &recordingDispatcher{} r := runners.NewWorkspaceFetch(repo, &fakeFetcher{err: errors.New("network")}, disp, discardLogger()) r.Run(context.Background()) assert.Contains(t, repo.finished[id], "network") require.Len(t, disp.msgs, 1) assert.Equal(t, "workspace.sync_failed", disp.msgs[0].Topic) assert.Equal(t, ownerID, disp.msgs[0].UserID) assert.Equal(t, notify.SeverityError, disp.msgs[0].Severity) } func TestWorkspaceFetch_NoTargetsNoOp(t *testing.T) { t.Parallel() repo := &fakeWorkspaceFetchRepo{} r := runners.NewWorkspaceFetch(repo, &fakeFetcher{}, &recordingDispatcher{}, discardLogger()) r.Run(context.Background()) assert.Empty(t, repo.startCAS) }