github.com/Redstoneguy129/cli@v0.0.0-20230211220159-15dca4e91917/internal/status/status_test.go (about)

     1  package status
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"io"
     8  	"net/http"
     9  	"os"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/Redstoneguy129/cli/internal/testing/apitest"
    14  	"github.com/Redstoneguy129/cli/internal/utils"
    15  	"github.com/docker/docker/api/types"
    16  	"github.com/spf13/afero"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  	"gopkg.in/h2non/gock.v1"
    20  )
    21  
    22  func TestStatusCommand(t *testing.T) {
    23  	t.Run("shows service status", func(t *testing.T) {
    24  		services := []string{
    25  			"supabase_db_",
    26  			"supabase_kong_",
    27  			"supabase_auth_",
    28  			"supabase_inbucket_",
    29  			"realtime-dev.supabase_realtime_",
    30  			"supabase_rest_",
    31  			"supabase_storage_",
    32  			"storage_imgproxy_",
    33  			"supabase_pg_meta_",
    34  			"supabase_studio_",
    35  		}
    36  		// Setup in-memory fs
    37  		fsys := afero.NewMemMapFs()
    38  		require.NoError(t, utils.WriteConfig(fsys, false))
    39  		// Setup mock docker
    40  		require.NoError(t, apitest.MockDocker(utils.Docker))
    41  		defer gock.OffAll()
    42  		for _, container := range services {
    43  			gock.New(utils.Docker.DaemonHost()).
    44  				Get("/v" + utils.Docker.ClientVersion() + "/containers/" + container).
    45  				Reply(http.StatusOK).
    46  				JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
    47  					State: &types.ContainerState{Running: true},
    48  				}})
    49  		}
    50  		// Run test
    51  		assert.NoError(t, Run(context.Background(), CustomName{}, OutputPretty, fsys))
    52  		// Check error
    53  		assert.Empty(t, apitest.ListUnmatchedRequests())
    54  	})
    55  
    56  	t.Run("throws error on missing config", func(t *testing.T) {
    57  		err := Run(context.Background(), CustomName{}, OutputPretty, afero.NewMemMapFs())
    58  		assert.ErrorIs(t, err, os.ErrNotExist)
    59  	})
    60  
    61  	t.Run("throws error on invalid config", func(t *testing.T) {
    62  		// Setup in-memory fs
    63  		fsys := afero.NewMemMapFs()
    64  		require.NoError(t, afero.WriteFile(fsys, utils.ConfigPath, []byte("malformed"), 0644))
    65  		// Run test
    66  		err := Run(context.Background(), CustomName{}, OutputPretty, fsys)
    67  		// Check error
    68  		assert.ErrorContains(t, err, "toml: line 0: unexpected EOF; expected key separator '='")
    69  	})
    70  
    71  	t.Run("throws error on missing docker", func(t *testing.T) {
    72  		// Setup in-memory fs
    73  		fsys := afero.NewMemMapFs()
    74  		require.NoError(t, utils.WriteConfig(fsys, false))
    75  		// Setup mock docker
    76  		require.NoError(t, apitest.MockDocker(utils.Docker))
    77  		defer gock.OffAll()
    78  		gock.New(utils.Docker.DaemonHost()).
    79  			Get("/v" + utils.Docker.ClientVersion() + "/containers/supabase_db_").
    80  			ReplyError(errors.New("network error"))
    81  		// Run test
    82  		err := Run(context.Background(), CustomName{}, OutputPretty, fsys)
    83  		// Check error
    84  		assert.ErrorContains(t, err, "network error")
    85  		assert.Empty(t, apitest.ListUnmatchedRequests())
    86  	})
    87  }
    88  
    89  func TestServiceHealth(t *testing.T) {
    90  	services := []string{"supabase_db", "supabase_auth"}
    91  
    92  	t.Run("checks all services", func(t *testing.T) {
    93  		// Setup mock docker
    94  		require.NoError(t, apitest.MockDocker(utils.Docker))
    95  		defer gock.OffAll()
    96  		gock.New(utils.Docker.DaemonHost()).
    97  			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + services[0] + "/json").
    98  			Reply(http.StatusOK).
    99  			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
   100  				State: &types.ContainerState{
   101  					Running: true,
   102  					Health:  &types.Health{Status: "Unhealthy"},
   103  				},
   104  			}})
   105  		gock.New(utils.Docker.DaemonHost()).
   106  			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + services[1] + "/json").
   107  			Reply(http.StatusOK).
   108  			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
   109  				State: &types.ContainerState{Status: "exited"},
   110  			}})
   111  		// Run test
   112  		var stderr bytes.Buffer
   113  		stopped := checkServiceHealth(context.Background(), services, &stderr)
   114  		// Check error
   115  		assert.Empty(t, stopped)
   116  		lines := strings.Split(strings.TrimSpace(stderr.String()), "\n")
   117  		assert.ElementsMatch(t, []string{
   118  			"supabase_db container is not ready: Unhealthy",
   119  			"supabase_auth container is not running: exited",
   120  		}, lines)
   121  		assert.Empty(t, apitest.ListUnmatchedRequests())
   122  	})
   123  
   124  	t.Run("throws error on missing container", func(t *testing.T) {
   125  		// Setup in-memory fs
   126  		fsys := afero.NewMemMapFs()
   127  		require.NoError(t, utils.WriteConfig(fsys, false))
   128  		// Setup mock docker
   129  		require.NoError(t, apitest.MockDocker(utils.Docker))
   130  		defer gock.OffAll()
   131  		for _, name := range services {
   132  			gock.New(utils.Docker.DaemonHost()).
   133  				Get("/v" + utils.Docker.ClientVersion() + "/containers/" + name + "/json").
   134  				Reply(http.StatusNotFound)
   135  		}
   136  		// Run test
   137  		stopped := checkServiceHealth(context.Background(), services, io.Discard)
   138  		// Check error
   139  		assert.ElementsMatch(t, services, stopped)
   140  		assert.Empty(t, apitest.ListUnmatchedRequests())
   141  	})
   142  }
   143  
   144  func TestPrintStatus(t *testing.T) {
   145  	utils.Config.Db.Port = 0
   146  	exclude := []string{
   147  		utils.ShortContainerImageName(utils.PostgrestImage),
   148  		utils.ShortContainerImageName(utils.StudioImage),
   149  		utils.GotrueId,
   150  		utils.InbucketId,
   151  	}
   152  
   153  	t.Run("outputs env var", func(t *testing.T) {
   154  		// Run test
   155  		var stdout bytes.Buffer
   156  		assert.NoError(t, printStatus(CustomName{DbURL: "DB_URL"}, OutputEnv, &stdout, exclude...))
   157  		// Check error
   158  		assert.Equal(t, "DB_URL=\"postgresql://postgres:postgres@"+utils.Config.Hostname+":0/postgres\"\n", stdout.String())
   159  	})
   160  
   161  	t.Run("outputs json object", func(t *testing.T) {
   162  		// Run test
   163  		var stdout bytes.Buffer
   164  		assert.NoError(t, printStatus(CustomName{DbURL: "DB_URL"}, OutputJson, &stdout, exclude...))
   165  		// Check error
   166  		assert.Equal(t, "{\"DB_URL\":\"postgresql://postgres:postgres@"+utils.Config.Hostname+":0/postgres\"}\n", stdout.String())
   167  	})
   168  
   169  	t.Run("outputs yaml properties", func(t *testing.T) {
   170  		// Run test
   171  		var stdout bytes.Buffer
   172  		assert.NoError(t, printStatus(CustomName{DbURL: "DB_URL"}, OutputYaml, &stdout, exclude...))
   173  		// Check error
   174  		assert.Equal(t, "DB_URL: postgresql://postgres:postgres@"+utils.Config.Hostname+":0/postgres\n", stdout.String())
   175  	})
   176  
   177  	t.Run("outputs toml fields", func(t *testing.T) {
   178  		// Run test
   179  		var stdout bytes.Buffer
   180  		assert.NoError(t, printStatus(CustomName{DbURL: "DB_URL"}, OutputToml, &stdout, exclude...))
   181  		// Check error
   182  		assert.Equal(t, "DB_URL = \"postgresql://postgres:postgres@"+utils.Config.Hostname+":0/postgres\"\n", stdout.String())
   183  	})
   184  }