| package sqlclstore |
| |
| import ( |
| "context" |
| "strconv" |
| "testing" |
| "time" |
| |
| "github.com/stretchr/testify/assert" |
| "github.com/stretchr/testify/require" |
| |
| "go.skia.org/infra/go/testutils/unittest" |
| "go.skia.org/infra/golden/go/clstore" |
| "go.skia.org/infra/golden/go/code_review" |
| "go.skia.org/infra/golden/go/sql/schema" |
| "go.skia.org/infra/golden/go/sql/sqltest" |
| ) |
| |
| func TestPutChangelist_CLDoesNotExist_Success(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| store := New(db, "gerrit") |
| |
| const unqualifiedID = "987654" |
| |
| // Should not exist initially |
| _, err := store.GetChangelist(ctx, unqualifiedID) |
| require.Error(t, err) |
| require.Equal(t, clstore.ErrNotFound, err) |
| |
| cl := code_review.Changelist{ |
| SystemID: unqualifiedID, |
| Owner: "test@example.com", |
| Status: code_review.Abandoned, |
| Subject: "some code", |
| Updated: time.Date(2019, time.August, 13, 12, 11, 10, 0, time.UTC), |
| } |
| |
| err = store.PutChangelist(ctx, cl) |
| require.NoError(t, err) |
| |
| actual, err := store.GetChangelist(ctx, unqualifiedID) |
| require.NoError(t, err) |
| assert.Equal(t, cl, actual) |
| |
| // Check the SQL directly so we can trust GetChangelist in other tests. |
| clRows := sqltest.GetAllRows(ctx, t, db, "Changelists", &schema.ChangelistRow{}).([]schema.ChangelistRow) |
| assert.Equal(t, []schema.ChangelistRow{{ |
| ChangelistID: "gerrit_987654", |
| System: "gerrit", |
| Status: schema.StatusAbandoned, |
| OwnerEmail: "test@example.com", |
| Subject: "some code", |
| LastIngestedData: time.Date(2019, time.August, 13, 12, 11, 10, 0, time.UTC), |
| }}, clRows) |
| } |
| |
| func TestPutChangelist_CLExists_CLUpdated(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| store := New(db, "gerrit") |
| |
| const unqualifiedID = "987654" |
| err := store.PutChangelist(ctx, code_review.Changelist{ |
| SystemID: unqualifiedID, |
| Owner: "test@example.com", |
| Status: code_review.Open, |
| Subject: "some code", |
| Updated: time.Date(2019, time.August, 13, 12, 11, 10, 0, time.UTC), |
| }) |
| require.NoError(t, err) |
| err = store.PutChangelist(ctx, code_review.Changelist{ |
| SystemID: unqualifiedID, |
| Owner: "test@example.com", |
| Status: code_review.Landed, |
| Subject: "some code", |
| Updated: time.Date(2021, time.January, 1, 2, 3, 4, 0, time.UTC), |
| }) |
| require.NoError(t, err) |
| |
| actual, err := store.GetChangelist(ctx, unqualifiedID) |
| require.NoError(t, err) |
| assert.Equal(t, code_review.Changelist{ |
| SystemID: unqualifiedID, |
| Owner: "test@example.com", |
| Status: code_review.Landed, |
| Subject: "some code", |
| Updated: time.Date(2021, time.January, 1, 2, 3, 4, 0, time.UTC), |
| }, actual) |
| } |
| |
| func TestGetChangelist_SameIDDifferentSystems_NoConflict(t *testing.T) { |
| unittest.LargeTest(t) |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| gerrit := New(db, "gerrit") |
| github := New(db, "github") |
| |
| const conflictingID = "987654" |
| gerritCL := code_review.Changelist{ |
| SystemID: conflictingID, |
| Owner: "test@example.com", |
| Status: code_review.Abandoned, |
| Subject: "some code on gerrit", |
| Updated: time.Date(2019, time.August, 13, 12, 11, 10, 0, time.UTC), |
| } |
| |
| githubCL := code_review.Changelist{ |
| SystemID: conflictingID, |
| Owner: "test2@example.com", |
| Status: code_review.Open, |
| Subject: "some code on github", |
| Updated: time.Date(2019, time.August, 15, 12, 11, 10, 0, time.UTC), |
| } |
| |
| // Both systems have a CL with the same ID |
| err := gerrit.PutChangelist(ctx, gerritCL) |
| require.NoError(t, err) |
| err = github.PutChangelist(ctx, githubCL) |
| require.NoError(t, err) |
| |
| actualGerrit, err := gerrit.GetChangelist(ctx, conflictingID) |
| require.NoError(t, err) |
| actualGithub, err := github.GetChangelist(ctx, conflictingID) |
| require.NoError(t, err) |
| |
| assert.NotEqual(t, actualGerrit, actualGithub) |
| assert.Equal(t, gerritCL, actualGerrit) |
| assert.Equal(t, githubCL, actualGithub) |
| } |
| |
| func TestPutPatchset_CLExists_Success(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| store := New(db, "gerrit") |
| |
| const unqualifiedCLID = "987654" |
| const unqualifiedPSID = "abcdef" |
| |
| err := store.PutChangelist(ctx, code_review.Changelist{ |
| SystemID: unqualifiedCLID, |
| Owner: "test@example.com", |
| Status: code_review.Open, |
| Subject: "some code", |
| Updated: time.Date(2021, time.January, 1, 2, 3, 4, 0, time.UTC), |
| }) |
| require.NoError(t, err) |
| |
| ps := code_review.Patchset{ |
| SystemID: unqualifiedPSID, |
| ChangelistID: unqualifiedCLID, |
| Order: 3, |
| GitHash: "fedcba98765443321", |
| CommentedOnCL: true, |
| Created: time.Date(2021, time.January, 1, 2, 40, 0, 0, time.UTC), |
| } |
| |
| err = store.PutPatchset(ctx, ps) |
| require.NoError(t, err) |
| |
| // Check the SQL directly so we can trust GetPatchset* in other tests. |
| psRows := sqltest.GetAllRows(ctx, t, db, "Patchsets", &schema.PatchsetRow{}).([]schema.PatchsetRow) |
| assert.Equal(t, []schema.PatchsetRow{{ |
| PatchsetID: "gerrit_abcdef", |
| System: "gerrit", |
| ChangelistID: "gerrit_987654", |
| Order: 3, |
| GitHash: "fedcba98765443321", |
| CommentedOnCL: true, |
| Created: time.Date(2021, time.January, 1, 2, 40, 0, 0, time.UTC), |
| }}, psRows) |
| |
| actual, err := store.GetPatchset(ctx, unqualifiedCLID, unqualifiedPSID) |
| require.NoError(t, err) |
| assert.Equal(t, ps, actual) |
| actual, err = store.GetPatchsetByOrder(ctx, unqualifiedCLID, 3) |
| require.NoError(t, err) |
| assert.Equal(t, ps, actual) |
| actualList, err := store.GetPatchsets(ctx, unqualifiedCLID) |
| require.NoError(t, err) |
| assert.Equal(t, []code_review.Patchset{ps}, actualList) |
| } |
| |
| func TestPutPatchset_CLDoesNotExists_ReturnsError(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| store := New(db, "gerrit") |
| |
| const unqualifiedCLID = "987654" |
| const unqualifiedPSID = "abcdef" |
| |
| ps := code_review.Patchset{ |
| SystemID: unqualifiedPSID, |
| ChangelistID: unqualifiedCLID, |
| Order: 3, |
| GitHash: "fedcba98765443321", |
| } |
| |
| err := store.PutPatchset(ctx, ps) |
| require.Error(t, err) |
| |
| _, err = store.GetPatchset(ctx, unqualifiedCLID, unqualifiedPSID) |
| require.Error(t, err) |
| assert.Equal(t, err, clstore.ErrNotFound) |
| } |
| |
| func TestGetPatchset_CreatedTimestampNull_TimeZero(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| const unqualifiedCLID = "55555" |
| existingData := schema.Tables{ |
| Changelists: []schema.ChangelistRow{{ |
| ChangelistID: "github_55555", |
| System: "github", |
| Status: schema.StatusOpen, |
| OwnerEmail: "foo@example.com", |
| Subject: "bar", |
| LastIngestedData: time.Date(2021, time.January, 1, 2, 40, 0, 0, time.UTC), |
| }}, |
| Patchsets: []schema.PatchsetRow{{ |
| PatchsetID: "github_bbbbbbbbbbbbbbbbbbbbbbbb", |
| System: "github", |
| ChangelistID: "github_55555", |
| Order: 6, |
| GitHash: "bbbbbbbbbbbbbbbbbbbbbbbb", |
| CommentedOnCL: true, |
| }}, |
| } |
| err := sqltest.BulkInsertDataTables(ctx, db, existingData) |
| require.NoError(t, err) |
| |
| store := New(db, "github") |
| |
| ps, err := store.GetPatchset(ctx, unqualifiedCLID, "bbbbbbbbbbbbbbbbbbbbbbbb") |
| require.NoError(t, err) |
| assert.Equal(t, code_review.Patchset{ |
| SystemID: "bbbbbbbbbbbbbbbbbbbbbbbb", |
| ChangelistID: unqualifiedCLID, |
| Order: 6, |
| GitHash: "bbbbbbbbbbbbbbbbbbbbbbbb", |
| Created: time.Time{}, |
| CommentedOnCL: true, |
| }, ps) |
| } |
| |
| func TestGetPatchsetByOrder_CreatedTimestampNull_TimeZero(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| const unqualifiedCLID = "55555" |
| existingData := schema.Tables{ |
| Changelists: []schema.ChangelistRow{{ |
| ChangelistID: "github_55555", |
| System: "github", |
| Status: schema.StatusOpen, |
| OwnerEmail: "foo@example.com", |
| Subject: "bar", |
| LastIngestedData: time.Date(2021, time.January, 1, 2, 40, 0, 0, time.UTC), |
| }}, |
| Patchsets: []schema.PatchsetRow{{ |
| PatchsetID: "github_bbbbbbbbbbbbbbbbbbbbbbbb", |
| System: "github", |
| ChangelistID: "github_55555", |
| Order: 6, |
| GitHash: "bbbbbbbbbbbbbbbbbbbbbbbb", |
| CommentedOnCL: true, |
| }}, |
| } |
| err := sqltest.BulkInsertDataTables(ctx, db, existingData) |
| require.NoError(t, err) |
| |
| store := New(db, "github") |
| |
| ps, err := store.GetPatchsetByOrder(ctx, unqualifiedCLID, 6) |
| require.NoError(t, err) |
| assert.Equal(t, code_review.Patchset{ |
| SystemID: "bbbbbbbbbbbbbbbbbbbbbbbb", |
| ChangelistID: unqualifiedCLID, |
| Order: 6, |
| GitHash: "bbbbbbbbbbbbbbbbbbbbbbbb", |
| Created: time.Time{}, |
| CommentedOnCL: true, |
| }, ps) |
| } |
| |
| func TestGetPatchsetByOrder_MultiplePSWithSameOrder_CreatedTSWins(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| const unqualifiedCLID = "55555" |
| existingData := schema.Tables{ |
| Changelists: []schema.ChangelistRow{{ |
| ChangelistID: "github_55555", |
| System: "github", |
| Status: schema.StatusOpen, |
| OwnerEmail: "foo@example.com", |
| Subject: "bar", |
| LastIngestedData: time.Date(2021, time.January, 1, 2, 40, 0, 0, time.UTC), |
| }}, |
| Patchsets: []schema.PatchsetRow{{ |
| PatchsetID: "github_bbbbbbbbbbbbbbbbbbbbbbbb", |
| System: "github", |
| ChangelistID: "github_55555", |
| Order: 1, |
| GitHash: "bbbbbbbbbbbbbbbbbbbbbbbb", |
| CommentedOnCL: false, |
| Created: time.Date(2021, time.January, 2, 2, 2, 0, 0, time.UTC), |
| }, { |
| PatchsetID: "github_ccccccccccccccccccccccc", |
| System: "github", |
| ChangelistID: "github_55555", |
| Order: 1, |
| GitHash: "ccccccccccccccccccccccc", |
| CommentedOnCL: false, |
| Created: time.Date(2021, time.January, 3, 3, 3, 0, 0, time.UTC), |
| }, { |
| PatchsetID: "github_dddddddddddddddddddddd", |
| System: "github", |
| ChangelistID: "github_55555", |
| Order: 1, |
| GitHash: "dddddddddddddddddddddd", |
| CommentedOnCL: false, |
| Created: time.Date(2021, time.January, 1, 1, 1, 0, 0, time.UTC), |
| }}, |
| } |
| err := sqltest.BulkInsertDataTables(ctx, db, existingData) |
| require.NoError(t, err) |
| |
| store := New(db, "github") |
| |
| ps, err := store.GetPatchsetByOrder(ctx, unqualifiedCLID, 1) |
| require.NoError(t, err) |
| assert.Equal(t, code_review.Patchset{ |
| SystemID: "ccccccccccccccccccccccc", |
| ChangelistID: unqualifiedCLID, |
| Order: 1, |
| GitHash: "ccccccccccccccccccccccc", |
| Created: time.Date(2021, time.January, 3, 3, 3, 0, 0, time.UTC), |
| CommentedOnCL: false, |
| }, ps) |
| } |
| |
| func TestGetPatchsets_PatchsetsSavedOutOfOrder_ReturnsPatchsetsInAscendingOrder(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| store := New(db, "gerrit") |
| |
| const unqualifiedCLID = "987654" |
| const unqualifiedPSID4 = "fourfourfour" |
| const unqualifiedPSID1 = "oneoneone" |
| |
| err := store.PutChangelist(ctx, code_review.Changelist{ |
| SystemID: unqualifiedCLID, |
| Owner: "test@example.com", |
| Status: code_review.Open, |
| Subject: "some code", |
| Updated: time.Date(2021, time.January, 1, 2, 3, 4, 0, time.UTC), |
| }) |
| require.NoError(t, err) |
| |
| ps4 := code_review.Patchset{ |
| SystemID: unqualifiedPSID4, |
| ChangelistID: unqualifiedCLID, |
| Order: 4, |
| Created: time.Date(2021, time.January, 2, 2, 2, 2, 0, time.UTC), |
| GitHash: "444444444444444444", |
| } |
| err = store.PutPatchset(ctx, ps4) |
| require.NoError(t, err) |
| ps1 := code_review.Patchset{ |
| SystemID: unqualifiedPSID1, |
| ChangelistID: unqualifiedCLID, |
| Order: 1, |
| Created: time.Date(2021, time.January, 1, 1, 1, 1, 0, time.UTC), |
| GitHash: "1111111111111111111", |
| } |
| err = store.PutPatchset(ctx, ps1) |
| require.NoError(t, err) |
| |
| actual, err := store.GetPatchsets(ctx, unqualifiedCLID) |
| require.NoError(t, err) |
| assert.Equal(t, []code_review.Patchset{ps1, ps4}, actual) |
| } |
| |
| func TestGetPatchsetByOrder_PSDoesNotExist_ReturnsError(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| store := New(db, "gerrit") |
| |
| const unqualifiedCLID = "987654" |
| const unqualifiedPSID4 = "fourfourfour" |
| |
| err := store.PutChangelist(ctx, code_review.Changelist{ |
| SystemID: unqualifiedCLID, |
| Owner: "test@example.com", |
| Status: code_review.Open, |
| Subject: "some code", |
| Updated: time.Date(2021, time.January, 1, 2, 3, 4, 0, time.UTC), |
| }) |
| require.NoError(t, err) |
| |
| ps4 := code_review.Patchset{ |
| SystemID: unqualifiedPSID4, |
| ChangelistID: unqualifiedCLID, |
| Order: 4, |
| GitHash: "444444444444444444", |
| } |
| err = store.PutPatchset(ctx, ps4) |
| require.NoError(t, err) |
| |
| _, err = store.GetPatchsetByOrder(ctx, unqualifiedCLID, 3) |
| require.Error(t, err) |
| assert.Equal(t, err, clstore.ErrNotFound) |
| } |
| |
| func TestGetChangelists_StartAndLimitProvided_RespectsStartAndLimit(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, makeTestCLs())) |
| |
| store := New(db, "gerrit") |
| waitForSystemTime() // GetChangelists has "AS OF SYSTEM TIME" |
| |
| // Get all of them |
| cls, total, err := store.GetChangelists(ctx, clstore.SearchOptions{ |
| StartIdx: 0, |
| Limit: 50, |
| }) |
| require.NoError(t, err) |
| assert.Len(t, cls, 30) |
| assert.Equal(t, 30, total) |
| |
| // Get the first ones |
| cls, total, err = store.GetChangelists(ctx, clstore.SearchOptions{ |
| StartIdx: 0, |
| Limit: 3, |
| }) |
| require.NoError(t, err) |
| require.Len(t, cls, 3) |
| assert.Equal(t, 30, total) |
| // spot check the dates to make sure the CLs are in the right order. We expect to see |
| // the most recent one first. |
| assert.Equal(t, time.Date(2021, time.January, 15, 0, 29, 0, 0, time.UTC), cls[0].Updated) |
| assert.Equal(t, time.Date(2021, time.January, 15, 0, 28, 0, 0, time.UTC), cls[1].Updated) |
| assert.Equal(t, time.Date(2021, time.January, 15, 0, 27, 0, 0, time.UTC), cls[2].Updated) |
| |
| // Get some in the middle. Check that the full data is filled out. |
| cls, total, err = store.GetChangelists(ctx, clstore.SearchOptions{ |
| StartIdx: 5, |
| Limit: 2, |
| }) |
| require.NoError(t, err) |
| require.Len(t, cls, 2) |
| require.Equal(t, 30, total) |
| assert.Equal(t, code_review.Changelist{ |
| SystemID: "cl24", |
| Owner: "test@example.com", |
| Status: code_review.Open, |
| Subject: "whatever", |
| Updated: time.Date(2021, time.January, 15, 0, 24, 0, 0, time.UTC), |
| }, cls[0]) |
| assert.Equal(t, code_review.Changelist{ |
| SystemID: "cl23", |
| Owner: "test@example.com", |
| Status: code_review.Abandoned, |
| Subject: "whatever", |
| Updated: time.Date(2021, time.January, 15, 0, 23, 0, 0, time.UTC), |
| }, cls[1]) |
| |
| // Get some at the end. |
| cls, total, err = store.GetChangelists(ctx, clstore.SearchOptions{ |
| StartIdx: 28, |
| Limit: 10, |
| }) |
| require.NoError(t, err) |
| require.Len(t, cls, 2) |
| assert.Equal(t, 30, total) |
| assert.Equal(t, time.Date(2021, time.January, 15, 0, 1, 0, 0, time.UTC), cls[0].Updated) |
| assert.Equal(t, time.Date(2021, time.January, 15, 0, 0, 0, 0, time.UTC), cls[1].Updated) |
| |
| // Way off the end |
| cls, total, err = store.GetChangelists(ctx, clstore.SearchOptions{ |
| StartIdx: 999, |
| Limit: 3, |
| }) |
| require.NoError(t, err) |
| assert.Empty(t, cls) |
| assert.Equal(t, 30, total) |
| } |
| |
| // waitForSystemTime waits for a time greater than the duration mentioned in "AS OF SYSTEM TIME" |
| // clauses in queries. This way, the queries will be accurate. |
| func waitForSystemTime() { |
| time.Sleep(150 * time.Millisecond) |
| } |
| |
| func TestGetChangelists_InvalidStartsAndLimits_ReturnsError(t *testing.T) { |
| unittest.SmallTest(t) |
| |
| ctx := context.Background() |
| store := New(nil, "gerrit") |
| |
| // Limit missing |
| _, _, err := store.GetChangelists(ctx, clstore.SearchOptions{}) |
| require.Error(t, err) |
| assert.Contains(t, err.Error(), "limit") |
| |
| // Limit negative |
| _, _, err = store.GetChangelists(ctx, clstore.SearchOptions{Limit: -10}) |
| require.Error(t, err) |
| assert.Contains(t, err.Error(), "limit") |
| |
| // Start index negative |
| _, _, err = store.GetChangelists(ctx, clstore.SearchOptions{StartIdx: -10, Limit: 10}) |
| require.Error(t, err) |
| assert.Contains(t, err.Error(), "start") |
| } |
| |
| func TestGetChangelists_OptionsRespected_Success(t *testing.T) { |
| unittest.LargeTest(t) |
| |
| ctx := context.Background() |
| db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t) |
| require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, makeTestCLs())) |
| |
| store := New(db, "gerrit") |
| waitForSystemTime() // GetChangelists has "AS OF SYSTEM TIME" |
| |
| // Get the ones after the 27th minute |
| cls, total, err := store.GetChangelists(ctx, clstore.SearchOptions{ |
| Limit: 5, |
| After: time.Date(2021, time.January, 15, 0, 27, 0, 0, time.UTC), |
| }) |
| require.NoError(t, err) |
| require.Len(t, cls, 2) |
| assert.Equal(t, 30, total) |
| // There are only 2 cls after the searched time. |
| assert.Equal(t, time.Date(2021, time.January, 15, 0, 29, 0, 0, time.UTC), cls[0].Updated) |
| assert.Equal(t, time.Date(2021, time.January, 15, 0, 28, 0, 0, time.UTC), cls[1].Updated) |
| |
| // Get the open ones |
| cls, total, err = store.GetChangelists(ctx, clstore.SearchOptions{ |
| Limit: 2, |
| OpenCLsOnly: true, |
| }) |
| require.NoError(t, err) |
| require.Len(t, cls, 2) |
| assert.Equal(t, 30, total) |
| // These are the first 2 open CLs. |
| assert.Equal(t, code_review.Changelist{ |
| SystemID: "cl27", |
| Owner: "test@example.com", |
| Status: code_review.Open, |
| Subject: "whatever", |
| Updated: time.Date(2021, time.January, 15, 0, 27, 0, 0, time.UTC), |
| }, cls[0]) |
| assert.Equal(t, code_review.Changelist{ |
| SystemID: "cl24", |
| Owner: "test@example.com", |
| Status: code_review.Open, |
| Subject: "whatever", |
| Updated: time.Date(2021, time.January, 15, 0, 24, 0, 0, time.UTC), |
| }, cls[1]) |
| } |
| |
| // makeTestCLs returns a tables data with 30 commits with procedurally generated ids (the first |
| // has id "cl0", the last has id "cl29" |
| func makeTestCLs() schema.Tables { |
| var rv schema.Tables |
| |
| for i := 0; i < 30; i++ { |
| row := schema.ChangelistRow{ |
| ChangelistID: "cl" + strconv.Itoa(i), |
| System: "gerrit", |
| OwnerEmail: "test@example.com", |
| Subject: "whatever", |
| // Note that the minute can tell us when this CL happened. |
| LastIngestedData: time.Date(2021, time.January, 15, 0, i, 0, 0, time.UTC), |
| } |
| // Rotate through the given states |
| switch i % 3 { |
| case 0: |
| row.Status = schema.StatusOpen |
| case 1: |
| row.Status = schema.StatusLanded |
| case 2: |
| row.Status = schema.StatusAbandoned |
| } |
| rv.Changelists = append(rv.Changelists, row) |
| } |
| return rv |
| } |