package zitcli
import (
"os/exec"
"testing"
"time"
"craftdesign.group/quarry/internal/obs"
)
// Every zit subprocess reaches the recorder, however it was started.
//
// The footer's store figure and the /fleet/ops subprocess tally are both
// computed from obs, so a call that never files there is invisible to
// both — and "invisible" reads as "the store was not to blame", which is
// the wrong conclusion stated confidently. The accept route showed it:
// 2.5 seconds, "of which zit: 0ms", on a request that spends half its
// time in a zit merge.
func TestEveryStoreCallReachesTheRecorder(t *testing.T) {
if _, err := exec.LookPath("zit"); err != nil {
t.Skip("zit not on PATH")
}
origin := t.TempDir()
for _, args := range [][]string{{"init", "."}} {
cmd := exec.Command("zit", args...)
cmd.Dir = origin
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("zit %v: %v: %s", args, err, out)
}
}
prev := obs.Default
obs.Default = obs.NewRecorder(50, nil)
defer func() { obs.Default = prev }()
c := New(origin)
// A write, on the path that used to reach exec.Command directly.
// Whether the store accepts it does not matter: the subprocess ran,
// and a subprocess that ran must be one the recorder saw.
_ = c.TagAdd(testToken, "seen-by-the-recorder", "trunk", "", false)
subs := obs.Default.Snapshot().Subprocess
var verbs []string
for _, s := range subs {
verbs = append(verbs, s.Verb)
}
if len(subs) == 0 {
t.Fatalf("FAIL: a write spawned a subprocess and the recorder saw none")
}
found := false
for _, s := range subs {
if s.Verb == "tag" {
found = true
if s.Duration <= 0 {
t.Error("FAIL: the recorded call has no duration")
}
if s.Repo == "" {
t.Error("FAIL: the recorded call names no repository")
}
}
}
if !found {
t.Errorf("FAIL: recorded %v, none of them the tag write", verbs)
}
}
// And because it reaches the recorder, the overlap figure the footer
// uses can see it — which is the whole point. A render that waits on a
// detached call is waiting whether or not the call belongs to it.
func TestADetachedCallIsVisibleToTheFooterFigure(t *testing.T) {
// A recorder of this test's own, never the process-wide one. The
// ring holds a bounded number of entries and the other tests in
// this package spawn real subprocesses into whatever Default points
// at — so a test that installs its own Default and then reads it
// back can have its entry evicted by a neighbour before it looks.
// That is exactly how this test flaked under full-suite load.
rec := obs.NewRecorder(50, nil)
start := time.Now()
// The shape every detached call files: no RenderID, because a
// detached call belongs to no render by construction.
rec.RecordSubprocess(obs.Subprocess{
At: start, Verb: "landing", Repo: "alpha", Duration: 5 * time.Millisecond,
})
if busy := rec.StoreBusy(start.Add(-time.Second), time.Now().Add(time.Second)); busy <= 0 {
t.Fatal("FAIL: a detached call contributes nothing to the store-busy figure")
}
}
// The detached path must not take an admission slot. Admission refuses
// a caller that cannot get one, and a landing merge failing because a
// listing was busy would be a worse bug than the invisibility this
// fixes — bringing writes under the cap is a separate decision.
func TestTheDetachedPathIsNotGated(t *testing.T) {
prev := obs.Default
obs.Default = obs.NewRecorder(50, nil)
defer func() { obs.Default = prev }()
c := New(t.TempDir())
cmd, done := c.unattended("version")
defer done()
if cmd.Err != nil {
t.Errorf("FAIL: the detached path refused with %v; it must not gate", cmd.Err)
}
}