# `CMDCTest.Plugin`
[🔗](https://github.com/tupleyun/cmdc_test/blob/v0.1.0/lib/cmdc_test/plugin.ex#L1)

Plugin 单元测试 helpers。

## API

- `run_hook/3` — 单次调用 plugin `handle_event/3`，返回 action 元组
- `default_ctx/1` — 构造默认 Context（用户可覆盖字段）

## 集成路径 hook 替换（v0.1 推荐 Spy plugin 模式）

v0.1 不提供 `with_mock_plugin` macro，**推荐用 `CMDCTest.Plugin.Spy`**:
把任意 anonymous 函数注入 Agent 作为一个 plugin，无需 macro / :meck。

## Quick Start — 单元测 plugin

    import CMDCTest.Plugin

    test "SecurityGuard 拦截危险 shell 命令" do
      assert {:block_tool, reason, _state} =
               run_hook(
                 CMDC.Plugin.Builtin.SecurityGuard,
                 {:before_tool, "shell", %{"cmd" => "rm -rf /"}}
               )

      assert reason =~ "dangerous"
    end

## Quick Start — 集成路径用 Spy

    {:ok, session} =
      CMDC.create_agent(
        model: "mock:test",
        plugins: [
          {CMDCTest.Plugin.Spy,
           handler: fn event, state, _ctx ->
             send(self(), {:plugin_saw, event})
             {:continue, state}
           end}
        ]
      )

    CMDC.prompt(session, "go")
    assert_receive {:plugin_saw, :session_start}
    assert_receive {:plugin_saw, {:before_prompt, "go"}}

# `default_ctx`

```elixir
@spec default_ctx(keyword()) :: CMDC.Context.t()
```

构造默认 Context。

## 覆盖字段

    default_ctx(session_id: "my-sid", user_data: %{tenant_id: "t-1"})

# `run_hook`

```elixir
@spec run_hook(module(), term(), keyword()) :: {:ok, term()} | {:error, term()}
```

调用 plugin `init/1` + `handle_event/3` 单次，返回 action 元组。

## 选项

- `:init_opts` — 传给 `plugin_module.init/1`，默认 `[]`
- `:ctx` — 自定义 Context（默认用 `default_ctx/1`）

## 返回

- `{:ok, action_tuple}` — 成功调用
- `{:error, reason}` — plugin `init/1` 失败

---

*Consult [api-reference.md](api-reference.md) for complete listing*
