github.com/dtroyer-salad/og2/v2@v2.0.0-20240412154159-c47231610877/registry/remote/credentials/file_store_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 credentials 17 18 import ( 19 "context" 20 "encoding/json" 21 "errors" 22 "os" 23 "path/filepath" 24 "reflect" 25 "testing" 26 27 "oras.land/oras-go/v2/registry/remote/auth" 28 "oras.land/oras-go/v2/registry/remote/credentials/internal/config/configtest" 29 ) 30 31 func TestNewFileStore_badPath(t *testing.T) { 32 tempDir := t.TempDir() 33 34 tests := []struct { 35 name string 36 configPath string 37 wantErr bool 38 }{ 39 { 40 name: "Path is a directory", 41 configPath: tempDir, 42 wantErr: true, 43 }, 44 { 45 name: "Empty file name", 46 configPath: filepath.Join(tempDir, ""), 47 wantErr: true, 48 }, 49 } 50 for _, tt := range tests { 51 t.Run(tt.name, func(t *testing.T) { 52 _, err := NewFileStore(tt.configPath) 53 if (err != nil) != tt.wantErr { 54 t.Errorf("NewFileStore() error = %v, wantErr %v", err, tt.wantErr) 55 return 56 } 57 }) 58 } 59 } 60 61 func TestNewFileStore_badFormat(t *testing.T) { 62 tests := []struct { 63 name string 64 configPath string 65 wantErr bool 66 }{ 67 { 68 name: "Bad JSON format", 69 configPath: "testdata/bad_config", 70 wantErr: true, 71 }, 72 { 73 name: "Invalid auths format", 74 configPath: "testdata/invalid_auths_config.json", 75 wantErr: true, 76 }, 77 { 78 name: "No auths field", 79 configPath: "testdata/no_auths_config.json", 80 wantErr: false, 81 }, 82 } 83 for _, tt := range tests { 84 t.Run(tt.name, func(t *testing.T) { 85 _, err := NewFileStore(tt.configPath) 86 if (err != nil) != tt.wantErr { 87 t.Errorf("NewFileStore() error = %v, wantErr %v", err, tt.wantErr) 88 return 89 } 90 }) 91 } 92 } 93 94 func TestFileStore_Get_validConfig(t *testing.T) { 95 ctx := context.Background() 96 fs, err := NewFileStore("testdata/valid_auths_config.json") 97 if err != nil { 98 t.Fatal("NewFileStore() error =", err) 99 } 100 101 tests := []struct { 102 name string 103 serverAddress string 104 want auth.Credential 105 wantErr bool 106 }{ 107 { 108 name: "Username and password", 109 serverAddress: "registry1.example.com", 110 want: auth.Credential{ 111 Username: "username", 112 Password: "password", 113 }, 114 }, 115 { 116 name: "Identity token", 117 serverAddress: "registry2.example.com", 118 want: auth.Credential{ 119 RefreshToken: "identity_token", 120 }, 121 }, 122 { 123 name: "Registry token", 124 serverAddress: "registry3.example.com", 125 want: auth.Credential{ 126 AccessToken: "registry_token", 127 }, 128 }, 129 { 130 name: "Username and password, identity token and registry token", 131 serverAddress: "registry4.example.com", 132 want: auth.Credential{ 133 Username: "username", 134 Password: "password", 135 RefreshToken: "identity_token", 136 AccessToken: "registry_token", 137 }, 138 }, 139 { 140 name: "Empty credential", 141 serverAddress: "registry5.example.com", 142 want: auth.EmptyCredential, 143 }, 144 { 145 name: "Username and password, no auth", 146 serverAddress: "registry6.example.com", 147 want: auth.Credential{ 148 Username: "username", 149 Password: "password", 150 }, 151 }, 152 { 153 name: "Auth overriding Username and password", 154 serverAddress: "registry7.example.com", 155 want: auth.Credential{ 156 Username: "username", 157 Password: "password", 158 }, 159 }, 160 { 161 name: "Not in auths", 162 serverAddress: "foo.example.com", 163 want: auth.EmptyCredential, 164 }, 165 { 166 name: "No record", 167 serverAddress: "registry999.example.com", 168 want: auth.EmptyCredential, 169 }, 170 } 171 for _, tt := range tests { 172 t.Run(tt.name, func(t *testing.T) { 173 got, err := fs.Get(ctx, tt.serverAddress) 174 if (err != nil) != tt.wantErr { 175 t.Errorf("FileStore.Get() error = %v, wantErr %v", err, tt.wantErr) 176 return 177 } 178 if !reflect.DeepEqual(got, tt.want) { 179 t.Errorf("FileStore.Get() = %v, want %v", got, tt.want) 180 } 181 }) 182 } 183 } 184 185 func TestFileStore_Get_invalidConfig(t *testing.T) { 186 ctx := context.Background() 187 fs, err := NewFileStore("testdata/invalid_auths_entry_config.json") 188 if err != nil { 189 t.Fatal("NewFileStore() error =", err) 190 } 191 192 tests := []struct { 193 name string 194 serverAddress string 195 want auth.Credential 196 wantErr bool 197 }{ 198 { 199 name: "Invalid auth encode", 200 serverAddress: "registry1.example.com", 201 want: auth.EmptyCredential, 202 wantErr: true, 203 }, 204 { 205 name: "Invalid auths format", 206 serverAddress: "registry2.example.com", 207 want: auth.EmptyCredential, 208 wantErr: true, 209 }, 210 { 211 name: "Invalid type", 212 serverAddress: "registry3.example.com", 213 want: auth.EmptyCredential, 214 wantErr: true, 215 }, 216 } 217 for _, tt := range tests { 218 t.Run(tt.name, func(t *testing.T) { 219 got, err := fs.Get(ctx, tt.serverAddress) 220 if (err != nil) != tt.wantErr { 221 t.Errorf("FileStore.Get() error = %v, wantErr %v", err, tt.wantErr) 222 return 223 } 224 if !reflect.DeepEqual(got, tt.want) { 225 t.Errorf("FileStore.Get() = %v, want %v", got, tt.want) 226 } 227 }) 228 } 229 } 230 231 func TestFileStore_Get_emptyConfig(t *testing.T) { 232 ctx := context.Background() 233 fs, err := NewFileStore("testdata/empty_config.json") 234 if err != nil { 235 t.Fatal("NewFileStore() error =", err) 236 } 237 238 tests := []struct { 239 name string 240 serverAddress string 241 want auth.Credential 242 wantErr error 243 }{ 244 { 245 name: "Not found", 246 serverAddress: "registry.example.com", 247 want: auth.EmptyCredential, 248 wantErr: nil, 249 }, 250 } 251 for _, tt := range tests { 252 t.Run(tt.name, func(t *testing.T) { 253 got, err := fs.Get(ctx, tt.serverAddress) 254 if !errors.Is(err, tt.wantErr) { 255 t.Errorf("FileStore.Get() error = %v, wantErr %v", err, tt.wantErr) 256 return 257 } 258 if !reflect.DeepEqual(got, tt.want) { 259 t.Errorf("FileStore.Get() = %v, want %v", got, tt.want) 260 } 261 }) 262 } 263 } 264 265 func TestFileStore_Get_notExistConfig(t *testing.T) { 266 ctx := context.Background() 267 fs, err := NewFileStore("whatever") 268 if err != nil { 269 t.Fatal("NewFileStore() error =", err) 270 } 271 272 tests := []struct { 273 name string 274 serverAddress string 275 want auth.Credential 276 wantErr error 277 }{ 278 { 279 name: "Not found", 280 serverAddress: "registry.example.com", 281 want: auth.EmptyCredential, 282 wantErr: nil, 283 }, 284 } 285 for _, tt := range tests { 286 t.Run(tt.name, func(t *testing.T) { 287 got, err := fs.Get(ctx, tt.serverAddress) 288 if !errors.Is(err, tt.wantErr) { 289 t.Errorf("FileStore.Get() error = %v, wantErr %v", err, tt.wantErr) 290 return 291 } 292 if !reflect.DeepEqual(got, tt.want) { 293 t.Errorf("FileStore.Get() = %v, want %v", got, tt.want) 294 } 295 }) 296 } 297 } 298 299 func TestFileStore_Put_notExistConfig(t *testing.T) { 300 tempDir := t.TempDir() 301 configPath := filepath.Join(tempDir, "config.json") 302 ctx := context.Background() 303 304 fs, err := NewFileStore(configPath) 305 if err != nil { 306 t.Fatal("NewFileStore() error =", err) 307 } 308 309 server := "test.example.com" 310 cred := auth.Credential{ 311 Username: "username", 312 Password: "password", 313 RefreshToken: "refresh_token", 314 AccessToken: "access_token", 315 } 316 317 // test put 318 if err := fs.Put(ctx, server, cred); err != nil { 319 t.Fatalf("FileStore.Put() error = %v", err) 320 } 321 322 // verify config file 323 configFile, err := os.Open(configPath) 324 if err != nil { 325 t.Fatalf("failed to open config file: %v", err) 326 } 327 defer configFile.Close() 328 329 var cfg configtest.Config 330 if err := json.NewDecoder(configFile).Decode(&cfg); err != nil { 331 t.Fatalf("failed to decode config file: %v", err) 332 } 333 want := configtest.Config{ 334 AuthConfigs: map[string]configtest.AuthConfig{ 335 server: { 336 Auth: "dXNlcm5hbWU6cGFzc3dvcmQ=", 337 IdentityToken: "refresh_token", 338 RegistryToken: "access_token", 339 }, 340 }, 341 } 342 if !reflect.DeepEqual(cfg, want) { 343 t.Errorf("Decoded config = %v, want %v", cfg, want) 344 } 345 346 // verify get 347 got, err := fs.Get(ctx, server) 348 if err != nil { 349 t.Fatalf("FileStore.Get() error = %v", err) 350 } 351 if want := cred; !reflect.DeepEqual(got, want) { 352 t.Errorf("FileStore.Get() = %v, want %v", got, want) 353 } 354 } 355 356 func TestFileStore_Put_addNew(t *testing.T) { 357 tempDir := t.TempDir() 358 configPath := filepath.Join(tempDir, "config.json") 359 ctx := context.Background() 360 361 // prepare test content 362 server1 := "registry1.example.com" 363 cred1 := auth.Credential{ 364 Username: "username", 365 Password: "password", 366 RefreshToken: "refresh_token", 367 AccessToken: "access_token", 368 } 369 370 cfg := configtest.Config{ 371 AuthConfigs: map[string]configtest.AuthConfig{ 372 server1: { 373 SomeAuthField: "whatever", 374 Auth: "dXNlcm5hbWU6cGFzc3dvcmQ=", 375 IdentityToken: cred1.RefreshToken, 376 RegistryToken: cred1.AccessToken, 377 }, 378 }, 379 SomeConfigField: 123, 380 } 381 jsonStr, err := json.Marshal(cfg) 382 if err != nil { 383 t.Fatalf("failed to marshal config: %v", err) 384 } 385 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 386 t.Fatalf("failed to write config file: %v", err) 387 } 388 389 // test put 390 fs, err := NewFileStore(configPath) 391 if err != nil { 392 t.Fatal("NewFileStore() error =", err) 393 } 394 server2 := "registry2.example.com" 395 cred2 := auth.Credential{ 396 Username: "username_2", 397 Password: "password_2", 398 RefreshToken: "refresh_token_2", 399 AccessToken: "access_token_2", 400 } 401 if err := fs.Put(ctx, server2, cred2); err != nil { 402 t.Fatalf("FileStore.Put() error = %v", err) 403 } 404 405 // verify config file 406 configFile, err := os.Open(configPath) 407 if err != nil { 408 t.Fatalf("failed to open config file: %v", err) 409 } 410 defer configFile.Close() 411 var gotCfg configtest.Config 412 if err := json.NewDecoder(configFile).Decode(&gotCfg); err != nil { 413 t.Fatalf("failed to decode config file: %v", err) 414 } 415 wantCfg := configtest.Config{ 416 AuthConfigs: map[string]configtest.AuthConfig{ 417 server1: { 418 SomeAuthField: "whatever", 419 Auth: "dXNlcm5hbWU6cGFzc3dvcmQ=", 420 IdentityToken: cred1.RefreshToken, 421 RegistryToken: cred1.AccessToken, 422 }, 423 server2: { 424 Auth: "dXNlcm5hbWVfMjpwYXNzd29yZF8y", 425 IdentityToken: "refresh_token_2", 426 RegistryToken: "access_token_2", 427 }, 428 }, 429 SomeConfigField: cfg.SomeConfigField, 430 } 431 if !reflect.DeepEqual(gotCfg, wantCfg) { 432 t.Errorf("Decoded config = %v, want %v", gotCfg, wantCfg) 433 } 434 435 // verify get 436 got, err := fs.Get(ctx, server1) 437 if err != nil { 438 t.Fatalf("FileStore.Get() error = %v", err) 439 } 440 if want := cred1; !reflect.DeepEqual(got, want) { 441 t.Errorf("FileStore.Get(%s) = %v, want %v", server1, got, want) 442 } 443 444 got, err = fs.Get(ctx, server2) 445 if err != nil { 446 t.Fatalf("FileStore.Get() error = %v", err) 447 } 448 if want := cred2; !reflect.DeepEqual(got, want) { 449 t.Errorf("FileStore.Get(%s) = %v, want %v", server2, got, want) 450 } 451 } 452 453 func TestFileStore_Put_updateOld(t *testing.T) { 454 tempDir := t.TempDir() 455 configPath := filepath.Join(tempDir, "config.json") 456 ctx := context.Background() 457 458 // prepare test content 459 server := "registry.example.com" 460 cfg := configtest.Config{ 461 AuthConfigs: map[string]configtest.AuthConfig{ 462 server: { 463 SomeAuthField: "whatever", 464 Username: "foo", 465 Password: "bar", 466 IdentityToken: "refresh_token", 467 }, 468 }, 469 SomeConfigField: 123, 470 } 471 jsonStr, err := json.Marshal(cfg) 472 if err != nil { 473 t.Fatalf("failed to marshal config: %v", err) 474 } 475 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 476 t.Fatalf("failed to write config file: %v", err) 477 } 478 479 // test put 480 fs, err := NewFileStore(configPath) 481 if err != nil { 482 t.Fatal("NewFileStore() error =", err) 483 } 484 cred := auth.Credential{ 485 Username: "username", 486 Password: "password", 487 AccessToken: "access_token", 488 } 489 if err := fs.Put(ctx, server, cred); err != nil { 490 t.Fatalf("FileStore.Put() error = %v", err) 491 } 492 493 // verify config file 494 configFile, err := os.Open(configPath) 495 if err != nil { 496 t.Fatalf("failed to open config file: %v", err) 497 } 498 defer configFile.Close() 499 var gotCfg configtest.Config 500 if err := json.NewDecoder(configFile).Decode(&gotCfg); err != nil { 501 t.Fatalf("failed to decode config file: %v", err) 502 } 503 wantCfg := configtest.Config{ 504 AuthConfigs: map[string]configtest.AuthConfig{ 505 server: { 506 Auth: "dXNlcm5hbWU6cGFzc3dvcmQ=", 507 RegistryToken: "access_token", 508 }, 509 }, 510 SomeConfigField: cfg.SomeConfigField, 511 } 512 if !reflect.DeepEqual(gotCfg, wantCfg) { 513 t.Errorf("Decoded config = %v, want %v", gotCfg, wantCfg) 514 } 515 516 // verify get 517 got, err := fs.Get(ctx, server) 518 if err != nil { 519 t.Fatalf("FileStore.Get() error = %v", err) 520 } 521 if want := cred; !reflect.DeepEqual(got, want) { 522 t.Errorf("FileStore.Get(%s) = %v, want %v", server, got, want) 523 } 524 } 525 526 func TestFileStore_Put_disablePut(t *testing.T) { 527 tempDir := t.TempDir() 528 configPath := filepath.Join(tempDir, "config.json") 529 ctx := context.Background() 530 531 fs, err := NewFileStore(configPath) 532 if err != nil { 533 t.Fatal("NewFileStore() error =", err) 534 } 535 fs.DisablePut = true 536 537 server := "test.example.com" 538 cred := auth.Credential{ 539 Username: "username", 540 Password: "password", 541 RefreshToken: "refresh_token", 542 AccessToken: "access_token", 543 } 544 err = fs.Put(ctx, server, cred) 545 if wantErr := ErrPlaintextPutDisabled; !errors.Is(err, wantErr) { 546 t.Errorf("FileStore.Put() error = %v, wantErr %v", err, wantErr) 547 } 548 } 549 550 func TestFileStore_Put_usernameContainsColon(t *testing.T) { 551 tempDir := t.TempDir() 552 configPath := filepath.Join(tempDir, "config.json") 553 ctx := context.Background() 554 555 fs, err := NewFileStore(configPath) 556 if err != nil { 557 t.Fatal("NewFileStore() error =", err) 558 } 559 serverAddr := "test.example.com" 560 cred := auth.Credential{ 561 Username: "x:y", 562 Password: "z", 563 } 564 if err := fs.Put(ctx, serverAddr, cred); err == nil { 565 t.Fatal("FileStore.Put() error is nil, want", ErrBadCredentialFormat) 566 } 567 } 568 569 func TestFileStore_Put_passwordContainsColon(t *testing.T) { 570 tempDir := t.TempDir() 571 configPath := filepath.Join(tempDir, "config.json") 572 ctx := context.Background() 573 574 fs, err := NewFileStore(configPath) 575 if err != nil { 576 t.Fatal("NewFileStore() error =", err) 577 } 578 serverAddr := "test.example.com" 579 cred := auth.Credential{ 580 Username: "y", 581 Password: "y:z", 582 } 583 if err := fs.Put(ctx, serverAddr, cred); err != nil { 584 t.Fatal("FileStore.Put() error =", err) 585 } 586 got, err := fs.Get(ctx, serverAddr) 587 if err != nil { 588 t.Fatal("FileStore.Get() error =", err) 589 } 590 if !reflect.DeepEqual(got, cred) { 591 t.Errorf("FileStore.Get() = %v, want %v", got, cred) 592 } 593 } 594 595 func TestFileStore_Delete(t *testing.T) { 596 tempDir := t.TempDir() 597 configPath := filepath.Join(tempDir, "config.json") 598 ctx := context.Background() 599 600 // prepare test content 601 server1 := "registry1.example.com" 602 cred1 := auth.Credential{ 603 Username: "username", 604 Password: "password", 605 RefreshToken: "refresh_token", 606 AccessToken: "access_token", 607 } 608 server2 := "registry2.example.com" 609 cred2 := auth.Credential{ 610 Username: "username_2", 611 Password: "password_2", 612 RefreshToken: "refresh_token_2", 613 AccessToken: "access_token_2", 614 } 615 616 cfg := configtest.Config{ 617 AuthConfigs: map[string]configtest.AuthConfig{ 618 server1: { 619 Auth: "dXNlcm5hbWU6cGFzc3dvcmQ=", 620 IdentityToken: cred1.RefreshToken, 621 RegistryToken: cred1.AccessToken, 622 }, 623 server2: { 624 Auth: "dXNlcm5hbWVfMjpwYXNzd29yZF8y", 625 IdentityToken: "refresh_token_2", 626 RegistryToken: "access_token_2", 627 }, 628 }, 629 SomeConfigField: 123, 630 } 631 jsonStr, err := json.Marshal(cfg) 632 if err != nil { 633 t.Fatalf("failed to marshal config: %v", err) 634 } 635 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 636 t.Fatalf("failed to write config file: %v", err) 637 } 638 639 fs, err := NewFileStore(configPath) 640 if err != nil { 641 t.Fatal("NewFileStore() error =", err) 642 } 643 // test get 644 got, err := fs.Get(ctx, server1) 645 if err != nil { 646 t.Fatalf("FileStore.Get() error = %v", err) 647 } 648 if want := cred1; !reflect.DeepEqual(got, want) { 649 t.Errorf("FileStore.Get(%s) = %v, want %v", server1, got, want) 650 } 651 got, err = fs.Get(ctx, server2) 652 if err != nil { 653 t.Fatalf("FileStore.Get() error = %v", err) 654 } 655 if want := cred2; !reflect.DeepEqual(got, want) { 656 t.Errorf("FileStore.Get(%s) = %v, want %v", server2, got, want) 657 } 658 659 // test delete 660 if err := fs.Delete(ctx, server1); err != nil { 661 t.Fatalf("FileStore.Delete() error = %v", err) 662 } 663 664 // verify config file 665 configFile, err := os.Open(configPath) 666 if err != nil { 667 t.Fatalf("failed to open config file: %v", err) 668 } 669 defer configFile.Close() 670 var gotCfg configtest.Config 671 if err := json.NewDecoder(configFile).Decode(&gotCfg); err != nil { 672 t.Fatalf("failed to decode config file: %v", err) 673 } 674 wantCfg := configtest.Config{ 675 AuthConfigs: map[string]configtest.AuthConfig{ 676 server2: cfg.AuthConfigs[server2], 677 }, 678 SomeConfigField: cfg.SomeConfigField, 679 } 680 if !reflect.DeepEqual(gotCfg, wantCfg) { 681 t.Errorf("Decoded config = %v, want %v", gotCfg, wantCfg) 682 } 683 684 // test get again 685 got, err = fs.Get(ctx, server1) 686 if err != nil { 687 t.Fatalf("FileStore.Get() error = %v", err) 688 } 689 if want := auth.EmptyCredential; !reflect.DeepEqual(got, want) { 690 t.Errorf("FileStore.Get(%s) = %v, want %v", server1, got, want) 691 } 692 got, err = fs.Get(ctx, server2) 693 if err != nil { 694 t.Fatalf("FileStore.Get() error = %v", err) 695 } 696 if want := cred2; !reflect.DeepEqual(got, want) { 697 t.Errorf("FileStore.Get(%s) = %v, want %v", server2, got, want) 698 } 699 } 700 701 func TestFileStore_Delete_lastConfig(t *testing.T) { 702 tempDir := t.TempDir() 703 configPath := filepath.Join(tempDir, "config.json") 704 ctx := context.Background() 705 706 // prepare test content 707 server := "registry1.example.com" 708 cred := auth.Credential{ 709 Username: "username", 710 Password: "password", 711 RefreshToken: "refresh_token", 712 AccessToken: "access_token", 713 } 714 715 cfg := configtest.Config{ 716 AuthConfigs: map[string]configtest.AuthConfig{ 717 server: { 718 Auth: "dXNlcm5hbWU6cGFzc3dvcmQ=", 719 IdentityToken: cred.RefreshToken, 720 RegistryToken: cred.AccessToken, 721 }, 722 }, 723 SomeConfigField: 123, 724 } 725 jsonStr, err := json.Marshal(cfg) 726 if err != nil { 727 t.Fatalf("failed to marshal config: %v", err) 728 } 729 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 730 t.Fatalf("failed to write config file: %v", err) 731 } 732 733 fs, err := NewFileStore(configPath) 734 if err != nil { 735 t.Fatal("NewFileStore() error =", err) 736 } 737 // test get 738 got, err := fs.Get(ctx, server) 739 if err != nil { 740 t.Fatalf("FileStore.Get() error = %v", err) 741 } 742 if want := cred; !reflect.DeepEqual(got, want) { 743 t.Errorf("FileStore.Get(%s) = %v, want %v", server, got, want) 744 } 745 746 // test delete 747 if err := fs.Delete(ctx, server); err != nil { 748 t.Fatalf("FileStore.Delete() error = %v", err) 749 } 750 751 // verify config file 752 configFile, err := os.Open(configPath) 753 if err != nil { 754 t.Fatalf("failed to open config file: %v", err) 755 } 756 defer configFile.Close() 757 var gotCfg configtest.Config 758 if err := json.NewDecoder(configFile).Decode(&gotCfg); err != nil { 759 t.Fatalf("failed to decode config file: %v", err) 760 } 761 wantCfg := configtest.Config{ 762 AuthConfigs: map[string]configtest.AuthConfig{}, 763 SomeConfigField: cfg.SomeConfigField, 764 } 765 if !reflect.DeepEqual(gotCfg, wantCfg) { 766 t.Errorf("Decoded config = %v, want %v", gotCfg, wantCfg) 767 } 768 769 // test get again 770 got, err = fs.Get(ctx, server) 771 if err != nil { 772 t.Fatalf("FileStore.Get() error = %v", err) 773 } 774 if want := auth.EmptyCredential; !reflect.DeepEqual(got, want) { 775 t.Errorf("FileStore.Get(%s) = %v, want %v", server, got, want) 776 } 777 } 778 779 func TestFileStore_Delete_notExistRecord(t *testing.T) { 780 tempDir := t.TempDir() 781 configPath := filepath.Join(tempDir, "config.json") 782 ctx := context.Background() 783 784 // prepare test content 785 server := "registry1.example.com" 786 cred := auth.Credential{ 787 Username: "username", 788 Password: "password", 789 RefreshToken: "refresh_token", 790 AccessToken: "access_token", 791 } 792 cfg := configtest.Config{ 793 AuthConfigs: map[string]configtest.AuthConfig{ 794 server: { 795 Auth: "dXNlcm5hbWU6cGFzc3dvcmQ=", 796 IdentityToken: cred.RefreshToken, 797 RegistryToken: cred.AccessToken, 798 }, 799 }, 800 SomeConfigField: 123, 801 } 802 jsonStr, err := json.Marshal(cfg) 803 if err != nil { 804 t.Fatalf("failed to marshal config: %v", err) 805 } 806 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 807 t.Fatalf("failed to write config file: %v", err) 808 } 809 810 fs, err := NewFileStore(configPath) 811 if err != nil { 812 t.Fatal("NewFileStore() error =", err) 813 } 814 // test get 815 got, err := fs.Get(ctx, server) 816 if err != nil { 817 t.Fatalf("FileStore.Get() error = %v", err) 818 } 819 if want := cred; !reflect.DeepEqual(got, want) { 820 t.Errorf("FileStore.Get(%s) = %v, want %v", server, got, want) 821 } 822 823 // test delete 824 if err := fs.Delete(ctx, "test.example.com"); err != nil { 825 t.Fatalf("FileStore.Delete() error = %v", err) 826 } 827 828 // verify config file 829 configFile, err := os.Open(configPath) 830 if err != nil { 831 t.Fatalf("failed to open config file: %v", err) 832 } 833 defer configFile.Close() 834 var gotCfg configtest.Config 835 if err := json.NewDecoder(configFile).Decode(&gotCfg); err != nil { 836 t.Fatalf("failed to decode config file: %v", err) 837 } 838 wantCfg := configtest.Config{ 839 AuthConfigs: map[string]configtest.AuthConfig{ 840 server: cfg.AuthConfigs[server], 841 }, 842 SomeConfigField: cfg.SomeConfigField, 843 } 844 if !reflect.DeepEqual(gotCfg, wantCfg) { 845 t.Errorf("Decoded config = %v, want %v", gotCfg, wantCfg) 846 } 847 848 // test get again 849 got, err = fs.Get(ctx, server) 850 if err != nil { 851 t.Fatalf("FileStore.Get() error = %v", err) 852 } 853 if want := cred; !reflect.DeepEqual(got, want) { 854 t.Errorf("FileStore.Get(%s) = %v, want %v", server, got, want) 855 } 856 } 857 858 func TestFileStore_Delete_notExistConfig(t *testing.T) { 859 tempDir := t.TempDir() 860 configPath := filepath.Join(tempDir, "config.json") 861 ctx := context.Background() 862 863 fs, err := NewFileStore(configPath) 864 if err != nil { 865 t.Fatal("NewFileStore() error =", err) 866 } 867 868 server := "test.example.com" 869 // test delete 870 if err := fs.Delete(ctx, server); err != nil { 871 t.Fatalf("FileStore.Delete() error = %v", err) 872 } 873 874 // verify config file is not created 875 _, err = os.Stat(configPath) 876 if wantErr := os.ErrNotExist; !errors.Is(err, wantErr) { 877 t.Errorf("Stat(%s) error = %v, wantErr %v", configPath, err, wantErr) 878 } 879 } 880 881 func Test_validateCredentialFormat(t *testing.T) { 882 tests := []struct { 883 name string 884 cred auth.Credential 885 wantErr error 886 }{ 887 { 888 name: "Username contains colon", 889 cred: auth.Credential{ 890 Username: "x:y", 891 Password: "z", 892 }, 893 wantErr: ErrBadCredentialFormat, 894 }, 895 { 896 name: "Password contains colon", 897 cred: auth.Credential{ 898 Username: "x", 899 Password: "y:z", 900 }, 901 }, 902 } 903 for _, tt := range tests { 904 t.Run(tt.name, func(t *testing.T) { 905 if err := validateCredentialFormat(tt.cred); !errors.Is(err, tt.wantErr) { 906 t.Errorf("validateCredentialFormat() error = %v, wantErr %v", err, tt.wantErr) 907 } 908 }) 909 } 910 }