Skip to content
quarry Artifact [cfa9c70a59]
Login

Artifact [cfa9c70a59]

Artifact cfa9c70a59bdc686ab0ab5d9c2efbe29c588903c1c8bd984014e85aafc1ad5de:


package web

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"craftdesign.group/quarry/internal/obs"
)

// A live stream is not a slow page. chat, the timeline and the ops
// panel hold a connection open for as long as the reader keeps the tab,
// so a stream's "duration" is that tab's lifetime. The fleet recorded
// timeline.sse at a p95 of 435 SECONDS, which headed the slowest-routes
// panel and buried every real finding under it.
//
// The second consequence was worse: every stream crosses the render
// budget by design, so the first-crossing alert fired on connections
// that were working perfectly — the muted-channel failure that rule
// exists to prevent, defeated from inside the same feature.
func TestStreamsAreNotRecordedAsRenders(t *testing.T) {
	for _, ct := range []string{
		"text/event-stream",
		"text/event-stream; charset=utf-8", // the charset must not defeat it
	} {
		if !isStream(ct) {
			t.Errorf("isStream(%q) = false, want true", ct)
		}
	}
	for _, ct := range []string{"text/html; charset=utf-8", "application/json", ""} {
		if isStream(ct) {
			t.Errorf("isStream(%q) = true, want false — an ordinary page must still be measured", ct)
		}
	}
}

// End to end through the middleware: a streaming handler files nothing,
// an ordinary one files a render.
func TestWithTimerSkipsStreamsAndKeepsPages(t *testing.T) {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /stream", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		w.Write([]byte("data: hi\n\n"))
	})
	mux.HandleFunc("GET /page", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Write([]byte("<p>hi</p>"))
	})

	// The middleware records into the process recorder; swap in a fresh
	// one so this test reads only its own traffic.
	saved := obs.Default
	obs.Default = obs.NewRecorder(16, nil)
	defer func() { obs.Default = saved }()

	h := withTimer(mux, nil, nil, mux)
	for _, path := range []string{"/stream", "/page", "/page"} {
		h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", path, nil))
	}

	snap := obs.Default.Snapshot()
	for _, p := range snap.Paths {
		if p.Path == "GET /stream" {
			t.Errorf("a stream was filed as a render: %+v", p)
		}
	}
	var pages int
	for _, p := range snap.Paths {
		if p.Path == "GET /page" {
			pages = p.N
		}
	}
	if pages != 2 {
		t.Errorf("ordinary renders recorded = %d, want 2 — the fix must not silence real pages", pages)
	}
}

// A Datastar fragment is delivered as SSE and is nevertheless a RENDER:
// it completes immediately, and its duration is work rather than a tab
// left open. Excluding it by content-type alone would leave the live
// timeline's refresh — the route it actually spends its time in — the
// one thing nothing measured.
func TestOneShotFragmentsAreStillMeasured(t *testing.T) {
	sw := &statusWriter{ResponseWriter: httptest.NewRecorder()}
	sw.Header().Set("Content-Type", "text/event-stream")
	if !isStream(sw.Header().Get("Content-Type")) || sw.oneShot {
		t.Fatal("precondition: a plain event-stream response is a stream")
	}
	markOneShot(sw)
	if !sw.oneShot {
		t.Error("markOneShot did not reach the writer the timer inspects")
	}
}

// ...and marking something that is not a statusWriter must not panic:
// handlers call this without knowing what they were wrapped in.
func TestMarkOneShotToleratesAnUnwrappedWriter(t *testing.T) {
	markOneShot(httptest.NewRecorder())
}

// A browser asks for sourcemaps nobody ships, so /assets/datastar.js.map
// fell through to the catch-all and filed a 404 against route "/" —
// several rows per page view in a panel whose job is to make the signal
// findable.
func TestAssetRequestsAreNotRenders(t *testing.T) {
	rec := obs.NewRecorder(64, nil)
	h := withTimerFor(rec, http.NewServeMux(), nil, nil, http.HandlerFunc(
		func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(404) }))
	for _, p := range []string{"/assets/datastar.js.map", "/assets/quarry.png"} {
		h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", p, nil))
	}
	if n := len(rec.Snapshot().Renders); n != 0 {
		t.Errorf("%d asset requests recorded as renders", n)
	}
	// A real page's 404 is still worth recording: a slow error is a
	// different finding from a slow success, not a thing to hide.
	h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", "/r/nope/timeline", nil))
	if n := len(rec.Snapshot().Renders); n != 1 {
		t.Errorf("%d renders after a page 404, want 1", n)
	}
}