Consolidate pager tests while adding more coverage (#4622)

Rewrite the pager tests to more uniformly test the different outcomes
when various pagers/configurations are used

This PR splits tests into two main categories: those that deal with
getting results from a single pager with no resets etc and those that
deal with resets, fallbacks, and other similar things

All tests now check for correctness when the pager does and doesn't
support returning mod times

---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No

#### Type of change

- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [x] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [ ] 🧹 Tech Debt/Cleanup

#### Test Plan

- [x] 💪 Manual
- [ ]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2023-11-09 17:51:10 -08:00 committed by GitHub
parent 16782d90d6
commit 7662cfca68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -305,258 +305,302 @@ func (suite *PagerUnitSuite) TestBatchEnumerateItems() {
} }
} }
func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() { func assertSliceEmptyOr[S ~[]E, E any](
type expected struct { t *testing.T,
added []testItem expect S,
removed []string got S,
deltaUpdate DeltaUpdate assertionFunc assert.ComparisonAssertionFunc,
msgAndArgs ...any,
) {
if len(expect) == 0 {
assert.Empty(t, got, msgAndArgs)
return
}
assertionFunc(t, expect, got, msgAndArgs)
}
func assertMapEmptyOr[M ~map[K]V, K comparable, V any](
t *testing.T,
expect M,
got M,
assertionFunc assert.ComparisonAssertionFunc,
msgAndArgs ...any,
) {
if len(expect) == 0 {
assert.Empty(t, got, msgAndArgs)
return
}
assertionFunc(t, expect, got, msgAndArgs)
}
func assertAddedAndRemoved(
t *testing.T,
validModTimes bool,
wantAdded []testItem,
gotAdded map[string]time.Time,
wantRemoved []testItem,
gotRemoved []string,
) {
epoch, err := time.Parse(time.DateOnly, "1970-01-01")
require.NoError(t, err, clues.ToCore(err))
expectAdded := map[string]time.Time{}
for _, item := range wantAdded {
expectAdded[item.id] = item.modTime
}
if validModTimes {
assertMapEmptyOr(
t,
expectAdded,
gotAdded,
assert.Equal,
"added item IDs and mod times")
} else {
assertSliceEmptyOr(
t,
maps.Keys(expectAdded),
maps.Keys(gotAdded),
assert.ElementsMatch,
"added item IDs")
for _, modtime := range gotAdded {
assert.True(t, modtime.After(epoch), "mod time after epoch")
assert.False(t, modtime.IsZero(), "non-zero mod time")
}
}
expectRemoved := []string{}
for _, item := range wantRemoved {
expectRemoved = append(expectRemoved, item.id)
}
assertSliceEmptyOr(
t,
expectRemoved,
gotRemoved,
assert.ElementsMatch,
"removed item IDs")
}
type modTimeTest struct {
name string
validModTimes bool validModTimes bool
} }
nilPager := func(t *testing.T) NonDeltaHandler[testItem] { var (
addedItem1 = addedItem("a_uno", time.Now())
addedItem2 = addedItem("a_dos", time.Now())
removedItem1 = removedItem("r_uno")
removedItem2 = removedItem("r_dos")
modTimeTests = []modTimeTest{
{
name: "ValidModTimes",
validModTimes: true,
},
{
name: "InvalidModTimes",
},
}
nilPager = func(*testing.T, bool) NonDeltaHandler[testItem] {
return nil return nil
} }
)
epoch, err := time.Parse(time.DateOnly, "1970-01-01") func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() {
require.NoError(suite.T(), err, clues.ToCore(err)) pagerTypeTests := []struct {
item1 := addedItem("uno", time.Now())
item2 := addedItem("dos", time.Now())
tests := []struct {
name string name string
pagerGetter func(
*testing.T,
) NonDeltaHandler[testItem]
deltaPagerGetter func(
*testing.T,
) DeltaHandler[testItem]
prevDelta string prevDelta string
filter func(a testItem) bool canUseDelta bool
expect expected pagersFunc func(
canDelta bool p *testIDsNonDeltaMultiPager,
validModTimes bool ) (NonDeltaHandler[testItem], DeltaHandler[testItem])
expectDeltaReset bool
}{ }{
{ {
name: "no prev delta", name: "NoPrevDelta",
pagerGetter: nilPager, canUseDelta: true,
deltaPagerGetter: func(t *testing.T) DeltaHandler[testItem] { pagersFunc: func(
return newDeltaPager( p *testIDsNonDeltaMultiPager,
&testIDsNonDeltaMultiPager{ ) (NonDeltaHandler[testItem], DeltaHandler[testItem]) {
t: t, return nil, newDeltaPager(p)
pages: []pageResult{
{
items: []testItem{
item1,
item2,
removedItem("tres"),
removedItem("quatro"),
}, },
}, expectDeltaReset: true,
},
validModTimes: true,
})
},
expect: expected{
added: []testItem{
item1,
item2,
},
removed: []string{"tres", "quatro"},
deltaUpdate: DeltaUpdate{Reset: true},
validModTimes: true,
},
canDelta: true,
}, },
{ {
name: "no prev delta invalid mod times", name: "PrevDelta",
pagerGetter: nilPager, prevDelta: "a",
deltaPagerGetter: func(t *testing.T) DeltaHandler[testItem] { canUseDelta: true,
return newDeltaPager( pagersFunc: func(
&testIDsNonDeltaMultiPager{ p *testIDsNonDeltaMultiPager,
t: t, ) (NonDeltaHandler[testItem], DeltaHandler[testItem]) {
pages: []pageResult{ return nil, newDeltaPager(p)
{
items: []testItem{
addedItem("uno", time.Time{}),
addedItem("dos", time.Time{}),
removedItem("tres"),
removedItem("quatro"),
}, },
}, },
},
})
},
expect: expected{
added: []testItem{
item1,
item2,
},
removed: []string{"tres", "quatro"},
deltaUpdate: DeltaUpdate{Reset: true},
},
canDelta: true,
},
{ {
name: "with prev delta", name: "DeltaNotAllowed",
pagerGetter: nilPager, prevDelta: "a",
deltaPagerGetter: func(t *testing.T) DeltaHandler[testItem] { pagersFunc: func(
return newDeltaPager( p *testIDsNonDeltaMultiPager,
&testIDsNonDeltaMultiPager{ ) (NonDeltaHandler[testItem], DeltaHandler[testItem]) {
t: t, return p, nil
pages: []pageResult{ },
expectDeltaReset: true,
},
}
type expected struct {
errCheck assert.ErrorAssertionFunc
added []testItem
removed []testItem
}
table := []struct {
name string
pagerGetter func(
t *testing.T,
validModTimes bool,
) *testIDsNonDeltaMultiPager
filter func(a testItem) bool
expect expected
}{
{ {
items: []testItem{ name: "OnePage",
item1, pagerGetter: func(t *testing.T, validModTime bool) *testIDsNonDeltaMultiPager {
item2,
removedItem("tres"),
removedItem("quatro"),
},
},
},
validModTimes: true,
})
},
prevDelta: "delta",
expect: expected{
added: []testItem{
item1,
item2,
},
removed: []string{"tres", "quatro"},
deltaUpdate: DeltaUpdate{Reset: false},
validModTimes: true,
},
canDelta: true,
},
{
name: "delta expired",
pagerGetter: nilPager,
deltaPagerGetter: func(t *testing.T) DeltaHandler[testItem] {
return newDeltaPager(
&testIDsNonDeltaMultiPager{
t: t,
pages: []pageResult{
{
errCode: "SyncStateNotFound",
needsReset: true,
},
{
items: []testItem{
item1,
item2,
removedItem("tres"),
removedItem("quatro"),
},
},
},
validModTimes: true,
})
},
prevDelta: "delta",
expect: expected{
added: []testItem{
item1,
item2,
},
removed: []string{"tres", "quatro"},
deltaUpdate: DeltaUpdate{Reset: true},
validModTimes: true,
},
canDelta: true,
},
{
name: "delta not allowed",
pagerGetter: func(t *testing.T) NonDeltaHandler[testItem] {
return &testIDsNonDeltaMultiPager{ return &testIDsNonDeltaMultiPager{
t: t, t: t,
pages: []pageResult{ pages: []pageResult{
{ {
items: []testItem{ items: []testItem{
item1, addedItem1,
item2, addedItem2,
removedItem("tres"), removedItem1,
removedItem("quatro"), removedItem2,
}, },
}, },
}, },
validModTimes: true, validModTimes: validModTime,
} }
}, },
deltaPagerGetter: func(t *testing.T) DeltaHandler[testItem] {
return nil
},
expect: expected{ expect: expected{
errCheck: assert.NoError,
added: []testItem{ added: []testItem{
item1, addedItem1,
item2, addedItem2,
}, },
removed: []string{"tres", "quatro"}, removed: []testItem{removedItem1, removedItem2},
deltaUpdate: DeltaUpdate{Reset: true},
validModTimes: true,
}, },
canDelta: false,
}, },
{ {
name: "no prev delta and fail all filter", name: "TwoPages",
pagerGetter: nilPager, pagerGetter: func(t *testing.T, validModTime bool) *testIDsNonDeltaMultiPager {
deltaPagerGetter: func(t *testing.T) DeltaHandler[testItem] { return &testIDsNonDeltaMultiPager{
return newDeltaPager(
&testIDsNonDeltaMultiPager{
t: t, t: t,
pages: []pageResult{ pages: []pageResult{
{ {
items: []testItem{ items: []testItem{
item1, addedItem1,
item2, removedItem1,
removedItem("tres"),
removedItem("quatro"),
}, },
}, },
},
validModTimes: true,
})
},
filter: func(testItem) bool { return false },
expect: expected{
removed: []string{},
deltaUpdate: DeltaUpdate{Reset: true},
validModTimes: true,
},
canDelta: true,
},
{ {
name: "with prev delta and fail all filter", items: []testItem{
pagerGetter: nilPager, addedItem2,
deltaPagerGetter: func(t *testing.T) DeltaHandler[testItem] { removedItem2,
return newDeltaPager( },
&testIDsNonDeltaMultiPager{ },
},
validModTimes: validModTime,
}
},
expect: expected{
errCheck: assert.NoError,
added: []testItem{
addedItem1,
addedItem2,
},
removed: []testItem{removedItem1, removedItem2},
},
},
{
name: "OnePage FilterFailsAll",
pagerGetter: func(t *testing.T, validModTimes bool) *testIDsNonDeltaMultiPager {
return &testIDsNonDeltaMultiPager{
t: t, t: t,
pages: []pageResult{ pages: []pageResult{
{ {
items: []testItem{ items: []testItem{
item1, addedItem1,
item2, addedItem2,
removedItem("tres"), removedItem1,
removedItem("quatro"), removedItem2,
}, },
}, },
}, },
validModTimes: true, validModTimes: validModTimes,
}) }
}, },
filter: func(testItem) bool { return false }, filter: func(testItem) bool { return false },
prevDelta: "delta",
expect: expected{ expect: expected{
removed: []string{}, errCheck: assert.NoError,
deltaUpdate: DeltaUpdate{Reset: false}, },
validModTimes: true, },
{
name: "TwoPages FilterFailsAll",
pagerGetter: func(t *testing.T, validModTimes bool) *testIDsNonDeltaMultiPager {
return &testIDsNonDeltaMultiPager{
t: t,
pages: []pageResult{
{
items: []testItem{
addedItem1,
removedItem1,
},
},
{
items: []testItem{
addedItem2,
removedItem2,
},
},
},
validModTimes: validModTimes,
}
},
filter: func(testItem) bool { return false },
expect: expected{
errCheck: assert.NoError,
},
},
{
name: "Error",
pagerGetter: func(t *testing.T, validModTimes bool) *testIDsNonDeltaMultiPager {
return &testIDsNonDeltaMultiPager{
t: t,
pages: []pageResult{
{
err: assert.AnError,
},
},
validModTimes: validModTimes,
}
},
expect: expected{
errCheck: assert.Error,
}, },
canDelta: true,
}, },
} }
for _, test := range tests { for _, modTimeTest := range modTimeTests {
suite.Run(modTimeTest.name, func() {
for _, pagerTypeTest := range pagerTypeTests {
suite.Run(pagerTypeTest.name, func() {
for _, test := range table {
suite.Run(test.name, func() { suite.Run(test.name, func() {
t := suite.T() t := suite.T()
@ -568,33 +612,281 @@ func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs() {
filters = append(filters, test.filter) filters = append(filters, test.filter)
} }
aar, err := GetAddedAndRemovedItemIDs[testItem]( basePager := test.pagerGetter(t, modTimeTest.validModTimes)
getter, deltaGetter := pagerTypeTest.pagersFunc(basePager)
addRemoved, err := GetAddedAndRemovedItemIDs[testItem](
ctx, ctx,
test.pagerGetter(t), getter,
test.deltaPagerGetter(t), deltaGetter,
test.prevDelta, pagerTypeTest.prevDelta,
test.canDelta, pagerTypeTest.canUseDelta,
AddedAndRemovedByAddtlData[testItem], AddedAndRemovedByAddtlData[testItem],
filters...) filters...)
test.expect.errCheck(t, err, "getting added and removed item IDs: %+v", clues.ToCore(err))
expectAddedMap := map[string]time.Time{} if err != nil {
for _, item := range test.expect.added { return
expectAddedMap[item.id] = item.modTime
} }
require.NoErrorf(t, err, "getting added and removed item IDs: %+v", clues.ToCore(err)) assert.Equal(t, modTimeTest.validModTimes, addRemoved.ValidModTimes, "valid mod times")
if aar.ValidModTimes { assert.Equal(t, pagerTypeTest.expectDeltaReset, addRemoved.DU.Reset, "delta update")
assert.Equal(t, expectAddedMap, aar.Added, "added item IDs and mod times")
} else { assertAddedAndRemoved(
assert.ElementsMatch(t, maps.Keys(expectAddedMap), maps.Keys(aar.Added), "added item IDs") t,
for _, modtime := range aar.Added { modTimeTest.validModTimes,
assert.True(t, modtime.After(epoch), "mod time after epoch") test.expect.added,
assert.False(t, modtime.Equal(time.Time{}), "non-zero mod time") addRemoved.Added,
test.expect.removed,
addRemoved.Removed)
})
}
})
}
})
} }
} }
assert.Equal(t, test.expect.validModTimes, aar.ValidModTimes, "valid mod times")
assert.EqualValues(t, test.expect.removed, aar.Removed, "removed item IDs") // TestGetAddedAndRemovedItemIDs_FallbackPagers tests that when pagers get reset
assert.Equal(t, test.expect.deltaUpdate, aar.DU, "delta update") // or need to fallback to other pager types things work as expected. This only
// tests for basic cases where we enumerate everything with the fallback pager.
// These are here mostly to ensure we clear the results from the invalid pager
// properly. If we can ensure that then other tests will ensure the fallback
// pager properly handles all the other things like item filtering, item limits,
// cancellation, etc.
func (suite *PagerUnitSuite) TestGetAddedAndRemovedItemIDs_FallbackPagers() {
type expected struct {
errCheck assert.ErrorAssertionFunc
added []testItem
removed []testItem
}
tests := []struct {
name string
pagerGetter func(
t *testing.T,
validModTimes bool,
) NonDeltaHandler[testItem]
deltaPagerGetter func(
t *testing.T,
validModTimes bool,
) DeltaHandler[testItem]
expect expected
}{
{
name: "TwoValidPages DeltaReset",
pagerGetter: nilPager,
deltaPagerGetter: func(
t *testing.T,
validModTimes bool,
) DeltaHandler[testItem] {
return newDeltaPager(
&testIDsNonDeltaMultiPager{
t: t,
pages: []pageResult{
{
items: []testItem{
addedItem1,
removedItem1,
},
},
{
errCode: "SyncStateNotFound",
needsReset: true,
},
{
items: []testItem{
removedItem2,
addedItem2,
},
},
},
validModTimes: validModTimes,
})
},
expect: expected{
errCheck: assert.NoError,
added: []testItem{
addedItem2,
},
removed: []testItem{
removedItem2,
},
},
},
{
name: "TwoPages DeltaResetAtEnd",
pagerGetter: nilPager,
deltaPagerGetter: func(
t *testing.T,
validModTimes bool,
) DeltaHandler[testItem] {
return newDeltaPager(
&testIDsNonDeltaMultiPager{
t: t,
pages: []pageResult{
{
items: []testItem{
addedItem("uno", time.Now()),
removedItem("tres"),
},
},
{
items: []testItem{
removedItem("quatro"),
addedItem("dos", time.Now()),
},
},
{
errCode: "SyncStateNotFound",
needsReset: true,
},
// Return an empty page to show no new results after reset.
{},
},
validModTimes: validModTimes,
})
},
expect: expected{
errCheck: assert.NoError,
},
},
{
name: "TwoValidPages DeltaNotSupported",
pagerGetter: func(
t *testing.T,
validModTimes bool,
) NonDeltaHandler[testItem] {
return &testIDsNonDeltaMultiPager{
t: t,
pages: []pageResult{
{
items: []testItem{
addedItem1,
removedItem1,
},
},
{
items: []testItem{
removedItem2,
addedItem2,
},
},
},
validModTimes: validModTimes,
}
},
deltaPagerGetter: func(
t *testing.T,
validModTimes bool,
) DeltaHandler[testItem] {
return newDeltaPager(
&testIDsNonDeltaMultiPager{
t: t,
pages: []pageResult{
{
err: graph.ErrDeltaNotSupported,
needsReset: true,
},
},
validModTimes: validModTimes,
})
},
expect: expected{
errCheck: assert.NoError,
added: []testItem{
addedItem1,
addedItem2,
},
removed: []testItem{
removedItem1,
removedItem2,
},
},
},
{
name: "TwoPages DeltaNotSupportedAtEnd",
pagerGetter: func(
t *testing.T,
validModTimes bool,
) NonDeltaHandler[testItem] {
return &testIDsNonDeltaMultiPager{
t: t,
pages: []pageResult{
// Return an empty page.
{},
},
validModTimes: validModTimes,
}
},
deltaPagerGetter: func(
t *testing.T,
validModTimes bool,
) DeltaHandler[testItem] {
return newDeltaPager(
&testIDsNonDeltaMultiPager{
t: t,
pages: []pageResult{
{
items: []testItem{
addedItem1,
removedItem1,
},
},
{
items: []testItem{
removedItem2,
addedItem2,
},
},
{
err: graph.ErrDeltaNotSupported,
needsReset: true,
},
},
validModTimes: validModTimes,
})
},
expect: expected{
errCheck: assert.NoError,
},
},
}
for _, modTimeTest := range modTimeTests {
suite.Run(modTimeTest.name, func() {
for _, test := range tests {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
addRemoved, err := GetAddedAndRemovedItemIDs[testItem](
ctx,
test.pagerGetter(t, modTimeTest.validModTimes),
test.deltaPagerGetter(t, modTimeTest.validModTimes),
"a",
true,
AddedAndRemovedByAddtlData[testItem])
require.NoError(
t,
err,
"getting added and removed item IDs: %+v",
clues.ToCore(err))
assert.Equal(t, modTimeTest.validModTimes, addRemoved.ValidModTimes, "valid mod times")
assert.True(t, addRemoved.DU.Reset, "delta update")
assertAddedAndRemoved(
t,
modTimeTest.validModTimes,
test.expect.added,
addRemoved.Added,
test.expect.removed,
addRemoved.Removed)
})
}
}) })
} }
} }