blob: 386536696018e549bcba66cead50618b880c38ed [file] [log] [blame]
package fs_ignorestore
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.skia.org/infra/go/deepequal"
"go.skia.org/infra/go/firestore"
"go.skia.org/infra/go/testutils/unittest"
"go.skia.org/infra/golden/go/ignore"
)
func TestCreateListIgnoreRule(t *testing.T) {
unittest.LargeTest(t)
c, cleanup := firestore.NewClientForTesting(t)
defer cleanup()
ctx := context.Background()
f := newEmptyStore(ctx, t, c)
xir := makeIgnoreRules()
// Add them in a not-sorted order to make sure List sorts them.
require.NoError(t, f.Create(ctx, xir[2]))
require.NoError(t, f.Create(ctx, xir[0]))
require.NoError(t, f.Create(ctx, xir[3]))
require.NoError(t, f.Create(ctx, xir[1]))
requireCurrentListMatchesExpected(t, ctx, f)
}
func TestCreateDelete(t *testing.T) {
unittest.LargeTest(t)
c, cleanup := firestore.NewClientForTesting(t)
defer cleanup()
ctx := context.Background()
f := newEmptyStore(ctx, t, c)
xir := makeIgnoreRules()
// Add 0, 1, 2, 2, 2, 2, 3 (there are 3 extra of index 2)
require.NoError(t, f.Create(ctx, xir[0]))
require.NoError(t, f.Create(ctx, xir[1]))
require.NoError(t, f.Create(ctx, xir[2]))
require.NoError(t, f.Create(ctx, xir[2]))
require.NoError(t, f.Create(ctx, xir[2]))
require.NoError(t, f.Create(ctx, xir[2]))
require.NoError(t, f.Create(ctx, xir[3]))
// Wait until those 7 rules are in the list
require.Eventually(t, func() bool {
actualRules, _ := f.List(ctx)
return len(actualRules) == 7
}, 5*time.Second, 200*time.Millisecond)
// Re-query the rules to make sure none got dropped or added unexpectedly.
actualRules, err := f.List(ctx)
require.NoError(t, err)
require.Len(t, actualRules, 7) // should still have 7 elements in the list
ok, err := f.Delete(ctx, actualRules[3].ID)
require.NoError(t, err)
assert.True(t, ok)
ok, err = f.Delete(ctx, actualRules[4].ID)
require.NoError(t, err)
assert.True(t, ok)
ok, err = f.Delete(ctx, actualRules[5].ID)
require.NoError(t, err)
assert.True(t, ok)
requireCurrentListMatchesExpected(t, ctx, f)
}
func TestDeleteNonExistentRule(t *testing.T) {
unittest.LargeTest(t)
c, cleanup := firestore.NewClientForTesting(t)
defer cleanup()
ctx := context.Background()
f := New(ctx, c)
ok, err := f.Delete(ctx, "Not in there")
require.NoError(t, err)
assert.False(t, ok)
}
func TestDeleteEmptyRule(t *testing.T) {
unittest.LargeTest(t)
c, cleanup := firestore.NewClientForTesting(t)
defer cleanup()
ctx := context.Background()
f := New(ctx, c)
_, err := f.Delete(ctx, "")
require.Error(t, err)
assert.Contains(t, err.Error(), "empty")
}
func TestCreateUpdate(t *testing.T) {
unittest.LargeTest(t)
c, cleanup := firestore.NewClientForTesting(t)
defer cleanup()
ctx := context.Background()
f := newEmptyStore(ctx, t, c)
xir := makeIgnoreRules()
require.NoError(t, f.Create(ctx, xir[1]))
// Wait until that rule is in the list
require.Eventually(t, func() bool {
actualRules, _ := f.List(ctx)
return len(actualRules) == 1
}, 5*time.Second, 200*time.Millisecond)
actualRules, err := f.List(ctx)
require.NoError(t, err)
toUpdateID := actualRules[0].ID
newContent := xir[0]
newContent.ID = toUpdateID
require.NoError(t, f.Update(ctx, newContent))
require.Eventually(t, func() bool {
rules, err := f.List(ctx)
assert.NoError(t, err)
return compareIgnoreRulesIgnoringIDs(rules, []ignore.Rule{
{
// Notice the Name is the same as the original rule, but everything else is changed.
CreatedBy: "beta@example.com",
UpdatedBy: "alpha@example.com",
Expires: now.Add(-time.Hour),
Query: "config=gpu",
Note: "expired",
},
})
}, 5*time.Second, 200*time.Millisecond)
}
func TestUpdateNonExistentRule(t *testing.T) {
unittest.LargeTest(t)
c, cleanup := firestore.NewClientForTesting(t)
defer cleanup()
ctx := context.Background()
f := New(ctx, c)
ir := makeIgnoreRules()[0]
ir.ID = "whoops"
err := f.Update(ctx, ir)
require.Error(t, err)
assert.Contains(t, err.Error(), "before updating")
}
func TestUpdateEmptyRule(t *testing.T) {
unittest.LargeTest(t)
c, cleanup := firestore.NewClientForTesting(t)
defer cleanup()
ctx := context.Background()
f := New(ctx, c)
err := f.Update(ctx, ignore.Rule{})
require.Error(t, err)
assert.Contains(t, err.Error(), "empty")
}
var now = time.Date(2020, time.January, 13, 14, 15, 16, 0, time.UTC)
func newEmptyStore(ctx context.Context, t *testing.T, c *firestore.Client) *StoreImpl {
f := New(ctx, c)
empty, err := f.List(ctx)
require.NoError(t, err)
require.Empty(t, empty)
return f
}
func makeIgnoreRules() []ignore.Rule {
return []ignore.Rule{
ignore.NewRule("alpha@example.com", now.Add(-time.Hour), "config=gpu", "expired"),
ignore.NewRule("beta@example.com", now.Add(time.Minute*10), "config=8888", "No good reason."),
ignore.NewRule("beta@example.com", now.Add(time.Minute*50), "extra=123&extra=abc", "Ignore multiple."),
ignore.NewRule("alpha@example.com", now.Add(time.Minute*100), "extra=123&extra=abc&config=8888", "Ignore multiple 2."),
}
}
// compareIgnoreRulesIgnoringIDs returns true if the two lists of rules match (disregarding the ID).
// ID is ignored because it is nondeterministic.
func compareIgnoreRulesIgnoringIDs(first []ignore.Rule, second []ignore.Rule) bool {
if len(first) != len(second) {
return false
}
for i := range first {
r1, r2 := first[i], second[i]
r1.ID = ""
r2.ID = ""
if !deepequal.DeepEqual(r1, r2) {
return false
}
}
return true
}
// requireCurrentListMatchesExpected either returns because the content in the given store
// matches makeIgnoreRules() or it panics because it does not match.
func requireCurrentListMatchesExpected(t *testing.T, ctx context.Context, f *StoreImpl) {
// List uses a query snapshot, which is not synchronous, so we might have to query a few times
// before everything syncs up.
require.Eventually(t, func() bool {
actualRules, err := f.List(ctx)
assert.NoError(t, err)
return compareIgnoreRulesIgnoringIDs(actualRules, makeIgnoreRules())
}, 5*time.Second, 200*time.Millisecond)
}