1 package client
2
3 import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "strings"
8 "testing"
9
10 "github.com/docker/docker-credential-helpers/credentials"
11 )
12
13 const (
14 validServerAddress = "https://registry.example.com/v1"
15 validUsername = "linus"
16 validServerAddress2 = "https://example.com:5002"
17 invalidServerAddress = "https://foobar.example.com"
18 missingCredsAddress = "https://missing.example.com/v1"
19 )
20
21 var errProgramExited = fmt.Errorf("exited 1")
22
23
24
25
26 type mockProgram struct {
27 arg string
28 input io.Reader
29 }
30
31
32
33 func (m *mockProgram) Output() ([]byte, error) {
34 in, err := io.ReadAll(m.input)
35 if err != nil {
36 return nil, err
37 }
38 inS := string(in)
39
40 switch m.arg {
41 case "erase":
42 switch inS {
43 case validServerAddress:
44 return nil, nil
45 default:
46 return []byte("program failed"), errProgramExited
47 }
48 case "get":
49 switch inS {
50 case validServerAddress:
51 return []byte(`{"Username": "foo", "Secret": "bar"}`), nil
52 case validServerAddress2:
53 return []byte(`{"Username": "<token>", "Secret": "abcd1234"}`), nil
54 case missingCredsAddress:
55 return []byte(credentials.NewErrCredentialsNotFound().Error()), errProgramExited
56 case invalidServerAddress:
57 return []byte("program failed"), errProgramExited
58 case "":
59 return []byte(credentials.NewErrCredentialsMissingServerURL().Error()), errProgramExited
60 }
61 case "store":
62 var c credentials.Credentials
63 err := json.NewDecoder(strings.NewReader(inS)).Decode(&c)
64 if err != nil {
65 return []byte("error storing credentials"), errProgramExited
66 }
67 switch c.ServerURL {
68 case validServerAddress:
69 return nil, nil
70 case validServerAddress2:
71 return nil, nil
72 default:
73 return []byte("error storing credentials"), errProgramExited
74 }
75 case "list":
76 return []byte(fmt.Sprintf(`{"%s": "%s"}`, validServerAddress, validUsername)), nil
77
78 }
79
80 return []byte(fmt.Sprintf("unknown argument %q with %q", m.arg, inS)), errProgramExited
81 }
82
83
84 func (m *mockProgram) Input(in io.Reader) {
85 m.input = in
86 }
87
88 func mockProgramFn(args ...string) Program {
89 return &mockProgram{
90 arg: args[0],
91 }
92 }
93
94 func ExampleStore() {
95 p := NewShellProgramFunc("docker-credential-pass")
96
97 c := &credentials.Credentials{
98 ServerURL: "https://registry.example.com",
99 Username: "exampleuser",
100 Secret: "my super secret token",
101 }
102
103 if err := Store(p, c); err != nil {
104 _, _ = fmt.Println(err)
105 }
106 }
107
108 func TestStore(t *testing.T) {
109 valid := []credentials.Credentials{
110 {ServerURL: validServerAddress, Username: "foo", Secret: "bar"},
111 {ServerURL: validServerAddress2, Username: "<token>", Secret: "abcd1234"},
112 }
113
114 for _, v := range valid {
115 if err := Store(mockProgramFn, &v); err != nil {
116 t.Error(err)
117 }
118 }
119
120 invalid := []credentials.Credentials{
121 {ServerURL: invalidServerAddress, Username: "foo", Secret: "bar"},
122 }
123
124 for _, v := range invalid {
125 if err := Store(mockProgramFn, &v); err == nil {
126 t.Errorf("Expected error for server %s, got nil", v.ServerURL)
127 }
128 }
129 }
130
131 func ExampleGet() {
132 p := NewShellProgramFunc("docker-credential-pass")
133
134 creds, err := Get(p, "https://registry.example.com")
135 if err != nil {
136 _, _ = fmt.Println(err)
137 }
138
139 _, _ = fmt.Printf("Got credentials for user `%s` in `%s`\n", creds.Username, creds.ServerURL)
140 }
141
142 func TestGet(t *testing.T) {
143 valid := []credentials.Credentials{
144 {ServerURL: validServerAddress, Username: "foo", Secret: "bar"},
145 {ServerURL: validServerAddress2, Username: "<token>", Secret: "abcd1234"},
146 }
147
148 for _, v := range valid {
149 c, err := Get(mockProgramFn, v.ServerURL)
150 if err != nil {
151 t.Fatal(err)
152 }
153
154 if c.Username != v.Username {
155 t.Errorf("expected username `%s`, got %s", v.Username, c.Username)
156 }
157 if c.Secret != v.Secret {
158 t.Errorf("expected secret `%s`, got %s", v.Secret, c.Secret)
159 }
160 }
161
162 missingServerURLErr := credentials.NewErrCredentialsMissingServerURL()
163
164 invalid := []struct {
165 serverURL string
166 err string
167 }{
168 {
169 serverURL: missingCredsAddress,
170 err: credentials.NewErrCredentialsNotFound().Error(),
171 },
172 {
173 serverURL: invalidServerAddress,
174 err: "error getting credentials - err: exited 1, out: `program failed`",
175 },
176 {
177 err: fmt.Sprintf("error getting credentials - err: %s, out: `%s`", missingServerURLErr.Error(), missingServerURLErr.Error()),
178 },
179 }
180
181 for _, v := range invalid {
182 _, err := Get(mockProgramFn, v.serverURL)
183 if err == nil {
184 t.Fatalf("Expected error for server %s, got nil", v.serverURL)
185 }
186 if err.Error() != v.err {
187 t.Errorf("Expected error `%s`, got `%v`", v.err, err)
188 }
189 }
190 }
191
192 func ExampleErase() {
193 p := NewShellProgramFunc("docker-credential-pass")
194
195 if err := Erase(p, "https://registry.example.com"); err != nil {
196 _, _ = fmt.Println(err)
197 }
198 }
199
200 func TestErase(t *testing.T) {
201 if err := Erase(mockProgramFn, validServerAddress); err != nil {
202 t.Error(err)
203 }
204
205 if err := Erase(mockProgramFn, invalidServerAddress); err == nil {
206 t.Errorf("Expected error for server %s, got nil", invalidServerAddress)
207 }
208 }
209
210 func TestList(t *testing.T) {
211 auths, err := List(mockProgramFn)
212 if err != nil {
213 t.Fatal(err)
214 }
215
216 if username, exists := auths[validServerAddress]; !exists || username != validUsername {
217 t.Errorf("auths[%s] returned %s, %t; expected %s, %t", validServerAddress, username, exists, validUsername, true)
218 }
219 }
220
View as plain text