package web
import (
"io"
"net/http/httptest"
"regexp"
"strings"
"testing"
"time"
"craftdesign.group/quarry/internal/api"
"craftdesign.group/quarry/internal/index"
"craftdesign.group/quarry/internal/obs"
"craftdesign.group/quarry/internal/ops"
)
// The footer must not under-report a render that waited on the store.
//
// command() records timing.Zit from the context it is GIVEN, and a call
// made on context.Background() carries no timer — so its time landed in
// the ops bucket with nothing to point at, and the panel's own note
// ("ops minus zit is index and OLTP queries") was false while it did.
// A page whose real cost was 6.4s of zit leaves reported "ops 11.09s,
// zit 32.9ms", and three diagnoses were wrong before the subprocess
// tally gave it away.
//
// The fix is not attribution — a detached call belongs to no render by
// construction — but OVERLAP: a render that waits on one is waiting
// whether or not the call is its own, and the footer has to say so.
func TestTheFooterCountsAStoreCallItCannotAttribute(t *testing.T) {
prev := obs.Default
obs.Default = obs.NewRecorder(50, nil)
defer func() { obs.Default = prev }()
ix, err := index.Open(t.TempDir() + "/i.db")
if err != nil {
t.Fatal(err)
}
defer ix.Close()
r := ops.NewRegistry()
if err := api.RegisterAll(r, api.Deps{Version: "v",
Repos: api.NewRepos(map[string]*api.Repo{"test": {Index: ix}})}); err != nil {
t.Fatal(err)
}
srv := httptest.NewServer(Handler(r, nil, nil))
defer srv.Close()
// A subprocess nobody can attribute: no RenderID, running across
// the moment this render happens. This is the shape every write and
// every background feed files.
obs.Default.RecordSubprocess(obs.Subprocess{
At: time.Now().Add(-40 * time.Millisecond), Verb: "landing",
Repo: "test", Duration: 80 * time.Millisecond,
})
resp, err := srv.Client().Get(srv.URL + "/r/test/timeline")
if err != nil {
t.Fatal(err)
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
foot := regexp.MustCompile(`rendered in [^<]*(?:<span class="f-ops">([^<]*)</span>)?`).FindString(string(body))
if foot == "" {
t.Fatal("FAIL: the page carries no timing footer to check")
}
if !strings.Contains(string(body), "store ") {
t.Errorf("FAIL: a render that overlapped an unattributable store call reports no store time.\n footer: %s", foot)
}
}
// And it stays quiet when there is nothing to say: a render that
// overlapped no store work must not invent a figure, or the number
// stops meaning anything.
func TestAQuietRenderReportsNoStoreTime(t *testing.T) {
prev := obs.Default
obs.Default = obs.NewRecorder(50, nil)
defer func() { obs.Default = prev }()
ix, err := index.Open(t.TempDir() + "/i.db")
if err != nil {
t.Fatal(err)
}
defer ix.Close()
r := ops.NewRegistry()
if err := api.RegisterAll(r, api.Deps{Version: "v",
Repos: api.NewRepos(map[string]*api.Repo{"test": {Index: ix}})}); err != nil {
t.Fatal(err)
}
srv := httptest.NewServer(Handler(r, nil, nil))
defer srv.Close()
resp, err := srv.Client().Get(srv.URL + "/r/test/timeline")
if err != nil {
t.Fatal(err)
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if strings.Contains(string(body), "store ") {
t.Error("FAIL: a render that touched no store reports store time anyway")
}
}