github.com/Redstoneguy129/cli@v0.0.0-20230211220159-15dca4e91917/internal/start/start_test.go (about) 1 package start 2 3 import ( 4 "context" 5 "errors" 6 "net/http" 7 "os" 8 "testing" 9 10 "github.com/Redstoneguy129/cli/internal/testing/apitest" 11 "github.com/Redstoneguy129/cli/internal/testing/pgtest" 12 "github.com/Redstoneguy129/cli/internal/utils" 13 "github.com/docker/docker/api/types" 14 "github.com/spf13/afero" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 "gopkg.in/h2non/gock.v1" 18 ) 19 20 func TestStartCommand(t *testing.T) { 21 t.Run("throws error on missing config", func(t *testing.T) { 22 err := Run(context.Background(), afero.NewMemMapFs(), []string{}, false) 23 assert.ErrorIs(t, err, os.ErrNotExist) 24 }) 25 26 t.Run("throws error on invalid config", func(t *testing.T) { 27 // Setup in-memory fs 28 fsys := afero.NewMemMapFs() 29 require.NoError(t, afero.WriteFile(fsys, utils.ConfigPath, []byte("malformed"), 0644)) 30 // Run test 31 err := Run(context.Background(), fsys, []string{}, false) 32 // Check error 33 assert.ErrorContains(t, err, "toml: line 0: unexpected EOF; expected key separator '='") 34 }) 35 36 t.Run("throws error on missing docker", func(t *testing.T) { 37 // Setup in-memory fs 38 fsys := afero.NewMemMapFs() 39 require.NoError(t, utils.WriteConfig(fsys, false)) 40 // Setup mock docker 41 require.NoError(t, apitest.MockDocker(utils.Docker)) 42 defer gock.OffAll() 43 gock.New(utils.Docker.DaemonHost()). 44 Head("/_ping"). 45 ReplyError(errors.New("network error")) 46 gock.New(utils.Docker.DaemonHost()). 47 Get("/_ping"). 48 ReplyError(errors.New("network error")) 49 // Run test 50 err := Run(context.Background(), fsys, []string{}, false) 51 // Check error 52 assert.ErrorContains(t, err, "network error") 53 assert.Empty(t, apitest.ListUnmatchedRequests()) 54 }) 55 56 t.Run("noop if database is already running", func(t *testing.T) { 57 // Setup in-memory fs 58 fsys := afero.NewMemMapFs() 59 require.NoError(t, utils.WriteConfig(fsys, false)) 60 // Setup mock docker 61 require.NoError(t, apitest.MockDocker(utils.Docker)) 62 defer gock.OffAll() 63 gock.New("http:///var/run/docker.sock"). 64 Head("/_ping"). 65 Reply(http.StatusOK). 66 SetHeader("API-Version", utils.Docker.ClientVersion()). 67 SetHeader("OSType", "linux") 68 gock.New(utils.Docker.DaemonHost()). 69 Get("/_ping"). 70 Reply(http.StatusOK). 71 SetHeader("API-Version", utils.Docker.ClientVersion()). 72 SetHeader("OSType", "linux") 73 gock.New(utils.Docker.DaemonHost()). 74 Get("/v" + utils.Docker.ClientVersion() + "/containers"). 75 Reply(http.StatusOK). 76 JSON(types.ContainerJSON{}) 77 // Run test 78 err := Run(context.Background(), fsys, []string{}, false) 79 // Check error 80 assert.NoError(t, err) 81 assert.Empty(t, apitest.ListUnmatchedRequests()) 82 }) 83 84 t.Run("throws error on image pull failure", func(t *testing.T) { 85 // Setup in-memory fs 86 fsys := afero.NewMemMapFs() 87 require.NoError(t, utils.WriteConfig(fsys, false)) 88 // Setup mock docker 89 require.NoError(t, apitest.MockDocker(utils.Docker)) 90 defer gock.OffAll() 91 gock.New(utils.Docker.DaemonHost()). 92 Head("/_ping"). 93 Reply(http.StatusOK). 94 SetHeader("API-Version", utils.Docker.ClientVersion()). 95 SetHeader("OSType", "linux") 96 gock.New(utils.Docker.DaemonHost()). 97 Get("/_ping"). 98 Reply(http.StatusOK). 99 SetHeader("API-Version", utils.Docker.ClientVersion()). 100 SetHeader("OSType", "linux") 101 gock.New(utils.Docker.DaemonHost()). 102 Get("/v" + utils.Docker.ClientVersion() + "/containers"). 103 Reply(http.StatusNotFound) 104 gock.New(utils.Docker.DaemonHost()). 105 Get("/v" + utils.Docker.ClientVersion() + "/images"). 106 ReplyError(errors.New("network error")) 107 // Cleans up network on error 108 gock.New(utils.Docker.DaemonHost()). 109 Delete("/v" + utils.Docker.ClientVersion() + "/networks/supabase_network_"). 110 Reply(http.StatusOK) 111 // Run test 112 err := Run(context.Background(), fsys, []string{}, false) 113 // Check error 114 assert.ErrorContains(t, err, "network error") 115 assert.Empty(t, apitest.ListUnmatchedRequests()) 116 }) 117 } 118 119 func TestDatabaseStart(t *testing.T) { 120 t.Run("starts database locally", func(t *testing.T) { 121 // Setup in-memory fs 122 fsys := afero.NewMemMapFs() 123 // Setup mock docker 124 require.NoError(t, apitest.MockDocker(utils.Docker)) 125 defer gock.OffAll() 126 gock.New(utils.Docker.DaemonHost()). 127 Post("/v" + utils.Docker.ClientVersion() + "/networks/create"). 128 Reply(http.StatusCreated). 129 JSON(types.NetworkCreateResponse{}) 130 // Caches all dependencies 131 utils.DbImage = utils.Pg15Image 132 imageUrl := utils.GetRegistryImageUrl(utils.DbImage) 133 gock.New(utils.Docker.DaemonHost()). 134 Get("/v" + utils.Docker.ClientVersion() + "/images/" + imageUrl + "/json"). 135 Reply(http.StatusOK). 136 JSON(types.ImageInspect{}) 137 for _, image := range utils.ServiceImages { 138 service := utils.GetRegistryImageUrl(image) 139 gock.New(utils.Docker.DaemonHost()). 140 Get("/v" + utils.Docker.ClientVersion() + "/images/" + service + "/json"). 141 Reply(http.StatusOK). 142 JSON(types.ImageInspect{}) 143 } 144 // Start postgres 145 utils.DbId = "test-postgres" 146 utils.Config.Db.Port = 54322 147 gock.New(utils.Docker.DaemonHost()). 148 Get("/v" + utils.Docker.ClientVersion() + "/volumes/" + utils.DbId). 149 Reply(http.StatusNotFound) 150 apitest.MockDockerStart(utils.Docker, imageUrl, utils.DbId) 151 // Start services 152 utils.KongId = "test-kong" 153 apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.KongImage), utils.KongId) 154 utils.GotrueId = "test-gotrue" 155 flag := true 156 utils.Config.Auth.EnableSignup = &flag 157 utils.Config.Auth.Email.EnableSignup = &flag 158 utils.Config.Auth.Email.DoubleConfirmChanges = &flag 159 utils.Config.Auth.Email.EnableConfirmations = &flag 160 apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.GotrueImage), utils.GotrueId) 161 utils.InbucketId = "test-inbucket" 162 apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.InbucketImage), utils.InbucketId) 163 utils.RealtimeId = "test-realtime" 164 apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.RealtimeImage), utils.RealtimeId) 165 utils.RestId = "test-rest" 166 apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.PostgrestImage), utils.RestId) 167 utils.StorageId = "test-storage" 168 apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.StorageImage), utils.StorageId) 169 utils.ImgProxyId = "test-imgproxy" 170 apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.ImageProxyImage), utils.ImgProxyId) 171 utils.DifferId = "test-differ" 172 apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.DifferImage), utils.DifferId) 173 utils.PgmetaId = "test-pgmeta" 174 apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.PgmetaImage), utils.PgmetaId) 175 utils.StudioId = "test-studio" 176 apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.StudioImage), utils.StudioId) 177 // Setup mock postgres 178 utils.GlobalsSql = "create schema public" 179 utils.InitialSchemaSql = "create schema private" 180 conn := pgtest.NewConn() 181 defer conn.Close(t) 182 conn.Query(utils.GlobalsSql). 183 Reply("CREATE SCHEMA"). 184 Query(utils.InitialSchemaSql). 185 Reply("CREATE SCHEMA") 186 // Setup health probes 187 started := []string{ 188 utils.DbId, utils.KongId, utils.GotrueId, utils.InbucketId, utils.RealtimeId, 189 utils.StorageId, utils.ImgProxyId, utils.PgmetaId, utils.StudioId, 190 } 191 for _, container := range started { 192 gock.New(utils.Docker.DaemonHost()). 193 Get("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/json"). 194 Reply(http.StatusOK). 195 JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{ 196 State: &types.ContainerState{ 197 Running: true, 198 Health: &types.Health{Status: "healthy"}, 199 }, 200 }}) 201 } 202 gock.New(utils.Config.Hostname). 203 Head("/rest/v1/"). 204 Reply(http.StatusOK) 205 // Run test 206 err := utils.RunProgram(context.Background(), func(p utils.Program, ctx context.Context) error { 207 return run(p, context.Background(), fsys, []string{}, conn.Intercept) 208 }) 209 // Check error 210 assert.NoError(t, err) 211 assert.Empty(t, apitest.ListUnmatchedRequests()) 212 }) 213 214 t.Run("skips excluded containers", func(t *testing.T) { 215 // Setup in-memory fs 216 fsys := afero.NewMemMapFs() 217 // Setup mock docker 218 require.NoError(t, apitest.MockDocker(utils.Docker)) 219 defer gock.OffAll() 220 gock.New(utils.Docker.DaemonHost()). 221 Post("/v" + utils.Docker.ClientVersion() + "/networks/create"). 222 Reply(http.StatusCreated). 223 JSON(types.NetworkCreateResponse{}) 224 // Caches all dependencies 225 utils.DbImage = utils.Pg15Image 226 imageUrl := utils.GetRegistryImageUrl(utils.DbImage) 227 gock.New(utils.Docker.DaemonHost()). 228 Get("/v" + utils.Docker.ClientVersion() + "/images/" + imageUrl + "/json"). 229 Reply(http.StatusOK). 230 JSON(types.ImageInspect{}) 231 // Start postgres 232 utils.DbId = "test-postgres" 233 utils.Config.Db.Port = 54322 234 gock.New(utils.Docker.DaemonHost()). 235 Get("/v" + utils.Docker.ClientVersion() + "/volumes/" + utils.DbId). 236 Reply(http.StatusOK). 237 JSON(types.Volume{}) 238 apitest.MockDockerStart(utils.Docker, imageUrl, utils.DbId) 239 gock.New(utils.Docker.DaemonHost()). 240 Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). 241 Reply(http.StatusOK). 242 JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{ 243 State: &types.ContainerState{ 244 Running: true, 245 Health: &types.Health{Status: "healthy"}, 246 }, 247 }}) 248 // Run test 249 exclude := ExcludableContainers() 250 exclude = append(exclude, "invalid", exclude[0]) 251 err := utils.RunProgram(context.Background(), func(p utils.Program, ctx context.Context) error { 252 return run(p, context.Background(), fsys, exclude) 253 }) 254 // Check error 255 assert.NoError(t, err) 256 assert.Empty(t, apitest.ListUnmatchedRequests()) 257 }) 258 }