package httpserver import ( "context" "encoding/json" "log/slog" "net/http" "net/http/httptest" "testing" "time" "git.wntrmute.dev/mc/mcdsl/config" ) func testConfig() config.ServerConfig { return config.ServerConfig{ ListenAddr: ":0", TLSCert: "/tmp/cert.pem", TLSKey: "/tmp/key.pem", ReadTimeout: config.Duration{Duration: 30 * time.Second}, WriteTimeout: config.Duration{Duration: 30 * time.Second}, IdleTimeout: config.Duration{Duration: 120 * time.Second}, ShutdownTimeout: config.Duration{Duration: 60 * time.Second}, } } func TestNew(t *testing.T) { srv := New(testConfig(), slog.Default()) if srv.Router == nil { t.Fatal("Router is nil") } if srv.Logger == nil { t.Fatal("Logger is nil") } } func TestLoggingMiddleware(t *testing.T) { srv := New(testConfig(), slog.Default()) handler := srv.LoggingMiddleware(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusCreated) })) rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/test", nil) handler.ServeHTTP(rec, req) if rec.Code != http.StatusCreated { t.Fatalf("status = %d, want %d", rec.Code, http.StatusCreated) } } func TestLoggingMiddlewareDefaultStatus(t *testing.T) { srv := New(testConfig(), slog.Default()) // Handler that writes body without explicit WriteHeader. handler := srv.LoggingMiddleware(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte("ok")) })) rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/test", nil) handler.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK) } } func TestStatusWriter(t *testing.T) { rec := httptest.NewRecorder() sw := &StatusWriter{ResponseWriter: rec, Status: http.StatusOK} sw.WriteHeader(http.StatusNotFound) if sw.Status != http.StatusNotFound { t.Fatalf("Status = %d, want %d", sw.Status, http.StatusNotFound) } if rec.Code != http.StatusNotFound { t.Fatalf("recorder Code = %d, want %d", rec.Code, http.StatusNotFound) } } func TestWriteJSON(t *testing.T) { rec := httptest.NewRecorder() WriteJSON(rec, http.StatusOK, map[string]string{"key": "value"}) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK) } if ct := rec.Header().Get("Content-Type"); ct != "application/json" { t.Fatalf("Content-Type = %q, want %q", ct, "application/json") } var body map[string]string if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { t.Fatalf("decode body: %v", err) } if body["key"] != "value" { t.Fatalf("body[key] = %q, want %q", body["key"], "value") } } func TestWriteError(t *testing.T) { rec := httptest.NewRecorder() WriteError(rec, http.StatusBadRequest, "something went wrong") if rec.Code != http.StatusBadRequest { t.Fatalf("status = %d, want %d", rec.Code, http.StatusBadRequest) } var body map[string]string if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { t.Fatalf("decode body: %v", err) } if body["error"] != "something went wrong" { t.Fatalf("body[error] = %q, want %q", body["error"], "something went wrong") } } func TestShutdown(t *testing.T) { srv := New(testConfig(), slog.Default()) // Shutdown without starting should not panic. if err := srv.Shutdown(context.Background()); err != nil { t.Fatalf("Shutdown: %v", err) } } func TestRouterIntegration(t *testing.T) { srv := New(testConfig(), slog.Default()) srv.Router.Use(srv.LoggingMiddleware) srv.Router.Get("/health", func(w http.ResponseWriter, _ *http.Request) { WriteJSON(w, http.StatusOK, map[string]string{"status": "ok"}) }) rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/health", nil) srv.Router.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK) } var body map[string]string if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { t.Fatalf("decode: %v", err) } if body["status"] != "ok" { t.Fatalf("status = %q, want %q", body["status"], "ok") } }