veza/veza-backend-api/internal/services/geoip_service_test.go
senke 6a675565e1 feat(v0.13.3): complete - Polish Sécurité Avancée
TASK-SECADV-001: WebAuthn/Passkeys (F022)
- WebAuthn credential model, service, handler
- Registration/authentication ceremony endpoints
- CRUD operations (list, rename, delete passkeys)
- Routes: GET/POST/PUT/DELETE /auth/passkeys/*

TASK-SECADV-002: Configurable password policy (F015)
- PasswordPolicyConfig with MinLength, MaxLength, RequireUpper/Lower/Number/Special
- NewPasswordValidatorWithPolicy constructor
- PasswordPolicyFromEnv() reads env vars (PASSWORD_MIN_LENGTH, etc.)
- All character class checks now respect policy configuration

TASK-SECADV-003: Géolocalisation connexions (F025)
- GeoIPResolver interface + GeoIPService implementation
- Country/city columns added to login_history table
- LoginHistoryService.Record() performs GeoIP lookup
- GetUserHistory returns geolocation data
- GET /auth/login-history endpoint

TASK-SECADV-004: Password expiration (F016)
- password_changed_at column on users table
- CheckPasswordExpiration() method on PasswordService
- All password change/reset methods now set password_changed_at
- NewPasswordServiceWithPolicy() supports expiration days config

Migration: 971_security_advanced_v0133.sql

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:09:01 +01:00

59 lines
1.5 KiB
Go

package services
import (
"os"
"testing"
"go.uber.org/zap"
)
func TestNewGeoIPServiceDisabled(t *testing.T) {
os.Unsetenv("GEOIP_DB_PATH")
logger := zap.NewNop()
svc := NewGeoIPService(logger)
if svc.IsEnabled() {
t.Error("expected GeoIP to be disabled when GEOIP_DB_PATH not set")
}
country, city := svc.Lookup("1.2.3.4")
if country != "" || city != "" {
t.Error("expected empty results when disabled")
}
}
func TestNewGeoIPServiceMissingDB(t *testing.T) {
os.Setenv("GEOIP_DB_PATH", "/nonexistent/path.mmdb")
defer os.Unsetenv("GEOIP_DB_PATH")
logger := zap.NewNop()
svc := NewGeoIPService(logger)
if svc.IsEnabled() {
t.Error("expected GeoIP to be disabled when DB file doesn't exist")
}
}
func TestGeoIPLookupPrivateIPs(t *testing.T) {
// Even if enabled, private IPs should return empty
svc := &GeoIPService{logger: zap.NewNop(), enabled: true}
tests := []string{"127.0.0.1", "::1", "192.168.1.1", "10.0.0.1", "0.0.0.0"}
for _, ip := range tests {
country, city := svc.Lookup(ip)
if country != "" || city != "" {
t.Errorf("expected empty for private IP %s, got country=%q city=%q", ip, country, city)
}
}
}
func TestGeoIPLookupInvalidIP(t *testing.T) {
svc := &GeoIPService{logger: zap.NewNop(), enabled: true}
country, city := svc.Lookup("not-an-ip")
if country != "" || city != "" {
t.Error("expected empty for invalid IP")
}
}
// TestGeoIPResolverInterface verifies GeoIPService satisfies GeoIPResolver.
func TestGeoIPResolverInterface(t *testing.T) {
var _ GeoIPResolver = &GeoIPService{}
}