package hash import ( "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestAPIKey(t *testing.T) { key := "test_api_key_12345" // gitleaks:allow hash1, err := APIKey(key) require.NoError(t, err) assert.NotEmpty(t, hash1) assert.NotEqual(t, key, hash1, "Hash should not equal plaintext") // Bcrypt hashes should start with $2 assert.True(t, strings.HasPrefix(hash1, "$2"), "Should be a bcrypt hash") // Same key should produce different hashes (due to salt) hash2, err := APIKey(key) require.NoError(t, err) assert.NotEqual(t, hash1, hash2, "Bcrypt should produce different hashes with different salts") } func TestCompareAPIKey_Bcrypt(t *testing.T) { key := "test_api_key_12345" // gitleaks:allow hash, err := APIKey(key) require.NoError(t, err) // Correct key should match assert.True(t, CompareAPIKey(hash, key)) // Wrong key should not match assert.False(t, CompareAPIKey(hash, "wrong_key")) } func TestCompareAPIKey_Legacy(t *testing.T) { key := "test_api_key_12345" // gitleaks:allow // Create a legacy SHA256 hash legacyHash := String(key) // Should still work with legacy hashes assert.True(t, CompareAPIKey(legacyHash, key)) // Wrong key should not match assert.False(t, CompareAPIKey(legacyHash, "wrong_key")) } func TestCompareAPIKey_BackwardCompatibility(t *testing.T) { tests := []struct { name string hashFunc func(string) string expectOK bool }{ { name: "bcrypt hash", hashFunc: func(k string) string { h, _ := APIKey(k) return h }, expectOK: true, }, { name: "legacy SHA256 hash", hashFunc: func(k string) string { return String(k) }, expectOK: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { key := "test_key_123" hash := tt.hashFunc(key) result := CompareAPIKey(hash, key) assert.Equal(t, tt.expectOK, result) }) } } func TestString(t *testing.T) { // Test that String function still works (for non-sensitive data) input := "test_string" hash1 := String(input) hash2 := String(input) // SHA256 should be deterministic assert.Equal(t, hash1, hash2) assert.NotEmpty(t, hash1) assert.NotEqual(t, input, hash1) } func TestIsLegacyHash(t *testing.T) { tests := []struct { name string hash string isLegacy bool }{ { name: "bcrypt hash", hash: "$2a$12$abcdefghijklmnopqrstuv", isLegacy: false, }, { name: "SHA256 hash", hash: "dXNfYWtfMTIzNDU2Nzg5MDEyMzQ1NuOwxEKY", isLegacy: true, }, { name: "empty string", hash: "", isLegacy: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.isLegacy, IsLegacyHash(tt.hash)) }) } } func TestMigrateAPIKeyHash(t *testing.T) { plainKey := "test_api_key_123" t.Run("migrate legacy hash", func(t *testing.T) { // Create a legacy SHA256 hash legacyHash := String(plainKey) // Migrate it newHash, migrated, err := MigrateAPIKeyHash(legacyHash, plainKey) require.NoError(t, err) assert.True(t, migrated, "Should indicate migration occurred") assert.NotEqual(t, legacyHash, newHash, "New hash should differ from legacy") assert.True(t, strings.HasPrefix(newHash, "$2"), "New hash should be bcrypt") // Verify new hash works assert.True(t, CompareAPIKey(newHash, plainKey)) }) t.Run("no migration needed for bcrypt", func(t *testing.T) { // Create a bcrypt hash bcryptHash, err := APIKey(plainKey) require.NoError(t, err) // Try to migrate it newHash, migrated, err := MigrateAPIKeyHash(bcryptHash, plainKey) require.NoError(t, err) assert.False(t, migrated, "Should not migrate bcrypt hash") assert.Equal(t, bcryptHash, newHash, "Hash should remain unchanged") }) t.Run("invalid key does not migrate", func(t *testing.T) { legacyHash := String("correct_key") // Try to migrate with wrong plaintext newHash, migrated, err := MigrateAPIKeyHash(legacyHash, "wrong_key") require.NoError(t, err) assert.False(t, migrated, "Should not migrate invalid key") assert.Empty(t, newHash, "Should return empty for invalid key") }) }