package password import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestHash(t *testing.T) { t.Parallel() tests := []struct { name string password string wantErr bool }{ { name: "valid password", password: "testpassword123", wantErr: false, }, { name: "empty password", password: "", wantErr: false, // Empty password is valid, just hashed }, { name: "long password", password: "this is a very long password with many characters and symbols !@#$%^&*()", wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hash, err := Hash(tt.password) if tt.wantErr { require.Error(t, err) assert.Empty(t, hash) } else { require.NoError(t, err) assert.NotEmpty(t, hash) // Verify hash format: $argon2id$v=19$m=65536,t=3,p=4$salt$hash assert.Contains(t, hash, "$argon2id$") assert.Contains(t, hash, "v=19") assert.Contains(t, hash, "m=65536") assert.Contains(t, hash, "t=3") assert.Contains(t, hash, "p=4") } }) } } func TestVerify(t *testing.T) { t.Parallel() tests := []struct { name string password string hash string want bool wantErr bool skip bool }{ { name: "correct password", password: "testpassword123", hash: "", // Will be generated want: true, wantErr: false, }, { name: "incorrect password", password: "wrongpassword", hash: "", // Will be generated from different password want: false, wantErr: false, }, { name: "invalid hash format - too few parts", password: "testpassword123", hash: "$argon2id$v=19$m=65536", want: false, wantErr: true, }, { name: "invalid hash format - wrong algorithm", password: "testpassword123", hash: "$bcrypt$v=19$m=65536,t=3,p=4$salt$hash", want: false, wantErr: true, }, { name: "invalid hash format - malformed version", password: "testpassword123", hash: "$argon2id$v=invalid$m=65536,t=3,p=4$salt$hash", want: false, wantErr: true, }, { name: "invalid hash format - malformed parameters", password: "testpassword123", hash: "$argon2id$v=19$m=invalid,t=3,p=4$salt$hash", want: false, wantErr: true, }, { name: "invalid hash format - invalid base64 salt", password: "testpassword123", hash: "$argon2id$v=19$m=65536,t=3,p=4$invalid-base64$hash", want: false, wantErr: true, }, { name: "invalid hash format - invalid base64 hash", password: "testpassword123", hash: "", // Will be generated and then corrupted want: false, wantErr: true, skip: true, // Skip this test - corrupting base64 is complex }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Skip test if marked if tt.skip { t.Skip("Skipping test") } var hash string var err error // Generate hash if needed if tt.hash == "" { if tt.name == "incorrect password" { // Generate hash for a different password hash, err = Hash("differentpassword") require.NoError(t, err) } else { hash, err = Hash(tt.password) require.NoError(t, err) } } else { hash = tt.hash } // Verify got, err := Verify(tt.password, hash) if tt.wantErr { require.Error(t, err) } else { require.NoError(t, err) } assert.Equal(t, tt.want, got) }) } } func TestHash_Verify_RoundTrip(t *testing.T) { t.Parallel() passwords := []string{ "testpassword123", "", "!@#$%^&*()", "very long password with spaces and special characters !@#$%^&*()_+-=[]{}|;:,.<>?", "unicode-测试-пароль", } for _, password := range passwords { t.Run(password, func(t *testing.T) { hash, err := Hash(password) require.NoError(t, err) assert.NotEmpty(t, hash) // Verify the same password valid, err := Verify(password, hash) require.NoError(t, err) assert.True(t, valid, "Password should verify correctly") // Verify different password invalid, err := Verify("wrongpassword", hash) require.NoError(t, err) assert.False(t, invalid, "Wrong password should not verify") }) } } func TestHash_Uniqueness(t *testing.T) { t.Parallel() password := "testpassword123" hashes := make(map[string]bool) // Generate multiple hashes for the same password // They should be different due to random salt for i := 0; i < 10; i++ { hash, err := Hash(password) require.NoError(t, err) assert.NotContains(t, hashes, hash, "Each hash should be unique due to random salt") hashes[hash] = true // But all should verify correctly valid, err := Verify(password, hash) require.NoError(t, err) assert.True(t, valid, "All hashes should verify correctly") } } // Helper functions for test func splitHash(hash string) []string { parts := make([]string, 0, 6) current := "" for _, char := range hash { if char == '$' { if current != "" { parts = append(parts, current) current = "" } } else { current += string(char) } } if current != "" { parts = append(parts, current) } return parts } func joinHash(parts []string) string { result := "" for i, part := range parts { if i > 0 { result += "$" } result += part } return result }