@@ -295,8 +295,9 @@ func TestGetPasswordStorage(t *testing.T) {
295295 {"keyring" , "keyring" , "*common.KeyringStorage" },
296296 {"pgpass" , "pgpass" , "*common.PgpassStorage" },
297297 {"none" , "none" , "*common.NoStorage" },
298- {"default" , "" , "*common.KeyringStorage" }, // Default case
299- {"invalid" , "invalid" , "*common.KeyringStorage" }, // Falls back to default
298+ {"auto" , "auto" , "*common.AutoFallbackStorage" },
299+ {"default" , "" , "*common.AutoFallbackStorage" }, // Default case now uses auto
300+ {"invalid" , "invalid" , "*common.AutoFallbackStorage" }, // Falls back to auto
300301 }
301302
302303 for _ , tt := range tests {
@@ -937,3 +938,186 @@ func TestSanitizeErrorMessage(t *testing.T) {
937938 })
938939 }
939940}
941+
942+ // Test AutoFallbackStorage using pgpass as fallback (since keyring may not be available in CI)
943+ func TestAutoFallbackStorage_Integration (t * testing.T ) {
944+ // Set up test service name for keyring
945+ config .SetTestServiceName (t )
946+
947+ // Create a temporary directory for pgpass
948+ tempDir := t .TempDir ()
949+ originalHome := os .Getenv ("HOME" )
950+ os .Setenv ("HOME" , tempDir )
951+ defer os .Setenv ("HOME" , originalHome )
952+
953+ storage := & AutoFallbackStorage {
954+ keyring : & KeyringStorage {},
955+ pgpass : & PgpassStorage {},
956+ }
957+ service := createTestService ("auto-fallback-test-service" )
958+ password := "test-password-auto"
959+ role := "tsdbadmin"
960+
961+ // Test Save - should succeed using either keyring or pgpass
962+ err := storage .Save (service , password , role )
963+ if err != nil {
964+ t .Fatalf ("AutoFallbackStorage.Save() failed: %v" , err )
965+ }
966+
967+ // Verify lastMethod is set to either keyring or pgpass
968+ if storage .lastMethod != "keyring" && storage .lastMethod != "pgpass" {
969+ t .Errorf ("AutoFallbackStorage.lastMethod = %q, want 'keyring' or 'pgpass'" , storage .lastMethod )
970+ }
971+
972+ // Test Get - should retrieve the password
973+ retrieved , err := storage .Get (service , role )
974+ if err != nil {
975+ t .Fatalf ("AutoFallbackStorage.Get() failed: %v" , err )
976+ }
977+ if retrieved != password {
978+ t .Errorf ("AutoFallbackStorage.Get() = %q, want %q" , retrieved , password )
979+ }
980+
981+ // Test Remove - should succeed
982+ err = storage .Remove (service , role )
983+ if err != nil {
984+ t .Fatalf ("AutoFallbackStorage.Remove() failed: %v" , err )
985+ }
986+
987+ // Verify password is gone
988+ _ , err = storage .Get (service , role )
989+ if err == nil {
990+ t .Error ("AutoFallbackStorage.Get() should fail after Remove()" )
991+ }
992+ }
993+
994+ // Test AutoFallbackStorage GetStorageResult with keyring success
995+ func TestAutoFallbackStorage_GetStorageResult_KeyringSuccess (t * testing.T ) {
996+ storage := & AutoFallbackStorage {
997+ keyring : & KeyringStorage {},
998+ pgpass : & PgpassStorage {},
999+ lastMethod : "keyring" ,
1000+ }
1001+ testPassword := "test-password"
1002+
1003+ result := storage .GetStorageResult (nil , testPassword )
1004+ if ! result .Success {
1005+ t .Errorf ("GetStorageResult() success should be true, got: %t" , result .Success )
1006+ }
1007+ if result .Method != "keyring" {
1008+ t .Errorf ("GetStorageResult() method should be 'keyring', got: %s" , result .Method )
1009+ }
1010+ if ! strings .Contains (result .Message , "Password saved to system keyring" ) {
1011+ t .Errorf ("GetStorageResult() should mention keyring, got: %s" , result .Message )
1012+ }
1013+ if strings .Contains (result .Message , testPassword ) {
1014+ t .Error ("GetStorageResult() should never include actual passwords" )
1015+ }
1016+ }
1017+
1018+ // Test AutoFallbackStorage GetStorageResult with pgpass fallback success
1019+ func TestAutoFallbackStorage_GetStorageResult_PgpassSuccess (t * testing.T ) {
1020+ storage := & AutoFallbackStorage {
1021+ keyring : & KeyringStorage {},
1022+ pgpass : & PgpassStorage {},
1023+ lastMethod : "pgpass" ,
1024+ }
1025+ testPassword := "test-password"
1026+
1027+ result := storage .GetStorageResult (nil , testPassword )
1028+ if ! result .Success {
1029+ t .Errorf ("GetStorageResult() success should be true, got: %t" , result .Success )
1030+ }
1031+ if result .Method != "pgpass" {
1032+ t .Errorf ("GetStorageResult() method should be 'pgpass', got: %s" , result .Method )
1033+ }
1034+ if ! strings .Contains (result .Message , "Password saved to ~/.pgpass" ) {
1035+ t .Errorf ("GetStorageResult() should mention pgpass, got: %s" , result .Message )
1036+ }
1037+ if strings .Contains (result .Message , testPassword ) {
1038+ t .Error ("GetStorageResult() should never include actual passwords" )
1039+ }
1040+ }
1041+
1042+ // Test AutoFallbackStorage GetStorageResult with failure
1043+ func TestAutoFallbackStorage_GetStorageResult_Failure (t * testing.T ) {
1044+ storage := & AutoFallbackStorage {
1045+ keyring : & KeyringStorage {},
1046+ pgpass : & PgpassStorage {},
1047+ lastMethod : "auto" ,
1048+ }
1049+ testPassword := "test-password"
1050+ testError := fmt .Errorf ("both storage methods failed" )
1051+
1052+ result := storage .GetStorageResult (testError , testPassword )
1053+ if result .Success {
1054+ t .Errorf ("GetStorageResult() success should be false, got: %t" , result .Success )
1055+ }
1056+ if result .Method != "auto" {
1057+ t .Errorf ("GetStorageResult() method should be 'auto', got: %s" , result .Method )
1058+ }
1059+ if ! strings .Contains (result .Message , "Failed to save password" ) {
1060+ t .Errorf ("GetStorageResult() should mention failure, got: %s" , result .Message )
1061+ }
1062+ if strings .Contains (result .Message , testPassword ) {
1063+ t .Error ("GetStorageResult() should never include actual passwords" )
1064+ }
1065+ }
1066+
1067+ // Test AutoFallbackStorage Remove succeeds if at least one location succeeds
1068+ func TestAutoFallbackStorage_Remove_PartialSuccess (t * testing.T ) {
1069+ // Set up test service name for keyring
1070+ config .SetTestServiceName (t )
1071+
1072+ // Create a temporary directory for pgpass
1073+ tempDir := t .TempDir ()
1074+ originalHome := os .Getenv ("HOME" )
1075+ os .Setenv ("HOME" , tempDir )
1076+ defer os .Setenv ("HOME" , originalHome )
1077+
1078+ // Save password only to pgpass (not keyring)
1079+ pgpassStorage := & PgpassStorage {}
1080+ service := createTestService ("partial-remove-test" )
1081+ password := "test-password"
1082+ role := "tsdbadmin"
1083+
1084+ err := pgpassStorage .Save (service , password , role )
1085+ if err != nil {
1086+ t .Fatalf ("PgpassStorage.Save() failed: %v" , err )
1087+ }
1088+
1089+ // Now remove using AutoFallbackStorage - should succeed even if keyring fails
1090+ autoStorage := & AutoFallbackStorage {
1091+ keyring : & KeyringStorage {},
1092+ pgpass : & PgpassStorage {},
1093+ }
1094+
1095+ err = autoStorage .Remove (service , role )
1096+ if err != nil {
1097+ t .Errorf ("AutoFallbackStorage.Remove() should succeed when at least one location succeeds, got: %v" , err )
1098+ }
1099+ }
1100+
1101+ // Security test: Ensure AutoFallbackStorage never includes passwords in messages
1102+ func TestAutoFallbackStorage_SecurityTest_NoPasswordInResults (t * testing.T ) {
1103+ testPassword := "super-secret-password-123"
1104+ testError := fmt .Errorf ("failed to save %s" , testPassword )
1105+
1106+ storage := & AutoFallbackStorage {
1107+ keyring : & KeyringStorage {},
1108+ pgpass : & PgpassStorage {},
1109+ lastMethod : "auto" ,
1110+ }
1111+
1112+ // Test success case
1113+ successResult := storage .GetStorageResult (nil , testPassword )
1114+ if strings .Contains (successResult .Message , testPassword ) {
1115+ t .Errorf ("GetStorageResult() success should never include password, got: %s" , successResult .Message )
1116+ }
1117+
1118+ // Test error case
1119+ errorResult := storage .GetStorageResult (testError , testPassword )
1120+ if strings .Contains (errorResult .Message , testPassword ) {
1121+ t .Errorf ("GetStorageResult() error should never include password, got: %s" , errorResult .Message )
1122+ }
1123+ }
0 commit comments