Artifact 7d9376f9579f705b2deb12642fb8ced610dff5a37742e85fb6af4ac686c25276:
- File
internal/obs/obs_test.go
— part of check-in
[5229b05522]
at
2026-08-25 19:02:08
on branch trunk
— Add internal/obs, which trunk imports but did not carry
Check-in 2cacce9b01aa added obs.Default.Spawned, RecordSubprocess, RecordRender and Snapshot calls across zitcli, web and api, but the package itself was never added — so trunk did not compile at all.
The files are the ones from the authoring session's tree, unmodified, tests included. Nothing here is my design; this only makes the import resolve so the tree builds again. (user: dmestas, size: 3635) [annotate] [blame] [check-ins using]
package obs import ( "testing" "time" ) // The number that made this package necessary is CONCURRENCY, not // duration: one slow listing is a performance note, and thirteen of the // same listing at once is an incident. Live must count what is running, // and peak must survive them finishing — otherwise the page shows zero // after the fire is out and the operator learns nothing. func TestLiveAndPeakSubprocessCount(t *testing.T) { r := NewRecorder(10, nil) for range 13 { r.Spawned() } if s := r.Snapshot(); s.LiveSubproc != 13 || s.PeakSubproc != 13 { t.Fatalf("during: live=%d peak=%d, want 13/13", s.LiveSubproc, s.PeakSubproc) } for range 13 { r.RecordSubprocess(Subprocess{Verb: "ls", Duration: time.Second}) } s := r.Snapshot() if s.LiveSubproc != 0 { t.Errorf("after: live=%d, want 0", s.LiveSubproc) } if s.PeakSubproc != 13 { t.Errorf("after: peak=%d, want it to survive — the evidence outlives the incident", s.PeakSubproc) } } // A recorder that never goes negative, however the pairing breaks. A // subprocess recorded without a matching Spawned is a bug, but it must // not make the panel show a nonsense number. func TestLiveNeverGoesNegative(t *testing.T) { r := NewRecorder(4, nil) r.RecordSubprocess(Subprocess{Verb: "cat"}) if s := r.Snapshot(); s.LiveSubproc != 0 { t.Errorf("live=%d, want 0", s.LiveSubproc) } } // Slowest first by p95, which is the question the page answers. A mean // would have hidden the 4.24s files page behind hundreds of 30ms ones. func TestPathsRankByTailNotAverage(t *testing.T) { r := NewRecorder(500, nil) // A path that is usually fast and occasionally terrible. for range 99 { r.RecordRender(Render{Path: "/r/{repo}", Elapsed: 30 * time.Millisecond}) } for range 20 { r.RecordRender(Render{Path: "/r/{repo}", Elapsed: 4 * time.Second}) } // A path that is uniformly middling — a higher MEAN is not the point. for range 60 { r.RecordRender(Render{Path: "/timeline", Elapsed: 300 * time.Millisecond}) } s := r.Snapshot() if len(s.Paths) != 2 { t.Fatalf("paths = %d, want 2", len(s.Paths)) } if s.Paths[0].Path != "/r/{repo}" { t.Errorf("first = %q, want the one with the bad tail", s.Paths[0].Path) } if s.Paths[0].P95 < time.Second { t.Errorf("p95 = %v, want it to reflect the slow tail", s.Paths[0].P95) } if s.Paths[0].Max != 4*time.Second { t.Errorf("max = %v, want 4s", s.Paths[0].Max) } if s.Paths[0].N != 119 { t.Errorf("n = %d, want every render counted even though samples are windowed", s.Paths[0].N) } } // The sink is where history goes; the rings are what the page reads. // A forge with no analytics store must still fill its panels. func TestRingsWorkWithoutASink(t *testing.T) { r := NewRecorder(3, nil) r.RecordRender(Render{Path: "/a", Elapsed: time.Millisecond}) if s := r.Snapshot(); len(s.Renders) != 1 { t.Fatalf("renders = %d, want 1 with a nil sink", len(s.Renders)) } } type capture struct{ renders, subs int } func (c *capture) Render(Render) { c.renders++ } func (c *capture) Subprocess(Subprocess) { c.subs++ } func TestSinkReceivesEverythingTheRingsDo(t *testing.T) { c := &capture{} r := NewRecorder(2, c) // More than the ring holds: the ring is a window, the sink is not. for range 5 { r.RecordRender(Render{Path: "/a"}) r.Spawned() r.RecordSubprocess(Subprocess{Verb: "ls"}) } if c.renders != 5 || c.subs != 5 { t.Errorf("sink saw %d renders / %d subs, want 5/5 — the window must not truncate history", c.renders, c.subs) } if s := r.Snapshot(); len(s.Renders) != 2 { t.Errorf("ring held %d, want its cap of 2", len(s.Renders)) } }