go-session/video_test.go
Snider f40caaa593 test: add comprehensive Phase 0 test suite — 67 tests, 90.9% coverage
Add parser_test.go (22 tests), search_test.go (9 tests), html_test.go (6 tests),
video_test.go (12 tests), and bench_test.go (4 benchmarks) covering all Phase 0
TODO items:

- ParseTranscript: minimal JSONL, all 7 tool types, errors, truncated/malformed
  input, large sessions (1100+ events), nested array/map results, mixed content
- ListSessions: empty dir, single/multi sorted, non-JSONL ignored, modtime fallback
- extractToolInput: all tool types plus nil, invalid JSON, unknown tool fallback
- extractResultContent: string, array, map, and other types
- Search: empty dir, no matches, multi-match, case insensitive, output matching
- RenderHTML: basic, empty, errors, XSS escaping, label types, invalid path
- generateTape/extractCommand: all event types, empty/failed commands, truncation
- Benchmarks: 2.2MB and 11MB ParseTranscript, ListSessions, Search with b.Loop()

go vet ./... clean, go test -race ./... clean.

Co-Authored-By: Virgil <virgil@lethean.io>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 05:01:28 +00:00

207 lines
5.4 KiB
Go

// SPDX-Licence-Identifier: EUPL-1.2
package session
import (
"os/exec"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGenerateTape_BasicSession_Good(t *testing.T) {
sess := &Session{
ID: "tape-test-12345678",
StartTime: time.Date(2026, 2, 20, 10, 0, 0, 0, time.UTC),
Events: []Event{
{
Type: "tool_use",
Tool: "Bash",
Input: "go test ./...",
Output: "PASS",
Success: true,
},
{
Type: "tool_use",
Tool: "Read",
Input: "/tmp/file.go",
Output: "package main",
Success: true,
},
},
}
tape := generateTape(sess, "/tmp/output.mp4")
assert.Contains(t, tape, "Output /tmp/output.mp4")
assert.Contains(t, tape, "Set FontSize 16")
assert.Contains(t, tape, "tape-tes") // shortID
assert.Contains(t, tape, "2026-02-20 10:00")
assert.Contains(t, tape, `"$ go test ./..."`)
assert.Contains(t, tape, "PASS")
assert.Contains(t, tape, `"# ✓ OK"`)
assert.Contains(t, tape, "# Read: /tmp/file.go")
}
func TestGenerateTape_SkipsNonToolEvents_Good(t *testing.T) {
sess := &Session{
ID: "skip-test",
StartTime: time.Date(2026, 2, 20, 10, 0, 0, 0, time.UTC),
Events: []Event{
{Type: "user", Input: "Hello"},
{Type: "assistant", Input: "Hi there"},
{Type: "tool_use", Tool: "Bash", Input: "echo hi", Output: "hi", Success: true},
},
}
tape := generateTape(sess, "/tmp/out.mp4")
// User and assistant events should NOT appear in the tape
assert.NotContains(t, tape, "Hello")
assert.NotContains(t, tape, "Hi there")
// Bash command should appear
assert.Contains(t, tape, "echo hi")
}
func TestGenerateTape_FailedCommand_Good(t *testing.T) {
sess := &Session{
ID: "fail-test",
StartTime: time.Date(2026, 2, 20, 10, 0, 0, 0, time.UTC),
Events: []Event{
{
Type: "tool_use",
Tool: "Bash",
Input: "cat /missing",
Output: "No such file",
Success: false,
},
},
}
tape := generateTape(sess, "/tmp/out.mp4")
assert.Contains(t, tape, `"# ✗ FAILED"`)
}
func TestGenerateTape_LongOutput_Good(t *testing.T) {
sess := &Session{
ID: "long-test",
StartTime: time.Date(2026, 2, 20, 10, 0, 0, 0, time.UTC),
Events: []Event{
{
Type: "tool_use",
Tool: "Bash",
Input: "cat huge.log",
Output: strings.Repeat("x", 300),
Success: true,
},
},
}
tape := generateTape(sess, "/tmp/out.mp4")
// Output should be truncated to 200 chars + "..."
assert.Contains(t, tape, "...")
}
func TestGenerateTape_TaskEvent_Good(t *testing.T) {
sess := &Session{
ID: "task-test",
StartTime: time.Date(2026, 2, 20, 10, 0, 0, 0, time.UTC),
Events: []Event{
{
Type: "tool_use",
Tool: "Task",
Input: "[research] Analyse code structure",
},
},
}
tape := generateTape(sess, "/tmp/out.mp4")
assert.Contains(t, tape, "# Agent: [research] Analyse code structure")
}
func TestGenerateTape_EditWriteEvents_Good(t *testing.T) {
sess := &Session{
ID: "edit-test",
StartTime: time.Date(2026, 2, 20, 10, 0, 0, 0, time.UTC),
Events: []Event{
{Type: "tool_use", Tool: "Edit", Input: "/tmp/app.go (edit)"},
{Type: "tool_use", Tool: "Write", Input: "/tmp/new.go (50 bytes)"},
},
}
tape := generateTape(sess, "/tmp/out.mp4")
assert.Contains(t, tape, "# Edit: /tmp/app.go (edit)")
assert.Contains(t, tape, "# Write: /tmp/new.go (50 bytes)")
}
func TestGenerateTape_EmptySession_Good(t *testing.T) {
sess := &Session{
ID: "empty-test",
StartTime: time.Date(2026, 2, 20, 10, 0, 0, 0, time.UTC),
Events: nil,
}
tape := generateTape(sess, "/tmp/out.mp4")
// Should still have the header and trailer
assert.Contains(t, tape, "Output /tmp/out.mp4")
assert.Contains(t, tape, "Sleep 3s")
// No tool events
lines := strings.Split(tape, "\n")
var toolLines int
for _, line := range lines {
if strings.Contains(line, "$ ") || strings.Contains(line, "# Read:") ||
strings.Contains(line, "# Edit:") || strings.Contains(line, "# Write:") {
toolLines++
}
}
assert.Equal(t, 0, toolLines)
}
func TestGenerateTape_BashEmptyCommand_Bad(t *testing.T) {
sess := &Session{
ID: "empty-cmd",
StartTime: time.Date(2026, 2, 20, 10, 0, 0, 0, time.UTC),
Events: []Event{
{Type: "tool_use", Tool: "Bash", Input: "", Output: "", Success: true},
},
}
tape := generateTape(sess, "/tmp/out.mp4")
// Empty command should be skipped (extractCommand returns "")
assert.NotContains(t, tape, `"$ "`)
}
func TestExtractCommand_Good(t *testing.T) {
assert.Equal(t, "ls -la", extractCommand("ls -la # list files"))
assert.Equal(t, "go test ./...", extractCommand("go test ./..."))
assert.Equal(t, "echo hello", extractCommand("echo hello"))
}
func TestExtractCommand_NoDescription_Good(t *testing.T) {
assert.Equal(t, "plain command", extractCommand("plain command"))
}
func TestExtractCommand_DescriptionAtStart_Good(t *testing.T) {
// " # " at position 0 means idx <= 0, so it returns the whole input
result := extractCommand(" # description only")
assert.Equal(t, " # description only", result)
}
func TestRenderMP4_NoVHS_Ugly(t *testing.T) {
// Skip if vhs is actually installed (this tests the error path)
if _, err := exec.LookPath("vhs"); err == nil {
t.Skip("vhs is installed; skipping missing-vhs test")
}
sess := &Session{
ID: "no-vhs",
StartTime: time.Now(),
}
err := RenderMP4(sess, "/tmp/test.mp4")
require.Error(t, err)
assert.Contains(t, err.Error(), "vhs not installed")
}