Skip to content
quarry Artifact [799aee7964]
Login

Artifact [799aee7964]

Artifact 799aee79646fc75ba7fef8b8a1a33c246fa5fdb0a56ccbe8b851e6e3418bca34:


package web

import (
	"regexp"
	"strings"
	"testing"
)

type laneRow = struct {
	Name     string     `json:"name"`
	Bound    int        `json:"bound"`
	Running  int        `json:"running"`
	Admitted int        `json:"admitted"`
	Refused  int        `json:"refused"`
	WaitedMs int64      `json:"waitedMs"`
	Waiting  int64      `json:"waiting"`
	Queue    int        `json:"queue"`
	Items    []laneItem `json:"items"`
}

type laneItem = struct {
	Verb    string `json:"verb"`
	Subject string `json:"subject"`
	AgeMs   int64  `json:"ageMs"`
	Running bool   `json:"running"`
}

func laneLine(t *testing.T, lanes ...laneRow) string {
	t.Helper()
	var d pageData
	d.Ops.BudgetMs = 500
	d.Ops.Lanes = lanes
	var buf strings.Builder
	if err := opsPage.ExecuteTemplate(&buf, "content", d); err != nil {
		t.Fatal(err)
	}
	m := regexp.MustCompile(`(?s)<p class="ops-phase-l">(.*?)</p>`).FindStringSubmatch(buf.String())
	if m == nil {
		t.Fatal("FAIL: the ops page drew no lane line")
	}
	return strings.Join(strings.Fields(regexp.MustCompile(`<[^>]*>`).ReplaceAllString(m[1], "")), " ")
}

// A saturated lane has to read as a diagnosis. All slots busy AND the
// queue full is capacity; all slots busy with an empty queue is a slot
// held too long. Running and refused alone cannot tell those apart,
// which is what left ten thousand "a store could not be ingested" rows
// unactionable.
func TestASaturatedLaneReadsAsADiagnosis(t *testing.T) {
	got := laneLine(t, laneRow{Name: "ingest", Bound: 3, Running: 3,
		Waiting: 22, Queue: 22, Admitted: 4201, Refused: 9871})
	for _, want := range []string{"ingest", "3/3 running", "22 of 22 queued", "4,201 admitted", "9,871 told busy"} {
		if !strings.Contains(got, want) {
			t.Errorf("FAIL: the line is missing %q: %s", want, got)
		}
	}
}

// Exact figures, not rounded ones: this is the page an operator reads
// to pick a limit, and "9.9K told busy" is not a number you can set a
// queue from.
func TestTheLaneCountsAreExact(t *testing.T) {
	got := laneLine(t, laneRow{Name: "ingest", Bound: 3, Admitted: 4201, Refused: 9871})
	if strings.Contains(got, "K told busy") || strings.Contains(got, "K admitted") {
		t.Errorf("FAIL: the counts are rounded: %s", got)
	}
}

// A quiet lane says zero rather than leaving the word dangling. count
// renders a zero as nothing — right for a nav badge, and here it read
// "archive builds 0/2 running, admitted".
func TestAQuietLaneStillSaysZero(t *testing.T) {
	got := laneLine(t, laneRow{Name: "archive builds", Bound: 2})
	if !strings.Contains(got, "0 admitted") {
		t.Errorf("FAIL: a lane that admitted nothing reads %q", got)
	}
	// A lane that never refused anybody does not mention refusals.
	if strings.Contains(got, "told busy") {
		t.Errorf("FAIL: a quiet lane reports refusals: %s", got)
	}
}

// The queue depth is stated wherever a lane queues at all: "0 of 8
// queued" is how a reader learns the queue exists. A lane that does not
// queue says nothing, because there is no depth to report.
func TestDepthIsShownWhereverThereIsAQueue(t *testing.T) {
	if got := laneLine(t, laneRow{Name: "bulk", Bound: 2, Queue: 8}); !strings.Contains(got, "0 of 8 queued") {
		t.Errorf("FAIL: an idle queue is not stated: %s", got)
	}
	if got := laneLine(t, laneRow{Name: "store subprocesses", Bound: 8, Admitted: 483}); strings.Contains(got, "queued") {
		t.Errorf("FAIL: a lane with no queue claims one: %s", got)
	}
}

