package web
import (
"bytes"
"encoding/json"
"strings"
"testing"
)
// A stack offers to land only what will land. The walk accepts each
// rung in turn and an accept refuses a rung whose required checks are
// not green, so a Land stack button over a waiting base landed nothing
// and said the rung would not merge. While a rung waits, the stack says
// which and for what, its green verdict is not shown, and the control
// lands the rungs below it or is not drawn.
func TestAStackOffersToLandOnlyWhatWillLand(t *testing.T) {
ids := []string{strings.Repeat("a1", 32), strings.Repeat("b2", 32), strings.Repeat("c3", 32)}
page := func(rungs, here int, stack string) string {
t.Helper()
var sv stackView
if err := json.Unmarshal([]byte(stack), &sv); err != nil {
t.Fatal(err)
}
sv.Segments = sv.Segments[:rungs]
for i := range sv.Segments {
sv.Segments[i].ID, sv.Segments[i].Position, sv.Segments[i].State = ids[i], i+1, "open"
sv.Segments[i].Branch = "b" + string(rune('1'+i))
}
sv.Top, sv.Base, sv.CI = ids[rungs-1], ids[0], "ci-pass"
d := landingDetail{
landingRow: landingRow{ID: ids[here-1], Branch: sv.Segments[here-1].Branch, Target: "trunk", State: "open"},
CanAccept: true, Mergeable: "clean", Stack: sv, Position: here,
}
if here > 1 {
d.Mergeable = "stacked"
}
var buf bytes.Buffer
if err := noHooks.template(landingPage).Execute(&buf, pageData{Title: "Landing", Repo: "quarry",
Section: "landings", Landing: &d}); err != nil {
t.Fatal(err)
}
return buf.String()
}
segs := `"segments":[{"preflight":"clean"},{"preflight":"clean"},{"preflight":"clean"}]`
// The base waits for its checks: nothing lands from the top.
top := page(2, 2, `{"preflight":"lands","landTo":"","landCount":0,"waitsAt":1,`+
`"segments":[{"preflight":"clean","waiting":["quarry","race"]},{"preflight":"clean"}]}`)
if strings.Contains(top, `/land"`) {
t.Error("FAIL: a stack whose base waits still offers to land")
}
if !strings.Contains(top, `data-hook="stack-blocked"`) || strings.Count(top, "waits for checks") != 1 {
t.Error("FAIL: the stack does not say once which rung waits")
}
if !strings.Contains(top, `data-hook="rung-waiting"`) || !strings.Contains(top, "<code>race</code>") {
t.Error("FAIL: the waiting rung does not name its checks")
}
if m := top[strings.Index(top, `data-hook="strata-map"`):]; strings.Contains(m[:strings.Index(m, "</p>")], "ci-pass") {
t.Error("FAIL: a waiting stack still shows a green verdict")
}
// Every rung green: the top lands up to itself, the whole stack.
green := page(2, 2, `{"preflight":"lands","landTo":"`+ids[1]+`","landCount":2,`+segs+`}`)
if !strings.Contains(green, `data-hook="land-stack" data-count="2" data-accept="stacked" formaction="/r/quarry/landing/`+ids[1][:12]+`/land"`) {
t.Error("FAIL: a green stack does not offer to land from its top")
}
// The third rung waits: the base lands the two below it.
part := page(3, 1, `{"preflight":"lands","landTo":"`+ids[1]+`","landCount":2,"waitsAt":3,`+segs+`}`)
if !strings.Contains(part, `data-hook="land-up-to" data-count="2" formaction="/r/quarry/landing/`+ids[1][:12]+`/land"`) {
t.Error("FAIL: the base does not offer to land the rungs below the waiting one")
}
if !strings.Contains(part, `data-hook="strata-waits" data-at="3"`) {
t.Error("FAIL: a stack landing in part does not say which rung waits")
}
// From the second rung of the same stack: up to itself, two rungs.
mid := page(3, 2, `{"preflight":"lands","landTo":"`+ids[2]+`","landCount":3,`+segs+`}`)
if !strings.Contains(mid, `data-count="2" data-accept="stacked" formaction="/r/quarry/landing/`+ids[1][:12]+`/land"`) {
t.Error("FAIL: a middle rung lands past itself")
}
}