Skip to content

test: cover public and operational HTTP endpoints - #65

Merged
ktrubilo9 merged 1 commit into
ktrubilo9:mainfrom
Furox-Art:test-public-operational-endpoints
Oct 13, 2026
Merged

ktrubilo9 merged 1 commit into
ktrubilo9:mainfrom
Furox-Art:test-public-operational-endpoints

Conversation

@Furox-Art

Copy link
Copy Markdown
Contributor

Closes #64

Summary

Adds tests for the public and operational HTTP endpoints, which previously had
no direct coverage.

To make the handlers testable without binding fixed ports (8080, 9091), the
two muxes that Proxy.Start() built inline are extracted into
Proxy.publicHandler() and Proxy.operationalHandler(). Start() now just
wires http.Server.Handler to those, so behaviour is unchanged and the tests
drive the exact same handler graph.

internal/proxy/proxy_test.go covers:

Public port (publicHandler)

  • GET /api/items is proxied through the client-IP and rate-limit middleware
    to the configured backend, and the backend receives the original path.
  • GET/HEAD /health returns 200 with application/json and the minimal
    {"status":"healthy"} body.
  • The public /health payload exposes only status — no backend URLs, and no
    backends/active_count/total_count keys.
  • /metrics and /metrics/prometheus return 404 on GET, HEAD, POST
    and DELETE, and never reach a backend.
  • POST/PUT/PATCH/DELETE/OPTIONS/TRACE /health return 405 with
    Allow: GET, HEAD, and the reserved path is not forwarded to a backend.

Operational port (operationalHandler)

  • GET/HEAD /health returns 200 with application/json, status
    healthy, active/total = 1/1, and the backend detail entry.
  • /health returns 503 once the single backend is probed unhealthy.
  • /metrics returns JSON containing system, backends, rate_limit and
    http.
  • /metrics/prometheus returns text/plain Prometheus output including
    reverse_proxy_requests_total.
  • Unsupported methods on all three operational routes return 405 with
    Allow: GET, HEAD.

Implementation notes

readOnlyHandler wraps a handler so that unsupported methods get 405 with an
Allow header rather than falling through to a broader mux pattern. The route
stays registered for every method — registering only GET /health would let
POST /health match the catch-all "/" pattern and be proxied to a backend.
A recordingBackend test double asserts on this: every method-rejection test
also checks the backend recorded no requests.

Verification

All run on go1.26.0 (the version in go.mod):

  • gofmt -l . — clean
  • go vet ./... — clean
  • go test ./... — all packages ok
  • go test -race ./... — all packages ok
  • golangci-lint run ./... (v2.13, the version CI pins) — 0 issues

Extract the public and operational muxes into Proxy.publicHandler() and
Proxy.operationalHandler() so they can be exercised without opening fixed
ports, and add tests covering both.

Public port: proxied traffic reaches the backend, /health returns a minimal
JSON payload that exposes no backend details, /metrics and
/metrics/prometheus return 404, and unsupported methods on /health get 405
with an Allow header instead of falling through to the catch-all proxy route.

Operational port: /health reports per-backend detail and 503 when no backend
is healthy, /metrics returns the JSON payload, /metrics/prometheus returns
Prometheus text format, and unsupported methods get 405.

mux := http.NewServeMux()
mux.Handle("/", finalHandler)
mux.Handle("/health", readOnlyHandler(handler.PublicHealthHandler(p.Runtime), http.MethodGet, http.MethodHead))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch here, keeping /health registered for every methods prevents unsupported requests from falling through to the catch-all proxy route and being forwarded to a backend.


// The reserved /health path must never fall through to the catch-all proxy
// route, otherwise an unsupported method would be forwarded to a backend.
if paths := backend.receivedPaths(); len(paths) != 0 {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that this test verifies the request never reached the backend instead of checking only the 405 response. This protects the actual behavior we care about and makes the test much more valuable

@ktrubilo9 ktrubilo9 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution, the implementation is clear, focused on the issue and adds meaningful coverage for both HTTP surfaces. I also appreciate the clear PR description and explanation of the routing edge case.

@ktrubilo9
ktrubilo9 merged commit ffeff4f into ktrubilo9:main Oct 13, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add tests for public and operational HTTP endpoints

2 participants