- main.go dispatch list/switch/label/stop/daemon via client socket - DisplaySessionInfos affiche [ws:N], label entre guillemets, "depuis X min" - formatDuration pour duree relative humaine (< 1 min, 3 min, 1 h 5 min) - Daemon mode integre i3 + X11 avec degradation gracieuse - Tests complets pour tous les champs optionnels du display
294 lines
6.4 KiB
Go
294 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestDisplaySessions_WithSessions(t *testing.T) {
|
|
sessions := []Session{
|
|
{
|
|
Process: Process{PID: 100, Cwd: "/home/user/project-a"},
|
|
State: Working,
|
|
CwdPath: "/home/user/project-a",
|
|
GitBranch: "feat-login",
|
|
Preview: "Implementing auth...",
|
|
},
|
|
{
|
|
Process: Process{PID: 200, Cwd: "/home/user/project-b"},
|
|
State: NeedsInput,
|
|
CwdPath: "/home/user/project-b",
|
|
GitBranch: "main",
|
|
Preview: "What should I do next?",
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
DisplaySessions(&buf, sessions, false)
|
|
output := buf.String()
|
|
|
|
for _, want := range []string{
|
|
"/home/user/project-a",
|
|
"feat-login",
|
|
"Working",
|
|
"Implementing auth...",
|
|
"/home/user/project-b",
|
|
"main",
|
|
"Needs Input",
|
|
"What should I do next?",
|
|
} {
|
|
if !strings.Contains(output, want) {
|
|
t.Errorf("output missing %q", want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDisplaySessions_NoColor(t *testing.T) {
|
|
sessions := []Session{
|
|
{
|
|
Process: Process{PID: 100, Cwd: "/tmp"},
|
|
State: Working,
|
|
CwdPath: "/tmp",
|
|
GitBranch: "main",
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
DisplaySessions(&buf, sessions, true)
|
|
output := buf.String()
|
|
|
|
if strings.Contains(output, "\033") {
|
|
t.Errorf("noColor=true but output contains ANSI escape: %q", output)
|
|
}
|
|
if !strings.Contains(output, "Working") {
|
|
t.Error("output missing state label")
|
|
}
|
|
}
|
|
|
|
func TestDisplaySessions_Empty(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
DisplaySessions(&buf, nil, false)
|
|
output := buf.String()
|
|
|
|
want := "No active Claude Code sessions found.\n"
|
|
if output != want {
|
|
t.Errorf("output = %q, want %q", output, want)
|
|
}
|
|
}
|
|
|
|
func TestDisplaySessions_WorktreeDiffers(t *testing.T) {
|
|
sessions := []Session{
|
|
{
|
|
Process: Process{PID: 100, Cwd: "/home/user/repo/src"},
|
|
State: Working,
|
|
CwdPath: "/home/user/repo/src",
|
|
Worktree: "/home/user/repo",
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
DisplaySessions(&buf, sessions, true)
|
|
output := buf.String()
|
|
|
|
if !strings.Contains(output, "[worktree: /home/user/repo]") {
|
|
t.Errorf("output should show worktree when different from cwd: %q", output)
|
|
}
|
|
}
|
|
|
|
func TestDisplaySessions_NoBranch(t *testing.T) {
|
|
sessions := []Session{
|
|
{
|
|
Process: Process{PID: 100, Cwd: "/tmp"},
|
|
State: Unknown,
|
|
CwdPath: "/tmp",
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
DisplaySessions(&buf, sessions, true)
|
|
output := buf.String()
|
|
|
|
if strings.Contains(output, "(") {
|
|
t.Errorf("no branch should mean no parentheses: %q", output)
|
|
}
|
|
}
|
|
|
|
// --- DisplaySessionInfos tests ---
|
|
|
|
func TestDisplayWithWorkspace(t *testing.T) {
|
|
sessions := []SessionInfo{
|
|
{
|
|
PID: 100,
|
|
SessionID: "sess-1",
|
|
Cwd: "/home/pierre/Code/vibe/vmux",
|
|
GitBranch: "feat-auth",
|
|
State: "Working",
|
|
Workspace: "3",
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
DisplaySessionInfos(&buf, sessions, true, time.Now())
|
|
output := buf.String()
|
|
|
|
if !strings.Contains(output, "[ws:3]") {
|
|
t.Errorf("output should contain [ws:3]: %q", output)
|
|
}
|
|
}
|
|
|
|
func TestDisplayWithLabel(t *testing.T) {
|
|
sessions := []SessionInfo{
|
|
{
|
|
PID: 100,
|
|
SessionID: "sess-1",
|
|
Cwd: "/home/user/project",
|
|
State: "Working",
|
|
Label: "review MR",
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
DisplaySessionInfos(&buf, sessions, true, time.Now())
|
|
output := buf.String()
|
|
|
|
if !strings.Contains(output, `"review MR"`) {
|
|
t.Errorf("output should contain quoted label: %q", output)
|
|
}
|
|
}
|
|
|
|
func TestDisplayWithWaitingSince(t *testing.T) {
|
|
threeMinAgo := time.Now().Add(-3 * time.Minute)
|
|
sessions := []SessionInfo{
|
|
{
|
|
PID: 100,
|
|
SessionID: "sess-1",
|
|
Cwd: "/home/user/project",
|
|
State: "Needs Input",
|
|
WaitingSince: &threeMinAgo,
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
now := time.Now()
|
|
DisplaySessionInfos(&buf, sessions, true, now)
|
|
output := buf.String()
|
|
|
|
if !strings.Contains(output, "depuis 3 min") {
|
|
t.Errorf("output should contain waiting duration: %q", output)
|
|
}
|
|
}
|
|
|
|
func TestDisplayWithoutOptionalFields(t *testing.T) {
|
|
sessions := []SessionInfo{
|
|
{
|
|
PID: 100,
|
|
SessionID: "sess-1",
|
|
Cwd: "/home/user/project",
|
|
State: "Working",
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
DisplaySessionInfos(&buf, sessions, true, time.Now())
|
|
output := buf.String()
|
|
|
|
if strings.Contains(output, "[ws:") {
|
|
t.Errorf("should not show workspace when empty: %q", output)
|
|
}
|
|
if strings.Contains(output, `"`) {
|
|
t.Errorf("should not show quotes when no label: %q", output)
|
|
}
|
|
if strings.Contains(output, "depuis") {
|
|
t.Errorf("should not show waiting when nil: %q", output)
|
|
}
|
|
}
|
|
|
|
func TestDisplaySessionInfosEmpty(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
DisplaySessionInfos(&buf, nil, false, time.Now())
|
|
output := buf.String()
|
|
|
|
want := "No active Claude Code sessions found.\n"
|
|
if output != want {
|
|
t.Errorf("output = %q, want %q", output, want)
|
|
}
|
|
}
|
|
|
|
func TestDisplayWithPreview(t *testing.T) {
|
|
sessions := []SessionInfo{
|
|
{
|
|
PID: 100,
|
|
SessionID: "sess-1",
|
|
Cwd: "/home/user/project",
|
|
State: "Needs Input",
|
|
Preview: "What should I do?",
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
DisplaySessionInfos(&buf, sessions, true, time.Now())
|
|
output := buf.String()
|
|
|
|
if !strings.Contains(output, "What should I do?") {
|
|
t.Errorf("output should contain preview: %q", output)
|
|
}
|
|
}
|
|
|
|
func TestFormatDuration(t *testing.T) {
|
|
tests := []struct {
|
|
d time.Duration
|
|
want string
|
|
}{
|
|
{30 * time.Second, "< 1 min"},
|
|
{3 * time.Minute, "3 min"},
|
|
{65 * time.Minute, "1 h 5 min"},
|
|
{2 * time.Hour, "2 h"},
|
|
{2*time.Hour + 30*time.Minute, "2 h 30 min"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := formatDuration(tt.d)
|
|
if got != tt.want {
|
|
t.Errorf("formatDuration(%v) = %q, want %q", tt.d, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDisplayFullLine(t *testing.T) {
|
|
threeMinAgo := time.Now().Add(-3 * time.Minute)
|
|
sessions := []SessionInfo{
|
|
{
|
|
PID: 100,
|
|
SessionID: "sess-1",
|
|
Cwd: "/home/pierre/Code/vibe/vmux",
|
|
GitBranch: "feat/auth",
|
|
State: "Needs Input",
|
|
Workspace: "3",
|
|
Label: "review MR !456",
|
|
WaitingSince: &threeMinAgo,
|
|
Preview: "preview line 1\npreview line 2",
|
|
},
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
DisplaySessionInfos(&buf, sessions, true, time.Now())
|
|
output := buf.String()
|
|
|
|
for _, want := range []string{
|
|
"[Needs Input]",
|
|
"/home/pierre/Code/vibe/vmux",
|
|
"(feat/auth)",
|
|
"[ws:3]",
|
|
`"review MR !456"`,
|
|
"(depuis 3 min)",
|
|
"preview line 1",
|
|
"preview line 2",
|
|
} {
|
|
if !strings.Contains(output, want) {
|
|
t.Errorf("output missing %q in: %q", want, output)
|
|
}
|
|
}
|
|
}
|