Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions backend/helpers/pluginhelper/api/subtask_state_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,6 @@ func bootstrapStateFromCollectorStateIfNeeded(db dal.Dal, preState *models.Subta
return nil, errors.Default.Wrap(err, "failed to load collector state for subtask bootstrap")
}

if collectorState.LatestSuccessStart != nil {
preState.PrevStartedAt = collectorState.LatestSuccessStart
}
if preState.TimeAfter == nil && collectorState.TimeAfter != nil {
preState.TimeAfter = collectorState.TimeAfter
}
Expand Down
50 changes: 46 additions & 4 deletions backend/helpers/pluginhelper/api/subtask_state_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func TestBootstrapStateFromCollectorStateIfNeeded(t *testing.T) {
latest := errors.Must1(time.Parse(time.RFC3339, "2026-04-27T18:05:31Z"))
timeAfter := errors.Must1(time.Parse(time.RFC3339, "2024-12-20T00:00:00Z"))

t.Run("bootstrap prev_started_at and time_after from collector state", func(t *testing.T) {
t.Run("bootstrap time_after but not prev_started_at from collector state", func(t *testing.T) {
mockDal := new(mockdal.Dal)
mockDal.On("HasTable", mock.Anything).Return(true).Once()
mockDal.On("First", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
Expand All @@ -212,9 +212,7 @@ func TestBootstrapStateFromCollectorStateIfNeeded(t *testing.T) {

bootstrapped, err := bootstrapStateFromCollectorStateIfNeeded(mockDal, state, args)
assert.Nil(t, err)
if assert.NotNil(t, bootstrapped.PrevStartedAt) {
assert.True(t, bootstrapped.PrevStartedAt.Equal(latest))
}
assert.Nil(t, bootstrapped.PrevStartedAt)
if assert.NotNil(t, bootstrapped.TimeAfter) {
assert.True(t, bootstrapped.TimeAfter.Equal(timeAfter))
}
Expand Down Expand Up @@ -346,6 +344,50 @@ func TestBootstrapStateFromCollectorStateIfNeeded(t *testing.T) {
mockDal.AssertNotCalled(t, "First", mock.Anything, mock.Anything)
mockDal.AssertExpectations(t)
})

t.Run("first-ever converter run stays full-sync even when collector already succeeded", func(t *testing.T) {
mockDal := new(mockdal.Dal)
mockDal.On("HasTable", mock.Anything).Return(true).Once()
mockDal.On("First", mock.MatchedBy(func(v interface{}) bool {
_, ok := v.(*models.SubtaskState)
return ok
}), mock.Anything).Run(func(args mock.Arguments) {
dst := args.Get(0).(*models.SubtaskState)
*dst = models.SubtaskState{
Plugin: "github",
Subtask: "Convert Workflow Runs",
Params: testGithubScopeParams,
}
}).Return(nil).Once()
mockDal.On("First", mock.MatchedBy(func(v interface{}) bool {
_, ok := v.(*models.CollectorLatestState)
return ok
}), mock.Anything).Run(func(args mock.Arguments) {
dst := args.Get(0).(*models.CollectorLatestState)
now := time.Now()
dst.LatestSuccessStart = &now
}).Return(nil).Once()
mockDal.On("CreateOrUpdate", mock.Anything, mock.Anything).Return(nil).Once()

mockTaskCtx := new(mockplugin.TaskContext)
mockTaskCtx.On("SyncPolicy").Return(&models.SyncPolicy{})
mockTaskCtx.On("GetName").Return("github")
mockSubtaskCtx := new(mockplugin.SubTaskContext)
mockSubtaskCtx.On("TaskContext").Return(mockTaskCtx)
mockSubtaskCtx.On("GetName").Return("Convert Workflow Runs")
mockSubtaskCtx.On("GetDal").Return(mockDal)

stateManager, err := NewSubtaskStateManager(&SubtaskCommonArgs{
SubTaskContext: mockSubtaskCtx,
Table: "github_api_runs",
Params: testGithubScopeParams,
})
assert.Nil(t, err)
assert.False(t, stateManager.IsIncremental(), "first-ever run must be full sync, not incremental")
assert.Nil(t, stateManager.Close())
assert.NotNil(t, stateManager.state.PrevStartedAt)
mockDal.AssertExpectations(t)
})
}

func TestIsStateTableNotReadyError(t *testing.T) {
Expand Down
Loading