test(02-01): add failing tests for protocol types, registry, and label store

- TestRequestMarshal, TestResponseWithSessions
- TestRegistryUpdate, TestRegistryWaitingSince, TestRegistryRemoveStale
- TestLabelStoreSetGet, TestLabelStorePersistence, TestLabelStoreLoadMissing
This commit is contained in:
Pierre Martin
2026-03-23 17:41:58 +01:00
parent 51544cab0b
commit a49f7d1c57
2 changed files with 282 additions and 0 deletions

179
daemon_test.go Normal file
View File

@@ -0,0 +1,179 @@
package main
import (
"os"
"path/filepath"
"testing"
"time"
)
func TestRegistryUpdate(t *testing.T) {
reg := NewRegistry()
info := SessionInfo{
PID: 1234,
SessionID: "sess-1",
Cwd: "/home/user/project",
State: "Working",
}
reg.Update(info)
list := reg.List()
if len(list) != 1 {
t.Fatalf("list len = %d, want 1", len(list))
}
if list[0].SessionID != "sess-1" {
t.Errorf("session_id = %q, want %q", list[0].SessionID, "sess-1")
}
}
func TestRegistryWaitingSince(t *testing.T) {
reg := NewRegistry()
// Session starts Working
reg.Update(SessionInfo{
PID: 1234,
SessionID: "sess-1",
State: "Working",
})
list := reg.List()
if list[0].WaitingSince != nil {
t.Error("WaitingSince should be nil when Working")
}
// Session transitions to NeedsInput
reg.Update(SessionInfo{
PID: 1234,
SessionID: "sess-1",
State: "Needs Input",
})
list = reg.List()
if list[0].WaitingSince == nil {
t.Fatal("WaitingSince should be set when NeedsInput")
}
waitStart := *list[0].WaitingSince
// Session goes back to Working -> WaitingSince reset
reg.Update(SessionInfo{
PID: 1234,
SessionID: "sess-1",
State: "Working",
})
list = reg.List()
if list[0].WaitingSince != nil {
t.Errorf("WaitingSince should be nil after returning to Working, got %v", list[0].WaitingSince)
}
_ = waitStart
}
func TestRegistryRemoveStale(t *testing.T) {
reg := NewRegistry()
reg.Update(SessionInfo{SessionID: "sess-1", State: "Working"})
reg.Update(SessionInfo{SessionID: "sess-2", State: "Working"})
// Only sess-1 is still active
active := map[string]bool{"sess-1": true}
reg.RemoveStale(active)
list := reg.List()
if len(list) != 1 {
t.Fatalf("list len = %d, want 1", len(list))
}
if list[0].SessionID != "sess-1" {
t.Errorf("session_id = %q, want %q", list[0].SessionID, "sess-1")
}
}
func TestLabelStoreSetGet(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "labels.json")
ls, err := NewLabelStore(path)
if err != nil {
t.Fatalf("new: %v", err)
}
if err := ls.Set("sess-1", "review MR"); err != nil {
t.Fatalf("set: %v", err)
}
got := ls.Get("sess-1")
if got != "review MR" {
t.Errorf("get = %q, want %q", got, "review MR")
}
// Non-existent key returns empty
if got := ls.Get("unknown"); got != "" {
t.Errorf("get unknown = %q, want empty", got)
}
}
func TestLabelStorePersistence(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "labels.json")
ls, err := NewLabelStore(path)
if err != nil {
t.Fatalf("new: %v", err)
}
if err := ls.Set("sess-1", "review MR"); err != nil {
t.Fatalf("set: %v", err)
}
// Create a new LabelStore from the same file
ls2, err := NewLabelStore(path)
if err != nil {
t.Fatalf("new2: %v", err)
}
got := ls2.Get("sess-1")
if got != "review MR" {
t.Errorf("persisted get = %q, want %q", got, "review MR")
}
}
func TestLabelStoreLoadMissing(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "nonexistent", "labels.json")
ls, err := NewLabelStore(path)
if err != nil {
t.Fatalf("should not error on missing file: %v", err)
}
if got := ls.Get("anything"); got != "" {
t.Errorf("get = %q, want empty", got)
}
}
func TestRegistryUpdateTimestamp(t *testing.T) {
reg := NewRegistry()
before := time.Now()
reg.Update(SessionInfo{
SessionID: "sess-1",
State: "Needs Input",
})
after := time.Now()
list := reg.List()
if list[0].WaitingSince == nil {
t.Fatal("WaitingSince should be set")
}
ws := *list[0].WaitingSince
if ws.Before(before) || ws.After(after) {
t.Errorf("WaitingSince %v not between %v and %v", ws, before, after)
}
}
// Placeholder to verify file exists
func init() {
_ = os.TempDir()
}

103
protocol_test.go Normal file
View File

@@ -0,0 +1,103 @@
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")
}
}