package web
import (
"strings"
"testing"
)
// The branch page says which branches are protected and who may join
// them.
//
// G-138, measured by jva from the seat a rule constrains: branch.list
// has carried protected/owners since the letters work and publishes
// them to anyone who may read the list — but the PAGE showed neither,
// so the people a rule binds still learned of it by being refused at
// push time. A refusal that names a capability is only useful if you
// can find out who holds it.
//
// The negative matters as much: an unprotected branch must not grow a
// badge, or the marker means nothing.
func TestTheBranchPageSaysWhichBranchesAreProtected(t *testing.T) {
f := newBranchForge(t, []branchRow{
{Name: "trunk", Tip: strings.Repeat("a", 64), Date: "2026-09-20T10:00:00",
Protected: true, Owners: []string{"jva"}},
{Name: "feature", Tip: strings.Repeat("b", 64), Date: "2026-09-20T11:00:00"},
{Name: "orphan", Tip: strings.Repeat("c", 64), Date: "2026-09-20T12:00:00",
Protected: true},
})
body := f.page(t)
if strings.Count(body, `data-hook="branch-protected"`) != 2 {
t.Errorf("FAIL: want a protected badge on exactly the two protected branches, got %d",
strings.Count(body, `data-hook="branch-protected"`))
}
if !strings.Contains(body, `data-hook="branch-owners"`) {
t.Fatalf("FAIL: the page never names who may join a protected branch")
}
// The owner is named where the constrained developer reads it.
row := body[strings.Index(body, "trunk"):]
if i := strings.Index(row, "feature"); i > 0 {
row = row[:i]
}
if !strings.Contains(row, "jva") {
t.Errorf("FAIL: trunk's owner is not named on trunk's row")
}
// A protected branch with NO owner is the silent-lockout shape and
// has to read as such rather than as an empty list.
if !strings.Contains(body, "nobody may join it") {
t.Errorf("FAIL: a protected branch nobody owns does not say so")
}
}
type branchForge struct{ rows []branchRow }
func newBranchForge(t *testing.T, rows []branchRow) branchForge {
t.Helper()
return branchForge{rows: rows}
}
// page renders the branches template directly: the assertion is about
// what the TEMPLATE does with rows branch.list already returns, and
// standing a whole forge up to prove it would test the op instead.
func (f branchForge) page(t *testing.T) string {
t.Helper()
var sb strings.Builder
closed := 0
for _, b := range f.rows {
if b.Closed {
closed++
}
}
if err := noHooks.template(branchesPage).ExecuteTemplate(&sb, "content",
pageData{Title: "Branches", Repo: "alpha", Branches: f.rows, ClosedCount: closed}); err != nil {
t.Fatal(err)
}
return sb.String()
}