Skip to content
quarry Artifact [b7d59f992d]
Login

Artifact [b7d59f992d]

Artifact b7d59f992d03ca8e345dcc9a0067dbe68fbfba538d18953b3fbadc8d689b6e09:


package web

import (
	"bytes"
	"strings"
	"testing"
)

// renderOps executes the operations page far enough to read the prose
// about the durable store. The panels themselves are fed by rings and
// render the same whatever the store is doing — which is the whole
// reason this line has to exist.
func renderOps(t *testing.T, v opsView) string {
	t.Helper()
	var buf bytes.Buffer
	if err := opsPage.ExecuteTemplate(&buf, "ops-body", pageData{Title: "operations", Ops: v}); err != nil {
		t.Fatal(err)
	}
	return buf.String()
}

// Off is a choice; broken is a fault. A panel that renders identically
// for both teaches an operator to trust a forge that is recording
// nothing — which is how the reserved-word schema failure would have
// reached production instead of a test run.
func TestOpsPageSaysWhenHistoryIsOff(t *testing.T) {
	var v opsView
	v.BudgetMs = 500
	body := renderOps(t, v)
	if !strings.Contains(body, "History is off") {
		t.Errorf("an unconfigured store renders no notice:\n%s", body)
	}
	if strings.Contains(body, "NOT being recorded") {
		t.Error("switched off is reported as a failure")
	}
}

func TestOpsPageSaysWhenHistoryIsBroken(t *testing.T) {
	var v opsView
	v.BudgetMs = 500
	v.Store.Configured = true
	v.Store.Path = "/data/ops.duckdb"
	v.Store.Err = "schema: at is reserved"
	body := renderOps(t, v)
	if !strings.Contains(body, "NOT being recorded") {
		t.Errorf("a broken store renders no warning:\n%s", body)
	}
	if !strings.Contains(body, "schema: at is reserved") {
		t.Error("the warning does not say what went wrong")
	}
	if !strings.Contains(body, "ci-fail-text") {
		t.Error("the warning is not marked as a failure")
	}
	if strings.Contains(body, "History is off") {
		t.Error("a broken store is reported as switched off")
	}
}

func TestOpsPageNamesTheStoreAndItsWindow(t *testing.T) {
	var v opsView
	v.BudgetMs = 500
	v.Store.Configured = true
	v.Store.Path = "/data/ops.duckdb"
	v.Store.Kept = "14d"
	body := renderOps(t, v)
	if !strings.Contains(body, "/data/ops.duckdb") {
		t.Errorf("a healthy store is not named:\n%s", body)
	}
	if !strings.Contains(body, "kept for 14d") {
		t.Error("the retention window is not stated, so nobody was asked about it")
	}
	if strings.Contains(body, "NOT being recorded") || strings.Contains(body, "History is off") {
		t.Error("a healthy store renders a fault")
	}
	// Retention off must not render a dangling "kept for".
	v.Store.Kept = ""
	if body := renderOps(t, v); strings.Contains(body, "kept for") {
		t.Error("retention off still renders a window")
	}
}

// SQL has no timing category of its own: ncruces/go-sqlite3 builds its
// connector through an unexported constructor, so there is no
// driver.Connector to wrap. The panel's answer is subtraction, and
// subtraction the reader has to do in their head is not an answer — so
// the leftover is a column.
func TestOpsPageNamesTheUntimedRemainder(t *testing.T) {
	var v opsView
	v.BudgetMs = 500
	v.Renders = append(v.Renders, opsRenderRow(300, 120, 40))

	body := renderOps(t, v)
	if !strings.Contains(body, ">180ms<") {
		t.Errorf("300ms total against 120ms of ops does not report 180ms of rest:\n%s", body)
	}
	if !strings.Contains(body, "SQL is not timed separately") {
		t.Error("the page does not say why SQL has no column of its own")
	}
}

// The two numbers are sampled separately, so rounding can put the parts
// above the whole. A negative remainder would read as a bug in the
// forge rather than in the measurement.
func TestRestNeverGoesNegative(t *testing.T) {
	var v opsView
	v.BudgetMs = 500
	v.Renders = append(v.Renders, opsRenderRow(100, 140, 40))
	body := renderOps(t, v)
	if strings.Contains(body, "-40ms") {
		t.Errorf("parts above the whole rendered a negative remainder:\n%s", body)
	}
	if !strings.Contains(body, ">0ms<") {
		t.Errorf("expected the remainder clamped to 0ms:\n%s", body)
	}
}

// opsRenderRow builds one row of the recent-renders table.
func opsRenderRow(elapsed, ops, zit int64) opsRenderView {
	return opsRenderView{At: "2026-08-25T12:00:00", Path: "GET /{$}",
		ElapsedMs: elapsed, OpsMs: ops, ZitMs: zit, Status: 200}
}

// A landing title is the first line of the message its author wrote,
// and authors write long ones. On the landing's own page the title IS
// the page; on a timeline row it is a decoration, and one that wraps to
// three lines has stopped decorating and started competing.
func TestClipBreaksOnAWordBoundary(t *testing.T) {
	long := "Three tickets from the observability queue: the first real finding the ops panel produced"
	got := clip(72, long)
	if len([]rune(got)) > 73 {
		t.Errorf("clip(72) returned %d characters: %q", len([]rune(got)), got)
	}
	if !strings.HasSuffix(got, "…") {
		t.Errorf("a clipped title does not say it was clipped: %q", got)
	}
	if strings.HasSuffix(strings.TrimSuffix(got, "…"), " ") {
		t.Errorf("trailing space before the ellipsis: %q", got)
	}
	// A title that fits is left exactly as written.
	if got := clip(72, "Closes 2058808c3504."); got != "Closes 2058808c3504." {
		t.Errorf("a short title was altered: %q", got)
	}
	// No room to break on a word: still bounded, still marked.
	if got := clip(8, "supercalifragilistic"); len([]rune(got)) > 9 || !strings.HasSuffix(got, "…") {
		t.Errorf("clip(8) of one long word gave %q", got)
	}
}