test(03-02): add failing tests for hook server integration and poll slowdown

- TestHookServerStartsWithDaemon, TestHookServerStopsWithDaemon
- TestHookServerPortBusy (graceful degradation)
- TestPollSlowdown (20s when hooks active, 5s otherwise)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pierre Martin
2026-03-23 19:44:30 +01:00
parent a59fc01933
commit 5f13eb174b

View File

@@ -2,9 +2,13 @@ package main
import (
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
@@ -212,7 +216,128 @@ func newTestDaemon(t *testing.T) *Daemon {
t.Fatalf("labels: %v", err)
}
return NewDaemon(sockPath, procDir, claudeDir, labels)
d := NewDaemon(sockPath, procDir, claudeDir, labels)
d.hookPort = 0 // disable hook server by default in tests
return d
}
func TestHookServerStartsWithDaemon(t *testing.T) {
d := newTestDaemon(t)
// Assign a dynamic port for the hook server
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen: %v", err)
}
port := ln.Addr().(*net.TCPAddr).Port
ln.Close()
d.hookPort = port
if err := d.Start(); err != nil {
t.Fatalf("start: %v", err)
}
defer d.Stop()
// POST to /hook should return 200
url := fmt.Sprintf("http://127.0.0.1:%d/hook", port)
resp, err := http.Post(url, "application/json", nil)
if err != nil {
t.Fatalf("POST /hook: %v", err)
}
defer resp.Body.Close()
// Empty body -> 400 (bad JSON), but the server is alive
// A valid POST should return 200
body := `{"session_id":"test","hook_event_name":"Stop","cwd":"/tmp"}`
resp2, err := http.Post(url, "application/json", io.NopCloser(
io.Reader(strings.NewReader(body)),
))
if err != nil {
t.Fatalf("POST /hook valid: %v", err)
}
defer resp2.Body.Close()
if resp2.StatusCode != http.StatusOK {
t.Errorf("status = %d, want 200", resp2.StatusCode)
}
}
func TestHookServerStopsWithDaemon(t *testing.T) {
d := newTestDaemon(t)
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen: %v", err)
}
port := ln.Addr().(*net.TCPAddr).Port
ln.Close()
d.hookPort = port
if err := d.Start(); err != nil {
t.Fatalf("start: %v", err)
}
d.Stop()
// Give a moment for shutdown
time.Sleep(50 * time.Millisecond)
// Port should no longer be listening
url := fmt.Sprintf("http://127.0.0.1:%d/hook", port)
_, err = http.Post(url, "application/json", nil)
if err == nil {
t.Error("expected connection error after Stop, but POST succeeded")
}
}
func TestHookServerPortBusy(t *testing.T) {
d := newTestDaemon(t)
// Occupy a port
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen: %v", err)
}
defer ln.Close()
port := ln.Addr().(*net.TCPAddr).Port
d.hookPort = port
// Start should succeed despite port being busy (graceful degradation)
if err := d.Start(); err != nil {
t.Fatalf("start should succeed even with port busy: %v", err)
}
defer d.Stop()
}
func TestPollSlowdown(t *testing.T) {
d := newTestDaemon(t)
// No hook received -> default 5s
got := d.currentPollInterval()
if got != 5*time.Second {
t.Errorf("default poll = %v, want 5s", got)
}
// Simulate recent hook
d.mu.Lock()
d.lastHookTime = time.Now()
d.mu.Unlock()
got = d.currentPollInterval()
if got != 20*time.Second {
t.Errorf("after hook poll = %v, want 20s", got)
}
// Simulate hook > 60s ago
d.mu.Lock()
d.lastHookTime = time.Now().Add(-61 * time.Second)
d.mu.Unlock()
got = d.currentPollInterval()
if got != 5*time.Second {
t.Errorf("after 60s poll = %v, want 5s", got)
}
}
func TestDaemonStartStop(t *testing.T) {