package server_test import ( "bytes" "context" // Added for mock provider "encoding/json" "errors" "net/http" "net/http/httptest" "testing" "git.cloonar.com/cloonar/updns/internal/config" // Added for mock provider "git.cloonar.com/cloonar/updns/internal/server" "go.uber.org/zap" // Added for logger ) // mockProvider is a simple mock for testing router logic without real provider interaction. type mockProvider struct { updateErr error // Allows simulating update errors } func (m *mockProvider) UpdateRecord(ctx context.Context, host, ip string) error { return m.updateErr } func newTestConfig(provider string) *config.Config { return &config.Config{ Server: config.ServerConfig{ BindAddress: ":0", TLS: config.TLSConfig{Enabled: false}, }, Upstream: config.UpstreamConfig{ Provider: provider, Hetzner: config.HetznerConfig{APIToken: "token"}, }, Clients: map[string]config.ClientConfig{ "client1": { Secret: "s3cr3t", Exact: []string{"a.example.com"}, Wildcard: []string{"example.net"}, }, }, } } func TestMetricsEndpoint(t *testing.T) { logger := zap.NewNop() mockProv := &mockProvider{} r := server.NewRouter(newTestConfig("unknown"), logger, mockProv) req := httptest.NewRequest("GET", "/metrics", nil) w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200 OK, got %d", w.Code) } } func TestUpdateInvalidJSON(t *testing.T) { logger := zap.NewNop() mockProv := &mockProvider{} r := server.NewRouter(newTestConfig("unknown"), logger, mockProv) req := httptest.NewRequest("POST", "/update", bytes.NewBufferString("{invalid")) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400 BadRequest, got %d", w.Code) } } func TestUpdateUnauthorizedKey(t *testing.T) { logger := zap.NewNop() mockProv := &mockProvider{} r := server.NewRouter(newTestConfig("unknown"), logger, mockProv) body := map[string]string{"key": "bad", "secret": "x", "host": "a.example.com"} data, _ := json.Marshal(body) req := httptest.NewRequest("POST", "/update", bytes.NewBuffer(data)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401 Unauthorized, got %d", w.Code) } } func TestUpdateHostForbidden(t *testing.T) { logger := zap.NewNop() mockProv := &mockProvider{} r := server.NewRouter(newTestConfig("unknown"), logger, mockProv) body := map[string]string{"key": "client1", "secret": "s3cr3t", "host": "bad.example.com"} data, _ := json.Marshal(body) req := httptest.NewRequest("POST", "/update", bytes.NewBuffer(data)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusForbidden { t.Errorf("expected 403 Forbidden, got %d", w.Code) } } // Note: This test case name is slightly misleading now. The router itself doesn't care // about the provider name from config anymore, as the provider is passed in. // The provider initialization logic is tested separately or in StartServer tests. // We keep the test to ensure the update path works correctly when auth passes. // If the *mock* provider returned an error, we'd get 500. func TestUpdateProviderInteraction(t *testing.T) { logger := zap.NewNop() // Simulate a provider error mockProv := &mockProvider{updateErr: errors.New("simulated provider update error")} r := server.NewRouter(newTestConfig("hetzner"), logger, mockProv) // Config provider name doesn't matter here body := map[string]string{"key": "client1", "secret": "s3cr3t", "host": "a.example.com"} data, _ := json.Marshal(body) req := httptest.NewRequest("POST", "/update", bytes.NewBuffer(data)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusInternalServerError { t.Errorf("expected 500 InternalServerError, got %d", w.Code) } } func TestUpdateSuccess(t *testing.T) { logger := zap.NewNop() mockProv := &mockProvider{} // No error for success case r := server.NewRouter(newTestConfig("hetzner"), logger, mockProv) body := map[string]string{"key": "client1", "secret": "s3cr3t", "host": "a.example.com", "ip": "1.2.3.4"} data, _ := json.Marshal(body) req := httptest.NewRequest("POST", "/update", bytes.NewBuffer(data)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 OK, got %d", w.Code) } }