// heldList returns the in-flight lines the ops page drew, one string
// per item, tags stripped.
func heldList(t *testing.T, lanes ...laneRow) []string {
	t.Helper()
	var d pageData
	d.Ops.BudgetMs = 500
	d.Ops.Lanes = lanes
	var buf strings.Builder
	if err := opsPage.ExecuteTemplate(&buf, "content", d); err != nil {
		t.Fatal(err)
	}
	var out []string
	for _, m := range regexp.MustCompile(`(?s)<li class="ops-held-i[^"]*"[^>]*>(.*?)</li>`).FindAllStringSubmatch(buf.String(), -1) {
		out = append(out, strings.Join(strings.Fields(regexp.MustCompile(`<[^>]*>`).ReplaceAllString(m[1], "")), " "))
	}
	return out
}

// The counters say a lane refused nine thousand callers; only the list
// says WHICH units it is holding. Verb, subject and age, because the
// question a saturated lane raises is "what is in there" and none of
// the three answers it alone.
func TestTheLanePanelNamesWhatItIsHolding(t *testing.T) {
	got := heldList(t, laneRow{Name: "ingest", Bound: 3, Running: 1, Queue: 22, Waiting: 1,
		Items: []laneItem{
			{Verb: "ingest", Subject: "pkgsrc", AgeMs: 94_000, Running: true},
			{Verb: "first ingest", Subject: "tcl", AgeMs: 1200},
		}})
	if len(got) != 2 {
		t.Fatalf("FAIL: drew %d held lines, want 2: %q", len(got), got)
	}
	for _, want := range []string{"ingest", "pkgsrc", "running", "1m 34s"} {
		if !strings.Contains(got[0], want) {
			t.Errorf("FAIL: the running line is missing %q: %s", want, got[0])
		}
	}
	for _, want := range []string{"first ingest", "tcl", "queued", "1s"} {
		if !strings.Contains(got[1], want) {
			t.Errorf("FAIL: the queued line is missing %q: %s", want, got[1])
		}
	}
}

// A queued unit is drawn differently from a running one. They are not
// the same fact: one is work in progress, the other is work the lane
// has not started, and an operator reading a saturated lane is looking
// for exactly that boundary.
func TestQueuedAndRunningAreToldApart(t *testing.T) {
	var d pageData
	d.Ops.BudgetMs = 500
	d.Ops.Lanes = []laneRow{{Name: "ingest", Bound: 1, Running: 1, Queue: 4, Waiting: 1,
		Items: []laneItem{{Verb: "ingest", Subject: "a", Running: true}, {Verb: "ingest", Subject: "b"}}}}
	var buf strings.Builder
	if err := opsPage.ExecuteTemplate(&buf, "content", d); err != nil {
		t.Fatal(err)
	}
	body := buf.String()
	for _, want := range []string{`data-running="true"`, `data-running="false"`, "ops-held-q"} {
		if !strings.Contains(body, want) {
			t.Errorf("FAIL: the panel does not mark %q", want)
		}
	}
}

// An idle lane draws no list at all. An empty <ul> is a promise of
// something to read, and a page of them would bury the one lane that
// actually has something in it.
func TestAnIdleLaneDrawsNoHeldList(t *testing.T) {
	if got := heldList(t, laneRow{Name: "bulk", Bound: 2, Queue: 8}); len(got) != 0 {
		t.Errorf("FAIL: an idle lane drew %q", got)
	}
}

// held is the only place the age is rendered, and the units are chosen
// so the subject beside them stays readable.
func TestHeldChoosesTheUnitAReaderActsOn(t *testing.T) {
	for _, tc := range []struct {
		ms   int64
		want string
	}{{0, "0ms"}, {412, "412ms"}, {1200, "1s"}, {59_999, "59s"}, {60_000, "1m"}, {94_000, "1m 34s"}, {600_000, "10m"}} {
		if got := held(tc.ms); got != tc.want {
			t.Errorf("FAIL: held(%d) = %q, want %q", tc.ms, got, tc.want)
		}
	}
}