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  }