Skip to content
quarry Artifact [cea556656c]
Login

Artifact [cea556656c]

Artifact cea556656c69fe73ef15d6ab83369a2f326f662449024ea0cdd9ebff0aefda06:


package web

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

// A stack landed in one press: two merges on trunk, the upper branch
// forked from the lower. Shaped like !1069 and !1070.
func stackEntries() []timelineEntry {
	return []timelineEntry{
		{Kind: "ci", Hash: "m2", Date: "2026-10-29T00:10:30", Branch: "trunk", IsMerge: true, Parents: []string{"m1", "f1"},
			Comment: "land agent-follow\n\nChecks tested the merge with trunk at f1e85a3ebe16; trunk has moved since.",
			Landing: "L1070", LandingNumber: 1070, LandingTitle: "A site agent follows its person", LandingApprover: "dmestas"},
		{Kind: "ci", Hash: "m1", Date: "2026-10-29T00:10:10", Branch: "trunk", IsMerge: true, Parents: []string{"t0", "r1"},
			Comment: "land rfc21-reach", Landing: "L1069", LandingNumber: 1069, LandingTitle: "RFC 0021: reach and following", LandingApprover: "dmestas"},
		{Kind: "ci", Hash: "f1", Date: "2026-10-28T23:47:00", Branch: "agent-follow", Parents: []string{"r1"}, Comment: "A site agent follows"},
		{Kind: "ci", Hash: "r1", Date: "2026-10-28T23:39:00", Branch: "rfc21-reach", Parents: []string{"t0"}, Comment: "RFC 0021"},
		{Kind: "ci", Hash: "t0", Date: "2026-10-28T22:38:00", Branch: "trunk", Comment: "land team-room-tab"},
	}
}

func rowOf(days []tlDay, hash string) *tlRow {
	for d := range days {
		for r := range days[d].Rows {
			if days[d].Rows[r].Hash == hash {
				return &days[d].Rows[r]
			}
		}
	}
	return nil
}

func TestALandedStackIsOneCardOnOneLane(t *testing.T) {
	days := buildTimeline("r", "trunk", stackEntries())
	m2, m1, f1, r1 := rowOf(days, "m2"), rowOf(days, "m1"), rowOf(days, "f1"), rowOf(days, "r1")
	if m2 == nil || m1 == nil || f1 == nil || r1 == nil {
		t.Fatal("timeline dropped a row")
	}
	if m2.Stack == nil || m2.Span != 2 || m2.Skip {
		t.Fatalf("FAIL: the newest merge does not carry the stack card: stack=%v span=%d skip=%v", m2.Stack, m2.Span, m2.Skip)
	}
	if !m1.Skip || m1.Stack != nil {
		t.Error("FAIL: the older merge still draws its own card")
	}
	if n := len(m2.Stack.Lines); n != 2 || m2.Stack.Lines[0].Number != 1070 || m2.Stack.Lines[1].Number != 1069 {
		t.Errorf("FAIL: stack lines %+v", m2.Stack.Lines)
	}
	if !strings.Contains(string(m2.Stack.Lines[0].Note), "f1e85a3ebe16") || m2.Stack.Lines[1].Note != "" {
		t.Error("FAIL: the tested-against note is not under its own landing")
	}
	// The card wears the bottom branch's colour; the graph and the
	// branch cards are left as the history draws them.
	if m2.StackTint == "" || m2.StackTint != r1.Tint {
		t.Errorf("FAIL: the card is not tinted as its bottom branch: %q vs %q", m2.StackTint, r1.Tint)
	}
	if !strings.Contains(string(m1.Head)+string(m1.Tail), "path") && !strings.Contains(string(m1.Head), "line") {
		t.Errorf("FAIL: the older merge lost its merge line:\n%s", m1.Head)
	}
	if f1.Branch != "agent-follow" || r1.Branch != "rfc21-reach" {
		t.Errorf("FAIL: a check-in lost its own branch name: %q %q", f1.Branch, r1.Branch)
	}
}

func TestMergesThatAreNotAStackKeepTheirCards(t *testing.T) {
	e := stackEntries()
	e[2].Parents = []string{"t0"} // agent-follow forked from trunk, not from rfc21-reach
	days := buildTimeline("r", "trunk", e)
	if m2 := rowOf(days, "m2"); m2.Stack != nil || m2.Skip {
		t.Error("FAIL: two unrelated landings were drawn as a stack")
	}
	e = stackEntries()
	e[1].LandingApprover = "jva" // decided by somebody else: not one press
	days = buildTimeline("r", "trunk", e)
	if m2 := rowOf(days, "m2"); m2.Stack != nil {
		t.Error("FAIL: merges approved by two people were drawn as one press")
	}
}

func TestAStackCardRendersItsLandingsAndFoldsTheRest(t *testing.T) {
	st := &tlStack{Approver: "dmestas"}
	for i := 0; i < 5; i++ {
		st.Lines = append(st.Lines, tlStackLine{Landing: "L", Number: 1070 + i, Title: "t"})
	}
	row := tlRow{Kind: "ci", Hash: "h", Short: "h", URL: "/u", Stack: st, Span: 5}
	var b bytes.Buffer
	if err := noHooks.template(timelinePage).ExecuteTemplate(&b, "tl-stack", map[string]any{"Repo": "r", "Row": row}); err != nil {
		t.Fatal(err)
	}
	out := b.String()
	if !strings.Contains(out, `data-hook="tl-stack" data-count="5"`) {
		t.Error("FAIL: no stack card")
	}
	if got := strings.Count(out, `data-hook="tl-stack-line"`); got != 5 {
		t.Errorf("FAIL: %d landing lines, want 5", got)
	}
	if !strings.Contains(out, `data-hook="tl-stack-more"`) {
		t.Error("FAIL: the landings past the first three are not folded")
	}
	if !strings.Contains(out, `#i-strata`) {
		t.Error("FAIL: the card has no strata icon")
	}
}