345 lines
7 KiB
Go
345 lines
7 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|