package main import ( "net/http" "net/http/httptest" "testing" ) func TestIsIP(t *testing.T) { tests := []struct { name string input string wantOK bool }{ {"ipv4", "192.168.1.1", true}, {"ipv4 loopback", "127.0.0.1", true}, {"ipv6", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", true}, {"ipv6 short", "::1", true}, {"empty", "", false}, {"hostname", "example.com", false}, {"garbage", "not-an-ip!!", false}, {"partial", "192.168", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := IsIP(tt.input); got != tt.wantOK { t.Errorf("IsIP(%q) = %v, want %v", tt.input, got, tt.wantOK) } }) } } func TestIsCIDR(t *testing.T) { tests := []struct { name string input string wantOK bool }{ {"ipv4 cidr", "192.168.1.0/24", true}, {"ipv6 cidr", "2001:db8::/32", true}, {"single host", "10.0.0.0/32", true}, {"plain ip", "10.0.0.1", false}, {"missing prefix", "192.168.1.0", false}, {"garbage", "foo/bar", false}, {"empty", "", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := IsCIDR(tt.input); got != tt.wantOK { t.Errorf("IsCIDR(%q) = %v, want %v", tt.input, got, tt.wantOK) } }) } } func TestIPMatchesEntry(t *testing.T) { tests := []struct { name string ip string entry string wantOK bool wantErr bool }{ {"exact match", "192.168.1.5", "192.168.1.5", true, false}, {"ipv4 cidr match", "10.0.0.5", "10.0.0.0/24", true, false}, {"ipv4 cidr no match", "10.0.1.5", "10.0.0.0/24", false, false}, {"ipv6 cidr match", "2001:db8::1", "2001:db8::/32", true, false}, {"invalid entry", "10.0.0.1", "not-a-cidr", false, false}, {"empty entry", "10.0.0.1", "", false, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ipMatchesEntry(tt.ip, tt.entry) if tt.wantErr { if err == nil { t.Errorf("ipMatchesEntry(%q, %q) = %v, want error", tt.ip, tt.entry, got) } } else { if err != nil { t.Errorf("ipMatchesEntry(%q, %q) error: %v", tt.ip, tt.entry, err) } if got != tt.wantOK { t.Errorf("ipMatchesEntry(%q, %q) = %v, want %v", tt.ip, tt.entry, got, tt.wantOK) } } }) } } func TestVerifyAPIKey(t *testing.T) { token := "super-secret" tests := []struct { name string auth string wantOK bool }{ {"correct token", "Bearer super-secret", true}, {"missing Bearer", "super-secret", false}, {"wrong token", "Bearer wrong", false}, {"empty", "", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := http.Request{Header: http.Header{}} r.Header.Set("Authorization", tt.auth) if got := verifyAPIKey(&r, token); got != tt.wantOK { t.Errorf("verifyAPIKey(%q) = %v, want %v", tt.auth, got, tt.wantOK) } }) } } func TestApiKeyMiddleware(t *testing.T) { token := "test-token" called := false next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called = true w.WriteHeader(http.StatusNoContent) }) wrapped := apiKeyMiddleware(next, token) t.Run("valid key", func(t *testing.T) { called = false r := httptest.NewRequest(http.MethodGet, "/test", nil) r.Header.Set("Authorization", "Bearer test-token") w := httptest.NewRecorder() wrapped(w, r) if !called { t.Error("middleware did not call next handler") } if w.Code != http.StatusNoContent { t.Errorf("expected 204, got %d", w.Code) } }) t.Run("invalid key", func(t *testing.T) { called = false r := httptest.NewRequest(http.MethodGet, "/test", nil) r.Header.Set("Authorization", "Bearer wrong") w := httptest.NewRecorder() wrapped(w, r) if called { t.Error("middleware called next handler despite invalid key") } if w.Code != http.StatusUnauthorized { t.Errorf("expected 401, got %d", w.Code) } }) t.Run("missing key", func(t *testing.T) { called = false r := httptest.NewRequest(http.MethodGet, "/test", nil) w := httptest.NewRecorder() wrapped(w, r) if called { t.Error("middleware called next handler despite missing key") } if w.Code != http.StatusUnauthorized { t.Errorf("expected 401, got %d", w.Code) } }) }