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  }