Files
BlackBean/pigo/internal/cli/run/hooks_session_test.go
T
2026-08-14 23:41:57 +08:00

77 lines
2.7 KiB
Go

package run
import (
"context"
"strings"
"testing"
"github.com/smallnest/pigo/internal/hooks"
"github.com/smallnest/pigo/internal/runtime"
)
// sessionDispatcher builds a Dispatcher for a single SessionStart matcher.
func sessionDispatcher(t *testing.T, cmd string) *hooks.Dispatcher {
t.Helper()
set := hooks.HookSet{
"SessionStart": {{Matcher: "*", Hooks: []hooks.HookConfig{{Command: cmd}}}},
}
d := hooks.NewDispatcher(set, t.TempDir(), nil)
if d == nil {
t.Fatal("expected non-nil dispatcher")
}
return d
}
// TestSessionStartInjectsOnce: a SessionStart hook printing additionalContext
// registers a one-shot reminder that fires on the first turn, then goes silent.
func TestSessionStartInjectsOnce(t *testing.T) {
d := sessionDispatcher(t, `echo '{"additionalContext":"project context loaded"}'`)
var cfg runtime.RunConfig
DispatchSessionStart(context.Background(), d, &cfg, HookDeps{SessionID: "s1"}, "startup")
if cfg.Reminders == nil || cfg.Reminders.Empty() {
t.Fatal("SessionStart additionalContext should register a reminder")
}
first := cfg.Reminders.Messages(context.Background(), nil)
if len(first) != 1 {
t.Fatalf("expected 1 reminder message on the first turn, got %d", len(first))
}
if txt := textOfUserMsg(first[0]); !strings.Contains(txt, "project context loaded") {
t.Fatalf("injected context missing, got %q", txt)
}
if second := cfg.Reminders.Messages(context.Background(), nil); len(second) != 0 {
t.Fatalf("one-shot reminder must not fire twice, got %d", len(second))
}
}
// TestSessionStartResumeSource: the source ("resume") is threaded into the hook
// input so the hook can differentiate startup from resume. The hook echoes its
// stdin JSON's source field into additionalContext, which we then observe.
func TestSessionStartResumeSource(t *testing.T) {
d := sessionDispatcher(t, `in=$(cat); case "$in" in *'"source":"resume"'*) echo '{"additionalContext":"source=resume"}';; *) echo '{}';; esac`)
var cfg runtime.RunConfig
DispatchSessionStart(context.Background(), d, &cfg, HookDeps{SessionID: "s1"}, "resume")
if cfg.Reminders == nil || cfg.Reminders.Empty() {
t.Fatal("expected a reminder registered")
}
msgs := cfg.Reminders.Messages(context.Background(), nil)
if len(msgs) != 1 {
t.Fatalf("expected 1 reminder message, got %d", len(msgs))
}
if txt := textOfUserMsg(msgs[0]); !strings.Contains(txt, "source=resume") {
t.Fatalf("resume source not threaded into hook input, got %q", txt)
}
}
// TestSessionStartNilDispatcher: no hooks configured is a no-op.
func TestSessionStartNilDispatcher(t *testing.T) {
var cfg runtime.RunConfig
DispatchSessionStart(context.Background(), nil, &cfg, HookDeps{}, "startup")
if cfg.Reminders != nil {
t.Fatal("nil dispatcher must not allocate a registry")
}
}