//go:build integration // test_helpers_test.go 是 supervisor_test.go 的共享脚手架: // - notifyRepoStub:满足 notify.Repository(5 方法),让 dispatcher 落库变成空操作 // - slogDiscard:丢弃日志的 logger // - encryptForTest:用与 AgentKindService 相同格式产出加密 env(map → JSON → AES-GCM) // - fakeAgentBinary:构建 cmd/fake-acp-agent 二进制供子进程 spawn // // 集成测试专用,不参与 go test 默认 build。 package acp_test import ( "context" "encoding/json" "io" "log/slog" "os" "os/exec" "path/filepath" "runtime" "testing" "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/yan1h/agent-coding-workflow/internal/infra/notify" ) // notifyRepoStub 实现 notify.Repository 全部 5 方法。supervisor.onExit // 会通过 dispatcher 落库一条 crashed 通知,落库走 stub → 永远成功 + 不存。 type notifyRepoStub struct{} func (notifyRepoStub) Insert(context.Context, notify.Message) error { return nil } func (notifyRepoStub) List(context.Context, uuid.UUID, bool, int) ([]notify.Message, error) { return nil, nil } func (notifyRepoStub) CountUnread(context.Context, uuid.UUID) (int, error) { return 0, nil } func (notifyRepoStub) MarkRead(context.Context, uuid.UUID, uuid.UUID) error { return nil } func (notifyRepoStub) MarkAllRead(context.Context, uuid.UUID) error { return nil } // slogDiscard 返回静音 logger,避免测试输出污染。 func slogDiscard() *slog.Logger { return slog.New(slog.NewTextHandler(io.Discard, nil)) } // encryptForTest 以 AgentKindService 同格式(JSON marshal → AES-GCM)产出 // EncryptedEnv 字节,让 supervisor.decryptEnv 还原为同一 map。 func encryptForTest(t *testing.T, m map[string]string) []byte { t.Helper() enc := testEncryptor(t) plain, err := json.Marshal(m) require.NoError(t, err) out, err := enc.Encrypt(plain) require.NoError(t, err) return out } // repoRoot 从测试 cwd 向上找 go.mod,返回仓库根目录。t.TempDir + go build 用。 func repoRoot(t *testing.T) string { t.Helper() wd, err := os.Getwd() require.NoError(t, err) for { if _, err := os.Stat(filepath.Join(wd, "go.mod")); err == nil { return wd } parent := filepath.Dir(wd) if parent == wd { t.Fatal("go.mod not found upward") } wd = parent } } // fakeAgentBinary 编译 cmd/fake-acp-agent 到 t.TempDir() 并返回路径。 // 测试结束后 t.TempDir 自动清理。 func fakeAgentBinary(t *testing.T) string { t.Helper() bin := filepath.Join(t.TempDir(), "fake-acp-agent") if runtime.GOOS == "windows" { bin += ".exe" } cmd := exec.Command("go", "build", "-o", bin, "./cmd/fake-acp-agent") cmd.Dir = repoRoot(t) out, err := cmd.CombinedOutput() require.NoError(t, err, "build fake-acp-agent: %s", out) return bin }