github.com/Redstoneguy129/cli@v0.0.0-20230211220159-15dca4e91917/internal/login/login_test.go (about) 1 package login 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "encoding/hex" 7 "io/fs" 8 "os" 9 "path/filepath" 10 "strings" 11 "testing" 12 13 "github.com/spf13/afero" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 "github.com/Redstoneguy129/cli/internal/utils" 17 "github.com/Redstoneguy129/cli/internal/utils/credentials" 18 "github.com/zalando/go-keyring" 19 ) 20 21 func randomToken(t *testing.T) []byte { 22 data := make([]byte, 20) 23 _, err := rand.Read(data) 24 require.NoError(t, err) 25 token := make([]byte, 44) 26 copy(token, "sbp_") 27 hex.Encode(token[4:], data) 28 return token 29 } 30 31 func TestLoginCommand(t *testing.T) { 32 keyring.MockInit() 33 34 t.Run("prompts and validates api token", func(t *testing.T) { 35 // Setup in-memory fs 36 fsys := afero.NewMemMapFs() 37 // Setup input with random token 38 token := randomToken(t) 39 stdin := bytes.NewBuffer(token) 40 // Run test 41 assert.NoError(t, Run(stdin, fsys)) 42 // Validate saved token 43 saved, err := credentials.Get(utils.AccessTokenKey) 44 assert.NoError(t, err) 45 assert.Equal(t, string(token), saved) 46 }) 47 48 t.Run("cancels when no input", func(t *testing.T) { 49 // Setup in-memory fs 50 fsys := afero.NewMemMapFs() 51 // Setup dependencies 52 stdin := &bytes.Buffer{} 53 // Run test 54 assert.NoError(t, Run(stdin, fsys)) 55 }) 56 57 t.Run("throws error on invalid token", func(t *testing.T) { 58 // Setup in-memory fs 59 fsys := afero.NewMemMapFs() 60 // Setup malformed token 61 stdin := bytes.NewBufferString("malformed") 62 // Run test 63 assert.Error(t, Run(stdin, fsys)) 64 }) 65 } 66 67 func TestSaveToken(t *testing.T) { 68 const token = "test-token" 69 70 t.Run("fallback saves to file", func(t *testing.T) { 71 // Setup in-memory fs 72 fsys := afero.NewMemMapFs() 73 // Run test 74 assert.NoError(t, fallbackSaveToken(token, fsys)) 75 // Validate saved token 76 home, err := os.UserHomeDir() 77 assert.NoError(t, err) 78 path := filepath.Join(home, ".supabase", utils.AccessTokenKey) 79 contents, err := afero.ReadFile(fsys, path) 80 assert.NoError(t, err) 81 assert.Equal(t, []byte(token), contents) 82 }) 83 84 t.Run("throws error on home dir failure", func(t *testing.T) { 85 // Setup in-memory fs 86 fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) 87 // Setup empty home directory 88 t.Setenv("HOME", "") 89 // Run test 90 err := fallbackSaveToken(token, fsys) 91 // Check error 92 assert.ErrorContains(t, err, "$HOME is not defined") 93 }) 94 95 t.Run("throws error on permission denied", func(t *testing.T) { 96 // Setup in-memory fs 97 fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) 98 // Run test 99 err := fallbackSaveToken(token, fsys) 100 // Check error 101 assert.ErrorContains(t, err, "operation not permitted") 102 }) 103 104 t.Run("throws error on write failure", func(t *testing.T) { 105 home, err := os.UserHomeDir() 106 assert.NoError(t, err) 107 // Setup in-memory fs 108 fsys := &MockFs{DenyPath: home} 109 // Run test 110 err = fallbackSaveToken(token, fsys) 111 // Check error 112 assert.ErrorContains(t, err, "permission denied") 113 }) 114 } 115 116 type MockFs struct { 117 afero.MemMapFs 118 DenyPath string 119 } 120 121 func (m *MockFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { 122 if strings.HasPrefix(name, m.DenyPath) { 123 return nil, fs.ErrPermission 124 } 125 return m.MemMapFs.OpenFile(name, flag, perm) 126 }