package web
import (
"net/http/httptest"
"strings"
"testing"
"craftdesign.group/quarry/internal/api"
"craftdesign.group/quarry/internal/index"
"craftdesign.group/quarry/internal/ops"
)
// WITHHELD IS NOT ABSENT.
//
// A reader without the CI letter gets the runs blanked (hideCI) and the
// required set left standing. The record page derived "no run yet" from
// that pair — Required minus an emptied Checks is every required check —
// and told the open web, about a check that had run and passed, that it
// never ran (ticket 69603f2900ca). The same landing, the same moment:
// signed in read "pass", anonymous read "waiting · required · no run
// yet".
//
// The fix is one fact carried instead of inferred: the op says the runs
// were withheld, the panel says so, and nothing on it claims a verdict
// either way. Asserted on hooks, not words: the claim is which ROW is
// drawn for this reader.
func hiddenServer(t *testing.T, ix *index.Index, letters string) *httptest.Server {
t.Helper()
r := ops.NewRegistry()
deps := api.Deps{Version: "v",
Repos: api.NewRepos(map[string]*api.Repo{"alpha": {Index: ix}}),
Checks: fakeChecks{required: map[string][]string{"trunk": {"test"}}},
// A green run EXISTS: the point is that the reader is not told
// about it, and is not told it does not exist either.
CI: fakeCI{runs: []api.CIRun{{RunID: "run-1", Workflow: "test", Pipeline: "ci/test.json", Status: "success", Started: "2026-09-21"}}}}
if err := api.RegisterAll(r, deps); err != nil {
t.Fatal(err)
}
srv := httptest.NewServer(HandlerWith(r, nil, nil,
[]Option{WithSessions(fakeSessions{user: "dan"}), WithCaps(stubCaps{letters: letters})}))
t.Cleanup(srv.Close)
return srv
}
func TestARecordPageDoesNotSayNoRunYetToAReaderItHidTheRunsFrom(t *testing.T) {
ix := landingIndex(t, false)
page := "/r/alpha/landing/" + strings.Repeat("a1", 6)
// Without the CI letter: the runs are withheld, and the page says
// THAT rather than manufacturing a verdict from their absence.
body := asDan(t, hiddenServer(t, ix, "i"), page)
for hook, want := range map[string]bool{
`data-hook="checks-hidden"`: true, // says the runs are not shown
`data-hook="check-required-only"`: true, // still names the policy: trunk requires test
`data-hook="check-missing"`: false, // never "no run yet" — nothing is known
`>pass<`: false, // and never the withheld verdict either
} {
if got := strings.Contains(body, hook); got != want {
t.Errorf("FAIL without the CI letter: page contains %s = %v, want %v", hook, got, want)
} else {
t.Logf("ok without the CI letter: %s present=%v", hook, want)
}
}
if !strings.Contains(body, `id="l-checks"`) {
t.Error("FAIL: the checks panel is gone entirely; the required set is policy and should still be drawn")
}
// With the letter: the same page, the same landing, shows the run
// and no hidden row. The fact is per reader, not per landing.
body = asDan(t, hiddenServer(t, ix, "i8"), page)
for hook, want := range map[string]bool{
`data-hook="checks-hidden"`: false,
`>pass<`: true,
"ci/test.json": true,
} {
if got := strings.Contains(body, hook); got != want {
t.Errorf("FAIL with the CI letter: page contains %s = %v, want %v", hook, got, want)
} else {
t.Logf("ok with the CI letter: %s present=%v", hook, want)
}
}
}
// The headline is where the second lie lived. With MissingChecks
// silenced and no rows, the summary's default branch would say "All
// checks have passed" — the same claim facing the other way. A hidden
// reader gets a summary in the hidden state, with no verdict state.
func TestChecksSummaryHasAHiddenStateRatherThanAVerdict(t *testing.T) {
hidden := landingDetail{Required: []string{"test"}, ChecksHidden: true}
s := hidden.ChecksSummary()
if s.State != "hidden" {
t.Errorf("hidden reader's summary state = %q, want hidden — %q is a claim about runs they cannot see", s.State, s.State)
}
if s.Merge || s.Open {
t.Errorf("hidden summary Merge=%v Open=%v, want neither: there is nothing to open onto", s.Merge, s.Open)
}
if got := hidden.MissingChecks(); len(got) != 0 {
t.Errorf("MissingChecks for a hidden reader = %v, want none: Required minus withheld rows is not a fact", got)
}
// Control: the same required set with the rows present and empty
// really is waiting, and says so.
waiting := landingDetail{Required: []string{"test"}}
if s := waiting.ChecksSummary(); s.State != "wait" {
t.Errorf("a reader who CAN see the runs, with none, gets state %q, want wait", s.State)
}
if got := waiting.MissingChecks(); len(got) != 1 {
t.Errorf("MissingChecks with rows visible and empty = %v, want [test]", got)
}
}