@@ -173,3 +173,187 @@ func TestInsertRegistryAuth(t *testing.T) {
173173 })
174174 }
175175}
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