@@ -173,3 +173,187 @@ func TestInsertRegistryAuth(t *testing.T) {
173
173
})
174
174
}
175
175
}
176
+
177
+ // tests the parsing logic of insertCredentialsIntoConfig
178
+ func TestInsertCredentialsIntoConfig_Parsing (t * testing.T ) {
179
+ tests := []struct {
180
+ name string
181
+ imageAuthInput string
182
+ expectedAuthsJSON string
183
+ expectedCount int
184
+ expectError bool
185
+ }{
186
+ {
187
+ name : "simple host and token" ,
188
+ imageAuthInput : "myregistry.com:secrettoken" ,
189
+ expectedAuthsJSON : `{
190
+ "myregistry.com": {"auth": "secrettoken"}
191
+ }` ,
192
+ expectedCount : 1 ,
193
+ },
194
+ {
195
+ name : "docker.io host and token" ,
196
+ imageAuthInput : "docker.io:dockertoken" ,
197
+ expectedAuthsJSON : `{
198
+ "https://index.docker.io/v1/": {"auth": "dockertoken"}
199
+ }` ,
200
+ expectedCount : 1 ,
201
+ },
202
+ {
203
+ name : "subdomain of docker.io and token" ,
204
+ imageAuthInput : "sub.docker.io:subdockertoken" ,
205
+ expectedAuthsJSON : `{
206
+ "https://index.docker.io/v1/": {"auth": "subdockertoken"}
207
+ }` ,
208
+ expectedCount : 1 ,
209
+ },
210
+ {
211
+ name : "host with port and token" ,
212
+ imageAuthInput : "myregistry.com:5000:supersecret" ,
213
+ expectedAuthsJSON : `{
214
+ "myregistry.com:5000": {"auth": "supersecret"}
215
+ }` ,
216
+ expectedCount : 1 ,
217
+ },
218
+ {
219
+ name : "multiple credentials, some with port" ,
220
+ imageAuthInput : "reg1.com:token1,reg2.com:6000:token2,docker.io:token3" ,
221
+ expectedAuthsJSON : `{
222
+ "reg1.com": {"auth": "token1"},
223
+ "reg2.com:6000": {"auth": "token2"},
224
+ "https://index.docker.io/v1/": {"auth": "token3"}
225
+ }` ,
226
+ expectedCount : 4 ,
227
+ },
228
+ {
229
+ name : "empty imageAuth string" ,
230
+ imageAuthInput : "" ,
231
+ expectedAuthsJSON : `{}` ,
232
+ expectedCount : 0 ,
233
+ },
234
+ {
235
+ name : "credential with empty token part" ,
236
+ imageAuthInput : "myregistry.com:" ,
237
+ expectedAuthsJSON : `{
238
+ "myregistry.com": {"auth": ""}
239
+ }` ,
240
+ expectedCount : 1 ,
241
+ },
242
+ {
243
+ name : "credential with empty host part" ,
244
+ imageAuthInput : ":mytoken" ,
245
+ expectedAuthsJSON : `{
246
+ "": {"auth": "mytoken"}
247
+ }` ,
248
+ expectedCount : 1 ,
249
+ },
250
+ {
251
+ name : "credential with only a colon" ,
252
+ imageAuthInput : ":" ,
253
+ expectedAuthsJSON : `{
254
+ "": {"auth": ""}
255
+ }` ,
256
+ expectedCount : 1 ,
257
+ },
258
+ {
259
+ name : "single invalid credential (no colon)" ,
260
+ imageAuthInput : "myregistry.com" ,
261
+ expectedAuthsJSON : `{}` ,
262
+ expectedCount : 0 ,
263
+ },
264
+ {
265
+ name : "mixed valid and invalid credentials" ,
266
+ imageAuthInput : "reg1.com:token1,invalidreg,reg2.com:token2" ,
267
+ expectedAuthsJSON : `{
268
+ "reg1.com": {"auth": "token1"},
269
+ "reg2.com": {"auth": "token2"}
270
+ }` ,
271
+ expectedCount : 2 ,
272
+ },
273
+ {
274
+ name : "input with leading/trailing spaces for the whole string" ,
275
+ imageAuthInput : " myregistry.com:spacedtoken " ,
276
+ expectedAuthsJSON : `{
277
+ "myregistry.com": {"auth": "spacedtoken"}
278
+ }` ,
279
+ expectedCount : 1 ,
280
+ },
281
+ {
282
+ name : "input with spaces around comma separator creating leading space in host" ,
283
+ imageAuthInput : "reg1.com:token1 , reg2.com:token2" ,
284
+ expectedAuthsJSON : `{
285
+ "reg1.com": {"auth": "token1 "},
286
+ " reg2.com": {"auth": "token2"}
287
+ }` ,
288
+ expectedCount : 2 ,
289
+ },
290
+ }
291
+
292
+ for _ , tc := range tests {
293
+ t .Run (tc .name , func (t * testing.T ) {
294
+ tmpDir , err := os .MkdirTemp ("" , "docker-auth-test-" )
295
+ if err != nil {
296
+ t .Fatalf ("Failed to create temp dir: %v" , err )
297
+ }
298
+ defer os .RemoveAll (tmpDir )
299
+
300
+ originalHome := os .Getenv ("HOME" )
301
+ os .Setenv ("HOME" , tmpDir )
302
+ defer os .Setenv ("HOME" , originalHome )
303
+
304
+ count , err := insertCredentialsIntoConfig (tc .imageAuthInput )
305
+
306
+ if tc .expectError {
307
+ if err == nil {
308
+ t .Errorf ("Expected an error, but got nil" )
309
+ }
310
+ return
311
+ }
312
+ if err != nil {
313
+ t .Fatalf ("insertCredentialsIntoConfig() returned an unexpected error: %v" , err )
314
+ }
315
+
316
+ if count != tc .expectedCount {
317
+ t .Errorf ("Expected count %d, got %d" , tc .expectedCount , count )
318
+ }
319
+
320
+ configPath := filepath .Join (tmpDir , ".docker" , "config.json" )
321
+
322
+ var expectedAuthsMap map [string ]RegistryAuth
323
+ if err := json .Unmarshal ([]byte (tc .expectedAuthsJSON ), & expectedAuthsMap ); err != nil {
324
+ t .Fatalf ("Failed to unmarshal expectedAuthsJSON for tc '%s': %v. JSON: %s" , tc .name , err , tc .expectedAuthsJSON )
325
+ }
326
+
327
+ _ , statErr := os .Stat (configPath )
328
+ if os .IsNotExist (statErr ) {
329
+ if len (expectedAuthsMap ) == 0 && tc .expectedCount == 0 {
330
+ return
331
+ }
332
+ t .Fatalf ("Config file %s does not exist, but expected auths (count: %d, map: %s)" , configPath , tc .expectedCount , tc .expectedAuthsJSON )
333
+ }
334
+ if statErr != nil && ! os .IsNotExist (statErr ) {
335
+ t .Fatalf ("Error stating config file %s: %v" , configPath , statErr )
336
+ }
337
+
338
+ configBytes , readErr := os .ReadFile (configPath )
339
+ if readErr != nil {
340
+ t .Fatalf ("Failed to read docker config file %s: %v. Expected auths: %s" , configPath , readErr , tc .expectedAuthsJSON )
341
+ }
342
+
343
+ var resultConfig DockerConfig
344
+ if err := json .Unmarshal (configBytes , & resultConfig ); err != nil {
345
+ t .Fatalf ("Failed to unmarshal result config: %v. Content: %s" , err , string (configBytes ))
346
+ }
347
+
348
+ if len (expectedAuthsMap ) == 0 {
349
+ if resultConfig .Auths != nil && len (resultConfig .Auths ) != 0 {
350
+ t .Errorf ("Expected empty auths, but got: %v" , resultConfig .Auths )
351
+ }
352
+ } else {
353
+ if diff := cmp .Diff (expectedAuthsMap , resultConfig .Auths ); diff != "" {
354
+ t .Errorf ("Unexpected auths map in config.json (-want +got):\n %s" , diff )
355
+ }
356
+ }
357
+ })
358
+ }
359
+ }
0 commit comments