feat(02-03): main.go subcommands + display enrichi avec workspace, label, duree

- 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
This commit is contained in:
Pierre Martin
2026-03-23 17:54:18 +01:00
parent a79a0e154c
commit 170790fcda
3 changed files with 402 additions and 62 deletions

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"strings"
"testing"
"time"
)
func TestDisplaySessions_WithSessions(t *testing.T) {
@@ -113,3 +114,180 @@ func TestDisplaySessions_NoBranch(t *testing.T) {
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)
}
}
}