oras.land/oras-go/v2@v2.5.1-0.20240520045656-aef90e4d04c4/registry/remote/credentials/internal/config/config_test.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package config
    17  
    18  import (
    19  	"encoding/json"
    20  	"errors"
    21  	"os"
    22  	"path/filepath"
    23  	"reflect"
    24  	"testing"
    25  
    26  	"oras.land/oras-go/v2/registry/remote/auth"
    27  	"oras.land/oras-go/v2/registry/remote/credentials/internal/config/configtest"
    28  )
    29  
    30  func TestLoad_badPath(t *testing.T) {
    31  	tempDir := t.TempDir()
    32  
    33  	tests := []struct {
    34  		name       string
    35  		configPath string
    36  		wantErr    bool
    37  	}{
    38  		{
    39  			name:       "Path is a directory",
    40  			configPath: tempDir,
    41  			wantErr:    true,
    42  		},
    43  		{
    44  			name:       "Empty file name",
    45  			configPath: filepath.Join(tempDir, ""),
    46  			wantErr:    true,
    47  		},
    48  	}
    49  	for _, tt := range tests {
    50  		t.Run(tt.name, func(t *testing.T) {
    51  			_, err := Load(tt.configPath)
    52  			if (err != nil) != tt.wantErr {
    53  				t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr)
    54  				return
    55  			}
    56  		})
    57  	}
    58  }
    59  
    60  func TestLoad_badFormat(t *testing.T) {
    61  	tests := []struct {
    62  		name       string
    63  		configPath string
    64  		wantErr    bool
    65  	}{
    66  		{
    67  			name:       "Bad JSON format",
    68  			configPath: "../../testdata/bad_config",
    69  			wantErr:    true,
    70  		},
    71  		{
    72  			name:       "Invalid auths format",
    73  			configPath: "../../testdata/invalid_auths_config.json",
    74  			wantErr:    true,
    75  		},
    76  		{
    77  			name:       "No auths field",
    78  			configPath: "../../testdata/no_auths_config.json",
    79  			wantErr:    false,
    80  		},
    81  	}
    82  	for _, tt := range tests {
    83  		t.Run(tt.name, func(t *testing.T) {
    84  			_, err := Load(tt.configPath)
    85  			if (err != nil) != tt.wantErr {
    86  				t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr)
    87  				return
    88  			}
    89  		})
    90  	}
    91  }
    92  
    93  func TestConfig_GetCredential_validConfig(t *testing.T) {
    94  	cfg, err := Load("../../testdata/valid_auths_config.json")
    95  	if err != nil {
    96  		t.Fatal("Load() error =", err)
    97  	}
    98  
    99  	tests := []struct {
   100  		name          string
   101  		serverAddress string
   102  		want          auth.Credential
   103  		wantErr       bool
   104  	}{
   105  		{
   106  			name:          "Username and password",
   107  			serverAddress: "registry1.example.com",
   108  			want: auth.Credential{
   109  				Username: "username",
   110  				Password: "password",
   111  			},
   112  		},
   113  		{
   114  			name:          "Identity token",
   115  			serverAddress: "registry2.example.com",
   116  			want: auth.Credential{
   117  				RefreshToken: "identity_token",
   118  			},
   119  		},
   120  		{
   121  			name:          "Registry token",
   122  			serverAddress: "registry3.example.com",
   123  			want: auth.Credential{
   124  				AccessToken: "registry_token",
   125  			},
   126  		},
   127  		{
   128  			name:          "Username and password, identity token and registry token",
   129  			serverAddress: "registry4.example.com",
   130  			want: auth.Credential{
   131  				Username:     "username",
   132  				Password:     "password",
   133  				RefreshToken: "identity_token",
   134  				AccessToken:  "registry_token",
   135  			},
   136  		},
   137  		{
   138  			name:          "Empty credential",
   139  			serverAddress: "registry5.example.com",
   140  			want:          auth.EmptyCredential,
   141  		},
   142  		{
   143  			name:          "Username and password, no auth",
   144  			serverAddress: "registry6.example.com",
   145  			want: auth.Credential{
   146  				Username: "username",
   147  				Password: "password",
   148  			},
   149  		},
   150  		{
   151  			name:          "Auth overriding Username and password",
   152  			serverAddress: "registry7.example.com",
   153  			want: auth.Credential{
   154  				Username: "username",
   155  				Password: "password",
   156  			},
   157  		},
   158  		{
   159  			name:          "Not in auths",
   160  			serverAddress: "foo.example.com",
   161  			want:          auth.EmptyCredential,
   162  		},
   163  		{
   164  			name:          "No record",
   165  			serverAddress: "registry999.example.com",
   166  			want:          auth.EmptyCredential,
   167  		},
   168  	}
   169  	for _, tt := range tests {
   170  		t.Run(tt.name, func(t *testing.T) {
   171  			got, err := cfg.GetCredential(tt.serverAddress)
   172  			if (err != nil) != tt.wantErr {
   173  				t.Errorf("Config.GetCredential() error = %v, wantErr %v", err, tt.wantErr)
   174  				return
   175  			}
   176  			if !reflect.DeepEqual(got, tt.want) {
   177  				t.Errorf("Config.GetCredential() = %v, want %v", got, tt.want)
   178  			}
   179  		})
   180  	}
   181  }
   182  
   183  func TestConfig_GetCredential_legacyConfig(t *testing.T) {
   184  	cfg, err := Load("../../testdata/legacy_auths_config.json")
   185  	if err != nil {
   186  		t.Fatal("Load() error =", err)
   187  	}
   188  
   189  	tests := []struct {
   190  		name          string
   191  		serverAddress string
   192  		want          auth.Credential
   193  		wantErr       bool
   194  	}{
   195  		{
   196  			name:          "Regular address matched",
   197  			serverAddress: "registry1.example.com",
   198  			want: auth.Credential{
   199  				Username: "username1",
   200  				Password: "password1",
   201  			},
   202  		},
   203  		{
   204  			name:          "Another entry for the same address matched",
   205  			serverAddress: "https://registry1.example.com/",
   206  			want: auth.Credential{
   207  				Username: "foo",
   208  				Password: "bar",
   209  			},
   210  		},
   211  		{
   212  			name:          "Address with different scheme unmached",
   213  			serverAddress: "http://registry1.example.com/",
   214  			want:          auth.EmptyCredential,
   215  		},
   216  		{
   217  			name:          "Address with http prefix matched",
   218  			serverAddress: "registry2.example.com",
   219  			want: auth.Credential{
   220  				Username: "username2",
   221  				Password: "password2",
   222  			},
   223  		},
   224  		{
   225  			name:          "Address with https prefix matched",
   226  			serverAddress: "registry3.example.com",
   227  			want: auth.Credential{
   228  				Username: "username3",
   229  				Password: "password3",
   230  			},
   231  		},
   232  		{
   233  			name:          "Address with http prefix and / suffix matched",
   234  			serverAddress: "registry4.example.com",
   235  			want: auth.Credential{
   236  				Username: "username4",
   237  				Password: "password4",
   238  			},
   239  		},
   240  		{
   241  			name:          "Address with https prefix and / suffix matched",
   242  			serverAddress: "registry5.example.com",
   243  			want: auth.Credential{
   244  				Username: "username5",
   245  				Password: "password5",
   246  			},
   247  		},
   248  		{
   249  			name:          "Address with https prefix and path suffix matched",
   250  			serverAddress: "registry6.example.com",
   251  			want: auth.Credential{
   252  				Username: "username6",
   253  				Password: "password6",
   254  			},
   255  		},
   256  	}
   257  	for _, tt := range tests {
   258  		t.Run(tt.name, func(t *testing.T) {
   259  			got, err := cfg.GetCredential(tt.serverAddress)
   260  			if (err != nil) != tt.wantErr {
   261  				t.Errorf("Config.GetCredential() error = %v, wantErr %v", err, tt.wantErr)
   262  				return
   263  			}
   264  			if !reflect.DeepEqual(got, tt.want) {
   265  				t.Errorf("Config.GetCredential() = %v, want %v", got, tt.want)
   266  			}
   267  		})
   268  	}
   269  }
   270  
   271  func TestConfig_GetCredential_invalidConfig(t *testing.T) {
   272  	cfg, err := Load("../../testdata/invalid_auths_entry_config.json")
   273  	if err != nil {
   274  		t.Fatal("Load() error =", err)
   275  	}
   276  
   277  	tests := []struct {
   278  		name          string
   279  		serverAddress string
   280  		want          auth.Credential
   281  		wantErr       bool
   282  	}{
   283  		{
   284  			name:          "Invalid auth encode",
   285  			serverAddress: "registry1.example.com",
   286  			want:          auth.EmptyCredential,
   287  			wantErr:       true,
   288  		},
   289  		{
   290  			name:          "Invalid auths format",
   291  			serverAddress: "registry2.example.com",
   292  			want:          auth.EmptyCredential,
   293  			wantErr:       true,
   294  		},
   295  		{
   296  			name:          "Invalid type",
   297  			serverAddress: "registry3.example.com",
   298  			want:          auth.EmptyCredential,
   299  			wantErr:       true,
   300  		},
   301  	}
   302  	for _, tt := range tests {
   303  		t.Run(tt.name, func(t *testing.T) {
   304  			got, err := cfg.GetCredential(tt.serverAddress)
   305  			if (err != nil) != tt.wantErr {
   306  				t.Errorf("Config.GetCredential() error = %v, wantErr %v", err, tt.wantErr)
   307  				return
   308  			}
   309  			if !reflect.DeepEqual(got, tt.want) {
   310  				t.Errorf("Config.GetCredential() = %v, want %v", got, tt.want)
   311  			}
   312  		})
   313  	}
   314  }
   315  
   316  func TestConfig_GetCredential_emptyConfig(t *testing.T) {
   317  	cfg, err := Load("../../testdata/empty_config.json")
   318  	if err != nil {
   319  		t.Fatal("Load() error =", err)
   320  	}
   321  
   322  	tests := []struct {
   323  		name          string
   324  		serverAddress string
   325  		want          auth.Credential
   326  		wantErr       error
   327  	}{
   328  		{
   329  			name:          "Not found",
   330  			serverAddress: "registry.example.com",
   331  			want:          auth.EmptyCredential,
   332  			wantErr:       nil,
   333  		},
   334  	}
   335  	for _, tt := range tests {
   336  		t.Run(tt.name, func(t *testing.T) {
   337  			got, err := cfg.GetCredential(tt.serverAddress)
   338  			if !errors.Is(err, tt.wantErr) {
   339  				t.Errorf("Config.GetCredential() error = %v, wantErr %v", err, tt.wantErr)
   340  				return
   341  			}
   342  			if !reflect.DeepEqual(got, tt.want) {
   343  				t.Errorf("Config.GetCredential() = %v, want %v", got, tt.want)
   344  			}
   345  		})
   346  	}
   347  }
   348  
   349  func TestConfig_GetCredential_notExistConfig(t *testing.T) {
   350  	cfg, err := Load("whatever")
   351  	if err != nil {
   352  		t.Fatal("Load() error =", err)
   353  	}
   354  
   355  	tests := []struct {
   356  		name          string
   357  		serverAddress string
   358  		want          auth.Credential
   359  		wantErr       error
   360  	}{
   361  		{
   362  			name:          "Not found",
   363  			serverAddress: "registry.example.com",
   364  			want:          auth.EmptyCredential,
   365  			wantErr:       nil,
   366  		},
   367  	}
   368  	for _, tt := range tests {
   369  		t.Run(tt.name, func(t *testing.T) {
   370  			got, err := cfg.GetCredential(tt.serverAddress)
   371  			if !errors.Is(err, tt.wantErr) {
   372  				t.Errorf("Config.GetCredential() error = %v, wantErr %v", err, tt.wantErr)
   373  				return
   374  			}
   375  			if !reflect.DeepEqual(got, tt.want) {
   376  				t.Errorf("Config.GetCredential() = %v, want %v", got, tt.want)
   377  			}
   378  		})
   379  	}
   380  }
   381  
   382  func TestConfig_PutCredential_notExistConfig(t *testing.T) {
   383  	tempDir := t.TempDir()
   384  	configPath := filepath.Join(tempDir, "config.json")
   385  
   386  	cfg, err := Load(configPath)
   387  	if err != nil {
   388  		t.Fatal("Load() error =", err)
   389  	}
   390  
   391  	server := "test.example.com"
   392  	cred := auth.Credential{
   393  		Username:     "username",
   394  		Password:     "password",
   395  		RefreshToken: "refresh_token",
   396  		AccessToken:  "access_token",
   397  	}
   398  
   399  	// test put
   400  	if err := cfg.PutCredential(server, cred); err != nil {
   401  		t.Fatalf("Config.PutCredential() error = %v", err)
   402  	}
   403  
   404  	// verify config file
   405  	configFile, err := os.Open(configPath)
   406  	if err != nil {
   407  		t.Fatalf("failed to open config file: %v", err)
   408  	}
   409  	defer configFile.Close()
   410  
   411  	var testCfg configtest.Config
   412  	if err := json.NewDecoder(configFile).Decode(&testCfg); err != nil {
   413  		t.Fatalf("failed to decode config file: %v", err)
   414  	}
   415  	want := configtest.Config{
   416  		AuthConfigs: map[string]configtest.AuthConfig{
   417  			server: {
   418  				Auth:          "dXNlcm5hbWU6cGFzc3dvcmQ=",
   419  				IdentityToken: "refresh_token",
   420  				RegistryToken: "access_token",
   421  			},
   422  		},
   423  	}
   424  	if !reflect.DeepEqual(testCfg, want) {
   425  		t.Errorf("Decoded config = %v, want %v", testCfg, want)
   426  	}
   427  
   428  	// verify get
   429  	got, err := cfg.GetCredential(server)
   430  	if err != nil {
   431  		t.Fatalf("Config.GetCredential() error = %v", err)
   432  	}
   433  	if want := cred; !reflect.DeepEqual(got, want) {
   434  		t.Errorf("Config.GetCredential() = %v, want %v", got, want)
   435  	}
   436  }
   437  
   438  func TestConfig_PutCredential_addNew(t *testing.T) {
   439  	tempDir := t.TempDir()
   440  	configPath := filepath.Join(tempDir, "config.json")
   441  	// prepare test content
   442  	server1 := "registry1.example.com"
   443  	cred1 := auth.Credential{
   444  		Username:     "username",
   445  		Password:     "password",
   446  		RefreshToken: "refresh_token",
   447  		AccessToken:  "access_token",
   448  	}
   449  
   450  	testCfg := configtest.Config{
   451  		AuthConfigs: map[string]configtest.AuthConfig{
   452  			server1: {
   453  				SomeAuthField: "whatever",
   454  				Auth:          "dXNlcm5hbWU6cGFzc3dvcmQ=",
   455  				IdentityToken: cred1.RefreshToken,
   456  				RegistryToken: cred1.AccessToken,
   457  			},
   458  		},
   459  		SomeConfigField: 123,
   460  	}
   461  	jsonStr, err := json.Marshal(testCfg)
   462  	if err != nil {
   463  		t.Fatalf("failed to marshal config: %v", err)
   464  	}
   465  	if err := os.WriteFile(configPath, jsonStr, 0666); err != nil {
   466  		t.Fatalf("failed to write config file: %v", err)
   467  	}
   468  
   469  	// test put
   470  	cfg, err := Load(configPath)
   471  	if err != nil {
   472  		t.Fatal("Load() error =", err)
   473  	}
   474  	server2 := "registry2.example.com"
   475  	cred2 := auth.Credential{
   476  		Username:     "username_2",
   477  		Password:     "password_2",
   478  		RefreshToken: "refresh_token_2",
   479  		AccessToken:  "access_token_2",
   480  	}
   481  	if err := cfg.PutCredential(server2, cred2); err != nil {
   482  		t.Fatalf("Config.PutCredential() error = %v", err)
   483  	}
   484  
   485  	// verify config file
   486  	configFile, err := os.Open(configPath)
   487  	if err != nil {
   488  		t.Fatalf("failed to open config file: %v", err)
   489  	}
   490  	defer configFile.Close()
   491  	var gotCfg configtest.Config
   492  	if err := json.NewDecoder(configFile).Decode(&gotCfg); err != nil {
   493  		t.Fatalf("failed to decode config file: %v", err)
   494  	}
   495  	wantTestCfg := configtest.Config{
   496  		AuthConfigs: map[string]configtest.AuthConfig{
   497  			server1: {
   498  				SomeAuthField: "whatever",
   499  				Auth:          "dXNlcm5hbWU6cGFzc3dvcmQ=",
   500  				IdentityToken: cred1.RefreshToken,
   501  				RegistryToken: cred1.AccessToken,
   502  			},
   503  			server2: {
   504  				Auth:          "dXNlcm5hbWVfMjpwYXNzd29yZF8y",
   505  				IdentityToken: "refresh_token_2",
   506  				RegistryToken: "access_token_2",
   507  			},
   508  		},
   509  		SomeConfigField: testCfg.SomeConfigField,
   510  	}
   511  	if !reflect.DeepEqual(gotCfg, wantTestCfg) {
   512  		t.Errorf("Decoded config = %v, want %v", gotCfg, wantTestCfg)
   513  	}
   514  
   515  	// verify get
   516  	got, err := cfg.GetCredential(server1)
   517  	if err != nil {
   518  		t.Fatalf("Config.GetCredential() error = %v", err)
   519  	}
   520  	if want := cred1; !reflect.DeepEqual(got, want) {
   521  		t.Errorf("Config.GetCredential(%s) = %v, want %v", server1, got, want)
   522  	}
   523  
   524  	got, err = cfg.GetCredential(server2)
   525  	if err != nil {
   526  		t.Fatalf("Config.GetCredential() error = %v", err)
   527  	}
   528  	if want := cred2; !reflect.DeepEqual(got, want) {
   529  		t.Errorf("Config.GetCredential(%s) = %v, want %v", server2, got, want)
   530  	}
   531  }
   532  
   533  func TestConfig_PutCredential_updateOld(t *testing.T) {
   534  	tempDir := t.TempDir()
   535  	configPath := filepath.Join(tempDir, "config.json")
   536  
   537  	// prepare test content
   538  	server := "registry.example.com"
   539  	testCfg := configtest.Config{
   540  		AuthConfigs: map[string]configtest.AuthConfig{
   541  			server: {
   542  				SomeAuthField: "whatever",
   543  				Username:      "foo",
   544  				Password:      "bar",
   545  				IdentityToken: "refresh_token",
   546  			},
   547  		},
   548  		SomeConfigField: 123,
   549  	}
   550  	jsonStr, err := json.Marshal(testCfg)
   551  	if err != nil {
   552  		t.Fatalf("failed to marshal config: %v", err)
   553  	}
   554  	if err := os.WriteFile(configPath, jsonStr, 0666); err != nil {
   555  		t.Fatalf("failed to write config file: %v", err)
   556  	}
   557  
   558  	// test put
   559  	cfg, err := Load(configPath)
   560  	if err != nil {
   561  		t.Fatal("Load() error =", err)
   562  	}
   563  	cred := auth.Credential{
   564  		Username:    "username",
   565  		Password:    "password",
   566  		AccessToken: "access_token",
   567  	}
   568  	if err := cfg.PutCredential(server, cred); err != nil {
   569  		t.Fatalf("Config.PutCredential() error = %v", err)
   570  	}
   571  
   572  	// verify config file
   573  	configFile, err := os.Open(configPath)
   574  	if err != nil {
   575  		t.Fatalf("failed to open config file: %v", err)
   576  	}
   577  	defer configFile.Close()
   578  	var gotCfg configtest.Config
   579  	if err := json.NewDecoder(configFile).Decode(&gotCfg); err != nil {
   580  		t.Fatalf("failed to decode config file: %v", err)
   581  	}
   582  	wantCfg := configtest.Config{
   583  		AuthConfigs: map[string]configtest.AuthConfig{
   584  			server: {
   585  				Auth:          "dXNlcm5hbWU6cGFzc3dvcmQ=",
   586  				RegistryToken: "access_token",
   587  			},
   588  		},
   589  		SomeConfigField: testCfg.SomeConfigField,
   590  	}
   591  	if !reflect.DeepEqual(gotCfg, wantCfg) {
   592  		t.Errorf("Decoded config = %v, want %v", gotCfg, wantCfg)
   593  	}
   594  
   595  	// verify get
   596  	got, err := cfg.GetCredential(server)
   597  	if err != nil {
   598  		t.Fatalf("Config.GetCredential() error = %v", err)
   599  	}
   600  	if want := cred; !reflect.DeepEqual(got, want) {
   601  		t.Errorf("Config.GetCredential(%s) = %v, want %v", server, got, want)
   602  	}
   603  }
   604  
   605  func TestConfig_DeleteCredential(t *testing.T) {
   606  	tempDir := t.TempDir()
   607  	configPath := filepath.Join(tempDir, "config.json")
   608  
   609  	// prepare test content
   610  	server1 := "registry1.example.com"
   611  	cred1 := auth.Credential{
   612  		Username:     "username",
   613  		Password:     "password",
   614  		RefreshToken: "refresh_token",
   615  		AccessToken:  "access_token",
   616  	}
   617  	server2 := "registry2.example.com"
   618  	cred2 := auth.Credential{
   619  		Username:     "username_2",
   620  		Password:     "password_2",
   621  		RefreshToken: "refresh_token_2",
   622  		AccessToken:  "access_token_2",
   623  	}
   624  
   625  	testCfg := configtest.Config{
   626  		AuthConfigs: map[string]configtest.AuthConfig{
   627  			server1: {
   628  				Auth:          "dXNlcm5hbWU6cGFzc3dvcmQ=",
   629  				IdentityToken: cred1.RefreshToken,
   630  				RegistryToken: cred1.AccessToken,
   631  			},
   632  			server2: {
   633  				Auth:          "dXNlcm5hbWVfMjpwYXNzd29yZF8y",
   634  				IdentityToken: "refresh_token_2",
   635  				RegistryToken: "access_token_2",
   636  			},
   637  		},
   638  		SomeConfigField: 123,
   639  	}
   640  	jsonStr, err := json.Marshal(testCfg)
   641  	if err != nil {
   642  		t.Fatalf("failed to marshal config: %v", err)
   643  	}
   644  	if err := os.WriteFile(configPath, jsonStr, 0666); err != nil {
   645  		t.Fatalf("failed to write config file: %v", err)
   646  	}
   647  
   648  	cfg, err := Load(configPath)
   649  	if err != nil {
   650  		t.Fatal("Load() error =", err)
   651  	}
   652  	// test get
   653  	got, err := cfg.GetCredential(server1)
   654  	if err != nil {
   655  		t.Fatalf("FileStore.GetCredential() error = %v", err)
   656  	}
   657  	if want := cred1; !reflect.DeepEqual(got, want) {
   658  		t.Errorf("FileStore.GetCredential(%s) = %v, want %v", server1, got, want)
   659  	}
   660  	got, err = cfg.GetCredential(server2)
   661  	if err != nil {
   662  		t.Fatalf("FileStore.GetCredential() error = %v", err)
   663  	}
   664  	if want := cred2; !reflect.DeepEqual(got, want) {
   665  		t.Errorf("FileStore.Get(%s) = %v, want %v", server2, got, want)
   666  	}
   667  
   668  	// test delete
   669  	if err := cfg.DeleteCredential(server1); err != nil {
   670  		t.Fatalf("Config.DeleteCredential() error = %v", err)
   671  	}
   672  
   673  	// verify config file
   674  	configFile, err := os.Open(configPath)
   675  	if err != nil {
   676  		t.Fatalf("failed to open config file: %v", err)
   677  	}
   678  	defer configFile.Close()
   679  	var gotTestCfg configtest.Config
   680  	if err := json.NewDecoder(configFile).Decode(&gotTestCfg); err != nil {
   681  		t.Fatalf("failed to decode config file: %v", err)
   682  	}
   683  	wantTestCfg := configtest.Config{
   684  		AuthConfigs: map[string]configtest.AuthConfig{
   685  			server2: testCfg.AuthConfigs[server2],
   686  		},
   687  		SomeConfigField: testCfg.SomeConfigField,
   688  	}
   689  	if !reflect.DeepEqual(gotTestCfg, wantTestCfg) {
   690  		t.Errorf("Decoded config = %v, want %v", gotTestCfg, wantTestCfg)
   691  	}
   692  
   693  	// test get again
   694  	got, err = cfg.GetCredential(server1)
   695  	if err != nil {
   696  		t.Fatalf("Config.GetCredential() error = %v", err)
   697  	}
   698  	if want := auth.EmptyCredential; !reflect.DeepEqual(got, want) {
   699  		t.Errorf("Config.GetCredential(%s) = %v, want %v", server1, got, want)
   700  	}
   701  	got, err = cfg.GetCredential(server2)
   702  	if err != nil {
   703  		t.Fatalf("Config.GetCredential() error = %v", err)
   704  	}
   705  	if want := cred2; !reflect.DeepEqual(got, want) {
   706  		t.Errorf("Config.GetCredential(%s) = %v, want %v", server2, got, want)
   707  	}
   708  }
   709  
   710  func TestConfig_DeleteCredential_lastConfig(t *testing.T) {
   711  	tempDir := t.TempDir()
   712  	configPath := filepath.Join(tempDir, "config.json")
   713  
   714  	// prepare test content
   715  	server := "registry1.example.com"
   716  	cred := auth.Credential{
   717  		Username:     "username",
   718  		Password:     "password",
   719  		RefreshToken: "refresh_token",
   720  		AccessToken:  "access_token",
   721  	}
   722  
   723  	testCfg := configtest.Config{
   724  		AuthConfigs: map[string]configtest.AuthConfig{
   725  			server: {
   726  				Auth:          "dXNlcm5hbWU6cGFzc3dvcmQ=",
   727  				IdentityToken: cred.RefreshToken,
   728  				RegistryToken: cred.AccessToken,
   729  			},
   730  		},
   731  		SomeConfigField: 123,
   732  	}
   733  	jsonStr, err := json.Marshal(testCfg)
   734  	if err != nil {
   735  		t.Fatalf("failed to marshal config: %v", err)
   736  	}
   737  	if err := os.WriteFile(configPath, jsonStr, 0666); err != nil {
   738  		t.Fatalf("failed to write config file: %v", err)
   739  	}
   740  
   741  	cfg, err := Load(configPath)
   742  	if err != nil {
   743  		t.Fatal("Load() error =", err)
   744  	}
   745  	// test get
   746  	got, err := cfg.GetCredential(server)
   747  	if err != nil {
   748  		t.Fatalf("Config.GetCredential() error = %v", err)
   749  	}
   750  	if want := cred; !reflect.DeepEqual(got, want) {
   751  		t.Errorf("Config.GetCredential(%s) = %v, want %v", server, got, want)
   752  	}
   753  
   754  	// test delete
   755  	if err := cfg.DeleteCredential(server); err != nil {
   756  		t.Fatalf("Config.DeleteCredential() error = %v", err)
   757  	}
   758  
   759  	// verify config file
   760  	configFile, err := os.Open(configPath)
   761  	if err != nil {
   762  		t.Fatalf("failed to open config file: %v", err)
   763  	}
   764  	defer configFile.Close()
   765  	var gotTestCfg configtest.Config
   766  	if err := json.NewDecoder(configFile).Decode(&gotTestCfg); err != nil {
   767  		t.Fatalf("failed to decode config file: %v", err)
   768  	}
   769  	wantTestCfg := configtest.Config{
   770  		AuthConfigs:     map[string]configtest.AuthConfig{},
   771  		SomeConfigField: testCfg.SomeConfigField,
   772  	}
   773  	if !reflect.DeepEqual(gotTestCfg, wantTestCfg) {
   774  		t.Errorf("Decoded config = %v, want %v", gotTestCfg, wantTestCfg)
   775  	}
   776  
   777  	// test get again
   778  	got, err = cfg.GetCredential(server)
   779  	if err != nil {
   780  		t.Fatalf("Config.GetCredential() error = %v", err)
   781  	}
   782  	if want := auth.EmptyCredential; !reflect.DeepEqual(got, want) {
   783  		t.Errorf("Config.GetCredential(%s) = %v, want %v", server, got, want)
   784  	}
   785  }
   786  
   787  func TestConfig_DeleteCredential_notExistRecord(t *testing.T) {
   788  	tempDir := t.TempDir()
   789  	configPath := filepath.Join(tempDir, "config.json")
   790  
   791  	// prepare test content
   792  	server := "registry1.example.com"
   793  	cred := auth.Credential{
   794  		Username:     "username",
   795  		Password:     "password",
   796  		RefreshToken: "refresh_token",
   797  		AccessToken:  "access_token",
   798  	}
   799  	testCfg := configtest.Config{
   800  		AuthConfigs: map[string]configtest.AuthConfig{
   801  			server: {
   802  				Auth:          "dXNlcm5hbWU6cGFzc3dvcmQ=",
   803  				IdentityToken: cred.RefreshToken,
   804  				RegistryToken: cred.AccessToken,
   805  			},
   806  		},
   807  		SomeConfigField: 123,
   808  	}
   809  	jsonStr, err := json.Marshal(testCfg)
   810  	if err != nil {
   811  		t.Fatalf("failed to marshal config: %v", err)
   812  	}
   813  	if err := os.WriteFile(configPath, jsonStr, 0666); err != nil {
   814  		t.Fatalf("failed to write config file: %v", err)
   815  	}
   816  
   817  	cfg, err := Load(configPath)
   818  	if err != nil {
   819  		t.Fatal("Load() error =", err)
   820  	}
   821  	// test get
   822  	got, err := cfg.GetCredential(server)
   823  	if err != nil {
   824  		t.Fatalf("Config.GetCredential() error = %v", err)
   825  	}
   826  	if want := cred; !reflect.DeepEqual(got, want) {
   827  		t.Errorf("Config.GetCredential(%s) = %v, want %v", server, got, want)
   828  	}
   829  
   830  	// test delete
   831  	if err := cfg.DeleteCredential("test.example.com"); err != nil {
   832  		t.Fatalf("Config.DeleteCredential() error = %v", err)
   833  	}
   834  
   835  	// verify config file
   836  	configFile, err := os.Open(configPath)
   837  	if err != nil {
   838  		t.Fatalf("failed to open config file: %v", err)
   839  	}
   840  	defer configFile.Close()
   841  	var gotTestCfg configtest.Config
   842  	if err := json.NewDecoder(configFile).Decode(&gotTestCfg); err != nil {
   843  		t.Fatalf("failed to decode config file: %v", err)
   844  	}
   845  	wantTestCfg := configtest.Config{
   846  		AuthConfigs: map[string]configtest.AuthConfig{
   847  			server: testCfg.AuthConfigs[server],
   848  		},
   849  		SomeConfigField: testCfg.SomeConfigField,
   850  	}
   851  	if !reflect.DeepEqual(gotTestCfg, wantTestCfg) {
   852  		t.Errorf("Decoded config = %v, want %v", gotTestCfg, wantTestCfg)
   853  	}
   854  
   855  	// test get again
   856  	got, err = cfg.GetCredential(server)
   857  	if err != nil {
   858  		t.Fatalf("Config.GetCredential() error = %v", err)
   859  	}
   860  	if want := cred; !reflect.DeepEqual(got, want) {
   861  		t.Errorf("Config.GetCredential(%s) = %v, want %v", server, got, want)
   862  	}
   863  }
   864  
   865  func TestConfig_DeleteCredential_notExistConfig(t *testing.T) {
   866  	tempDir := t.TempDir()
   867  	configPath := filepath.Join(tempDir, "config.json")
   868  
   869  	cfg, err := Load(configPath)
   870  	if err != nil {
   871  		t.Fatal("Load() error =", err)
   872  	}
   873  
   874  	server := "test.example.com"
   875  	// test delete
   876  	if err := cfg.DeleteCredential(server); err != nil {
   877  		t.Fatalf("Config.DeleteCredential() error = %v", err)
   878  	}
   879  
   880  	// verify config file is not created
   881  	_, err = os.Stat(configPath)
   882  	if wantErr := os.ErrNotExist; !errors.Is(err, wantErr) {
   883  		t.Errorf("Stat(%s) error = %v, wantErr %v", configPath, err, wantErr)
   884  	}
   885  }
   886  
   887  func TestConfig_GetCredentialHelper(t *testing.T) {
   888  	cfg, err := Load("../../testdata/credHelpers_config.json")
   889  	if err != nil {
   890  		t.Fatal("Load() error =", err)
   891  	}
   892  
   893  	tests := []struct {
   894  		name          string
   895  		serverAddress string
   896  		want          string
   897  	}{
   898  		{
   899  			name:          "Get cred helper: registry_helper1",
   900  			serverAddress: "registry1.example.com",
   901  			want:          "registry1-helper",
   902  		},
   903  		{
   904  			name:          "Get cred helper: registry_helper2",
   905  			serverAddress: "registry2.example.com",
   906  			want:          "registry2-helper",
   907  		},
   908  		{
   909  			name:          "Empty cred helper configured",
   910  			serverAddress: "registry3.example.com",
   911  			want:          "",
   912  		},
   913  		{
   914  			name:          "No cred helper configured",
   915  			serverAddress: "whatever.example.com",
   916  			want:          "",
   917  		},
   918  	}
   919  	for _, tt := range tests {
   920  		t.Run(tt.name, func(t *testing.T) {
   921  			if got := cfg.GetCredentialHelper(tt.serverAddress); got != tt.want {
   922  				t.Errorf("Config.GetCredentialHelper() = %v, want %v", got, tt.want)
   923  			}
   924  		})
   925  	}
   926  }
   927  
   928  func TestConfig_CredentialsStore(t *testing.T) {
   929  	tests := []struct {
   930  		name       string
   931  		configPath string
   932  		want       string
   933  	}{
   934  		{
   935  			name:       "creds store configured",
   936  			configPath: "../../testdata/credsStore_config.json",
   937  			want:       "teststore",
   938  		},
   939  		{
   940  			name:       "No creds store configured",
   941  			configPath: "../../testdata/credsHelpers_config.json",
   942  			want:       "",
   943  		},
   944  	}
   945  	for _, tt := range tests {
   946  		t.Run(tt.name, func(t *testing.T) {
   947  			cfg, err := Load(tt.configPath)
   948  			if err != nil {
   949  				t.Fatal("Load() error =", err)
   950  			}
   951  			if got := cfg.CredentialsStore(); got != tt.want {
   952  				t.Errorf("Config.CredentialsStore() = %v, want %v", got, tt.want)
   953  			}
   954  		})
   955  	}
   956  }
   957  
   958  func TestConfig_SetCredentialsStore(t *testing.T) {
   959  	// prepare test content
   960  	tempDir := t.TempDir()
   961  	configPath := filepath.Join(tempDir, "config.json")
   962  	testCfg := configtest.Config{
   963  		SomeConfigField: 123,
   964  	}
   965  	jsonStr, err := json.Marshal(testCfg)
   966  	if err != nil {
   967  		t.Fatalf("failed to marshal config: %v", err)
   968  	}
   969  	if err := os.WriteFile(configPath, jsonStr, 0666); err != nil {
   970  		t.Fatalf("failed to write config file: %v", err)
   971  	}
   972  
   973  	// test SetCredentialsStore
   974  	cfg, err := Load(configPath)
   975  	if err != nil {
   976  		t.Fatal("Load() error =", err)
   977  	}
   978  	credsStore := "testStore"
   979  	if err := cfg.SetCredentialsStore(credsStore); err != nil {
   980  		t.Fatal("Config.SetCredentialsStore() error =", err)
   981  	}
   982  
   983  	// verify
   984  	if got := cfg.credentialsStore; got != credsStore {
   985  		t.Errorf("Config.credentialsStore = %v, want %v", got, credsStore)
   986  	}
   987  	// verify config file
   988  	configFile, err := os.Open(configPath)
   989  	if err != nil {
   990  		t.Fatalf("failed to open config file: %v", err)
   991  	}
   992  	var gotTestCfg1 configtest.Config
   993  	if err := json.NewDecoder(configFile).Decode(&gotTestCfg1); err != nil {
   994  		t.Fatalf("failed to decode config file: %v", err)
   995  	}
   996  	if err := configFile.Close(); err != nil {
   997  		t.Fatal("failed to close config file:", err)
   998  	}
   999  
  1000  	wantTestCfg1 := configtest.Config{
  1001  		AuthConfigs:      make(map[string]configtest.AuthConfig),
  1002  		CredentialsStore: credsStore,
  1003  		SomeConfigField:  testCfg.SomeConfigField,
  1004  	}
  1005  	if !reflect.DeepEqual(gotTestCfg1, wantTestCfg1) {
  1006  		t.Errorf("Decoded config = %v, want %v", gotTestCfg1, wantTestCfg1)
  1007  	}
  1008  
  1009  	// test SetCredentialsStore: set as empty
  1010  	if err := cfg.SetCredentialsStore(""); err != nil {
  1011  		t.Fatal("Config.SetCredentialsStore() error =", err)
  1012  	}
  1013  	// verify
  1014  	if got := cfg.credentialsStore; got != "" {
  1015  		t.Errorf("Config.credentialsStore = %v, want empty", got)
  1016  	}
  1017  	// verify config file
  1018  	configFile, err = os.Open(configPath)
  1019  	if err != nil {
  1020  		t.Fatalf("failed to open config file: %v", err)
  1021  	}
  1022  	var gotTestCfg2 configtest.Config
  1023  	if err := json.NewDecoder(configFile).Decode(&gotTestCfg2); err != nil {
  1024  		t.Fatalf("failed to decode config file: %v", err)
  1025  	}
  1026  	if err := configFile.Close(); err != nil {
  1027  		t.Fatal("failed to close config file:", err)
  1028  	}
  1029  
  1030  	wantTestCfg2 := configtest.Config{
  1031  		AuthConfigs:     make(map[string]configtest.AuthConfig),
  1032  		SomeConfigField: testCfg.SomeConfigField,
  1033  	}
  1034  	if !reflect.DeepEqual(gotTestCfg2, wantTestCfg2) {
  1035  		t.Errorf("Decoded config = %v, want %v", gotTestCfg2, wantTestCfg2)
  1036  	}
  1037  }
  1038  
  1039  func TestConfig_IsAuthConfigured(t *testing.T) {
  1040  	tempDir := t.TempDir()
  1041  
  1042  	tests := []struct {
  1043  		name             string
  1044  		fileName         string
  1045  		shouldCreateFile bool
  1046  		cfg              configtest.Config
  1047  		want             bool
  1048  	}{
  1049  		{
  1050  			name:             "not existing file",
  1051  			fileName:         "config.json",
  1052  			shouldCreateFile: false,
  1053  			cfg:              configtest.Config{},
  1054  			want:             false,
  1055  		},
  1056  		{
  1057  			name:             "no auth",
  1058  			fileName:         "config.json",
  1059  			shouldCreateFile: true,
  1060  			cfg: configtest.Config{
  1061  				SomeConfigField: 123,
  1062  			},
  1063  			want: false,
  1064  		},
  1065  		{
  1066  			name:             "empty auths exist",
  1067  			fileName:         "empty_auths.json",
  1068  			shouldCreateFile: true,
  1069  			cfg: configtest.Config{
  1070  				AuthConfigs: map[string]configtest.AuthConfig{},
  1071  			},
  1072  			want: false,
  1073  		},
  1074  		{
  1075  			name:             "auths exist, but no credential",
  1076  			fileName:         "no_cred_auths.json",
  1077  			shouldCreateFile: true,
  1078  			cfg: configtest.Config{
  1079  				AuthConfigs: map[string]configtest.AuthConfig{
  1080  					"test.example.com": {},
  1081  				},
  1082  			},
  1083  			want: true,
  1084  		},
  1085  		{
  1086  			name:             "auths exist",
  1087  			fileName:         "auths.json",
  1088  			shouldCreateFile: true,
  1089  			cfg: configtest.Config{
  1090  				AuthConfigs: map[string]configtest.AuthConfig{
  1091  					"test.example.com": {
  1092  						Auth: "dXNlcm5hbWU6cGFzc3dvcmQ=",
  1093  					},
  1094  				},
  1095  			},
  1096  			want: true,
  1097  		},
  1098  		{
  1099  			name:             "credsStore exists",
  1100  			fileName:         "credsStore.json",
  1101  			shouldCreateFile: true,
  1102  			cfg: configtest.Config{
  1103  				CredentialsStore: "teststore",
  1104  			},
  1105  			want: true,
  1106  		},
  1107  		{
  1108  			name:             "empty credHelpers exist",
  1109  			fileName:         "empty_credsStore.json",
  1110  			shouldCreateFile: true,
  1111  			cfg: configtest.Config{
  1112  				CredentialHelpers: map[string]string{},
  1113  			},
  1114  			want: false,
  1115  		},
  1116  		{
  1117  			name:             "credHelpers exist",
  1118  			fileName:         "credsStore.json",
  1119  			shouldCreateFile: true,
  1120  			cfg: configtest.Config{
  1121  				CredentialHelpers: map[string]string{
  1122  					"test.example.com": "testhelper",
  1123  				},
  1124  			},
  1125  			want: true,
  1126  		},
  1127  		{
  1128  			name:             "all exist",
  1129  			fileName:         "credsStore.json",
  1130  			shouldCreateFile: true,
  1131  			cfg: configtest.Config{
  1132  				SomeConfigField: 123,
  1133  				AuthConfigs: map[string]configtest.AuthConfig{
  1134  					"test.example.com": {},
  1135  				},
  1136  				CredentialsStore: "teststore",
  1137  				CredentialHelpers: map[string]string{
  1138  					"test.example.com": "testhelper",
  1139  				},
  1140  			},
  1141  			want: true,
  1142  		},
  1143  	}
  1144  	for _, tt := range tests {
  1145  		t.Run(tt.name, func(t *testing.T) {
  1146  			// prepare test content
  1147  			configPath := filepath.Join(tempDir, tt.fileName)
  1148  			if tt.shouldCreateFile {
  1149  				jsonStr, err := json.Marshal(tt.cfg)
  1150  				if err != nil {
  1151  					t.Fatalf("failed to marshal config: %v", err)
  1152  				}
  1153  				if err := os.WriteFile(configPath, jsonStr, 0666); err != nil {
  1154  					t.Fatalf("failed to write config file: %v", err)
  1155  				}
  1156  			}
  1157  
  1158  			cfg, err := Load(configPath)
  1159  			if err != nil {
  1160  				t.Fatal("Load() error =", err)
  1161  			}
  1162  			if got := cfg.IsAuthConfigured(); got != tt.want {
  1163  				t.Errorf("IsAuthConfigured() = %v, want %v", got, tt.want)
  1164  			}
  1165  		})
  1166  	}
  1167  }
  1168  
  1169  func TestConfig_saveFile(t *testing.T) {
  1170  	tempDir := t.TempDir()
  1171  	tests := []struct {
  1172  		name             string
  1173  		fileName         string
  1174  		shouldCreateFile bool
  1175  		oldCfg           configtest.Config
  1176  		newCfg           configtest.Config
  1177  		wantCfg          configtest.Config
  1178  	}{
  1179  		{
  1180  			name:     "set credsStore in a non-existing file",
  1181  			fileName: "config.json",
  1182  			oldCfg:   configtest.Config{},
  1183  			newCfg: configtest.Config{
  1184  				CredentialsStore: "teststore",
  1185  			},
  1186  			wantCfg: configtest.Config{
  1187  				AuthConfigs:      make(map[string]configtest.AuthConfig),
  1188  				CredentialsStore: "teststore",
  1189  			},
  1190  			shouldCreateFile: false,
  1191  		},
  1192  		{
  1193  			name:     "set credsStore in empty file",
  1194  			fileName: "empty.json",
  1195  			oldCfg:   configtest.Config{},
  1196  			newCfg: configtest.Config{
  1197  				CredentialsStore: "teststore",
  1198  			},
  1199  			wantCfg: configtest.Config{
  1200  				AuthConfigs:      make(map[string]configtest.AuthConfig),
  1201  				CredentialsStore: "teststore",
  1202  			},
  1203  			shouldCreateFile: true,
  1204  		},
  1205  		{
  1206  			name:     "set credsStore in a no-auth-configured file",
  1207  			fileName: "empty.json",
  1208  			oldCfg: configtest.Config{
  1209  				SomeConfigField: 123,
  1210  			},
  1211  			newCfg: configtest.Config{
  1212  				CredentialsStore: "teststore",
  1213  			},
  1214  			wantCfg: configtest.Config{
  1215  				SomeConfigField:  123,
  1216  				AuthConfigs:      make(map[string]configtest.AuthConfig),
  1217  				CredentialsStore: "teststore",
  1218  			},
  1219  			shouldCreateFile: true,
  1220  		},
  1221  		{
  1222  			name:     "Set credsStore and credHelpers in an auth-configured file",
  1223  			fileName: "auth_configured.json",
  1224  			oldCfg: configtest.Config{
  1225  				SomeConfigField: 123,
  1226  				AuthConfigs: map[string]configtest.AuthConfig{
  1227  					"registry1.example.com": {
  1228  						SomeAuthField: "something",
  1229  						Auth:          "dXNlcm5hbWU6cGFzc3dvcmQ=",
  1230  					},
  1231  				},
  1232  				CredentialsStore: "oldstore",
  1233  				CredentialHelpers: map[string]string{
  1234  					"registry2.example.com": "testhelper",
  1235  				},
  1236  			},
  1237  			newCfg: configtest.Config{
  1238  				AuthConfigs:      make(map[string]configtest.AuthConfig),
  1239  				SomeConfigField:  123,
  1240  				CredentialsStore: "newstore",
  1241  				CredentialHelpers: map[string]string{
  1242  					"xxx": "yyy",
  1243  				},
  1244  			},
  1245  			wantCfg: configtest.Config{
  1246  				SomeConfigField: 123,
  1247  				AuthConfigs: map[string]configtest.AuthConfig{
  1248  					"registry1.example.com": {
  1249  						SomeAuthField: "something",
  1250  						Auth:          "dXNlcm5hbWU6cGFzc3dvcmQ=",
  1251  					},
  1252  				},
  1253  				CredentialsStore: "newstore",
  1254  				CredentialHelpers: map[string]string{
  1255  					"registry2.example.com": "testhelper", // cred helpers will not be updated
  1256  				},
  1257  			},
  1258  			shouldCreateFile: true,
  1259  		},
  1260  	}
  1261  	for _, tt := range tests {
  1262  		t.Run(tt.name, func(t *testing.T) {
  1263  			// prepare test content
  1264  			configPath := filepath.Join(tempDir, tt.fileName)
  1265  			if tt.shouldCreateFile {
  1266  				jsonStr, err := json.Marshal(tt.oldCfg)
  1267  				if err != nil {
  1268  					t.Fatalf("failed to marshal config: %v", err)
  1269  				}
  1270  				if err := os.WriteFile(configPath, jsonStr, 0666); err != nil {
  1271  					t.Fatalf("failed to write config file: %v", err)
  1272  				}
  1273  			}
  1274  
  1275  			cfg, err := Load(configPath)
  1276  			if err != nil {
  1277  				t.Fatal("Load() error =", err)
  1278  			}
  1279  			cfg.credentialsStore = tt.newCfg.CredentialsStore
  1280  			cfg.credentialHelpers = tt.newCfg.CredentialHelpers
  1281  			if err := cfg.saveFile(); err != nil {
  1282  				t.Fatal("saveFile() error =", err)
  1283  			}
  1284  
  1285  			// verify config file
  1286  			configFile, err := os.Open(configPath)
  1287  			if err != nil {
  1288  				t.Fatalf("failed to open config file: %v", err)
  1289  			}
  1290  			defer configFile.Close()
  1291  			var gotCfg configtest.Config
  1292  			if err := json.NewDecoder(configFile).Decode(&gotCfg); err != nil {
  1293  				t.Fatalf("failed to decode config file: %v", err)
  1294  			}
  1295  			if !reflect.DeepEqual(gotCfg, tt.wantCfg) {
  1296  				t.Errorf("Decoded config = %v, want %v", gotCfg, tt.wantCfg)
  1297  			}
  1298  		})
  1299  	}
  1300  }
  1301  
  1302  func Test_encodeAuth(t *testing.T) {
  1303  	tests := []struct {
  1304  		name     string
  1305  		username string
  1306  		password string
  1307  		want     string
  1308  	}{
  1309  		{
  1310  			name:     "Username and password",
  1311  			username: "username",
  1312  			password: "password",
  1313  			want:     "dXNlcm5hbWU6cGFzc3dvcmQ=",
  1314  		},
  1315  		{
  1316  			name:     "Username only",
  1317  			username: "username",
  1318  			password: "",
  1319  			want:     "dXNlcm5hbWU6",
  1320  		},
  1321  		{
  1322  			name:     "Password only",
  1323  			username: "",
  1324  			password: "password",
  1325  			want:     "OnBhc3N3b3Jk",
  1326  		},
  1327  		{
  1328  			name:     "Empty username and empty password",
  1329  			username: "",
  1330  			password: "",
  1331  			want:     "",
  1332  		},
  1333  	}
  1334  	for _, tt := range tests {
  1335  		t.Run(tt.name, func(t *testing.T) {
  1336  			if got := encodeAuth(tt.username, tt.password); got != tt.want {
  1337  				t.Errorf("encodeAuth() = %v, want %v", got, tt.want)
  1338  			}
  1339  		})
  1340  	}
  1341  }
  1342  
  1343  func Test_decodeAuth(t *testing.T) {
  1344  	tests := []struct {
  1345  		name     string
  1346  		authStr  string
  1347  		username string
  1348  		password string
  1349  		wantErr  bool
  1350  	}{
  1351  		{
  1352  			name:     "Valid base64",
  1353  			authStr:  "dXNlcm5hbWU6cGFzc3dvcmQ=", // username:password
  1354  			username: "username",
  1355  			password: "password",
  1356  		},
  1357  		{
  1358  			name:     "Valid base64, username only",
  1359  			authStr:  "dXNlcm5hbWU6", // username:
  1360  			username: "username",
  1361  		},
  1362  		{
  1363  			name:     "Valid base64, password only",
  1364  			authStr:  "OnBhc3N3b3Jk", // :password
  1365  			password: "password",
  1366  		},
  1367  		{
  1368  			name:     "Valid base64, bad format",
  1369  			authStr:  "d2hhdGV2ZXI=", // whatever
  1370  			username: "",
  1371  			password: "",
  1372  			wantErr:  true,
  1373  		},
  1374  		{
  1375  			name:     "Invalid base64",
  1376  			authStr:  "whatever",
  1377  			username: "",
  1378  			password: "",
  1379  			wantErr:  true,
  1380  		},
  1381  		{
  1382  			name:     "Empty string",
  1383  			authStr:  "",
  1384  			username: "",
  1385  			password: "",
  1386  			wantErr:  false,
  1387  		},
  1388  	}
  1389  	for _, tt := range tests {
  1390  		t.Run(tt.name, func(t *testing.T) {
  1391  			gotUsername, gotPassword, err := decodeAuth(tt.authStr)
  1392  			if (err != nil) != tt.wantErr {
  1393  				t.Errorf("decodeAuth() error = %v, wantErr %v", err, tt.wantErr)
  1394  				return
  1395  			}
  1396  			if gotUsername != tt.username {
  1397  				t.Errorf("decodeAuth() got = %v, want %v", gotUsername, tt.username)
  1398  			}
  1399  			if gotPassword != tt.password {
  1400  				t.Errorf("decodeAuth() got1 = %v, want %v", gotPassword, tt.password)
  1401  			}
  1402  		})
  1403  	}
  1404  }
  1405  
  1406  func Test_toHostname(t *testing.T) {
  1407  	tests := []struct {
  1408  		name string
  1409  		addr string
  1410  		want string
  1411  	}{
  1412  		{
  1413  			addr: "http://test.example.com",
  1414  			want: "test.example.com",
  1415  		},
  1416  		{
  1417  			addr: "http://test.example.com/",
  1418  			want: "test.example.com",
  1419  		},
  1420  		{
  1421  			addr: "http://test.example.com/foo/bar",
  1422  			want: "test.example.com",
  1423  		},
  1424  		{
  1425  			addr: "https://test.example.com",
  1426  			want: "test.example.com",
  1427  		},
  1428  		{
  1429  			addr: "https://test.example.com/",
  1430  			want: "test.example.com",
  1431  		},
  1432  		{
  1433  			addr: "http://test.example.com/foo/bar",
  1434  			want: "test.example.com",
  1435  		},
  1436  		{
  1437  			addr: "test.example.com",
  1438  			want: "test.example.com",
  1439  		},
  1440  		{
  1441  			addr: "test.example.com/foo/bar/",
  1442  			want: "test.example.com",
  1443  		},
  1444  	}
  1445  	for _, tt := range tests {
  1446  		t.Run(tt.name, func(t *testing.T) {
  1447  			if got := toHostname(tt.addr); got != tt.want {
  1448  				t.Errorf("toHostname() = %v, want %v", got, tt.want)
  1449  			}
  1450  		})
  1451  	}
  1452  }
  1453  
  1454  func TestConfig_Path(t *testing.T) {
  1455  	mockedPath := "/path/to/config.json"
  1456  	config := Config{
  1457  		path: mockedPath,
  1458  	}
  1459  	if got := config.Path(); got != mockedPath {
  1460  		t.Errorf("Config.Path() = %v, want %v", got, mockedPath)
  1461  	}
  1462  }