github.com/supabase/cli@v1.168.1/internal/stop/stop_test.go (about)

     1  package stop
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"net/http"
     8  	"os"
     9  	"testing"
    10  
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/api/types/volume"
    13  	"github.com/spf13/afero"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  	"github.com/supabase/cli/internal/testing/apitest"
    17  	"github.com/supabase/cli/internal/utils"
    18  	"gopkg.in/h2non/gock.v1"
    19  )
    20  
    21  func TestStopCommand(t *testing.T) {
    22  	t.Run("stops containers with backup", func(t *testing.T) {
    23  		// Setup in-memory fs
    24  		fsys := afero.NewMemMapFs()
    25  		require.NoError(t, utils.WriteConfig(fsys, false))
    26  		// Setup mock docker
    27  		require.NoError(t, apitest.MockDocker(utils.Docker))
    28  		defer gock.OffAll()
    29  		gock.New(utils.Docker.DaemonHost()).
    30  			Get("/v" + utils.Docker.ClientVersion() + "/containers/json").
    31  			Reply(http.StatusOK).
    32  			JSON([]types.Container{})
    33  		gock.New(utils.Docker.DaemonHost()).
    34  			Post("/v" + utils.Docker.ClientVersion() + "/containers/prune").
    35  			Reply(http.StatusOK).
    36  			JSON(types.ContainersPruneReport{})
    37  		gock.New(utils.Docker.DaemonHost()).
    38  			Post("/v" + utils.Docker.ClientVersion() + "/networks/prune").
    39  			Reply(http.StatusOK).
    40  			JSON(types.NetworksPruneReport{})
    41  		gock.New(utils.Docker.DaemonHost()).
    42  			Get("/v" + utils.Docker.ClientVersion() + "/volumes").
    43  			Reply(http.StatusOK).
    44  			JSON(volume.ListResponse{Volumes: []*volume.Volume{{
    45  				Name: utils.DbId,
    46  			}}})
    47  		// Run test
    48  		err := Run(context.Background(), true, "", fsys)
    49  		// Check error
    50  		assert.NoError(t, err)
    51  		assert.Empty(t, apitest.ListUnmatchedRequests())
    52  	})
    53  
    54  	t.Run("throws error on invalid config", func(t *testing.T) {
    55  		// Setup in-memory fs
    56  		fsys := afero.NewMemMapFs()
    57  		// Run test
    58  		err := Run(context.Background(), false, "", fsys)
    59  		// Check error
    60  		assert.ErrorIs(t, err, os.ErrNotExist)
    61  	})
    62  
    63  	t.Run("throws error on stop failure", func(t *testing.T) {
    64  		// Setup in-memory fs
    65  		fsys := afero.NewMemMapFs()
    66  		require.NoError(t, utils.WriteConfig(fsys, false))
    67  		// Setup mock docker
    68  		require.NoError(t, apitest.MockDocker(utils.Docker))
    69  		defer gock.OffAll()
    70  		gock.New(utils.Docker.DaemonHost()).
    71  			Get("/v" + utils.Docker.ClientVersion() + "/containers/json").
    72  			Reply(http.StatusServiceUnavailable)
    73  		// Run test
    74  		err := Run(context.Background(), false, "test", afero.NewReadOnlyFs(fsys))
    75  		// Check error
    76  		assert.ErrorContains(t, err, "request returned Service Unavailable for API route and version")
    77  		assert.Empty(t, apitest.ListUnmatchedRequests())
    78  	})
    79  }
    80  
    81  func TestStopServices(t *testing.T) {
    82  	t.Run("stops all services", func(t *testing.T) {
    83  		containers := []types.Container{{ID: "c1", State: "running"}, {ID: "c2"}}
    84  		// Setup mock docker
    85  		require.NoError(t, apitest.MockDocker(utils.Docker))
    86  		defer gock.OffAll()
    87  		gock.New(utils.Docker.DaemonHost()).
    88  			Get("/v" + utils.Docker.ClientVersion() + "/containers/json").
    89  			Reply(http.StatusOK).
    90  			JSON(containers)
    91  		gock.New(utils.Docker.DaemonHost()).
    92  			Post("/v" + utils.Docker.ClientVersion() + "/containers/" + containers[0].ID + "/stop").
    93  			Reply(http.StatusOK)
    94  		gock.New(utils.Docker.DaemonHost()).
    95  			Post("/v" + utils.Docker.ClientVersion() + "/containers/prune").
    96  			Reply(http.StatusOK).
    97  			JSON(types.ContainersPruneReport{})
    98  		gock.New(utils.Docker.DaemonHost()).
    99  			Post("/v" + utils.Docker.ClientVersion() + "/networks/prune").
   100  			Reply(http.StatusOK).
   101  			JSON(types.NetworksPruneReport{})
   102  		// Run test
   103  		err := stop(context.Background(), true, io.Discard)
   104  		// Check error
   105  		assert.NoError(t, err)
   106  		assert.Empty(t, apitest.ListUnmatchedRequests())
   107  	})
   108  
   109  	t.Run("removes data volumes", func(t *testing.T) {
   110  		utils.DbId = "test-db"
   111  		utils.ConfigId = "test-config"
   112  		utils.StorageId = "test-storage"
   113  		utils.EdgeRuntimeId = "test-functions"
   114  		utils.InbucketId = "test-inbucket"
   115  		// Setup mock docker
   116  		require.NoError(t, apitest.MockDocker(utils.Docker))
   117  		defer gock.OffAll()
   118  		apitest.MockDockerStop(utils.Docker)
   119  		// Run test
   120  		err := stop(context.Background(), false, io.Discard)
   121  		// Check error
   122  		assert.NoError(t, err)
   123  		assert.Empty(t, apitest.ListUnmatchedRequests())
   124  	})
   125  
   126  	t.Run("throws error on prune failure", func(t *testing.T) {
   127  		// Setup mock docker
   128  		require.NoError(t, apitest.MockDocker(utils.Docker))
   129  		defer gock.OffAll()
   130  		gock.New(utils.Docker.DaemonHost()).
   131  			Get("/v" + utils.Docker.ClientVersion() + "/containers/json").
   132  			Reply(http.StatusOK).
   133  			JSON([]types.Container{})
   134  		gock.New(utils.Docker.DaemonHost()).
   135  			Post("/v" + utils.Docker.ClientVersion() + "/containers/prune").
   136  			ReplyError(errors.New("network error"))
   137  		// Run test
   138  		err := stop(context.Background(), true, io.Discard)
   139  		// Check error
   140  		assert.ErrorContains(t, err, "network error")
   141  		assert.Empty(t, apitest.ListUnmatchedRequests())
   142  	})
   143  }