From 2560b66ebffe51c517c13af66804ae0889f9c2d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Rold=C3=A1n=20Betancort?= Date: Wed, 2 Oct 2024 12:06:27 +0100 Subject: [PATCH] make unit tests faster by enabling parallel tests. Made unit tests run 30% faster Also added some parallelism to Datastore tests, but opt-in. MemDB and datastore proxy tests are now parallel. Also changed CRDB test entry points to run in parallel, since the introduction of integrity tests meant double the tests to run, and sequentially --- internal/datastore/crdb/crdb_test.go | 10 +- internal/datastore/memdb/memdb_test.go | 5 +- internal/datastore/mysql/datastore_test.go | 6 +- internal/datastore/mysql/migrations/driver.go | 5 - .../postgres/postgres_shared_test.go | 4 +- internal/datastore/proxy/observable_test.go | 2 +- .../proxy/schemacaching/estimatedsize_test.go | 2 + internal/datastore/spanner/spanner_test.go | 4 +- internal/dispatch/graph/check_test.go | 11 +- internal/dispatch/graph/dispatch_test.go | 6 + internal/dispatch/graph/expand_test.go | 7 +- internal/dispatch/graph/graph_test.go | 4 + .../dispatch/graph/lookupresources2_test.go | 23 +-- .../dispatch/graph/lookupresources_test.go | 21 ++- .../dispatch/graph/lookupsubjects_test.go | 7 +- internal/dispatch/graph/package_test.go | 14 ++ .../dispatch/graph/reachableresources_test.go | 40 ++--- internal/dispatch/remote/cluster_test.go | 1 + internal/relationships/validation_test.go | 2 + .../services/integrationtesting/cert_test.go | 2 + .../integrationtesting/consistency_test.go | 1 + .../services/integrationtesting/ops_test.go | 3 + internal/services/v1/experimental_test.go | 3 + .../taskrunner/preloadedtaskrunner_test.go | 15 +- internal/testutil/subjects_test.go | 2 + magefiles/test.go | 20 ++- pkg/cmd/migrate.go | 8 + pkg/datastore/pagination/iterator_test.go | 2 + pkg/datastore/test/datastore.go | 166 ++++++++++-------- pkg/schemadsl/compiler/compiler_test.go | 7 + pkg/schemadsl/parser/parser_test.go | 1 + 31 files changed, 258 insertions(+), 146 deletions(-) create mode 100644 internal/dispatch/graph/package_test.go diff --git a/internal/datastore/crdb/crdb_test.go b/internal/datastore/crdb/crdb_test.go index 4bbefb8929..d90e91e25b 100644 --- a/internal/datastore/crdb/crdb_test.go +++ b/internal/datastore/crdb/crdb_test.go @@ -54,6 +54,7 @@ func (cds *crdbDatastore) ExampleRetryableError() error { } func TestCRDBDatastoreWithoutIntegrity(t *testing.T) { + t.Parallel() b := testdatastore.RunCRDBForTesting(t, "") test.All(t, test.DatastoreTesterFunc(func(revisionQuantization, gcInterval, gcWindow time.Duration, watchBufferLength uint16) (datastore.Datastore, error) { ctx := context.Background() @@ -72,10 +73,11 @@ func TestCRDBDatastoreWithoutIntegrity(t *testing.T) { }) return ds, nil - })) + }), false) } func TestCRDBDatastoreWithFollowerReads(t *testing.T) { + t.Parallel() followerReadDelay := time.Duration(4.8 * float64(time.Second)) gcWindow := 100 * time.Second @@ -136,6 +138,7 @@ var defaultKeyForTesting = proxy.KeyConfig{ } func TestCRDBDatastoreWithIntegrity(t *testing.T) { + t.Parallel() b := testdatastore.RunCRDBForTesting(t, "") test.All(t, test.DatastoreTesterFunc(func(revisionQuantization, gcInterval, gcWindow time.Duration, watchBufferLength uint16) (datastore.Datastore, error) { @@ -159,7 +162,7 @@ func TestCRDBDatastoreWithIntegrity(t *testing.T) { }) return ds, nil - })) + }), false) unwrappedTester := test.DatastoreTesterFunc(func(revisionQuantization, gcInterval, gcWindow time.Duration, watchBufferLength uint16) (datastore.Datastore, error) { ctx := context.Background() @@ -187,6 +190,7 @@ func TestCRDBDatastoreWithIntegrity(t *testing.T) { } func TestWatchFeatureDetection(t *testing.T) { + t.Parallel() pool, err := dockertest.NewPool("") require.NoError(t, err) cases := []struct { @@ -229,7 +233,9 @@ func TestWatchFeatureDetection(t *testing.T) { }, } for _, tt := range cases { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) adminConn, connStrings := newCRDBWithUser(t, pool) diff --git a/internal/datastore/memdb/memdb_test.go b/internal/datastore/memdb/memdb_test.go index 238ecee9e6..43b14d0880 100644 --- a/internal/datastore/memdb/memdb_test.go +++ b/internal/datastore/memdb/memdb_test.go @@ -26,10 +26,12 @@ func (mdbt memDBTest) New(revisionQuantization, _, gcWindow time.Duration, watch } func TestMemdbDatastore(t *testing.T) { - test.All(t, memDBTest{}) + t.Parallel() + test.All(t, memDBTest{}, true) } func TestConcurrentWritePanic(t *testing.T) { + t.Parallel() require := require.New(t) ds, err := NewMemdbDatastore(0, 1*time.Hour, 1*time.Hour) @@ -81,6 +83,7 @@ func TestConcurrentWritePanic(t *testing.T) { } func TestConcurrentWriteRelsError(t *testing.T) { + t.Parallel() require := require.New(t) ds, err := NewMemdbDatastore(0, 1*time.Hour, 1*time.Hour) diff --git a/internal/datastore/mysql/datastore_test.go b/internal/datastore/mysql/datastore_test.go index c9eeeec066..671749f595 100644 --- a/internal/datastore/mysql/datastore_test.go +++ b/internal/datastore/mysql/datastore_test.go @@ -93,14 +93,16 @@ func createDatastoreTest(b testdatastore.RunningEngineForTest, tf datastoreTestF } func TestMySQLDatastoreDSNWithoutParseTime(t *testing.T) { + t.Parallel() _, err := NewMySQLDatastore(context.Background(), "root:password@(localhost:1234)/mysql") require.ErrorContains(t, err, "https://spicedb.dev/d/parse-time-mysql") } func TestMySQL8Datastore(t *testing.T) { + t.Parallel() b := testdatastore.RunMySQLForTestingWithOptions(t, testdatastore.MySQLTesterOptions{MigrateForNewDatastore: true}, "") dst := datastoreTester{b: b, t: t} - test.AllWithExceptions(t, test.DatastoreTesterFunc(dst.createDatastore), test.WithCategories(test.WatchSchemaCategory, test.WatchCheckpointsCategory)) + test.AllWithExceptions(t, test.DatastoreTesterFunc(dst.createDatastore), test.WithCategories(test.WatchSchemaCategory, test.WatchCheckpointsCategory), true) additionalMySQLTests(t, b) } @@ -660,6 +662,7 @@ func TransactionTimestampsTest(t *testing.T, ds datastore.Datastore) { } func TestMySQLMigrations(t *testing.T) { + t.Parallel() req := require.New(t) db := datastoreDB(t, false) @@ -681,6 +684,7 @@ func TestMySQLMigrations(t *testing.T) { } func TestMySQLMigrationsWithPrefix(t *testing.T) { + t.Parallel() req := require.New(t) prefix := "spicedb_" diff --git a/internal/datastore/mysql/migrations/driver.go b/internal/datastore/mysql/migrations/driver.go index e09bf828ba..cddcf6ccb2 100644 --- a/internal/datastore/mysql/migrations/driver.go +++ b/internal/datastore/mysql/migrations/driver.go @@ -16,7 +16,6 @@ import ( sq "github.com/Masterminds/squirrel" sqlDriver "github.com/go-sql-driver/mysql" - log "github.com/authzed/spicedb/internal/logging" "github.com/authzed/spicedb/pkg/migrate" ) @@ -57,10 +56,6 @@ func NewMySQLDriverFromDSN(url string, tablePrefix string, credentialsProvider d } db := sql.OpenDB(connector) - err = sqlDriver.SetLogger(&log.Logger) - if err != nil { - return nil, fmt.Errorf("unable to set logging to mysql driver: %w", err) - } return NewMySQLDriverFromDB(db, tablePrefix), nil } diff --git a/internal/datastore/postgres/postgres_shared_test.go b/internal/datastore/postgres/postgres_shared_test.go index 6bd77b8271..ee8df58b92 100644 --- a/internal/datastore/postgres/postgres_shared_test.go +++ b/internal/datastore/postgres/postgres_shared_test.go @@ -101,7 +101,7 @@ func testPostgresDatastore(t *testing.T, pc []postgresConfig) { return ds }) return ds, nil - })) + }), false) t.Run("TransactionTimestamps", createDatastoreTest( b, @@ -236,7 +236,7 @@ func testPostgresDatastoreWithoutCommitTimestamps(t *testing.T, pc []postgresCon return ds }) return ds, nil - }), test.WithCategories(test.WatchCategory)) + }), test.WithCategories(test.WatchCategory), false) }) } } diff --git a/internal/datastore/proxy/observable_test.go b/internal/datastore/proxy/observable_test.go index 9644c82cdd..63a388957c 100644 --- a/internal/datastore/proxy/observable_test.go +++ b/internal/datastore/proxy/observable_test.go @@ -20,7 +20,7 @@ func (obs observableTest) New(revisionQuantization, _, gcWindow time.Duration, w } func TestObservableProxy(t *testing.T) { - test.All(t, observableTest{}) + test.All(t, observableTest{}, true) } func (p *observableProxy) ExampleRetryableError() error { diff --git a/internal/datastore/proxy/schemacaching/estimatedsize_test.go b/internal/datastore/proxy/schemacaching/estimatedsize_test.go index 7848d1843f..275f544bf7 100644 --- a/internal/datastore/proxy/schemacaching/estimatedsize_test.go +++ b/internal/datastore/proxy/schemacaching/estimatedsize_test.go @@ -91,6 +91,8 @@ func TestEstimatedDefinitionSizes(t *testing.T) { for _, caveatDef := range fullyResolved.CaveatDefinitions { caveatDef := caveatDef t.Run("caveat "+caveatDef.Name, func(t *testing.T) { + t.Parallel() + serialized, _ := caveatDef.MarshalVT() sizevt := caveatDef.SizeVT() estimated := estimatedCaveatDefinitionSize(sizevt) diff --git a/internal/datastore/spanner/spanner_test.go b/internal/datastore/spanner/spanner_test.go index 1fbc6c49f2..35cb4a3926 100644 --- a/internal/datastore/spanner/spanner_test.go +++ b/internal/datastore/spanner/spanner_test.go @@ -28,6 +28,8 @@ func (sd *spannerDatastore) ExampleRetryableError() error { } func TestSpannerDatastore(t *testing.T) { + t.Parallel() + ctx := context.Background() b := testdatastore.RunSpannerForTesting(t, "", "head") @@ -43,7 +45,7 @@ func TestSpannerDatastore(t *testing.T) { return ds }) return ds, nil - }), test.WithCategories(test.GCCategory, test.WatchCategory, test.StatsCategory)) + }), test.WithCategories(test.GCCategory, test.WatchCategory, test.StatsCategory), true) t.Run("TestFakeStats", createDatastoreTest( b, diff --git a/internal/dispatch/graph/check_test.go b/internal/dispatch/graph/check_test.go index eab41591f7..80a7615879 100644 --- a/internal/dispatch/graph/check_test.go +++ b/internal/dispatch/graph/check_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.uber.org/goleak" "github.com/authzed/spicedb/internal/datastore/common" "github.com/authzed/spicedb/internal/datastore/memdb" @@ -30,7 +29,7 @@ import ( var ONR = tuple.ObjectAndRelation func TestSimpleCheck(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() type expected struct { relation string @@ -119,6 +118,7 @@ func TestSimpleCheck(t *testing.T) { userset := userset expected := expected t.Run(name, func(t *testing.T) { + t.Parallel() require := require.New(t) ctx, dispatch, revision := newLocalDispatcher(t) @@ -150,6 +150,7 @@ func TestSimpleCheck(t *testing.T) { } func TestMaxDepth(t *testing.T) { + t.Parallel() require := require.New(t) rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) @@ -182,6 +183,7 @@ func TestMaxDepth(t *testing.T) { } func TestCheckMetadata(t *testing.T) { + t.Parallel() type expected struct { relation string isMember bool @@ -287,6 +289,7 @@ func TestCheckMetadata(t *testing.T) { } func TestCheckPermissionOverSchema(t *testing.T) { + t.Parallel() testCases := []struct { name string schema string @@ -1370,6 +1373,7 @@ func addFrame(trace *v1.CheckDebugTrace, foundFrames *mapz.Set[string]) { } func TestCheckDebugging(t *testing.T) { + t.Parallel() type expectedFrame struct { resourceType *core.RelationReference resourceIDs []string @@ -1482,6 +1486,7 @@ func TestCheckDebugging(t *testing.T) { } func TestCheckWithHints(t *testing.T) { + t.Parallel() testCases := []struct { name string schema string @@ -1853,6 +1858,7 @@ func TestCheckWithHints(t *testing.T) { } func TestCheckHintsPartialApplication(t *testing.T) { + t.Parallel() require := require.New(t) dispatcher := NewLocalOnlyDispatcher(10, 100) @@ -1898,6 +1904,7 @@ func TestCheckHintsPartialApplication(t *testing.T) { } func TestCheckHintsPartialApplicationOverArrow(t *testing.T) { + t.Parallel() require := require.New(t) dispatcher := NewLocalOnlyDispatcher(10, 100) diff --git a/internal/dispatch/graph/dispatch_test.go b/internal/dispatch/graph/dispatch_test.go index 5d428e7662..48ab615e63 100644 --- a/internal/dispatch/graph/dispatch_test.go +++ b/internal/dispatch/graph/dispatch_test.go @@ -15,6 +15,7 @@ import ( ) func TestDispatchChunking(t *testing.T) { + t.Parallel() schema := ` definition user { relation self: user @@ -35,6 +36,7 @@ func TestDispatchChunking(t *testing.T) { ctx, dispatcher, revision := newLocalDispatcherWithSchemaAndRels(t, schema, append(enabled, resources...)) t.Run("check", func(t *testing.T) { + t.Parallel() for _, tpl := range resources[:1] { checkResult, err := dispatcher.DispatchCheck(ctx, &v1.DispatchCheckRequest{ ResourceRelation: RR(tpl.ResourceAndRelation.Namespace, "view"), @@ -55,6 +57,8 @@ func TestDispatchChunking(t *testing.T) { }) t.Run("lookup-resources", func(t *testing.T) { + t.Parallel() + for _, tpl := range resources[:1] { stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupResourcesResponse](ctx) err := dispatcher.DispatchLookupResources(&v1.DispatchLookupResourcesRequest{ @@ -75,6 +79,8 @@ func TestDispatchChunking(t *testing.T) { }) t.Run("lookup-subjects", func(t *testing.T) { + t.Parallel() + for _, tpl := range resources[:1] { stream := dispatch.NewCollectingDispatchStream[*v1.DispatchLookupSubjectsResponse](ctx) diff --git a/internal/dispatch/graph/expand_test.go b/internal/dispatch/graph/expand_test.go index 06d2350e90..40b6aa552b 100644 --- a/internal/dispatch/graph/expand_test.go +++ b/internal/dispatch/graph/expand_test.go @@ -11,7 +11,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" - "go.uber.org/goleak" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/testing/protocmp" @@ -137,7 +136,7 @@ var ( ) func TestExpand(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() testCases := []struct { start *core.ObjectAndRelation @@ -275,7 +274,7 @@ func onrExpr(onr *core.ObjectAndRelation) ast.Expr { } func TestMaxDepthExpand(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() require := require.New(t) @@ -306,7 +305,7 @@ func TestMaxDepthExpand(t *testing.T) { } func TestExpandOverSchema(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() testCases := []struct { name string diff --git a/internal/dispatch/graph/graph_test.go b/internal/dispatch/graph/graph_test.go index 564bebce14..ee50150521 100644 --- a/internal/dispatch/graph/graph_test.go +++ b/internal/dispatch/graph/graph_test.go @@ -13,11 +13,14 @@ import ( ) func TestUnwrapStatusError(t *testing.T) { + t.Parallel() + err := rewriteError(context.Background(), graph.NewCheckFailureErr(status.Error(codes.Canceled, "canceled"))) grpcutil.RequireStatus(t, codes.Canceled, err) } func TestConcurrencyLimitsWithOverallDefaultLimit(t *testing.T) { + t.Parallel() cl := ConcurrencyLimits{} require.Equal(t, uint16(0), cl.Check) require.Equal(t, uint16(0), cl.LookupResources) @@ -38,6 +41,7 @@ func TestConcurrencyLimitsWithOverallDefaultLimit(t *testing.T) { } func TestSharedConcurrencyLimits(t *testing.T) { + t.Parallel() cl := SharedConcurrencyLimits(42) require.Equal(t, uint16(42), cl.Check) require.Equal(t, uint16(42), cl.LookupResources) diff --git a/internal/dispatch/graph/lookupresources2_test.go b/internal/dispatch/graph/lookupresources2_test.go index fdf123ff17..32bfd34c3f 100644 --- a/internal/dispatch/graph/lookupresources2_test.go +++ b/internal/dispatch/graph/lookupresources2_test.go @@ -21,13 +21,11 @@ import ( "github.com/authzed/spicedb/pkg/genutil/mapz" core "github.com/authzed/spicedb/pkg/proto/core/v1" v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" - "github.com/authzed/spicedb/pkg/testutil" "github.com/authzed/spicedb/pkg/tuple" ) func TestSimpleLookupResources2(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) - + // FIXME marking this parallel makes goleak detect a leaked goroutine testCases := []struct { start *core.RelationReference target *core.ObjectAndRelation @@ -159,7 +157,7 @@ func TestSimpleLookupResources2(t *testing.T) { } func TestSimpleLookupResourcesWithCursor2(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() for _, tc := range []struct { subject string @@ -242,7 +240,7 @@ func TestSimpleLookupResourcesWithCursor2(t *testing.T) { } func TestLookupResourcesCursorStability2(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() require := require.New(t) ctx, dispatcher, revision := newLocalDispatcher(t) @@ -309,6 +307,7 @@ func processResults2(stream *dispatch.CollectingDispatchStream[*v1.DispatchLooku } func TestMaxDepthLookup2(t *testing.T) { + t.Parallel() require := require.New(t) rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) @@ -338,6 +337,7 @@ func TestMaxDepthLookup2(t *testing.T) { } func TestLookupResources2OverSchemaWithCursors(t *testing.T) { + t.Parallel() testCases := []struct { name string schema string @@ -682,9 +682,11 @@ func TestLookupResources2OverSchemaWithCursors(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() for _, pageSize := range []int{0, 104, 1023} { pageSize := pageSize t.Run(fmt.Sprintf("ps-%d_", pageSize), func(t *testing.T) { + t.Parallel() require := require.New(t) dispatcher := NewLocalOnlyDispatcher(10, 100) @@ -741,10 +743,11 @@ func TestLookupResources2OverSchemaWithCursors(t *testing.T) { } foundResourceIDsSlice := foundResourceIDs.AsSlice() + expectedResourceIDs := slices.Clone(tc.expectedResourceIDs) slices.Sort(foundResourceIDsSlice) - slices.Sort(tc.expectedResourceIDs) + slices.Sort(expectedResourceIDs) - require.Equal(tc.expectedResourceIDs, foundResourceIDsSlice) + require.Equal(expectedResourceIDs, foundResourceIDsSlice) }) } }) @@ -752,7 +755,7 @@ func TestLookupResources2OverSchemaWithCursors(t *testing.T) { } func TestLookupResources2ImmediateTimeout(t *testing.T) { - defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) + t.Parallel() require := require.New(t) @@ -787,7 +790,7 @@ func TestLookupResources2ImmediateTimeout(t *testing.T) { } func TestLookupResources2WithError(t *testing.T) { - defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) + t.Parallel() require := require.New(t) @@ -822,7 +825,7 @@ func TestLookupResources2WithError(t *testing.T) { } func TestLookupResources2EnsureCheckHints(t *testing.T) { - defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) + t.Parallel() tcs := []struct { name string diff --git a/internal/dispatch/graph/lookupresources_test.go b/internal/dispatch/graph/lookupresources_test.go index 009eccb6e9..d8ecdca6b0 100644 --- a/internal/dispatch/graph/lookupresources_test.go +++ b/internal/dispatch/graph/lookupresources_test.go @@ -9,7 +9,6 @@ import ( "github.com/ccoveille/go-safecast" "github.com/stretchr/testify/require" - "go.uber.org/goleak" "github.com/authzed/spicedb/internal/datastore/memdb" "github.com/authzed/spicedb/internal/dispatch" @@ -18,7 +17,6 @@ import ( "github.com/authzed/spicedb/pkg/genutil/mapz" core "github.com/authzed/spicedb/pkg/proto/core/v1" v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" - "github.com/authzed/spicedb/pkg/testutil" "github.com/authzed/spicedb/pkg/tuple" ) @@ -39,7 +37,7 @@ func resolvedRes(resourceID string) *v1.ResolvedResource { } func TestSimpleLookupResources(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() testCases := []struct { start *core.RelationReference @@ -166,7 +164,7 @@ func TestSimpleLookupResources(t *testing.T) { } func TestSimpleLookupResourcesWithCursor(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() for _, tc := range []struct { subject string @@ -245,7 +243,7 @@ func TestSimpleLookupResourcesWithCursor(t *testing.T) { } func TestLookupResourcesCursorStability(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() require := require.New(t) ctx, dispatcher, revision := newLocalDispatcher(t) @@ -307,6 +305,7 @@ func processResults(stream *dispatch.CollectingDispatchStream[*v1.DispatchLookup } func TestMaxDepthLookup(t *testing.T) { + t.Parallel() require := require.New(t) rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) @@ -389,6 +388,7 @@ func genResourceIds(resourceName string, number int) []string { } func TestLookupResourcesOverSchemaWithCursors(t *testing.T) { + t.Parallel() testCases := []struct { name string schema string @@ -597,9 +597,11 @@ func TestLookupResourcesOverSchemaWithCursors(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() for _, pageSize := range []int{0, 104, 1023} { pageSize := pageSize t.Run(fmt.Sprintf("ps-%d_", pageSize), func(t *testing.T) { + t.Parallel() require := require.New(t) dispatcher := NewLocalOnlyDispatcher(10, 100) @@ -646,10 +648,11 @@ func TestLookupResourcesOverSchemaWithCursors(t *testing.T) { } foundResourceIDsSlice := foundResourceIDs.AsSlice() + expectedResourceIDs := slices.Clone(tc.expectedResourceIDs) slices.Sort(foundResourceIDsSlice) - slices.Sort(tc.expectedResourceIDs) + slices.Sort(expectedResourceIDs) - require.Equal(tc.expectedResourceIDs, foundResourceIDsSlice) + require.Equal(expectedResourceIDs, foundResourceIDsSlice) }) } }) @@ -657,7 +660,7 @@ func TestLookupResourcesOverSchemaWithCursors(t *testing.T) { } func TestLookupResourcesImmediateTimeout(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() require := require.New(t) @@ -690,7 +693,7 @@ func TestLookupResourcesImmediateTimeout(t *testing.T) { } func TestLookupResourcesWithError(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() require := require.New(t) diff --git a/internal/dispatch/graph/lookupsubjects_test.go b/internal/dispatch/graph/lookupsubjects_test.go index 7ec1e9c3da..98abf655db 100644 --- a/internal/dispatch/graph/lookupsubjects_test.go +++ b/internal/dispatch/graph/lookupsubjects_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.uber.org/goleak" "github.com/authzed/spicedb/internal/caveats" "github.com/authzed/spicedb/internal/datastore/common" @@ -19,7 +18,6 @@ import ( itestutil "github.com/authzed/spicedb/internal/testutil" corev1 "github.com/authzed/spicedb/pkg/proto/core/v1" v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" - "github.com/authzed/spicedb/pkg/testutil" "github.com/authzed/spicedb/pkg/tuple" ) @@ -32,7 +30,7 @@ var ( ) func TestSimpleLookupSubjects(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() testCases := []struct { resourceType string @@ -194,6 +192,7 @@ func TestSimpleLookupSubjects(t *testing.T) { } func TestLookupSubjectsMaxDepth(t *testing.T) { + t.Parallel() require := require.New(t) rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) @@ -224,6 +223,7 @@ func TestLookupSubjectsMaxDepth(t *testing.T) { } func TestLookupSubjectsDispatchCount(t *testing.T) { + t.Parallel() testCases := []struct { resourceType string resourceID string @@ -277,6 +277,7 @@ func TestLookupSubjectsDispatchCount(t *testing.T) { } func TestLookupSubjectsOverSchema(t *testing.T) { + t.Parallel() testCases := []struct { name string schema string diff --git a/internal/dispatch/graph/package_test.go b/internal/dispatch/graph/package_test.go new file mode 100644 index 0000000000..50d508e110 --- /dev/null +++ b/internal/dispatch/graph/package_test.go @@ -0,0 +1,14 @@ +package graph + +import ( + "testing" + + "go.uber.org/goleak" + + "github.com/authzed/spicedb/pkg/testutil" +) + +// done so we can do t.Parallel() and still use goleak +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) +} diff --git a/internal/dispatch/graph/reachableresources_test.go b/internal/dispatch/graph/reachableresources_test.go index 9ccb97b86c..4a73ec2888 100644 --- a/internal/dispatch/graph/reachableresources_test.go +++ b/internal/dispatch/graph/reachableresources_test.go @@ -12,7 +12,6 @@ import ( "github.com/ccoveille/go-safecast" "github.com/stretchr/testify/require" - "go.uber.org/goleak" "golang.org/x/sync/errgroup" "github.com/authzed/spicedb/internal/datastore/memdb" @@ -27,7 +26,6 @@ import ( "github.com/authzed/spicedb/pkg/genutil/mapz" core "github.com/authzed/spicedb/pkg/proto/core/v1" v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" - "github.com/authzed/spicedb/pkg/testutil" "github.com/authzed/spicedb/pkg/tuple" ) @@ -43,7 +41,7 @@ func reachable(onr *core.ObjectAndRelation, hasPermission bool) reachableResourc } func TestSimpleReachableResources(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) + t.Parallel() testCases := []struct { start *core.RelationReference @@ -198,6 +196,7 @@ func TestSimpleReachableResources(t *testing.T) { } func TestMaxDepthreachableResources(t *testing.T) { + t.Parallel() require := require.New(t) ctx, dispatcher, revision := newLocalDispatcher(t) @@ -303,6 +302,7 @@ func BenchmarkReachableResources(b *testing.B) { } func TestCaveatedReachableResources(t *testing.T) { + t.Parallel() testCases := []struct { name string schema string @@ -616,8 +616,7 @@ func TestCaveatedReachableResources(t *testing.T) { } func TestReachableResourcesWithConsistencyLimitOf1(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) - + t.Parallel() ctx, dispatcher, revision := newLocalDispatcherWithConcurrencyLimit(t, 1) defer dispatcher.Close() @@ -646,8 +645,7 @@ func TestReachableResourcesWithConsistencyLimitOf1(t *testing.T) { } func TestReachableResourcesMultipleEntrypointEarlyCancel(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) - + t.Parallel() rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) require.NoError(t, err) @@ -723,8 +721,7 @@ func TestReachableResourcesMultipleEntrypointEarlyCancel(t *testing.T) { } func TestReachableResourcesCursors(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) - + t.Parallel() rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) require.NoError(t, err) @@ -840,8 +837,7 @@ func TestReachableResourcesCursors(t *testing.T) { } func TestReachableResourcesPaginationWithLimit(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) - + t.Parallel() rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) require.NoError(t, err) @@ -870,6 +866,7 @@ func TestReachableResourcesPaginationWithLimit(t *testing.T) { for _, limit := range []uint32{1, 10, 50, 100, 150, 250, 500} { limit := limit t.Run(fmt.Sprintf("limit-%d", limit), func(t *testing.T) { + // FIXME t.Parallel() here introduces a race condition over the dispatcher dispatcher := NewLocalOnlyDispatcher(2, 100) var cursor *v1.Cursor foundResources := mapz.NewSet[string]() @@ -921,8 +918,7 @@ func TestReachableResourcesPaginationWithLimit(t *testing.T) { } func TestReachableResourcesWithQueryError(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) - + t.Parallel() rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) require.NoError(t, err) @@ -1004,6 +1000,8 @@ func (br *breakingReader) ReverseQueryRelationships( } func TestReachableResourcesOverSchema(t *testing.T) { + t.Parallel() + testCases := []struct { name string schema string @@ -1221,9 +1219,11 @@ func TestReachableResourcesOverSchema(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() for _, pageSize := range []int{0, 100, 1000} { pageSize := pageSize t.Run(fmt.Sprintf("ps-%d_", pageSize), func(t *testing.T) { + t.Parallel() require := require.New(t) dispatcher := NewLocalOnlyDispatcher(10, 100) @@ -1276,9 +1276,10 @@ func TestReachableResourcesOverSchema(t *testing.T) { foundResourceIDsSlice := foundResourceIDs.AsSlice() sort.Strings(foundResourceIDsSlice) - sort.Strings(tc.expectedResourceIDs) + expectedResourceIDs := slices.Clone(tc.expectedResourceIDs) + sort.Strings(expectedResourceIDs) - require.Equal(tc.expectedResourceIDs, foundResourceIDsSlice) + require.Equal(expectedResourceIDs, foundResourceIDsSlice) }) } }) @@ -1286,8 +1287,7 @@ func TestReachableResourcesOverSchema(t *testing.T) { } func TestReachableResourcesWithPreCancelation(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) - + t.Parallel() rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) require.NoError(t, err) @@ -1341,8 +1341,7 @@ func TestReachableResourcesWithPreCancelation(t *testing.T) { } func TestReachableResourcesWithUnexpectedContextCancelation(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) - + t.Parallel() rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) require.NoError(t, err) @@ -1426,8 +1425,7 @@ func (cr *cancelingReader) ReverseQueryRelationships( } func TestReachableResourcesWithCachingInParallelTest(t *testing.T) { - defer goleak.VerifyNone(t, append(testutil.GoLeakIgnores(), goleak.IgnoreCurrent())...) - + t.Parallel() rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) require.NoError(t, err) diff --git a/internal/dispatch/remote/cluster_test.go b/internal/dispatch/remote/cluster_test.go index 58c7ee2115..50b8c1e70d 100644 --- a/internal/dispatch/remote/cluster_test.go +++ b/internal/dispatch/remote/cluster_test.go @@ -236,6 +236,7 @@ func TestCheckSecondaryDispatch(t *testing.T) { } { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() conn := connectionForDispatching(t, &fakeDispatchSvc{dispatchCount: 1, sleepTime: tc.primarySleepTime}) secondaryConn := connectionForDispatching(t, &fakeDispatchSvc{dispatchCount: 2, sleepTime: 0 * time.Millisecond}) diff --git a/internal/relationships/validation_test.go b/internal/relationships/validation_test.go index 4749ad1684..a7d42430a9 100644 --- a/internal/relationships/validation_test.go +++ b/internal/relationships/validation_test.go @@ -242,7 +242,9 @@ func TestValidateRelationshipOperations(t *testing.T) { } for _, tc := range tcs { + tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() req := require.New(t) ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) diff --git a/internal/services/integrationtesting/cert_test.go b/internal/services/integrationtesting/cert_test.go index 91cc9e0df4..7b30b511b2 100644 --- a/internal/services/integrationtesting/cert_test.go +++ b/internal/services/integrationtesting/cert_test.go @@ -36,6 +36,8 @@ import ( ) func TestCertRotation(t *testing.T) { + t.Parallel() + const ( // length of time the initial cert is valid initialValidDuration = 3 * time.Second diff --git a/internal/services/integrationtesting/consistency_test.go b/internal/services/integrationtesting/consistency_test.go index bc17eb7df4..bf5a5dd5e8 100644 --- a/internal/services/integrationtesting/consistency_test.go +++ b/internal/services/integrationtesting/consistency_test.go @@ -55,6 +55,7 @@ func TestConsistency(t *testing.T) { filePath := filePath t.Run(path.Base(filePath), func(t *testing.T) { + t.Parallel() for _, dispatcherKind := range []string{"local", "caching"} { dispatcherKind := dispatcherKind diff --git a/internal/services/integrationtesting/ops_test.go b/internal/services/integrationtesting/ops_test.go index d98adf0882..263f384603 100644 --- a/internal/services/integrationtesting/ops_test.go +++ b/internal/services/integrationtesting/ops_test.go @@ -120,6 +120,8 @@ func (dr deleteCaveatedRelationship) Execute(tester opsTester) error { } func TestSchemaAndRelationshipsOperations(t *testing.T) { + t.Parallel() + tcs := []schemaTestCase{ // Test: write a basic, valid schema. { @@ -751,6 +753,7 @@ func TestSchemaAndRelationshipsOperations(t *testing.T) { for _, tc := range tcs { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() for _, testerName := range []string{"v1"} { testerName := testerName t.Run(testerName, func(t *testing.T) { diff --git a/internal/services/v1/experimental_test.go b/internal/services/v1/experimental_test.go index 1b6b0ab78c..28f5c9ea39 100644 --- a/internal/services/v1/experimental_test.go +++ b/internal/services/v1/experimental_test.go @@ -51,7 +51,9 @@ func TestBulkImportRelationships(t *testing.T) { } for _, tc := range testCases { + tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() require := require.New(t) conn, cleanup, _, _ := testserver.NewTestServer(require, 0, memdb.DisableGC, true, tf.StandardDatastoreWithSchema) @@ -300,6 +302,7 @@ func TestBulkExportRelationshipsWithFilter(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() require := require.New(t) conn, cleanup, _, _ := testserver.NewTestServer(require, 0, memdb.DisableGC, true, tf.StandardDatastoreWithSchema) diff --git a/internal/taskrunner/preloadedtaskrunner_test.go b/internal/taskrunner/preloadedtaskrunner_test.go index acee93d83c..1290992ffc 100644 --- a/internal/taskrunner/preloadedtaskrunner_test.go +++ b/internal/taskrunner/preloadedtaskrunner_test.go @@ -14,8 +14,13 @@ import ( "go.uber.org/goleak" ) +// done so we can do t.Parallel() and still use goleak +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} + func TestPreloadedTaskRunnerCompletesAllTasks(t *testing.T) { - defer goleak.VerifyNone(t) + t.Parallel() tr := NewPreloadedTaskRunner(context.Background(), 2, 5) wg := sync.WaitGroup{} @@ -38,7 +43,7 @@ func TestPreloadedTaskRunnerCompletesAllTasks(t *testing.T) { } func TestPreloadedTaskRunnerCancelsEarlyDueToError(t *testing.T) { - defer goleak.VerifyNone(t) + t.Parallel() ctx := context.Background() ctx, cancel := context.WithCancel(ctx) @@ -76,7 +81,7 @@ func TestPreloadedTaskRunnerCancelsEarlyDueToError(t *testing.T) { } func TestPreloadedTaskRunnerCancelsEarlyDueToCancel(t *testing.T) { - defer goleak.VerifyNone(t) + t.Parallel() ctx := context.Background() ctx, cancel := context.WithCancel(ctx) @@ -115,7 +120,7 @@ func TestPreloadedTaskRunnerCancelsEarlyDueToCancel(t *testing.T) { } func TestPreloadedTaskRunnerReturnsError(t *testing.T) { - defer goleak.VerifyNone(t) + t.Parallel() ctx := context.Background() ctx, cancel := context.WithCancel(ctx) @@ -154,7 +159,7 @@ func TestPreloadedTaskRunnerReturnsError(t *testing.T) { } func TestPreloadedTaskRunnerEmpty(t *testing.T) { - defer goleak.VerifyNone(t) + t.Parallel() ctx := context.Background() ctx, cancel := context.WithCancel(ctx) diff --git a/internal/testutil/subjects_test.go b/internal/testutil/subjects_test.go index 55b93ccf11..b07254ea12 100644 --- a/internal/testutil/subjects_test.go +++ b/internal/testutil/subjects_test.go @@ -22,6 +22,8 @@ var ( ) func TestCompareSubjects(t *testing.T) { + t.Parallel() + tcs := []struct { first *v1.FoundSubject second *v1.FoundSubject diff --git a/magefiles/test.go b/magefiles/test.go index e1615d35bb..9af877e87e 100644 --- a/magefiles/test.go +++ b/magefiles/test.go @@ -23,17 +23,27 @@ func (t Test) All() error { return nil } -// Runs the unit tests and generates a coverage report -func (Test) UnitCover() error { - mg.Deps(Test{}.Unit) +// UnitCover Runs the unit tests and generates a coverage report +func (t Test) UnitCover() error { + if err := t.unit(true); err != nil { + return err + } fmt.Println("Running coverage...") return sh.RunV("go", "tool", "cover", "-html=coverage.txt") } // Unit Runs the unit tests -func (Test) Unit() error { +func (t Test) Unit() error { + return t.unit(false) +} + +func (Test) unit(coverage bool) error { fmt.Println("running unit tests") - return goTest("./...", "-tags", "ci,skipintegrationtests", "-race", "-timeout", "10m", "-covermode=atomic", "-count=1", "-coverprofile=coverage.txt") + args := []string{"-tags", "ci,skipintegrationtests", "-race", "-timeout", "10m", "-count=1"} + if coverage { + args = append(args, "-covermode=atomic", "-coverprofile=coverage.txt") + } + return goTest("./...", args...) } // Image Run tests that run the built image diff --git a/pkg/cmd/migrate.go b/pkg/cmd/migrate.go index b074909b6f..2880f79783 100644 --- a/pkg/cmd/migrate.go +++ b/pkg/cmd/migrate.go @@ -9,6 +9,8 @@ import ( "github.com/jzelinskie/cobrautil/v2" "github.com/spf13/cobra" + sqlDriver "github.com/go-sql-driver/mysql" + crdbmigrations "github.com/authzed/spicedb/internal/datastore/crdb/migrations" mysqlmigrations "github.com/authzed/spicedb/internal/datastore/mysql/migrations" "github.com/authzed/spicedb/internal/datastore/postgres/migrations" @@ -111,6 +113,12 @@ func migrateRun(cmd *cobra.Command, args []string) error { } } + // Do this outside NewMySQLDriverFromDSN to avoid races on MySQL datastore tests + err = sqlDriver.SetLogger(&log.Logger) + if err != nil { + return fmt.Errorf("unable to set logging to mysql driver: %w", err) + } + migrationDriver, err := mysqlmigrations.NewMySQLDriverFromDSN(dbURL, tablePrefix, credentialsProvider) if err != nil { return fmt.Errorf("unable to create migration driver for %s: %w", datastoreEngine, err) diff --git a/pkg/datastore/pagination/iterator_test.go b/pkg/datastore/pagination/iterator_test.go index 4cde4d61b8..22a924e2e5 100644 --- a/pkg/datastore/pagination/iterator_test.go +++ b/pkg/datastore/pagination/iterator_test.go @@ -142,7 +142,9 @@ func TestPaginatedIterator(t *testing.T) { } for _, tc := range testCases { + tc := tc t.Run(fmt.Sprintf("%d/%d-%d", tc.pageSize, tc.totalRelationships, tc.order), func(t *testing.T) { + t.Parallel() require := require.New(t) tpls := make([]*core.RelationTuple, 0, tc.totalRelationships) diff --git a/pkg/datastore/test/datastore.go b/pkg/datastore/test/datastore.go index cc8a0999f5..983b8435ca 100644 --- a/pkg/datastore/test/datastore.go +++ b/pkg/datastore/test/datastore.go @@ -84,104 +84,122 @@ func WithCategories(cats ...string) Categories { return c } +func parallel(t *testing.T, tester DatastoreTester, tt func(t *testing.T, tester DatastoreTester)) func(t *testing.T) { + return func(t *testing.T) { + t.Parallel() + tt(t, tester) + } +} + +func serial(t *testing.T, tester DatastoreTester, tt func(t *testing.T, tester DatastoreTester)) func(t *testing.T) { + return func(t *testing.T) { + tt(t, tester) + } +} + // AllWithExceptions runs all generic datastore tests on a DatastoreTester, except // those specified test categories -func AllWithExceptions(t *testing.T, tester DatastoreTester, except Categories) { - t.Run("TestUseAfterClose", func(t *testing.T) { UseAfterCloseTest(t, tester) }) - - t.Run("TestNamespaceNotFound", func(t *testing.T) { NamespaceNotFoundTest(t, tester) }) - t.Run("TestNamespaceWrite", func(t *testing.T) { NamespaceWriteTest(t, tester) }) - t.Run("TestNamespaceDelete", func(t *testing.T) { NamespaceDeleteTest(t, tester) }) - t.Run("TestNamespaceMultiDelete", func(t *testing.T) { NamespaceMultiDeleteTest(t, tester) }) - t.Run("TestEmptyNamespaceDelete", func(t *testing.T) { EmptyNamespaceDeleteTest(t, tester) }) - t.Run("TestStableNamespaceReadWrite", func(t *testing.T) { StableNamespaceReadWriteTest(t, tester) }) - - t.Run("TestSimple", func(t *testing.T) { SimpleTest(t, tester) }) - t.Run("TestObjectIDs", func(t *testing.T) { ObjectIDsTest(t, tester) }) - t.Run("TestDeleteRelationships", func(t *testing.T) { DeleteRelationshipsTest(t, tester) }) - t.Run("TestDeleteNonExistant", func(t *testing.T) { DeleteNotExistantTest(t, tester) }) - t.Run("TestDeleteAlreadyDeleted", func(t *testing.T) { DeleteAlreadyDeletedTest(t, tester) }) - t.Run("TestRecreateRelationshipsAfterDeleteWithFilter", func(t *testing.T) { RecreateRelationshipsAfterDeleteWithFilter(t, tester) }) - t.Run("TestWriteDeleteWrite", func(t *testing.T) { WriteDeleteWriteTest(t, tester) }) - t.Run("TestCreateAlreadyExisting", func(t *testing.T) { CreateAlreadyExistingTest(t, tester) }) - t.Run("TestTouchAlreadyExistingWithoutCaveat", func(t *testing.T) { TouchAlreadyExistingTest(t, tester) }) - t.Run("TestCreateDeleteTouch", func(t *testing.T) { CreateDeleteTouchTest(t, tester) }) - t.Run("TestDeleteOneThousandIndividualInOneCall", func(t *testing.T) { DeleteOneThousandIndividualInOneCallTest(t, tester) }) - t.Run("TestCreateTouchDeleteTouch", func(t *testing.T) { CreateTouchDeleteTouchTest(t, tester) }) - t.Run("TestTouchAlreadyExistingCaveated", func(t *testing.T) { TouchAlreadyExistingCaveatedTest(t, tester) }) - t.Run("TestBulkDeleteRelationships", func(t *testing.T) { BulkDeleteRelationshipsTest(t, tester) }) - t.Run("TestDeleteCaveatedTuple", func(t *testing.T) { DeleteCaveatedTupleTest(t, tester) }) - t.Run("TestDeleteWithLimit", func(t *testing.T) { DeleteWithLimitTest(t, tester) }) - t.Run("TestQueryRelationshipsWithVariousFilters", func(t *testing.T) { QueryRelationshipsWithVariousFiltersTest(t, tester) }) - t.Run("TestDeleteRelationshipsWithVariousFilters", func(t *testing.T) { DeleteRelationshipsWithVariousFiltersTest(t, tester) }) - t.Run("TestTouchTypedAlreadyExistingWithoutCaveat", func(t *testing.T) { TypedTouchAlreadyExistingTest(t, tester) }) - t.Run("TestTouchTypedAlreadyExistingWithCaveat", func(t *testing.T) { TypedTouchAlreadyExistingWithCaveatTest(t, tester) }) - - t.Run("TestMultipleReadsInRWT", func(t *testing.T) { MultipleReadsInRWTTest(t, tester) }) - t.Run("TestConcurrentWriteSerialization", func(t *testing.T) { ConcurrentWriteSerializationTest(t, tester) }) - - t.Run("TestOrdering", func(t *testing.T) { OrderingTest(t, tester) }) - t.Run("TestLimit", func(t *testing.T) { LimitTest(t, tester) }) - t.Run("TestOrderedLimit", func(t *testing.T) { OrderedLimitTest(t, tester) }) - t.Run("TestResume", func(t *testing.T) { ResumeTest(t, tester) }) - t.Run("TestCursorErrors", func(t *testing.T) { CursorErrorsTest(t, tester) }) - t.Run("TestReverseQueryCursor", func(t *testing.T) { ReverseQueryCursorTest(t, tester) }) - - t.Run("TestRevisionQuantization", func(t *testing.T) { RevisionQuantizationTest(t, tester) }) - t.Run("TestRevisionSerialization", func(t *testing.T) { RevisionSerializationTest(t, tester) }) - t.Run("TestSequentialRevisions", func(t *testing.T) { SequentialRevisionsTest(t, tester) }) - t.Run("TestConcurrentRevisions", func(t *testing.T) { ConcurrentRevisionsTest(t, tester) }) - t.Run("TestCheckRevisions", func(t *testing.T) { CheckRevisionsTest(t, tester) }) +func AllWithExceptions(t *testing.T, tester DatastoreTester, except Categories, concurrent bool) { + runner := serial + if concurrent { + runner = parallel + } + + t.Run("TestUseAfterClose", runner(t, tester, UseAfterCloseTest)) + + t.Run("TestNamespaceNotFound", runner(t, tester, NamespaceNotFoundTest)) + t.Run("TestNamespaceWrite", runner(t, tester, NamespaceWriteTest)) + t.Run("TestNamespaceDelete", runner(t, tester, NamespaceDeleteTest)) + t.Run("TestNamespaceMultiDelete", runner(t, tester, NamespaceMultiDeleteTest)) + t.Run("TestEmptyNamespaceDelete", runner(t, tester, EmptyNamespaceDeleteTest)) + t.Run("TestStableNamespaceReadWrite", runner(t, tester, StableNamespaceReadWriteTest)) + + t.Run("TestSimple", runner(t, tester, SimpleTest)) + t.Run("TestObjectIDs", runner(t, tester, ObjectIDsTest)) + t.Run("TestDeleteRelationships", runner(t, tester, DeleteRelationshipsTest)) + t.Run("TestDeleteNonExistant", runner(t, tester, DeleteNotExistantTest)) + t.Run("TestDeleteAlreadyDeleted", runner(t, tester, DeleteAlreadyDeletedTest)) + t.Run("TestRecreateRelationshipsAfterDeleteWithFilter", runner(t, tester, RecreateRelationshipsAfterDeleteWithFilter)) + t.Run("TestWriteDeleteWrite", runner(t, tester, WriteDeleteWriteTest)) + t.Run("TestCreateAlreadyExisting", runner(t, tester, CreateAlreadyExistingTest)) + t.Run("TestTouchAlreadyExistingWithoutCaveat", runner(t, tester, TouchAlreadyExistingTest)) + t.Run("TestCreateDeleteTouch", runner(t, tester, CreateDeleteTouchTest)) + t.Run("TestDeleteOneThousandIndividualInOneCall", runner(t, tester, DeleteOneThousandIndividualInOneCallTest)) + t.Run("TestCreateTouchDeleteTouch", runner(t, tester, CreateTouchDeleteTouchTest)) + t.Run("TestTouchAlreadyExistingCaveated", runner(t, tester, TouchAlreadyExistingCaveatedTest)) + t.Run("TestBulkDeleteRelationships", runner(t, tester, BulkDeleteRelationshipsTest)) + t.Run("TestDeleteCaveatedTuple", runner(t, tester, DeleteCaveatedTupleTest)) + t.Run("TestDeleteWithLimit", runner(t, tester, DeleteWithLimitTest)) + t.Run("TestQueryRelationshipsWithVariousFilters", runner(t, tester, QueryRelationshipsWithVariousFiltersTest)) + t.Run("TestDeleteRelationshipsWithVariousFilters", runner(t, tester, DeleteRelationshipsWithVariousFiltersTest)) + t.Run("TestTouchTypedAlreadyExistingWithoutCaveat", runner(t, tester, TypedTouchAlreadyExistingTest)) + t.Run("TestTouchTypedAlreadyExistingWithCaveat", runner(t, tester, TypedTouchAlreadyExistingWithCaveatTest)) + + t.Run("TestMultipleReadsInRWT", runner(t, tester, MultipleReadsInRWTTest)) + t.Run("TestConcurrentWriteSerialization", runner(t, tester, ConcurrentWriteSerializationTest)) + + t.Run("TestOrdering", runner(t, tester, OrderingTest)) + t.Run("TestLimit", runner(t, tester, LimitTest)) + t.Run("TestOrderedLimit", runner(t, tester, OrderedLimitTest)) + t.Run("TestResume", runner(t, tester, ResumeTest)) + t.Run("TestCursorErrors", runner(t, tester, CursorErrorsTest)) + t.Run("TestReverseQueryCursor", runner(t, tester, ReverseQueryCursorTest)) + + t.Run("TestRevisionQuantization", runner(t, tester, RevisionQuantizationTest)) + t.Run("TestRevisionSerialization", runner(t, tester, RevisionSerializationTest)) + t.Run("TestSequentialRevisions", runner(t, tester, SequentialRevisionsTest)) + t.Run("TestConcurrentRevisions", runner(t, tester, ConcurrentRevisionsTest)) + t.Run("TestCheckRevisions", runner(t, tester, CheckRevisionsTest)) if !except.GC() { - t.Run("TestRevisionGC", func(t *testing.T) { RevisionGCTest(t, tester) }) - t.Run("TestInvalidReads", func(t *testing.T) { InvalidReadsTest(t, tester) }) + t.Run("TestRevisionGC", runner(t, tester, RevisionGCTest)) + t.Run("TestInvalidReads", runner(t, tester, InvalidReadsTest)) } - t.Run("TestBulkUpload", func(t *testing.T) { BulkUploadTest(t, tester) }) - t.Run("TestBulkUploadErrors", func(t *testing.T) { BulkUploadErrorsTest(t, tester) }) - t.Run("TestBulkUploadAlreadyExistsError", func(t *testing.T) { BulkUploadAlreadyExistsErrorTest(t, tester) }) - t.Run("TestBulkUploadAlreadyExistsSameCallError", func(t *testing.T) { BulkUploadAlreadyExistsSameCallErrorTest(t, tester) }) - t.Run("BulkUploadEditCaveat", func(t *testing.T) { BulkUploadEditCaveat(t, tester) }) + t.Run("TestBulkUpload", runner(t, tester, BulkUploadTest)) + t.Run("TestBulkUploadErrors", runner(t, tester, BulkUploadErrorsTest)) + t.Run("TestBulkUploadAlreadyExistsError", runner(t, tester, BulkUploadAlreadyExistsErrorTest)) + t.Run("TestBulkUploadAlreadyExistsSameCallError", runner(t, tester, BulkUploadAlreadyExistsSameCallErrorTest)) + t.Run("BulkUploadEditCaveat", runner(t, tester, BulkUploadEditCaveat)) if !except.Stats() { - t.Run("TestStats", func(t *testing.T) { StatsTest(t, tester) }) + t.Run("TestStats", runner(t, tester, StatsTest)) } - t.Run("TestRetries", func(t *testing.T) { RetryTest(t, tester) }) + t.Run("TestRetries", runner(t, tester, RetryTest)) - t.Run("TestCaveatNotFound", func(t *testing.T) { CaveatNotFoundTest(t, tester) }) - t.Run("TestWriteReadDeleteCaveat", func(t *testing.T) { WriteReadDeleteCaveatTest(t, tester) }) - t.Run("TestWriteCaveatedRelationship", func(t *testing.T) { WriteCaveatedRelationshipTest(t, tester) }) - t.Run("TestCaveatedRelationshipFilter", func(t *testing.T) { CaveatedRelationshipFilterTest(t, tester) }) - t.Run("TestCaveatSnapshotReads", func(t *testing.T) { CaveatSnapshotReadsTest(t, tester) }) + t.Run("TestCaveatNotFound", runner(t, tester, CaveatNotFoundTest)) + t.Run("TestWriteReadDeleteCaveat", runner(t, tester, WriteReadDeleteCaveatTest)) + t.Run("TestWriteCaveatedRelationship", runner(t, tester, WriteCaveatedRelationshipTest)) + t.Run("TestCaveatedRelationshipFilter", runner(t, tester, CaveatedRelationshipFilterTest)) + t.Run("TestCaveatSnapshotReads", runner(t, tester, CaveatSnapshotReadsTest)) if !except.Watch() { - t.Run("TestWatchBasic", func(t *testing.T) { WatchTest(t, tester) }) - t.Run("TestWatchCancel", func(t *testing.T) { WatchCancelTest(t, tester) }) - t.Run("TestCaveatedRelationshipWatch", func(t *testing.T) { CaveatedRelationshipWatchTest(t, tester) }) - t.Run("TestWatchWithTouch", func(t *testing.T) { WatchWithTouchTest(t, tester) }) - t.Run("TestWatchWithDelete", func(t *testing.T) { WatchWithDeleteTest(t, tester) }) - t.Run("TestWatchWithMetadata", func(t *testing.T) { WatchWithMetadataTest(t, tester) }) + t.Run("TestWatchBasic", runner(t, tester, WatchTest)) + t.Run("TestWatchCancel", runner(t, tester, WatchCancelTest)) + t.Run("TestCaveatedRelationshipWatch", runner(t, tester, CaveatedRelationshipWatchTest)) + t.Run("TestWatchWithTouch", runner(t, tester, WatchWithTouchTest)) + t.Run("TestWatchWithDelete", runner(t, tester, WatchWithDeleteTest)) + t.Run("TestWatchWithMetadata", runner(t, tester, WatchWithMetadataTest)) } if !except.Watch() && !except.WatchSchema() { - t.Run("TestWatchSchema", func(t *testing.T) { WatchSchemaTest(t, tester) }) - t.Run("TestWatchAll", func(t *testing.T) { WatchAllTest(t, tester) }) + t.Run("TestWatchSchema", runner(t, tester, WatchSchemaTest)) + t.Run("TestWatchAll", runner(t, tester, WatchAllTest)) } if !except.Watch() && !except.WatchCheckpoints() { - t.Run("TestWatchCheckpoints", func(t *testing.T) { WatchCheckpointsTest(t, tester) }) + t.Run("TestWatchCheckpoints", runner(t, tester, WatchCheckpointsTest)) } - t.Run("TestRelationshipCounters", func(t *testing.T) { RelationshipCountersTest(t, tester) }) - t.Run("TestUpdateRelationshipCounter", func(t *testing.T) { UpdateRelationshipCounterTest(t, tester) }) - t.Run("TestDeleteAllData", func(t *testing.T) { DeleteAllDataTest(t, tester) }) + t.Run("TestRelationshipCounters", runner(t, tester, RelationshipCountersTest)) + t.Run("TestUpdateRelationshipCounter", runner(t, tester, UpdateRelationshipCounterTest)) + t.Run("TestDeleteAllData", runner(t, tester, DeleteAllDataTest)) } // All runs all generic datastore tests on a DatastoreTester. -func All(t *testing.T, tester DatastoreTester) { - AllWithExceptions(t, tester, noException) +func All(t *testing.T, tester DatastoreTester, concurrent bool) { + AllWithExceptions(t, tester, noException, concurrent) } var testResourceNS = namespace.Namespace( diff --git a/pkg/schemadsl/compiler/compiler_test.go b/pkg/schemadsl/compiler/compiler_test.go index f060f78f6c..cd796169c3 100644 --- a/pkg/schemadsl/compiler/compiler_test.go +++ b/pkg/schemadsl/compiler/compiler_test.go @@ -21,6 +21,8 @@ var ( ) func TestCompile(t *testing.T) { + t.Parallel() + type compileTest struct { name string objectPrefix ObjectPrefixOption @@ -977,6 +979,7 @@ func TestCompile(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { + t.Parallel() require := require.New(t) compiled, err := Compile(InputSchema{ input.Source(test.name), test.input, @@ -1056,6 +1059,8 @@ func filterSourcePositions(m protoreflect.Message) { } func TestSkipValidation(t *testing.T) { + t.Parallel() + _, err := Compile(InputSchema{"test", `definition a/def {}`}, AllowUnprefixedObjectType()) require.Error(t, err) @@ -1064,6 +1069,8 @@ func TestSkipValidation(t *testing.T) { } func TestSuperLargeCaveatCompile(t *testing.T) { + t.Parallel() + b, err := os.ReadFile("../parser/tests/superlarge.zed") if err != nil { panic(err) diff --git a/pkg/schemadsl/parser/parser_test.go b/pkg/schemadsl/parser/parser_test.go index 453f89c0c9..f5992e5177 100644 --- a/pkg/schemadsl/parser/parser_test.go +++ b/pkg/schemadsl/parser/parser_test.go @@ -127,6 +127,7 @@ func TestParser(t *testing.T) { for _, test := range parserTests { test := test t.Run(test.name, func(t *testing.T) { + t.Parallel() root := Parse(createAstNode, input.Source(test.name), test.input()) parseTree := getParseTree((root).(*testNode), 0) assert := assert.New(t)