blob: e69eee7aa681ce8ef8de0f93c389570a312e874e [file] [log] [blame]
package sqlignorestore
import (
"context"
"strings"
"testing"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.skia.org/infra/go/paramtools"
"go.skia.org/infra/go/testutils/unittest"
"go.skia.org/infra/golden/go/ignore"
"go.skia.org/infra/golden/go/sql/databuilder"
dks "go.skia.org/infra/golden/go/sql/datakitchensink"
"go.skia.org/infra/golden/go/sql/schema"
"go.skia.org/infra/golden/go/sql/sqltest"
"go.skia.org/infra/golden/go/types"
)
func TestCreate_RulesAppearInSQLTableAndCanBeListed(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
store := New(db)
require.NoError(t, store.Create(ctx, ignore.Rule{
CreatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Query: "model=NvidiaShield2015",
Note: "skbug.com/1234",
}))
require.NoError(t, store.Create(ctx, ignore.Rule{
CreatedBy: "otheruser@example.com",
Expires: time.Date(2018, time.January, 10, 10, 10, 0, 0, time.UTC),
Query: "model=Pixel1&os=foo&model=Pixel2",
Note: "skbug.com/54678",
}))
// It's good to query the database directly for at least one test, so we can verify List()
// is returning the proper data.
actualRows := sqltest.GetAllRows(ctx, t, db, "IgnoreRules", &schema.IgnoreRuleRow{}).([]schema.IgnoreRuleRow)
require.Len(t, actualRows, 2)
firstID := actualRows[0].IgnoreRuleID
secondID := actualRows[1].IgnoreRuleID
assert.Equal(t, []schema.IgnoreRuleRow{{
IgnoreRuleID: firstID,
CreatorEmail: "otheruser@example.com",
UpdatedEmail: "otheruser@example.com",
Expires: time.Date(2018, time.January, 10, 10, 10, 0, 0, time.UTC),
Note: "skbug.com/54678",
Query: paramtools.ReadOnlyParamSet{
"model": []string{"Pixel1", "Pixel2"},
"os": []string{"foo"},
},
}, {
IgnoreRuleID: secondID,
CreatorEmail: "me@example.com",
UpdatedEmail: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Note: "skbug.com/1234",
Query: paramtools.ReadOnlyParamSet{"model": []string{"NvidiaShield2015"}},
}}, actualRows)
rules, err := store.List(ctx)
require.NoError(t, err)
assert.Equal(t, []ignore.Rule{{
ID: firstID.String(),
CreatedBy: "otheruser@example.com",
UpdatedBy: "otheruser@example.com",
Expires: time.Date(2018, time.January, 10, 10, 10, 0, 0, time.UTC),
Query: "model=Pixel1&model=Pixel2&os=foo",
Note: "skbug.com/54678",
}, {
ID: secondID.String(),
CreatedBy: "me@example.com",
UpdatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Query: "model=NvidiaShield2015",
Note: "skbug.com/1234",
}}, rules)
}
func TestCreate_InvalidQuery_ReturnsError(t *testing.T) {
unittest.SmallTest(t)
store := New(nil)
require.Error(t, store.Create(context.Background(), ignore.Rule{
CreatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Query: "%NOT A VALID QUERY",
Note: "skbug.com/1234",
}))
}
func TestCreate_AllIgnoreStatusesNull_TracesMatchingRuleUpdated(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := loadTestData()
// Set everything to null
for i := range existingData.Traces {
existingData.Traces[i].MatchesAnyIgnoreRule = schema.NBNull
}
for i := range existingData.ValuesAtHead {
existingData.ValuesAtHead[i].MatchesAnyIgnoreRule = schema.NBNull
}
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
store := New(db)
require.NoError(t, store.Create(ctx, ignore.Rule{
CreatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Query: "model=Sailfish&os=Android",
Note: "skbug.com/1234",
}))
expectedStatuses := map[string]schema.NullableBool{
"SailfishOne": schema.NBTrue, // changed
"SailfishTwo": schema.NBTrue, // changed
"SailfishThree": schema.NBTrue, // changed
"BullheadOne": schema.NBNull, // untouched
"BullheadTwo": schema.NBNull, // untouched
"BullheadThree": schema.NBNull, // untouched
}
actualTraces := getTracesAndStatus(ctx, t, db, "model", "name")
assert.Equal(t, expectedStatuses, actualTraces)
actualValuesAtHead := getValuesAtHeadAndStatus(ctx, t, db, "model", "name")
assert.Equal(t, expectedStatuses, actualValuesAtHead)
}
func TestCreate_RuleWithSomeMissingValues_TracesMatchingRuleUpdated(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := loadTestData()
// Set everything to null
for i := range existingData.Traces {
existingData.Traces[i].MatchesAnyIgnoreRule = schema.NBNull
}
for i := range existingData.ValuesAtHead {
existingData.ValuesAtHead[i].MatchesAnyIgnoreRule = schema.NBNull
}
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
store := New(db)
require.NoError(t, store.Create(ctx, ignore.Rule{
CreatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
// This rule has some models and names that don't exist. That shouldn't cause anything
// to fail - only those traces that do match will be counted.
Query: "model=Bullhead&model=Snorlax&model=Bellsprout&os=Android&name=Three&name=Two&name=Four",
Note: "skbug.com/1234",
}))
expectedStatuses := map[string]schema.NullableBool{
"SailfishOne": schema.NBNull, // untouched
"SailfishTwo": schema.NBNull, // untouched
"SailfishThree": schema.NBNull, // untouched
"BullheadOne": schema.NBNull, // untouched
"BullheadTwo": schema.NBTrue, // changed
"BullheadThree": schema.NBTrue, // changed
}
actualTraces := getTracesAndStatus(ctx, t, db, "model", "name")
assert.Equal(t, expectedStatuses, actualTraces)
actualValuesAtHead := getValuesAtHeadAndStatus(ctx, t, db, "model", "name")
assert.Equal(t, expectedStatuses, actualValuesAtHead)
}
func TestCreate_RuleWithMissingKey_NothingUpdated(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := loadTestData()
// Set everything to null
for i := range existingData.Traces {
existingData.Traces[i].MatchesAnyIgnoreRule = schema.NBNull
}
for i := range existingData.ValuesAtHead {
existingData.ValuesAtHead[i].MatchesAnyIgnoreRule = schema.NBNull
}
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
store := New(db)
require.NoError(t, store.Create(ctx, ignore.Rule{
CreatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Query: "model=Snorlax&os=Android",
Note: "skbug.com/1234",
}))
expectedStatuses := map[string]schema.NullableBool{
"SailfishOne": schema.NBNull, // untouched
"SailfishTwo": schema.NBNull, // untouched
"SailfishThree": schema.NBNull, // untouched
"BullheadOne": schema.NBNull, // untouched
"BullheadTwo": schema.NBNull, // untouched
"BullheadThree": schema.NBNull, // untouched
}
actualTraces := getTracesAndStatus(ctx, t, db, "model", "name")
assert.Equal(t, expectedStatuses, actualTraces)
actualValuesAtHead := getValuesAtHeadAndStatus(ctx, t, db, "model", "name")
assert.Equal(t, expectedStatuses, actualValuesAtHead)
}
// loadTestData creates 6 traces of varying ignore states (2 ignored, 4 not) with a single
// ignore rule.
func loadTestData() schema.Tables {
data := databuilder.TablesBuilder{}
data.CommitsWithData().Insert("123", "whoever@example.com", "initial commit", "2021-01-11T16:00:00Z")
data.SetDigests(map[rune]types.Digest{
'a': dks.DigestA04Unt,
})
data.SetGroupingKeys(types.CorpusField, types.PrimaryKeyField)
data.AddTracesWithCommonKeys(paramtools.Params{types.CorpusField: "gm", "os": "Android"}).
History("a", "a", "a", "a", "a", "a").Keys([]paramtools.Params{
{"model": "Sailfish", types.PrimaryKeyField: "One"},
{"model": "Sailfish", types.PrimaryKeyField: "Two"},
{"model": "Sailfish", types.PrimaryKeyField: "Three"},
{"model": "Bullhead", types.PrimaryKeyField: "One"},
{"model": "Bullhead", types.PrimaryKeyField: "Two"},
{"model": "Bullhead", types.PrimaryKeyField: "Three"},
}).OptionsAll(paramtools.Params{"ext": "png"}).
IngestedFrom([]string{"file"}, []string{"2021-01-11T16:05:00Z"})
data.AddIgnoreRule("me@example.com", "me@example.com", "2021-01-11T17:00:00Z", "ignore test 2",
paramtools.ParamSet{types.PrimaryKeyField: []string{"Two"}})
return data.Build()
}
func getTracesAndStatus(ctx context.Context, t *testing.T, db *pgxpool.Pool, keys ...string) map[string]schema.NullableBool {
require.NotEmpty(t, keys)
rows := sqltest.GetAllRows(ctx, t, db, "Traces", &schema.TraceRow{}).([]schema.TraceRow)
actualTraces := map[string]schema.NullableBool{}
for _, r := range rows {
combined := ""
for _, key := range keys {
combined += r.Keys[key]
}
actualTraces[combined] = r.MatchesAnyIgnoreRule
}
return actualTraces
}
func getValuesAtHeadAndStatus(ctx context.Context, t *testing.T, db *pgxpool.Pool, keys ...string) map[string]schema.NullableBool {
require.NotEmpty(t, keys)
rows := sqltest.GetAllRows(ctx, t, db, "ValuesAtHead", &schema.ValueAtHeadRow{}).([]schema.ValueAtHeadRow)
actualValues := map[string]schema.NullableBool{}
for _, r := range rows {
combined := ""
for _, key := range keys {
combined += r.Keys[key]
}
actualValues[combined] = r.MatchesAnyIgnoreRule
}
return actualValues
}
func TestUpdate_ExistingRuleExpanded_AdditionalTracesIgnored(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := dks.Build()
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
newExpires := time.Date(2022, time.January, 1, 1, 1, 1, 0, time.UTC)
store := New(db)
require.NoError(t, store.Update(ctx, ignore.Rule{
ID: idForRule(existingData.IgnoreRules, "Taimen"),
CreatedBy: dks.UserTwo,
UpdatedBy: dks.UserFour,
Expires: newExpires,
// This rule previously ignored the square and circle test. Now it ignores all three tests.
Query: "device=taimen&name=square&name=triangle&name=circle",
Note: "Should ignore all 3 tests now",
}))
actualTraces := getTracesAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBTrue, actualTraces["taimencircle"]) // Still ignored
assert.Equal(t, schema.NBTrue, actualTraces["taimensquare"]) // Still ignored
assert.Equal(t, schema.NBTrue, actualTraces["taimentriangle"]) // Updated
assert.Equal(t, schema.NBFalse, actualTraces["walleyeround rect"]) // not affected
assert.Equal(t, schema.NBFalse, actualTraces["iPad6,3square"]) // not affected
actualValuesAtHead := getValuesAtHeadAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBTrue, actualValuesAtHead["taimencircle"]) // Still ignored
assert.Equal(t, schema.NBTrue, actualValuesAtHead["taimensquare"]) // Still ignored
assert.Equal(t, schema.NBTrue, actualValuesAtHead["taimentriangle"]) // Updated
// walleye + round rect is not landed, so it is not in the table ValuesAtHead
assert.Equal(t, schema.NBFalse, actualValuesAtHead["iPad6,3square"]) // not affected
actualIgnoreRules := sqltest.GetAllRows(ctx, t, db, "IgnoreRules", &schema.IgnoreRuleRow{}).([]schema.IgnoreRuleRow)
assert.ElementsMatch(t, []schema.IgnoreRuleRow{{
IgnoreRuleID: uuid.MustParse(idForRule(existingData.IgnoreRules, "Taimen")),
CreatorEmail: dks.UserTwo,
UpdatedEmail: dks.UserFour, // Updated
Expires: newExpires, // Updated
Note: "Should ignore all 3 tests now", // Updated
Query: paramtools.ReadOnlyParamSet{
dks.DeviceKey: []string{dks.TaimenDevice},
types.PrimaryKeyField: []string{dks.CircleTest, dks.SquareTest, dks.TriangleTest},
},
}, {
IgnoreRuleID: uuid.MustParse(idForRule(existingData.IgnoreRules, "expired")),
CreatorEmail: dks.UserTwo,
UpdatedEmail: dks.UserOne,
Expires: time.Date(2020, time.February, 14, 13, 12, 11, 0, time.UTC),
Note: "This rule has expired (and does not apply to anything)",
Query: paramtools.ReadOnlyParamSet{
dks.DeviceKey: []string{"Nokia4"},
types.CorpusField: []string{dks.CornersCorpus},
},
}}, actualIgnoreRules)
}
func TestUpdate_QueryNotChanged_IgnoreRuleUpdated(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := dks.Build()
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
newExpires := time.Date(2022, time.January, 1, 1, 1, 1, 0, time.UTC)
store := New(db)
require.NoError(t, store.Update(ctx, ignore.Rule{
ID: idForRule(existingData.IgnoreRules, "Taimen"),
CreatedBy: dks.UserTwo,
UpdatedBy: dks.UserFour,
Expires: newExpires,
Query: "device=taimen&name=square&name=circle",
Note: "Note and expires was updated",
}))
actualTraces := getTracesAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBTrue, actualTraces["taimencircle"])
assert.Equal(t, schema.NBTrue, actualTraces["taimensquare"])
assert.Equal(t, schema.NBFalse, actualTraces["taimentriangle"])
assert.Equal(t, schema.NBFalse, actualTraces["walleyeround rect"])
assert.Equal(t, schema.NBFalse, actualTraces["iPad6,3square"])
actualValuesAtHead := getValuesAtHeadAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBTrue, actualValuesAtHead["taimencircle"])
assert.Equal(t, schema.NBTrue, actualValuesAtHead["taimensquare"])
assert.Equal(t, schema.NBFalse, actualValuesAtHead["taimentriangle"])
assert.Equal(t, schema.NBFalse, actualValuesAtHead["iPad6,3square"])
actualIgnoreRules := sqltest.GetAllRows(ctx, t, db, "IgnoreRules", &schema.IgnoreRuleRow{}).([]schema.IgnoreRuleRow)
assert.ElementsMatch(t, []schema.IgnoreRuleRow{{
IgnoreRuleID: uuid.MustParse(idForRule(existingData.IgnoreRules, "Taimen")),
CreatorEmail: dks.UserTwo,
UpdatedEmail: dks.UserFour, // Updated
Expires: newExpires, // Updated
Note: "Note and expires was updated", // Updated
Query: paramtools.ReadOnlyParamSet{
dks.DeviceKey: []string{dks.TaimenDevice},
types.PrimaryKeyField: []string{dks.CircleTest, dks.SquareTest},
},
}, {
IgnoreRuleID: uuid.MustParse(idForRule(existingData.IgnoreRules, "expired")),
CreatorEmail: dks.UserTwo,
UpdatedEmail: dks.UserOne,
Expires: time.Date(2020, time.February, 14, 13, 12, 11, 0, time.UTC),
Note: "This rule has expired (and does not apply to anything)",
Query: paramtools.ReadOnlyParamSet{
dks.DeviceKey: []string{"Nokia4"},
types.CorpusField: []string{dks.CornersCorpus},
},
}}, actualIgnoreRules)
}
func TestUpdate_ExistingRuleReduced_FewerTracesIgnored(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := dks.Build()
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
newExpires := time.Date(2022, time.January, 1, 1, 1, 1, 0, time.UTC)
store := New(db)
require.NoError(t, store.Update(ctx, ignore.Rule{
ID: idForRule(existingData.IgnoreRules, "Taimen"),
CreatedBy: dks.UserTwo,
UpdatedBy: dks.UserFour,
Expires: newExpires,
// This rule previously ignored the square and circle test. Now it ignores just the one.
Query: "device=taimen&name=square",
Note: "Should ignore one test now",
}))
actualTraces := getTracesAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBFalse, actualTraces["taimencircle"]) // updated
assert.Equal(t, schema.NBTrue, actualTraces["taimensquare"]) // still ignored
assert.Equal(t, schema.NBFalse, actualTraces["taimentriangle"]) // still not ignored
assert.Equal(t, schema.NBFalse, actualTraces["walleyeround rect"]) // not affected
assert.Equal(t, schema.NBFalse, actualTraces["iPad6,3square"]) // not affected
actualValuesAtHead := getValuesAtHeadAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBFalse, actualValuesAtHead["taimencircle"]) // updated
assert.Equal(t, schema.NBTrue, actualValuesAtHead["taimensquare"]) // Still ignored
assert.Equal(t, schema.NBFalse, actualValuesAtHead["taimentriangle"]) // still not ignored
// walleye + round rect is not landed, so it is not in the table ValuesAtHead
assert.Equal(t, schema.NBFalse, actualValuesAtHead["iPad6,3square"]) // not affected
actualIgnoreRules := sqltest.GetAllRows(ctx, t, db, "IgnoreRules", &schema.IgnoreRuleRow{}).([]schema.IgnoreRuleRow)
assert.ElementsMatch(t, []schema.IgnoreRuleRow{{
IgnoreRuleID: uuid.MustParse(idForRule(existingData.IgnoreRules, "Taimen")),
CreatorEmail: dks.UserTwo,
UpdatedEmail: dks.UserFour, // Updated
Expires: newExpires, // Updated
Note: "Should ignore one test now", // Updated
Query: paramtools.ReadOnlyParamSet{
dks.DeviceKey: []string{dks.TaimenDevice},
types.PrimaryKeyField: []string{dks.SquareTest},
},
}, {
IgnoreRuleID: uuid.MustParse(idForRule(existingData.IgnoreRules, "expired")),
CreatorEmail: dks.UserTwo,
UpdatedEmail: dks.UserOne,
Expires: time.Date(2020, time.February, 14, 13, 12, 11, 0, time.UTC),
Note: "This rule has expired (and does not apply to anything)",
Query: paramtools.ReadOnlyParamSet{
dks.DeviceKey: []string{"Nokia4"},
types.CorpusField: []string{dks.CornersCorpus},
},
}}, actualIgnoreRules)
}
func TestUpdate_ExistingRuleChanged_DifferentTracesIgnored(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := dks.Build()
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
newExpires := time.Date(2022, time.January, 1, 1, 1, 1, 0, time.UTC)
store := New(db)
require.NoError(t, store.Update(ctx, ignore.Rule{
ID: idForRule(existingData.IgnoreRules, "Taimen"),
CreatedBy: dks.UserTwo,
UpdatedBy: dks.UserFour,
Expires: newExpires,
// This rule previously ignored the taimen square and circle test. Now it ignores the
// walleye device and two different tests.
Query: "device=walleye&name=triangle&name=round rect",
Note: "Should ignore walleye now",
}))
actualTraces := getTracesAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBFalse, actualTraces["taimencircle"]) // updated
assert.Equal(t, schema.NBFalse, actualTraces["taimensquare"]) // updated
assert.Equal(t, schema.NBFalse, actualTraces["taimentriangle"]) // still not ignored
assert.Equal(t, schema.NBFalse, actualTraces["walleyecircle"]) // still not ignored
assert.Equal(t, schema.NBFalse, actualTraces["walleyesquare"]) // updated
assert.Equal(t, schema.NBTrue, actualTraces["walleyetriangle"]) // still not ignored
assert.Equal(t, schema.NBTrue, actualTraces["walleyeround rect"]) // updated
assert.Equal(t, schema.NBFalse, actualTraces["iPad6,3square"]) // not affected
actualValuesAtHead := getValuesAtHeadAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBFalse, actualValuesAtHead["taimencircle"]) // updated
assert.Equal(t, schema.NBFalse, actualValuesAtHead["taimensquare"]) // updated
assert.Equal(t, schema.NBFalse, actualValuesAtHead["taimentriangle"]) // still not ignored
assert.Equal(t, schema.NBFalse, actualValuesAtHead["walleyecircle"]) // still not ignored
// walleye + round rect is not landed, so it is not in the table ValuesAtHead
assert.Equal(t, schema.NBTrue, actualValuesAtHead["walleyetriangle"]) // updated
assert.Equal(t, schema.NBFalse, actualValuesAtHead["iPad6,3square"]) // not affected
actualIgnoreRules := sqltest.GetAllRows(ctx, t, db, "IgnoreRules", &schema.IgnoreRuleRow{}).([]schema.IgnoreRuleRow)
assert.ElementsMatch(t, []schema.IgnoreRuleRow{{
IgnoreRuleID: uuid.MustParse(idForRule(existingData.IgnoreRules, "Taimen")),
CreatorEmail: dks.UserTwo,
UpdatedEmail: dks.UserFour, // Updated
Expires: newExpires, // Updated
Note: "Should ignore walleye now", // Updated
Query: paramtools.ReadOnlyParamSet{
dks.DeviceKey: []string{dks.WalleyeDevice},
types.PrimaryKeyField: []string{dks.RoundRectTest, dks.TriangleTest},
},
}, {
IgnoreRuleID: uuid.MustParse(idForRule(existingData.IgnoreRules, "expired")),
CreatorEmail: dks.UserTwo,
UpdatedEmail: dks.UserOne,
Expires: time.Date(2020, time.February, 14, 13, 12, 11, 0, time.UTC),
Note: "This rule has expired (and does not apply to anything)",
Query: paramtools.ReadOnlyParamSet{
dks.DeviceKey: []string{"Nokia4"},
types.CorpusField: []string{dks.CornersCorpus},
},
}}, actualIgnoreRules)
}
func idForRule(rules []schema.IgnoreRuleRow, s string) string {
for _, r := range rules {
if strings.Contains(r.Note, s) {
return r.IgnoreRuleID.String()
}
}
panic("Could not find rule with matching note")
}
func TestUpdate_InvalidID_ReturnsErrorAndNothingIsModified(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
store := New(db)
require.NoError(t, store.Create(ctx, ignore.Rule{
CreatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Query: "model=NvidiaShield2015",
Note: "skbug.com/1234",
}))
rules, err := store.List(ctx)
require.NoError(t, err)
require.Len(t, rules, 1)
recordID := rules[0].ID
require.Error(t, store.Update(ctx, ignore.Rule{
ID: "00000000-1111-2222-3333-444444444444",
UpdatedBy: "updator@example.com",
Expires: time.Date(2020, time.August, 3, 3, 3, 3, 0, time.UTC),
Query: "model=NvidiaShield2015&model=Pixel3",
Note: "See skbug.com/1234 for more",
}))
rules, err = store.List(ctx)
require.NoError(t, err)
require.Len(t, rules, 1)
assert.Equal(t, ignore.Rule{
ID: recordID,
CreatedBy: "me@example.com",
UpdatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Query: "model=NvidiaShield2015",
Note: "skbug.com/1234",
}, rules[0])
}
func TestUpdate_InvalidQuery_ReturnsError(t *testing.T) {
unittest.SmallTest(t)
store := New(nil)
require.Error(t, store.Update(context.Background(), ignore.Rule{
CreatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Query: "%NOT A VALID QUERY",
Note: "skbug.com/1234",
}))
}
func TestDelete_ExistingRule_RuleIsDeleted(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
store := New(db)
require.NoError(t, store.Create(ctx, ignore.Rule{
CreatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Query: "model=NvidiaShield2015",
Note: "skbug.com/1234",
}))
require.NoError(t, store.Create(ctx, ignore.Rule{
CreatedBy: "otheruser@example.com",
Expires: time.Date(2018, time.January, 10, 10, 10, 0, 0, time.UTC),
Query: "model=Pixel1&os=foo&model=Pixel2",
Note: "skbug.com/54678",
}))
rules, err := store.List(ctx)
require.NoError(t, err)
require.Len(t, rules, 2)
firstID := rules[0].ID
secondID := rules[1].ID
require.NoError(t, store.Delete(ctx, secondID))
rules, err = store.List(ctx)
require.NoError(t, err)
require.Len(t, rules, 1)
assert.Equal(t, ignore.Rule{
ID: firstID,
CreatedBy: "otheruser@example.com",
UpdatedBy: "otheruser@example.com",
Expires: time.Date(2018, time.January, 10, 10, 10, 0, 0, time.UTC),
Query: "model=Pixel1&model=Pixel2&os=foo",
Note: "skbug.com/54678",
}, rules[0])
}
func TestDelete_MissingID_ErrorReturnedAndNothingIsDeleted(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
store := New(db)
require.NoError(t, store.Create(ctx, ignore.Rule{
CreatedBy: "me@example.com",
Expires: time.Date(2020, time.May, 11, 10, 9, 0, 0, time.UTC),
Query: "model=NvidiaShield2015",
Note: "skbug.com/1234",
}))
rules, err := store.List(ctx)
require.NoError(t, err)
require.Len(t, rules, 1)
// It is extremely unlikely this is the actual record ID.
require.Error(t, store.Delete(ctx, "00000000-1111-2222-3333-444444444444"))
rules, err = store.List(ctx)
require.NoError(t, err)
require.Len(t, rules, 1)
}
func TestDelete_NoRulesRemain_NoTracesAreIgnored(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := loadTestData()
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
store := New(db)
require.NoError(t, store.Delete(ctx, existingData.IgnoreRules[0].IgnoreRuleID.String()))
expectedStatuses := map[string]schema.NullableBool{
"SailfishOne": schema.NBFalse, // changed
"SailfishTwo": schema.NBFalse, // changed
"SailfishThree": schema.NBFalse, // changed
"BullheadOne": schema.NBFalse, // untouched
"BullheadTwo": schema.NBFalse, // untouched
"BullheadThree": schema.NBFalse, // untouched
}
actualTraces := getTracesAndStatus(ctx, t, db, "model", "name")
assert.Equal(t, expectedStatuses, actualTraces)
actualValuesAtHead := getValuesAtHeadAndStatus(ctx, t, db, "model", "name")
assert.Equal(t, expectedStatuses, actualValuesAtHead)
}
func TestDelete_RuleAffectingNothingDeleted_RemainingRuleStillApplies(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := dks.Build()
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
store := New(db)
require.NoError(t, store.Delete(ctx, idForRule(existingData.IgnoreRules, "expired")))
actualTraces := getTracesAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBTrue, actualTraces["taimencircle"]) // Still ignored
assert.Equal(t, schema.NBTrue, actualTraces["taimensquare"]) // Still ignored
assert.Equal(t, schema.NBFalse, actualTraces["taimentriangle"]) // not affected
assert.Equal(t, schema.NBFalse, actualTraces["walleyeround rect"]) // not affected
assert.Equal(t, schema.NBFalse, actualTraces["iPad6,3square"]) // not affected
actualValuesAtHead := getValuesAtHeadAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBTrue, actualValuesAtHead["taimencircle"]) // Still ignored
assert.Equal(t, schema.NBTrue, actualValuesAtHead["taimensquare"]) // Still ignored
assert.Equal(t, schema.NBFalse, actualValuesAtHead["taimentriangle"]) // not affected
assert.Equal(t, schema.NBFalse, actualValuesAtHead["iPad6,3square"]) // not affected
}
func TestDelete_RuleAffectingSomethingDeleted_RemainingRuleStillApplies(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := dks.Build()
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
store := New(db)
require.NoError(t, store.Delete(ctx, idForRule(existingData.IgnoreRules, "Taimen")))
actualTraces := getTracesAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBFalse, actualTraces["taimencircle"]) // no longer ignored
assert.Equal(t, schema.NBFalse, actualTraces["taimensquare"]) // no longer ignored
assert.Equal(t, schema.NBFalse, actualTraces["taimentriangle"]) // not affected
assert.Equal(t, schema.NBFalse, actualTraces["walleyeround rect"]) // not affected
assert.Equal(t, schema.NBFalse, actualTraces["iPad6,3square"]) // not affected
actualValuesAtHead := getValuesAtHeadAndStatus(ctx, t, db, "device", "name")
assert.Equal(t, schema.NBFalse, actualValuesAtHead["taimencircle"]) // no longer ignored
assert.Equal(t, schema.NBFalse, actualValuesAtHead["taimensquare"]) // no longer ignored
assert.Equal(t, schema.NBFalse, actualValuesAtHead["taimentriangle"]) // not affected
assert.Equal(t, schema.NBFalse, actualValuesAtHead["iPad6,3square"]) // not affected
}
func TestList_Success(t *testing.T) {
unittest.LargeTest(t)
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
existingData := dks.Build()
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
store := New(db)
rules, err := store.List(ctx)
require.NoError(t, err)
assert.Equal(t, []ignore.Rule{{
ID: idForRule(existingData.IgnoreRules, "expired"),
CreatedBy: dks.UserTwo,
UpdatedBy: dks.UserOne,
Expires: time.Date(2020, time.February, 14, 13, 12, 11, 0, time.UTC),
Query: "device=Nokia4&source_type=corners",
Note: "This rule has expired (and does not apply to anything)",
}, {
ID: idForRule(existingData.IgnoreRules, "Taimen"),
CreatedBy: dks.UserTwo,
UpdatedBy: dks.UserOne,
Expires: time.Date(2030, time.December, 30, 15, 16, 17, 0, time.UTC),
Query: "device=taimen&name=square&name=circle",
Note: "Taimen isn't drawing correctly enough yet",
}}, rules)
}