From cb05c363045bf0e7af5858f6d7bc26f026fa9d70 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Fri, 24 Jul 2026 11:39:36 -0300 Subject: --- vrsite/app_test.go | 456 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 vrsite/app_test.go (limited to 'vrsite/app_test.go') diff --git a/vrsite/app_test.go b/vrsite/app_test.go new file mode 100644 index 0000000..c56fb0d --- /dev/null +++ b/vrsite/app_test.go @@ -0,0 +1,456 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "net/url" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" +) + +func logLines(lines ...string) string { return strings.Join(lines, "\n") + "\n" } + +const ( + noteA = `{"time":"2026-07-20T10:00:00Z","op":"note","session":"s1","agent":"codex","model":"gpt-5.6-sol","reasoning":"max","dir":"/repo","change":"aaa","file":"src/lib.rs","start":10,"end":20,"note_id":1,"type":"struct","text":"Cache is a two-level map"}` + noteB = `{"time":"2026-07-25T10:00:00Z","op":"note","session":"s2","agent":"claude","model":"claude-fable-5","reasoning":"xhigh","dir":"/repo","change":"aaa","file":"src/main.rs","note_id":1,"text":"entry point does too much"}` + readA = `{"time":"2026-07-20T09:59:00Z","op":"read","session":"s1","agent":"codex","model":"gpt-5.6-sol","dir":"/repo","change":"aaa","file":"src/lib.rs","start":1,"end":30}` + grepA = `{"time":"2026-07-20T09:58:00Z","op":"grep","session":"s1","agent":"codex","dir":"/repo","change":"aaa","file":"src/lib.rs","pattern":"Cache","matches":3}` +) + +func testSite(t *testing.T) *Site { + t.Helper() + s := buildModel("test", parseLog([]byte(logLines(grepA, readA, noteA, noteB)))) + s.Theme, s.Light = loadTheme("", "", "dark"), loadTheme("", "", "light") + return s +} + +func TestBuildModel(t *testing.T) { + s := testSite(t) + if s.Change != "aaa" { + t.Errorf("site change = %q, want aaa", s.Change) + } + if len(s.Agents) != 2 || s.Agents[0].Bit != 1 || s.Agents[1].Bit != 2 { + t.Errorf("agents = %+v", s.Agents) + } + f := s.Files["src/lib.rs"] + if f == nil || f.Reads != 1 || f.Greps != 1 || len(f.Notes) != 1 { + t.Fatalf("src/lib.rs = %+v", f) + } + if f.Cov[15] != 1 || f.Mask != 1 { + t.Errorf("coverage = %v mask %d", f.Cov, f.Mask) + } + if got := s.Notes[0].When; got == "" { + t.Error("note has no rendered timestamp") + } +} + +// A ninth agent gets no color instead of overflowing the uint8 mask (and the +// per-mask CSS it would generate). +func TestAgentColorsAreBounded(t *testing.T) { + var lines []string + for i := 0; i < maxAgents+3; i++ { + lines = append(lines, `{"time":"2026-07-20T10:00:00Z","op":"note","agent":"a`+string(rune('a'+i))+`","change":"aaa","note_id":1,"text":"x"}`) + } + s := buildModel("t", parseLog([]byte(logLines(lines...)))) + if len(s.Agents) != maxAgents+3 { + t.Fatalf("agents = %d", len(s.Agents)) + } + for i, a := range s.Agents { + if i < maxAgents && a.Bit == 0 { + t.Errorf("agent %d has no bit", i) + } + if i >= maxAgents && a.Bit != 0 { + t.Errorf("agent %d got bit %d, want none", i, a.Bit) + } + } + if n := len(s.masks()); n != 1<