From ab57f64182b4270d11bf1e70dec4f9a3b7fe32fc Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Mon, 29 Jan 2024 12:47:56 +0800 Subject: [PATCH] chore: update unit tests --- cmd/unlink.go | 21 ++---------- internal/unlink/unlink.go | 47 +++++++++----------------- internal/unlink/unlink_test.go | 62 +++++++++++++++++++++++----------- 3 files changed, 60 insertions(+), 70 deletions(-) diff --git a/cmd/unlink.go b/cmd/unlink.go index c475a4830..e017e75ed 100644 --- a/cmd/unlink.go +++ b/cmd/unlink.go @@ -13,31 +13,14 @@ var ( unlinkCmd = &cobra.Command{ GroupID: groupLocalDev, Use: "unlink", - Short: "Unlink to a Supabase project", - PreRunE: func(cmd *cobra.Command, args []string) error { - return cmd.MarkFlagRequired("project-ref") - }, + Short: "Unlink a Supabase project", RunE: func(cmd *cobra.Command, args []string) error { ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt) - fsys := afero.NewOsFs() - if err := unlink.PreRun(projectRef, fsys); err != nil { - return err - } - if len(projectRef) == 0 { - if err := PromptProjectRef(ctx); err != nil { - return err - } - } - return unlink.Run(ctx, projectRef, fsys) - }, - PostRunE: func(cmd *cobra.Command, args []string) error { - return unlink.PostRun("", os.Stdout, afero.NewOsFs()) + return unlink.Run(ctx, afero.NewOsFs()) }, } ) func init() { - flags := unlinkCmd.Flags() - flags.StringVar(&projectRef, "project-ref", "", "Project ref of the Supabase project.") rootCmd.AddCommand(unlinkCmd) } diff --git a/internal/unlink/unlink.go b/internal/unlink/unlink.go index 648376118..25411c6b9 100644 --- a/internal/unlink/unlink.go +++ b/internal/unlink/unlink.go @@ -3,49 +3,34 @@ package unlink import ( "context" "fmt" - "io" + "os" + "github.com/go-errors/errors" "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" "github.com/supabase/cli/internal/utils/credentials" + "github.com/supabase/cli/internal/utils/flags" + "github.com/zalando/go-keyring" ) -func PreRun(projectRef string, fsys afero.Fs) error { - return utils.LoadConfigFS(fsys) -} +func Run(ctx context.Context, fsys afero.Fs) error { + projectRef, err := flags.LoadProjectRef(fsys) + if err != nil { + return err + } -func Run(ctx context.Context, projectRef string, fsys afero.Fs) error { + fmt.Fprintln(os.Stderr, "Unlinking project:", projectRef) // Remove temp directory - if err := removeTempDir(fsys); err != nil { - return err + if err := fsys.RemoveAll(utils.TempDir); err != nil { + return errors.Errorf("failed to remove temp directory: %w", err) } // Remove linked credentials - if err := removeLinkedCredentials(projectRef); err != nil { + if err := credentials.Delete(projectRef); err != nil && + !errors.Is(err, credentials.ErrNotSupported) && + !errors.Is(err, keyring.ErrNotFound) { return err } - fmt.Println("Successfully unlinked project.") - return nil -} - -func PostRun(projectRef string, stdout io.Writer, fsys afero.Fs) error { - fmt.Fprintln(stdout, "Finished "+utils.Aqua("supabase unlink")+".") - return nil -} - -// removeLinkedCredentials removes the database password associated with the projectRef -func removeLinkedCredentials(projectRef string) error { - fmt.Printf("Removing credentials for project %s...\n", projectRef) - if err := credentials.Set(projectRef, ""); err != nil { - return fmt.Errorf("failed to remove credentials for project '%s': %w", projectRef, err) - } - fmt.Println("Credentials for project", projectRef, "have been successfully removed.") - return nil -} -func removeTempDir(fsys afero.Fs) error { - tempDir := utils.TempDir - if err := fsys.RemoveAll(tempDir); err != nil { - return fmt.Errorf("failed to remove temp directory %s: %w", tempDir, err) - } + fmt.Fprintln(os.Stdout, "Finished "+utils.Aqua("supabase unlink")+".") return nil } diff --git a/internal/unlink/unlink_test.go b/internal/unlink/unlink_test.go index 4075e107b..e8bd284b6 100644 --- a/internal/unlink/unlink_test.go +++ b/internal/unlink/unlink_test.go @@ -2,10 +2,13 @@ package unlink import ( "context" + "os" "testing" "github.com/spf13/afero" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/supabase/cli/internal/testing/apitest" "github.com/supabase/cli/internal/utils" "github.com/supabase/cli/internal/utils/credentials" "github.com/zalando/go-keyring" @@ -13,34 +16,53 @@ import ( func TestUnlinkCommand(t *testing.T) { keyring.MockInit() - project := "test-project" - password := "test-password" + project := apitest.RandomProjectRef() - t.Run("unlink valid project", func(t *testing.T) { + t.Run("unlinks project", func(t *testing.T) { // Setup in-memory fs - // Set up a mock filesystem or a temp directory - fs := afero.NewMemMapFs() // or setup a real temp directory - err := afero.WriteFile(fs, "supabase/.temp/project-ref", []byte(project), 0644) + fsys := afero.NewMemMapFs() + require.NoError(t, afero.WriteFile(fsys, utils.ProjectRefPath, []byte(project), 0644)) + // Save database password + require.NoError(t, credentials.Set(project, "test")) // Run test + err := Run(context.Background(), fsys) // Check error assert.NoError(t, err) + // Validate file does not exist + exists, err := afero.Exists(fsys, utils.ProjectRefPath) + assert.NoError(t, err) + assert.False(t, exists) + // Check credentials does not exist + _, err = credentials.Get(project) + assert.ErrorIs(t, err, keyring.ErrNotFound) + }) - // Save database password - err = credentials.Set(project, password) + t.Run("unlinks project without credentials", func(t *testing.T) { + // Setup in-memory fs + fsys := afero.NewMemMapFs() + require.NoError(t, afero.WriteFile(fsys, utils.ProjectRefPath, []byte(project), 0644)) + // Run test + err := Run(context.Background(), fsys) // Check error assert.NoError(t, err) - // Run unlink test - err = Run(context.Background(), project, fs) + }) + + t.Run("throws error if not linked", func(t *testing.T) { + // Setup in-memory fs + fsys := afero.NewMemMapFs() + // Run test + err := Run(context.Background(), fsys) // Check error - assert.NoError(t, err) - // Validate file does not exist - _, err = afero.ReadFile(fs, utils.ProjectRefPath) - assert.Error(t, err) - // check credentials - // FIXME: only works this way because of the global state - // of credentials - content, err := credentials.Get(project) - assert.Equal(t, "", content) - assert.NoError(t, err) + assert.ErrorIs(t, err, utils.ErrNotLinked) + }) + + t.Run("throws error on permission denied", func(t *testing.T) { + // Setup in-memory fs + fsys := afero.NewMemMapFs() + require.NoError(t, afero.WriteFile(fsys, utils.ProjectRefPath, []byte(project), 0644)) + // Run test + err := Run(context.Background(), afero.NewReadOnlyFs(fsys)) + // Check error + assert.ErrorIs(t, err, os.ErrPermission) }) }