state: use runCase pattern for large test

The TestServiceHealthEventsFromChanges function was over 1400 lines.
Attempting to debug test failures in test functions this large is
difficult. It requires scrolling to the line which defines the testcase
because the failure message only includes the line number of the
assertion, not the line number of the test case.

This is an excellent example of where test tables stop working well, and
start being a problem. To mitigate this problem, the runCase pattern can
be used. When one of these tests fails, a failure message will print the
line number of both the test case and the assertion. This allows a
developer to quickly jump to both of the relevant lines, signficanting
reducing the time it takes to debug test failures.

For example, one such failure could look like this:

    catalog_events_test.go:1610: case: service reg, new node
    catalog_events_test.go:1605: assertion failed: values are not equal
pull/9880/head
Daniel Nephin 2021-03-15 17:35:42 -04:00
parent a1d5e1fb41
commit 4d456922a9
2 changed files with 1401 additions and 1398 deletions

View File

@ -1,7 +0,0 @@
// +build !consulent
package state
func withServiceHealthEnterpriseCases(cases []serviceHealthTestCase) []serviceHealthTestCase {
return cases
}

View File

@ -196,7 +196,7 @@ func newIndexCounter() *indexCounter {
var _ stream.SnapshotAppender = (*snapshotAppender)(nil)
type serviceHealthTestCase struct {
type eventsTestCase struct {
Name string
Setup func(s *Store, tx *txn) error
Mutate func(s *Store, tx *txn) error
@ -206,10 +206,13 @@ type serviceHealthTestCase struct {
func TestServiceHealthEventsFromChanges(t *testing.T) {
setupIndex := uint64(10)
mutateIndex := uint64(100)
cases := []serviceHealthTestCase{
{
run := func(t *testing.T, tc eventsTestCase) {
t.Helper()
runCase(t, tc.Name, tc.run)
}
run(t, eventsTestCase{
Name: "irrelevant events",
Mutate: func(s *Store, tx *txn) error {
return kvsSetTxn(tx, tx.Index, &structs.DirEntry{
@ -219,8 +222,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
},
WantEvents: nil,
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "service reg, new node",
Mutate: func(s *Store, tx *txn) error {
return s.ensureRegistrationTxn(tx, tx.Index, false,
@ -230,8 +233,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthEvent(t, "web"),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "service reg, existing node",
Setup: func(s *Store, tx *txn) error {
return s.ensureRegistrationTxn(tx, tx.Index, false,
@ -246,8 +249,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthEvent(t, "web", evNodeUnchanged),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "service dereg, existing node",
Setup: func(s *Store, tx *txn) error {
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
@ -268,8 +271,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthDeregistrationEvent(t, "web"),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "node dereg",
Setup: func(s *Store, tx *txn) error {
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "db"), false); err != nil {
@ -289,8 +292,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthDeregistrationEvent(t, "web"),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "connect native reg, new node",
Mutate: func(s *Store, tx *txn) error {
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web", regConnectNative), false)
@ -302,8 +305,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthEvent(t, "web", evConnectNative, evConnectTopic),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "connect native reg, existing node",
Setup: func(s *Store, tx *txn) error {
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "db"), false)
@ -323,8 +326,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evConnectTopic),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "connect native dereg, existing node",
Setup: func(s *Store, tx *txn) error {
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "db"), false); err != nil {
@ -342,8 +345,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthDeregistrationEvent(t, "web", evConnectNative, evConnectTopic),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "connect sidecar reg, new node",
Mutate: func(s *Store, tx *txn) error {
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web"), false); err != nil {
@ -359,8 +362,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthEvent(t, "web", evConnectTopic, evSidecar),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "connect sidecar reg, existing node",
Setup: func(s *Store, tx *txn) error {
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
@ -381,8 +384,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthEvent(t, "web", evConnectTopic, evSidecar, evNodeUnchanged),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "connect sidecar dereg, existing node",
Setup: func(s *Store, tx *txn) error {
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
@ -406,8 +409,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthDeregistrationEvent(t, "web", evConnectTopic, evSidecar),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "connect sidecar mutate svc",
Setup: func(s *Store, tx *txn) error {
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
@ -437,8 +440,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "connect sidecar mutate sidecar",
Setup: func(s *Store, tx *txn) error {
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
@ -474,8 +477,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evChecksUnchanged),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "connect sidecar rename service",
Setup: func(s *Store, tx *txn) error {
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
@ -531,8 +534,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "connect sidecar change destination service",
Setup: func(s *Store, tx *txn) error {
// Register a web_changed service
@ -583,8 +586,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "multi-service node update",
Setup: func(s *Store, tx *txn) error {
// Register a db service
@ -641,8 +644,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "multi-service node rename",
Setup: func(s *Store, tx *txn) error {
// Register a db service
@ -718,8 +721,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthDeregistrationEvent(t, "web", evConnectTopic, evSidecar),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "multi-service node check failure",
Setup: func(s *Store, tx *txn) error {
// Register a db service
@ -778,8 +781,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "multi-service node service check failure",
Setup: func(s *Store, tx *txn) error {
// Register a db service
@ -841,8 +844,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "multi-service node node-level check delete",
Setup: func(s *Store, tx *txn) error {
// Register a db service
@ -895,8 +898,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "multi-service node service check delete",
Setup: func(s *Store, tx *txn) error {
// Register a db service
@ -953,8 +956,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
),
},
WantErr: false,
},
{
})
run(t, eventsTestCase{
Name: "many services on many nodes in one TX",
Setup: func(s *Store, tx *txn) error {
// Node1
@ -1028,8 +1031,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
testServiceHealthEvent(t, "api", evNode2, evConnectNative, evNodeUnchanged),
testServiceHealthEvent(t, "api", evNode2, evConnectTopic, evConnectNative, evNodeUnchanged),
},
},
{
})
run(t, eventsTestCase{
Name: "terminating gateway registered with no config entry",
Mutate: func(s *Store, tx *txn) error {
return s.ensureRegistrationTxn(tx, tx.Index, false,
@ -1040,8 +1043,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
"tgate1",
evServiceTermingGateway("tgate1")),
},
},
{
})
run(t, eventsTestCase{
Name: "config entry created with no terminating gateway instance",
Mutate: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1058,8 +1061,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
return ensureConfigEntryTxn(tx, tx.Index, configEntry)
},
WantEvents: []stream.Event{},
},
{
})
run(t, eventsTestCase{
Name: "terminating gateway registered after config entry exists",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1117,8 +1120,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evServiceTermingGateway("srv2"),
evNode2),
},
},
{
})
run(t, eventsTestCase{
Name: "terminating gateway updated after config entry exists",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1174,8 +1177,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evNodeChecksMutated,
evServiceUnchanged),
},
},
{
})
run(t, eventsTestCase{
Name: "terminating gateway config entry created after gateway exists",
Setup: func(s *Store, tx *txn) error {
return s.ensureRegistrationTxn(tx, tx.Index, false,
@ -1211,8 +1214,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evServiceTermingGateway("srv2"),
evServiceIndex(setupIndex)),
},
},
{
})
run(t, eventsTestCase{
Name: "change the terminating gateway config entry to add a linked service",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1258,8 +1261,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evServiceTermingGateway("srv2"),
evServiceIndex(setupIndex)),
},
},
{
})
run(t, eventsTestCase{
Name: "change the terminating gateway config entry to remove a linked service",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1304,8 +1307,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evConnectTopic,
evServiceTermingGateway("srv1")),
},
},
{
})
run(t, eventsTestCase{
Name: "update a linked service within a terminating gateway config entry",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1352,8 +1355,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evServiceTermingGateway("srv1"),
evServiceIndex(setupIndex)),
},
},
{
})
run(t, eventsTestCase{
Name: "delete a terminating gateway config entry with a linked service",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1394,8 +1397,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evServiceTermingGateway("srv1"),
evNode2),
},
},
{
})
run(t, eventsTestCase{
Name: "create an instance of a linked service in a terminating gateway",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1422,8 +1425,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
WantEvents: []stream.Event{
testServiceHealthEvent(t, "srv1", evNodeUnchanged),
},
},
{
})
run(t, eventsTestCase{
Name: "delete an instance of a linked service in a terminating gateway",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1454,8 +1457,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
WantEvents: []stream.Event{
testServiceHealthDeregistrationEvent(t, "srv1"),
},
},
{
})
run(t, eventsTestCase{
Name: "rename a terminating gateway instance",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1524,8 +1527,8 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evServiceChecksMutated,
evTerminatingGatewayRenamed("tgate2")),
},
},
{
})
run(t, eventsTestCase{
Name: "delete a terminating gateway instance",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
@ -1566,14 +1569,14 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evConnectTopic,
evServiceTermingGateway("srv2")),
},
},
}
cases = withServiceHealthEnterpriseCases(cases)
})
}
for _, tc := range cases {
tc := tc
t.Run(tc.Name, func(t *testing.T) {
s := testStateStore(t)
func (tc eventsTestCase) run(t *testing.T) {
s := NewStateStore(nil)
setupIndex := uint64(10)
mutateIndex := uint64(100)
if tc.Setup != nil {
// Bypass the publish mechanism for this test or we get into odd
@ -1600,8 +1603,15 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
require.NoError(t, err)
assertDeepEqual(t, tc.WantEvents, got, cmpPartialOrderEvents, cmpopts.EquateEmpty())
}
func runCase(t *testing.T, name string, fn func(t *testing.T)) {
t.Helper()
t.Run(name, func(t *testing.T) {
t.Helper()
t.Log("case:", name)
fn(t)
})
}
}
func regTerminatingGateway(req *structs.RegisterRequest) error {