Repository navigation
Testing
Everything in aikido can be tested without network or API key: a scripted model client, conformance suites for your backends, and an end-to-end suite against a fake OpenRouter.
llmtest.NewStubClient(turns...) is an llm.Client that plays one llmtest.TurnScript per Stream or Complete call and records every request. From llm/llmtest/example_test.go:
stub := llmtest.NewStubClient(
llmtest.TurnScript{Events: []llm.Event{
{Kind: llm.EventToolCall, Tool: &llm.ToolCall{ID: "call-1", Name: "weather", Arguments: `{"city":"Stuttgart"}`}},
{Kind: llm.EventEnd, FinishReason: "tool_calls"},
}},
llmtest.TurnScript{Events: []llm.Event{
{Kind: llm.EventTextDelta, Text: "It is sunny in Stuttgart."},
{Kind: llm.EventEnd, FinishReason: "stop"},
}},
)- The stub adds no
EventEnd: script it. - A call after the last script fails with
llmtest.ErrStubExhaustedand is not recorded;Requests()returns what the code under test sent (model, messages, tools, cache breakpoints). -
TurnScript.Blockholds a turn until the channel is closed orctxends, for timeout and cancellation tests. - The stub also implements the optional capabilities, each with its own scripts and records:
ScriptImages/ImageRequests,ScriptSpeech/SpeechRequests,ScriptVideos/VideoRequests,ScriptDecisions/DecisionRequests.
Script the model's turns, register your real tools over vfs/memory, run the session and assert on events, the store and History. From agent/example_test.go (ExampleSession_RunWithMessages):
client := llmtest.NewStubClient(
llmtest.TurnScript{Events: []llm.Event{
{Kind: llm.EventToolCall, Tool: &llm.ToolCall{ID: "call-1", Name: "clock", Arguments: "{}"}},
{Kind: llm.EventEnd},
}},
llmtest.TurnScript{Events: []llm.Event{
{Kind: llm.EventTextDelta, Text: "It is noon."},
{Kind: llm.EventEnd},
}},
)
session, err := agent.NewLocalSession(&agent.SessionOptions{
ID: "conversation-2", Client: client, Model: "openai/gpt-5.4", Tools: registry,
})Useful assertions: the tool calls and their results (ToolResult.OK and Content of EventToolResult), the final files (storage.ListFiles), what History holds after Run, and client.Requests() for the prompt and tool definitions the model got. A tool handler alone is tested through registry.Dispatch (Tools).
Your own backends run the shared suites in their tests:
-
vfstest.RunConformance(t, func() vfs.Storage { ... })for avfs.Storage; the search sub-tests run when the store isSearchable(Files). -
historytest.RunConformance(t, func() agent.History { ... })for anagent.History, as agent/history/memory/history_test.go does:
historytest.RunConformance(t, func() agent.History { return memory.New() })The factory must return a new, empty instance per call. There is no suite for agent.Locker: test that two runs of the same session ID on different Session values serialize when they share your Locker, and that different IDs run in parallel.
openrouter.Options.BaseURL points the client at an httptest.Server; the examples in llm/openrouter/example_test.go show the minimal SSE and JSON answers. The CLI takes the same root from OPENROUTER_BASE_URL.
-
just test: unit tests with-raceand the 80 % coverage gate. -
just test-e2e: test/e2e runs the builtaikidobinary and the public library API against a fake OpenRouter that speaks the real wire format (build tage2e); skipped when suite and inputs equal a green run. -
just smoke: thesmoke-tagged tests against the live API; needsOPENROUTER_API_KEYand spends money. - Every package's
example_test.goholds runnable examples; the snippets in this wiki are taken from them.
Mirror of docs/wiki at v1.2.0 (dd94d84). Edit the repository, not this wiki.