| package recent_rolls |
| |
| import ( |
| "context" |
| "testing" |
| "time" |
| |
| "github.com/google/uuid" |
| "github.com/stretchr/testify/require" |
| "go.skia.org/infra/go/autoroll" |
| "go.skia.org/infra/go/deepequal/assertdeep" |
| "go.skia.org/infra/go/ds" |
| "go.skia.org/infra/go/ds/testutil" |
| ) |
| |
| // TestRecentRolls verifies that we correctly track roll history. |
| func TestRecentRolls(t *testing.T) { |
| ctx := context.Background() |
| testutil.InitDatastore(t, ds.KIND_AUTOROLL_ROLL) |
| |
| // Create the RecentRolls. |
| r, err := NewRecentRolls(ctx, NewDatastoreRollsDB(ctx), "test-roller") |
| require.NoError(t, err) |
| |
| // Use this function for checking expectations. |
| check := func(current, last *autoroll.AutoRollIssue, history []*autoroll.AutoRollIssue) { |
| assertdeep.Equal(t, history, r.GetRecentRolls()) |
| assertdeep.Equal(t, current, r.CurrentRoll()) |
| assertdeep.Equal(t, last, r.LastRoll()) |
| } |
| |
| // Add one issue. |
| now := time.Now().UTC() |
| ari1 := &autoroll.AutoRollIssue{ |
| Closed: false, |
| Committed: false, |
| Created: now, |
| IsDryRun: false, |
| Issue: 1010101, |
| Modified: now, |
| Patchsets: []int64{1}, |
| Result: autoroll.ROLL_RESULT_IN_PROGRESS, |
| Subject: "FAKE DEPS ROLL 1", |
| TryResults: []*autoroll.TryResult(nil), |
| } |
| expect := []*autoroll.AutoRollIssue{ari1} |
| require.NoError(t, r.Add(ctx, ari1)) |
| check(ari1, nil, expect) |
| |
| // Try to add it again. We should log an error but not fail. |
| require.NoError(t, r.Add(ctx, ari1)) |
| check(ari1, nil, expect) |
| |
| // Close the issue as successful. Ensure that it's now the last roll |
| // instead of the current roll. |
| ari1.Closed = true |
| ari1.Committed = true |
| ari1.CqFinished = true |
| ari1.CqSuccess = true |
| ari1.Result = autoroll.ROLL_RESULT_SUCCESS |
| require.NoError(t, r.Update(ctx, ari1)) |
| check(nil, ari1, expect) |
| |
| // Add another issue. Ensure that it's the current roll with the |
| // previously-added roll as the last roll. |
| now = time.Now().UTC() |
| ari2 := &autoroll.AutoRollIssue{ |
| Closed: false, |
| Committed: false, |
| Created: now, |
| Issue: 1010102, |
| Modified: now, |
| Patchsets: []int64{1}, |
| Result: autoroll.ROLL_RESULT_IN_PROGRESS, |
| Subject: "FAKE DEPS ROLL 2", |
| TryResults: []*autoroll.TryResult(nil), |
| } |
| require.NoError(t, r.Add(ctx, ari2)) |
| expect = []*autoroll.AutoRollIssue{ari2, ari1} |
| check(ari2, ari1, expect) |
| |
| // Try to add another active issue. We should log an error but not fail. |
| now = time.Now().UTC() |
| ari3 := &autoroll.AutoRollIssue{ |
| Closed: false, |
| Committed: false, |
| Created: now, |
| Issue: 1010103, |
| Modified: now, |
| Patchsets: []int64{1}, |
| Result: autoroll.ROLL_RESULT_IN_PROGRESS, |
| Subject: "FAKE DEPS ROLL 3", |
| TryResults: []*autoroll.TryResult(nil), |
| } |
| require.NoError(t, r.Add(ctx, ari3)) |
| expect = []*autoroll.AutoRollIssue{ari3, ari2, ari1} |
| check(ari3, ari2, expect) |
| |
| // Close the issue as failed. Ensure that it's now the last roll |
| // instead of the current roll. |
| ari2.Closed = true |
| ari2.Committed = false |
| ari2.CqFinished = true |
| ari2.CqSuccess = false |
| ari2.Result = autoroll.ROLL_RESULT_FAILURE |
| require.NoError(t, r.Update(ctx, ari2)) |
| check(ari3, ari2, expect) |
| |
| // Same with ari3. |
| ari3.Closed = true |
| ari3.Committed = false |
| ari3.CqFinished = true |
| ari3.CqSuccess = false |
| ari3.Result = autoroll.ROLL_RESULT_FAILURE |
| require.NoError(t, r.Update(ctx, ari3)) |
| check(nil, ari3, expect) |
| |
| // Try to add a bogus issue. |
| now = time.Now().UTC() |
| bad2 := &autoroll.AutoRollIssue{ |
| Closed: false, |
| Committed: true, |
| Created: now, |
| Issue: 1010104, |
| Modified: now, |
| Patchsets: []int64{1}, |
| Result: autoroll.ROLL_RESULT_FAILURE, |
| Subject: "FAKE DEPS ROLL 4", |
| TryResults: []*autoroll.TryResult(nil), |
| } |
| require.Error(t, r.Add(ctx, bad2)) |
| |
| // Add one more roll. Ensure that it's the current roll. |
| now = time.Now().UTC() |
| ari4 := &autoroll.AutoRollIssue{ |
| Closed: false, |
| Committed: false, |
| Created: now, |
| Issue: 1010105, |
| Modified: now, |
| Patchsets: []int64{1}, |
| Result: autoroll.ROLL_RESULT_IN_PROGRESS, |
| Subject: "FAKE DEPS ROLL 5", |
| TryResults: []*autoroll.TryResult(nil), |
| } |
| require.NoError(t, r.Add(ctx, ari4)) |
| expect = []*autoroll.AutoRollIssue{ari4, ari3, ari2, ari1} |
| check(ari4, ari3, expect) |
| } |
| |
| func TestRecentRolls_NumFailuresAndLastSucessfulRollTime(t *testing.T) { |
| ctx := context.Background() |
| testutil.InitDatastore(t, ds.KIND_AUTOROLL_ROLL) |
| |
| db := NewDatastoreRollsDB(ctx) |
| r, err := NewRecentRolls(ctx, NewDatastoreRollsDB(ctx), "test-roller") |
| require.NoError(t, err) |
| |
| issue := int64(0) |
| const startTs int64 = 1678218051 |
| now := time.Unix(startTs, 0) // Arbitrary starting point. |
| createAndInsertRoll := func(result string) { |
| issue += 1 |
| now = now.Add(time.Second) |
| require.NoError(t, db.Put(ctx, r.roller, &autoroll.AutoRollIssue{ |
| Closed: true, |
| Committed: result != autoroll.ROLL_RESULT_IN_PROGRESS, |
| Created: now, |
| Issue: issue, |
| Modified: now, |
| Patchsets: []int64{1}, |
| Result: result, |
| Subject: "fake roll", |
| TryResults: []*autoroll.TryResult(nil), |
| })) |
| } |
| |
| // One successful roll followed by a number of failures. We should see the |
| // correct number of failed rolls and the timestamp of the successful roll. |
| createAndInsertRoll(autoroll.ROLL_RESULT_SUCCESS) |
| for i := 0; i < 2*RecentRollsLength; i++ { |
| createAndInsertRoll(autoroll.ROLL_RESULT_FAILURE) |
| } |
| require.NoError(t, r.refreshRecentRolls(ctx)) |
| |
| require.Equal(t, 2*RecentRollsLength, r.NumFailedRolls()) |
| require.Equal(t, time.Unix(startTs+int64(1), 0), r.LastSuccessfulRollTime()) |
| |
| // Add a new successful roll. The number of failures should be zero, and |
| // last successful roll timestamp should be updated. |
| createAndInsertRoll(autoroll.ROLL_RESULT_SUCCESS) |
| require.NoError(t, r.refreshRecentRolls(ctx)) |
| require.Equal(t, 0, r.NumFailedRolls()) |
| require.Equal(t, time.Unix(startTs+int64(2*RecentRollsLength+2), 0), r.LastSuccessfulRollTime()) |
| |
| // Add an in-progress roll. It shouldn't throw off the number of failures. |
| createAndInsertRoll(autoroll.ROLL_RESULT_IN_PROGRESS) |
| require.NoError(t, r.refreshRecentRolls(ctx)) |
| require.Equal(t, 0, r.NumFailedRolls()) |
| require.Equal(t, time.Unix(startTs+int64(2*RecentRollsLength+2), 0), r.LastSuccessfulRollTime()) |
| } |
| |
| func TestDatastoreRollsDB_GetRolls(t *testing.T) { |
| ctx := context.Background() |
| testutil.InitDatastore(t, ds.KIND_AUTOROLL_ROLL) |
| db := NewDatastoreRollsDB(ctx) |
| roller := uuid.New().String() |
| |
| issue := int64(0) |
| now := time.Unix(1678218051, 0) // Arbitrary starting point. |
| makeRoll := func() *autoroll.AutoRollIssue { |
| issue += 1 |
| now = now.Add(time.Second) |
| return &autoroll.AutoRollIssue{ |
| Closed: true, |
| Committed: true, |
| Created: now, |
| Issue: issue, |
| Modified: now, |
| Patchsets: []int64{1}, |
| Result: autoroll.ROLL_RESULT_SUCCESS, |
| Subject: "fake roll", |
| TryResults: []*autoroll.TryResult(nil), |
| } |
| } |
| |
| // Ensure no error when no rolls exist. |
| rolls, cursor, err := db.GetRolls(ctx, roller, "") |
| require.NoError(t, err) |
| require.Equal(t, "", cursor) |
| require.Equal(t, 0, len(rolls)) |
| |
| // Insert a single roll. |
| r1 := makeRoll() |
| require.NoError(t, db.Put(ctx, roller, r1)) |
| rolls, cursor, err = db.GetRolls(ctx, roller, "") |
| require.NoError(t, err) |
| require.Equal(t, "", cursor) |
| require.Equal(t, 1, len(rolls)) |
| require.Equal(t, rolls[0], r1) |
| |
| // Insert enough rolls to necessitate multiple pages. |
| for i := 0; i < 3*loadRollsPageSize; i++ { |
| require.NoError(t, db.Put(ctx, roller, makeRoll())) |
| } |
| allRolls := []*autoroll.AutoRollIssue{} |
| // Batch 1. |
| rolls, cursor, err = db.GetRolls(ctx, roller, "") |
| require.NoError(t, err) |
| require.NotEqual(t, "", cursor) |
| require.Equal(t, loadRollsPageSize, len(rolls)) |
| allRolls = append(allRolls, rolls...) |
| // Batch 2. |
| rolls, cursor, err = db.GetRolls(ctx, roller, cursor) |
| require.NoError(t, err) |
| require.NotEqual(t, "", cursor) |
| require.Equal(t, loadRollsPageSize, len(rolls)) |
| allRolls = append(allRolls, rolls...) |
| // Batch 3. |
| rolls, cursor, err = db.GetRolls(ctx, roller, cursor) |
| require.NoError(t, err) |
| require.NotEqual(t, "", cursor) |
| require.Equal(t, loadRollsPageSize, len(rolls)) |
| allRolls = append(allRolls, rolls...) |
| // Batch 4. Only one roll left to retrieve. Cursor should be empty. |
| rolls, cursor, err = db.GetRolls(ctx, roller, cursor) |
| require.NoError(t, err) |
| require.Equal(t, "", cursor) |
| require.Equal(t, 1, len(rolls)) |
| allRolls = append(allRolls, rolls...) |
| // Ensure that we found all of the rolls we expected. |
| require.Equal(t, 76, len(allRolls)) |
| |
| // Test Get(). |
| for _, expect := range allRolls { |
| actual, err := db.Get(ctx, roller, expect.Issue) |
| require.NoError(t, err) |
| require.Equal(t, expect, actual) |
| } |
| } |