package web
import (
"net/url"
"strings"
"testing"
"craftdesign.group/quarry/internal/ci"
)
func chunk(n int, data string) ci.LogChunk {
return ci.LogChunk{Seq: n, Stream: "out", Data: data}
}
// Filtering a log has one job, and a collapsed step hiding the only
// match is that job failing. So a filter opens what it kept and closes
// what it emptied, regardless of how the step ended.
func TestFilterOpensTheStepsThatStillHaveLines(t *testing.T) {
v := &ciRunView{Steps: []ciStepView{
{ID: "test", State: "pass", Chunks: []ci.LogChunk{
chunk(1, "ok internal/web 11.8s"), chunk(2, "PASS")}},
{ID: "build", State: "pass", Chunks: []ci.LogChunk{chunk(3, "go build ./...")}},
}}
v.applyLogView("BUILD", false) // case-insensitive: readers do not shout
if v.Total != 3 {
t.Errorf("Total = %d, want every line counted before filtering", v.Total)
}
if v.Matched != 1 {
t.Errorf("Matched = %d, want 1", v.Matched)
}
if v.Steps[0].Open || len(v.Steps[0].Chunks) != 0 {
t.Error("a step the filter emptied is still open")
}
if !v.Steps[1].Open {
t.Error("the step holding the only match is collapsed — the filter hid what it found")
}
if !v.Filtering() {
t.Error("Filtering() is false while a filter is set")
}
}
// Expand-all opens everything, including the steps a filter emptied —
// it is the reader saying "show me the shape of the whole run".
func TestExpandAllOpensEveryStep(t *testing.T) {
v := &ciRunView{Steps: []ciStepView{{ID: "a"}, {ID: "b"}}}
v.applyLogView("", true)
for _, st := range v.Steps {
if !st.Open {
t.Errorf("step %s stayed closed under open=all", st.ID)
}
}
}
// The stream has to push the view the reader is actually looking at.
// Without the query the first push replaced a filtered log with the
// whole thing, which reads as the filter spontaneously clearing.
func TestStreamKeepsTheReadersFilter(t *testing.T) {
q := url.Values{"q": {"go test"}, "open": {"all"}}
got := logViewQuery(q)
if !strings.Contains(got, "q=go+test") || !strings.Contains(got, "open=all") {
t.Errorf("logViewQuery(%v) = %q, which drops the reader's view", q, got)
}
// Round-trips: what the stream is handed parses back to the same view.
parsed, err := url.ParseQuery(strings.TrimPrefix(got, "?"))
if err != nil {
t.Fatal(err)
}
if f, open := logViewOf(parsed); f != "go test" || !open {
t.Errorf("round trip lost the view: %q %v", f, open)
}
// An unfiltered page adds nothing, so the common URL stays clean.
if logViewQuery(url.Values{}) != "" {
t.Error("an unfiltered view still decorates the stream URL")
}
}