package utils import ( "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/testing/fstest" "github.com/supabase/cli/internal/utils/credentials" "github.com/zalando/go-keyring" ) func TestLoadToken(t *testing.T) { keyring.MockInit() token := string(apitest.RandomAccessToken(t)) t.Run("loads token from env var", func(t *testing.T) { t.Setenv("SUPABASE_ACCESS_TOKEN", token) // Setup in-memory fs fsys := afero.NewMemMapFs() // Run test loaded, err := LoadAccessTokenFS(fsys) // Check error assert.NoError(t, err) assert.Equal(t, token, loaded) }) t.Run("throws error on invalid token", func(t *testing.T) { t.Setenv("SUPABASE_ACCESS_TOKEN", "invalid") // Setup in-memory fs fsys := afero.NewMemMapFs() // Run test loaded, err := LoadAccessTokenFS(fsys) // Check error assert.ErrorIs(t, err, ErrInvalidToken) assert.Empty(t, loaded) }) t.Run("throws error on missing token", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() // Run test loaded, err := LoadAccessTokenFS(fsys) // Check error assert.ErrorIs(t, err, ErrMissingToken) assert.Empty(t, loaded) }) } func TestLoadTokenFallback(t *testing.T) { t.Run("fallback loads from file", func(t *testing.T) { path, err := getAccessTokenPath() assert.NoError(t, err) // Setup in-memory fs fsys := afero.NewMemMapFs() require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0600)) // Run test token, err := fallbackLoadToken(fsys) // Check error assert.NoError(t, err) assert.Empty(t, token) }) t.Run("throws error on home dir failure", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) // Setup empty home directory t.Setenv("HOME", "") // Run test token, err := fallbackLoadToken(fsys) // Check error assert.ErrorContains(t, err, "$HOME is not defined") assert.Empty(t, token) }) t.Run("throws error on read failure", func(t *testing.T) { path, err := getAccessTokenPath() assert.NoError(t, err) // Setup in-memory fs fsys := &fstest.OpenErrorFs{DenyPath: path} // Run test token, err := fallbackLoadToken(fsys) // Check error assert.ErrorContains(t, err, "permission denied") assert.Empty(t, token) }) } func TestSaveToken(t *testing.T) { keyring.MockInit() token := string(apitest.RandomAccessToken(t)) t.Run("saves token to keyring", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() // Run test assert.NoError(t, SaveAccessToken(token, fsys)) // Validate saved token saved, err := LoadAccessTokenFS(fsys) assert.NoError(t, err) assert.Equal(t, token, saved) }) t.Run("throws error on invalid token", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() // Run test err := SaveAccessToken("invalid", fsys) // Check error assert.ErrorIs(t, err, ErrInvalidToken) }) } func TestSaveTokenFallback(t *testing.T) { token := string(apitest.RandomAccessToken(t)) t.Run("fallback saves to file", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() // Run test assert.NoError(t, fallbackSaveToken(token, fsys)) // Validate saved token path, err := getAccessTokenPath() assert.NoError(t, err) contents, err := afero.ReadFile(fsys, path) assert.NoError(t, err) assert.Equal(t, []byte(token), contents) }) t.Run("throws error on home dir failure", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) // Setup empty home directory t.Setenv("HOME", "") // Run test err := fallbackSaveToken(token, fsys) // Check error assert.ErrorContains(t, err, "$HOME is not defined") }) t.Run("throws error on permission denied", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) // Run test err := fallbackSaveToken(token, fsys) // Check error assert.ErrorContains(t, err, "operation not permitted") }) t.Run("throws error on write failure", func(t *testing.T) { home, err := os.UserHomeDir() assert.NoError(t, err) // Setup in-memory fs fsys := &fstest.OpenErrorFs{DenyPath: home} // Run test err = fallbackSaveToken(token, fsys) // Check error assert.ErrorContains(t, err, "permission denied") }) } func TestDeleteToken(t *testing.T) { t.Run("deletes both keyring and fallback", func(t *testing.T) { token := string(apitest.RandomAccessToken(t)) require.NoError(t, credentials.StoreProvider.Set(AccessTokenKey, token)) // Setup in-memory fs fsys := afero.NewMemMapFs() require.NoError(t, fallbackSaveToken(token, fsys)) // Run test err := DeleteAccessToken(fsys) // Check error assert.NoError(t, err) _, err = credentials.StoreProvider.Get(AccessTokenKey) assert.ErrorIs(t, err, keyring.ErrNotFound) path, err := getAccessTokenPath() assert.NoError(t, err) exists, err := afero.Exists(fsys, path) assert.NoError(t, err) assert.False(t, exists) }) t.Run("throws error if not logged in", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() // Run test err := DeleteAccessToken(fsys) // Check error assert.ErrorIs(t, err, ErrNotLoggedIn) }) t.Run("throws error on home dir failure", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) // Setup empty home directory t.Setenv("HOME", "") // Run test err := DeleteAccessToken(fsys) // Check error assert.ErrorContains(t, err, "$HOME is not defined") }) }