- HookEvent struct parses Claude Code hook JSON payload - processHookEvent maps Notification/Stop/PostToolUse/PreToolUse to State+WaitType - UpdateFromHook creates new entries and manages WaitingSince transitions - SessionInfo.WaitType serialized in JSON with omitempty - 12 tests cover all event mappings, edge cases, and JSON serialization Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
150 lines
3.1 KiB
Go
150 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestRequestMarshal(t *testing.T) {
|
|
req := Request{Action: "list"}
|
|
|
|
data, err := json.Marshal(req)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
|
|
var got Request
|
|
if err := json.Unmarshal(data, &got); err != nil {
|
|
t.Fatalf("unmarshal: %v", err)
|
|
}
|
|
|
|
if got.Action != "list" {
|
|
t.Errorf("action = %q, want %q", got.Action, "list")
|
|
}
|
|
}
|
|
|
|
func TestRequestMarshalWithArgs(t *testing.T) {
|
|
args, _ := json.Marshal(LabelArgs{SessionID: "abc", Label: "review MR"})
|
|
req := Request{Action: "label", Args: args}
|
|
|
|
data, err := json.Marshal(req)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
|
|
var got Request
|
|
if err := json.Unmarshal(data, &got); err != nil {
|
|
t.Fatalf("unmarshal: %v", err)
|
|
}
|
|
|
|
if got.Action != "label" {
|
|
t.Errorf("action = %q, want %q", got.Action, "label")
|
|
}
|
|
|
|
var la LabelArgs
|
|
if err := json.Unmarshal(got.Args, &la); err != nil {
|
|
t.Fatalf("unmarshal args: %v", err)
|
|
}
|
|
if la.SessionID != "abc" || la.Label != "review MR" {
|
|
t.Errorf("args = %+v, want {abc, review MR}", la)
|
|
}
|
|
}
|
|
|
|
func TestResponseWithSessions(t *testing.T) {
|
|
now := time.Now()
|
|
resp := Response{
|
|
OK: true,
|
|
Sessions: []SessionInfo{
|
|
{
|
|
PID: 1234,
|
|
SessionID: "sess-1",
|
|
Cwd: "/home/user/project",
|
|
GitBranch: "main",
|
|
State: "Working",
|
|
Preview: "Building...",
|
|
Workspace: "3",
|
|
Label: "feature-x",
|
|
WaitingSince: &now,
|
|
},
|
|
},
|
|
}
|
|
|
|
data, err := json.Marshal(resp)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
|
|
var got Response
|
|
if err := json.Unmarshal(data, &got); err != nil {
|
|
t.Fatalf("unmarshal: %v", err)
|
|
}
|
|
|
|
if !got.OK {
|
|
t.Error("ok = false, want true")
|
|
}
|
|
if len(got.Sessions) != 1 {
|
|
t.Fatalf("sessions len = %d, want 1", len(got.Sessions))
|
|
}
|
|
|
|
s := got.Sessions[0]
|
|
if s.PID != 1234 {
|
|
t.Errorf("pid = %d, want 1234", s.PID)
|
|
}
|
|
if s.Workspace != "3" {
|
|
t.Errorf("workspace = %q, want %q", s.Workspace, "3")
|
|
}
|
|
if s.Label != "feature-x" {
|
|
t.Errorf("label = %q, want %q", s.Label, "feature-x")
|
|
}
|
|
if s.WaitingSince == nil {
|
|
t.Error("waiting_since = nil, want non-nil")
|
|
}
|
|
}
|
|
|
|
func TestSessionInfoWaitTypeJSON(t *testing.T) {
|
|
// WaitType="permission" should appear in JSON
|
|
info := SessionInfo{
|
|
SessionID: "sess-1",
|
|
State: "Needs Input",
|
|
WaitType: "permission",
|
|
}
|
|
|
|
data, err := json.Marshal(info)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
|
|
var m map[string]interface{}
|
|
if err := json.Unmarshal(data, &m); err != nil {
|
|
t.Fatalf("unmarshal map: %v", err)
|
|
}
|
|
|
|
if wt, ok := m["wait_type"]; !ok {
|
|
t.Error("wait_type field missing from JSON")
|
|
} else if wt != "permission" {
|
|
t.Errorf("wait_type = %v, want %q", wt, "permission")
|
|
}
|
|
|
|
// WaitType="" should be omitted (omitempty)
|
|
info2 := SessionInfo{
|
|
SessionID: "sess-2",
|
|
State: "Working",
|
|
WaitType: "",
|
|
}
|
|
|
|
data2, err := json.Marshal(info2)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
|
|
var m2 map[string]interface{}
|
|
if err := json.Unmarshal(data2, &m2); err != nil {
|
|
t.Fatalf("unmarshal map: %v", err)
|
|
}
|
|
|
|
if _, ok := m2["wait_type"]; ok {
|
|
t.Error("wait_type should be omitted when empty")
|
|
}
|
|
}
|