github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/cmd/spicedb/schemawatch_integration_test.go (about) 1 //go:build docker && image 2 // +build docker,image 3 4 package main 5 6 import ( 7 "context" 8 "fmt" 9 "testing" 10 "time" 11 12 "github.com/google/uuid" 13 "github.com/ory/dockertest/v3" 14 "github.com/ory/dockertest/v3/docker" 15 "github.com/stretchr/testify/require" 16 17 testdatastore "github.com/authzed/spicedb/internal/testserver/datastore" 18 "github.com/authzed/spicedb/pkg/datastore" 19 ) 20 21 func TestSchemaWatch(t *testing.T) { 22 engines := map[string]bool{ 23 "postgres": false, 24 "mysql": false, 25 "cockroachdb": true, 26 "spanner": false, 27 } 28 require.Equal(t, len(engines), len(datastore.Engines)) 29 30 for driverName, shouldRun := range engines { 31 if !shouldRun { 32 continue 33 } 34 35 t.Run(driverName, func(t *testing.T) { 36 bridgeNetworkName := fmt.Sprintf("bridge-%s", uuid.New().String()) 37 38 pool, err := dockertest.NewPool("") 39 require.NoError(t, err) 40 41 // Create a bridge network for testing. 42 network, err := pool.Client.CreateNetwork(docker.CreateNetworkOptions{ 43 Name: bridgeNetworkName, 44 }) 45 require.NoError(t, err) 46 t.Cleanup(func() { 47 pool.Client.RemoveNetwork(network.ID) 48 }) 49 50 engine := testdatastore.RunDatastoreEngineWithBridge(t, driverName, bridgeNetworkName) 51 52 envVars := []string{} 53 if wev, ok := engine.(testdatastore.RunningEngineForTestWithEnvVars); ok { 54 envVars = wev.ExternalEnvVars() 55 } 56 57 // Run the migrate command and wait for it to complete. 58 db := engine.NewDatabase(t) 59 migrateResource, err := pool.RunWithOptions(&dockertest.RunOptions{ 60 Repository: "authzed/spicedb", 61 Tag: "ci", 62 Cmd: []string{"migrate", "head", "--datastore-engine", driverName, "--datastore-conn-uri", db}, 63 NetworkID: bridgeNetworkName, 64 Env: envVars, 65 }, func(config *docker.HostConfig) { 66 config.RestartPolicy = docker.RestartPolicy{ 67 Name: "no", 68 } 69 }) 70 require.NoError(t, err) 71 72 waitCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 73 defer cancel() 74 75 // Ensure the command completed successfully. 76 status, err := pool.Client.WaitContainerWithContext(migrateResource.Container.ID, waitCtx) 77 require.NoError(t, err) 78 require.Equal(t, 0, status) 79 80 // Run a serve and immediately close, ensuring it shuts down gracefully. 81 serveResource, err := pool.RunWithOptions(&dockertest.RunOptions{ 82 Repository: "authzed/spicedb", 83 Tag: "ci", 84 Cmd: []string{"serve", "--grpc-preshared-key", "firstkey", "--datastore-engine", driverName, "--datastore-conn-uri", db, "--datastore-gc-interval", "1s", "--telemetry-endpoint", "", "--log-level", "trace", "--enable-experimental-watchable-schema-cache"}, 85 NetworkID: bridgeNetworkName, 86 Env: envVars, 87 }, func(config *docker.HostConfig) { 88 config.RestartPolicy = docker.RestartPolicy{ 89 Name: "no", 90 } 91 }) 92 require.NoError(t, err) 93 t.Cleanup(func() { 94 _ = pool.Purge(serveResource) 95 }) 96 97 ww := &watchingWriter{make(chan bool, 1), "starting watching cache"} 98 99 // Grab logs and ensure GC has run before starting a graceful shutdown. 100 opts := docker.LogsOptions{ 101 Context: context.Background(), 102 Stderr: true, 103 Stdout: true, 104 Follow: true, 105 Timestamps: true, 106 RawTerminal: true, 107 Container: serveResource.Container.ID, 108 OutputStream: ww, 109 } 110 111 go (func() { 112 err = pool.Client.Logs(opts) 113 require.NoError(t, err) 114 })() 115 116 select { 117 case <-ww.c: 118 break 119 120 case <-time.After(10 * time.Second): 121 require.Fail(t, "timed out waiting for schema watch to run") 122 } 123 124 require.True(t, gracefulShutdown(pool, serveResource)) 125 }) 126 } 127 }