package logger import ( "testing" ) func TestRedactEmail(t *testing.T) { redactor := NewSensitiveFieldRedactor() tests := []struct { name string input string expected string }{ { name: "normal email", input: "john.doe@example.com", expected: "jo***@example.com", }, { name: "short local part", input: "ab@example.com", expected: "**@example.com", }, { name: "single character local part", input: "a@example.com", expected: "**@example.com", }, { name: "empty email", input: "", expected: "[empty]", }, { name: "invalid email", input: "notanemail", expected: "[invalid-email]", }, { name: "long email", input: "very.long.email.address@subdomain.example.com", expected: "ve***@subdomain.example.com", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := redactor.RedactEmail(tt.input) if result != tt.expected { t.Errorf("RedactEmail(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } func TestHashForLogging(t *testing.T) { redactor := NewSensitiveFieldRedactor() tests := []struct { name string input string }{ { name: "email", input: "john.doe@example.com", }, { name: "tenant slug", input: "my-company", }, { name: "another email", input: "jane.smith@test.com", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hash1 := redactor.HashForLogging(tt.input) hash2 := redactor.HashForLogging(tt.input) // Hash should be consistent if hash1 != hash2 { t.Errorf("HashForLogging is not consistent: %q != %q", hash1, hash2) } // Hash should be 16 characters (8 bytes in hex) if len(hash1) != 16 { t.Errorf("HashForLogging length = %d, want 16", len(hash1)) } // Hash should not contain original value if hash1 == tt.input { t.Errorf("HashForLogging returned original value") } }) } // Different inputs should produce different hashes hash1 := redactor.HashForLogging("john.doe@example.com") hash2 := redactor.HashForLogging("jane.smith@example.com") if hash1 == hash2 { t.Error("Different inputs produced same hash") } // Empty string emptyHash := redactor.HashForLogging("") if emptyHash != "[empty]" { t.Errorf("HashForLogging(\"\") = %q, want [empty]", emptyHash) } } func TestRedactTenantSlug(t *testing.T) { redactor := NewSensitiveFieldRedactor() tests := []struct { name string input string expected string }{ { name: "normal slug", input: "my-company", expected: "my***", }, { name: "short slug", input: "abc", expected: "***", }, { name: "very short slug", input: "ab", expected: "***", }, { name: "empty slug", input: "", expected: "[empty]", }, { name: "long slug", input: "very-long-company-name", expected: "ve***", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := redactor.RedactTenantSlug(tt.input) if result != tt.expected { t.Errorf("RedactTenantSlug(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } func TestRedactAPIKey(t *testing.T) { redactor := NewSensitiveFieldRedactor() tests := []struct { name string input string expected string }{ { name: "live API key", input: "live_sk_abc123def456ghi789", expected: "live_sk_***i789", }, { name: "test API key", input: "test_sk_xyz789uvw456rst123", expected: "test_sk_***t123", }, { name: "short live key", input: "live_sk_abc", expected: "live_sk_***", }, { name: "other format", input: "sk_abc123def456", expected: "***f456", }, { name: "very short key", input: "abc", expected: "***", }, { name: "empty key", input: "", expected: "[empty]", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := redactor.RedactAPIKey(tt.input) if result != tt.expected { t.Errorf("RedactAPIKey(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } func TestRedactJWTToken(t *testing.T) { redactor := NewSensitiveFieldRedactor() tests := []struct { name string input string expected string }{ { name: "normal JWT", input: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U", expected: "eyJhbGci...P0THsR8U", }, { name: "short token", input: "short", expected: "***", }, { name: "empty token", input: "", expected: "[empty]", }, { name: "minimum length token", input: "1234567890123456", expected: "12345678...90123456", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := redactor.RedactJWTToken(tt.input) if result != tt.expected { t.Errorf("RedactJWTToken(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } func TestRedactIPAddress(t *testing.T) { redactor := NewSensitiveFieldRedactor() tests := []struct { name string input string expected string }{ { name: "IPv4 address", input: "192.168.1.100", expected: "192.168.*.*", }, { name: "IPv4 public", input: "8.8.8.8", expected: "8.8.*.*", }, { name: "IPv6 address", input: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", expected: "2001:0db8:85a3:0000:****", }, { name: "IPv6 shortened", input: "2001:db8::1", expected: "2001:db8::1:****", }, { name: "empty IP", input: "", expected: "[empty]", }, { name: "invalid IP", input: "notanip", expected: "***", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := redactor.RedactIPAddress(tt.input) if result != tt.expected { t.Errorf("RedactIPAddress(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } func TestUserIdentifier(t *testing.T) { userID := "user_123" email := "john.doe@example.com" fields := UserIdentifier(userID, email) if len(fields) != 3 { t.Errorf("UserIdentifier returned %d fields, want 3", len(fields)) } // Check that fields contain expected keys fieldKeys := make(map[string]bool) for _, field := range fields { fieldKeys[field.Key] = true } expectedKeys := []string{"user_id", "email_hash", "email_redacted"} for _, key := range expectedKeys { if !fieldKeys[key] { t.Errorf("UserIdentifier missing key: %s", key) } } } func TestTenantIdentifier(t *testing.T) { tenantID := "tenant_123" slug := "my-company" fields := TenantIdentifier(tenantID, slug) if len(fields) != 3 { t.Errorf("TenantIdentifier returned %d fields, want 3", len(fields)) } // Check that fields contain expected keys fieldKeys := make(map[string]bool) for _, field := range fields { fieldKeys[field.Key] = true } expectedKeys := []string{"tenant_id", "tenant_slug_hash", "tenant_slug_redacted"} for _, key := range expectedKeys { if !fieldKeys[key] { t.Errorf("TenantIdentifier missing key: %s", key) } } }