13 KiB
13 KiB
Validation: YiemAgent Agent Framework
Test Architecture Overview
Because YiemAgent is a Julia library (not a web service), validation tests focus on:
- Unit tests: message type conversion, tool execution logic, format extraction
- Integration tests: full agent loop with stubbed LLM and MCP
- Contract tests: response format compatibility with OpenAI/LMStudio specs
- Performance tests: agent loop overhead for sequential tool calls
Every specification rule (SP-XXX), walkthrough journey (WT-XXX), and requirement (FR-XXX) must have a corresponding test.
Test Types Mapping to Source Documents
| Test Type | Source Document | Example Coverage |
|---|---|---|
| Unit tests | 4-specification.md (SP-001..SP-024), src/utils.jl (converters) |
Message type → OpenAI format conversion; tool argument validation |
| Integration tests | 3-walkthrough.md (WT-001..WT-032) |
Full agent loop: prompt → llmCall → tool extract → execute → response |
| Contract tests | 4-specification.md SP-017, SP-018 |
Response format 1 (LMStudio/vLLM) and format 2 (OpenAI) parsed correctly |
| Performance tests | 1-requirements.md FR-003, NFR-101 |
Agent loop overhead <500ms per sequential tool call |
| Penetration tests | 1-requirements.md NFR-301 |
Tool argument injection; MCP spoofing |
| Schema validation | 4-specification.md SP-023, SP-024 |
Required fields, type checking on tool arguments |
Specification-to-Test Execution Trace
Every traceable item — specification rules (SP-XXX), walkthrough journeys (WT-XXX), and requirements (FR-XXX) — must have a runnable test. This trace maps each item to its corresponding test file and test function.
Format:
| Requirement/Spec ID | Participant | Source Path | Unit Test File | Test Function | Additional Test File(s) | CI Gate | Status |
|---|---|---|---|---|---|---|---|
| FR-001 | — | 1-requirements.md:3.1 |
tests/unit/channel-handling.jl |
test_input_channel_priority |
tests/integration/full-agent-loop.jl |
ci/julia-unit.yaml |
TODO |
| FR-002 | — | 1-requirements.md:3.2 |
tests/unit/llm-format-extraction.jl |
test_format1_tool_calls |
tests/contract/response-format.jl |
ci/julia-unit.yaml |
TODO |
| FR-003 | — | 1-requirements.md:3.3 |
tests/integration/tool-call-loop.jl |
test_loop_exits_on_no_tool_calls |
— | ci/julia-integration.yaml |
TODO |
| FR-004 | — | 1-requirements.md:3.4 |
tests/unit/mcp-tool-discovery.jl |
test_tools_list_with_pagination |
tests/contract/mcp-json-rpc.jl |
ci/julia-unit.yaml |
TODO |
| FR-005 | — | 1-requirements.md:3.5 |
tests/unit/tool-hooks.jl |
test_beforeToolCall_blocks |
tests/integration/agent-loop.jl |
ci/julia-unit.yaml |
TODO |
| FR-006 | — | 1-requirements.md:3.6 |
tests/unit/message-conversion.jl |
test_userMessage_to_OpenAI |
— | ci/julia-unit.yaml |
TODO |
| WT-001 | Caller | 3-walkthrough.md:#agent-creation |
integration/agent-creation.jl |
test_agent_construction_and_lifecycle |
— | ci/julia-integration.yaml |
TODO |
| WT-002 | Caller | 3-walkthrough.md:#send-message |
integration/message-flow.jl |
test_promptAgent_and_takeResponse |
— | ci/julia-integration.yaml |
TODO |
| WT-003 | Caller | 3-walkthrough.md:#receive-response |
integration/response-format.jl |
test_output_channel_delivery |
— | ci/julia-integration.yaml |
TODO |
| WT-004 | Caller | 3-walkthrough.md:#follow-up |
integration/followup.jl |
test_followUp_while_busy |
— | ci/julia-integration.yaml |
TODO |
| WT-005 | Caller | 3-walkthrough.md:#stop-agent |
integration/shutdown.jl |
test_stopAgent_channel_cleanup |
— | ci/julia-integration.yaml |
TODO |
| WT-010 | LLM Provider | 3-walkthrough.md:#receive-formatted |
integration/llm-call.jl |
test_formatMsgForLLM_output_structure |
— | ci/julia-integration.yaml |
TODO |
| WT-011 | LLM Provider | 3-walkthrough.md:#response-no-tool-calls |
integration/non-tool-response.jl |
test_lmm_response_no_tool_calls_breaks_loop |
— | ci/julia-integration.yaml |
TODO |
| WT-012 | LLM Provider | 3-walkthrough.md:#response-with-tool |
integration/tool-response.jl |
test_lmm_response_with_tool_calls |
— | ci/julia-integration.yaml |
TODO |
| WT-020 | MCP Server | 3-walkthrough.md:#discover-tools |
unit/mcp-discovery.jl |
test_mcp_tools_list_parsed |
contract/mcp-json-rpc.jl |
ci/julia-unit.yaml |
TODO |
| WT-021 | MCP Server | 3-walkthrough.md:#execute-tool |
unit/mcp-tool-execute.jl |
test_mcp_tools_call_response_parsed |
— | ci/julia-unit.yaml |
TODO |
| WT-022 | MCP Server | 3-walkthrough.md:#mcp-error |
unit/mcp-error-handling.jl |
test_mcp_response_error_returned |
— | ci/julia-unit.yaml |
TODO |
| WT-030 | Error | 3-walkthrough.md:#llm-timeout |
integration/llm-timeout.jl |
test_LLM_timeout_agent_loop_exits |
— | ci/julia-integration.yaml |
TODO |
| WT-031 | Error | 3-walkthrough.md:#tool-throw |
integration/tool-error.jl |
test_tool_execute_throws_returns_error_result |
— | ci/julia-integration.yaml |
TODO |
| WT-032 | Error | 3-walkthrough.md:#beforeToolCall-block |
unit/beforeToolCall-block.jl |
test_beforeToolCall_blocks_execution |
— | ci/julia-unit.yaml |
TODO |
| SP-001 | — | 4-specification.md:#SP-001:userMessage |
unit/message-types.jl |
test_userMessage_struct_default_roles |
— | ci/julia-unit.yaml |
TODO |
| SP-002 | — | 4-specification.md:#SP-002:assistantMessage |
unit/message-types.jl |
test_assistantMessage_struct_default_stopReason_end_turn |
— | ci/julia-unit.yaml |
TODO |
| SP-003 | — | 4-specification.md:#SP-003:assistantMessageToolCall |
unit/message-types.jl |
test_assistantMessageToolCall_struct_default_stopReason_tool_calls |
— | ci/julia-unit.yaml |
TODO |
| SP-004 | — | 4-specification.md:#SP-004:toolResultMessage |
unit/message-types.jl |
test_toolResultMessage_struct_defaults |
— | ci/julia-unit.yaml |
TODO |
| SP-005 | — | 4-specification.md:#SP-005:agentToolCall |
unit/message-types.jl |
test_agentToolCall_type_is_always_function |
— | ci/julia-unit.yaml |
TODO |
| SP-006.1,2,3 | — | 4-specification.md:#SP-006:content-types |
unit/content-types.jl |
test_textContent_imageContent_reasoningContent |
— | ci/julia-unit.yaml |
TODO |
| SP-017 | — | 4-specification.md:#SP-017 |
contract/response-format.jl |
test_lmStudio_vLLM_format_parsed |
— | ci/julia-contract.yaml |
TODO |
| SP-018.1,2,3,4 | — | 4-specification.md:#SP-018 |
contract/response-format.jl |
test_openAI_format_blocks_parsed |
— | ci/julia-contract.yaml |
TODO |
| SP-023 | — | 4-specification.md:#SP-023 |
unit/tool-validation.jl |
test_validateRequiredArgs_missing_fields |
— | ci/julia-unit.yaml |
TODO |
| SP-024 | — | 4-specification.md:#SP-024 |
unit/tool-validation.jl |
test_validateToolArguments_throws_on_missing |
— | ci/julia-unit.yaml |
TODO |
| NFR-101 | — | 1-requirements.md:4.1 |
perf/agent-loop.jl |
perf_agent_loop_overhead_<500ms |
tests/perf/concurrency.jl |
ci/julia-perf.yaml |
TODO |
| NFR-102 | — | 1-requirements.md:4.1 |
perf/mcp-discovery.jl |
perf_mcp_discovery_50_tools_<2s |
— | ci/julia-perf.yaml |
TODO |
| NFR-201 | — | 1-requirements.md:4.2 |
unit/error-handling.jl |
test_agent_loop_catches_and_logs_errors |
integration/llm-timeout.jl |
ci/julia-unit.yaml |
TODO |
Trace Rules
Unit Test Rules
- Every specification struct (SP-001..SP-008, SP-019..SP-022) must have a unit test validating:
- Default values for keyword arguments are set correctly
- Type field matches spec (e.g.,
agentToolCall.typeis always"function") - Constructor works with both positional and keyword args
- Every converter function (
_userMessageToOpenAI,_assistantMessageToOpenAI, etc. SP-011..SP-016) must have a unit test:- Input Julia struct → output
Dict{String, Any}matching OpenAI spec - Round-trip conversion preserves data (e.g.,
textContenttext is preserved)
- Input Julia struct → output
- Every tool validation function (SP-023, SP-024) must have unit tests:
- Missing required field → returns error string (not throws)
- All required present → returns
nothing - Custom
validateRequiredArgshook on tool takes precedence over default
Integration Test Rules
- Every walkthrough journey (WT-XXX) must have at least one integration test:
_agentLoopprocesses messages through the full pipeline with stubbed LLM/MCPpromptAgent→takeResponseround-trip worksfollowUpwhile busy queues tofollowUpChannel, processes after current workstopAgentsends:shutdown, drains channels, closes all 3 channels
- Tool call lifecycle (FR-003, FR-005):
llmCallreturns tool calls → loop callsexecuteToolCalls→ results appended- Tool calls → no tool calls on next
llmCall→ loop exits toolResultBatch.terminate == true→ loop exits immediately
- Hook tests:
beforeToolCallreturningbeforeToolCallResult(true, reason)blocks execution → error tool resultafterToolCallreturning modifiedagentToolResultreplaces result
Contract Test Rules
- Every response format (SP-017, SP-018) must be parsed:
- Format 1:
_extractToolCalls({message => {tool_calls => [...]}})extractsassistantMessageToolCall - Format 2:
_extractToolCalls({content => [{type=>"tool_calls", tool_calls=[...]}}])extractsassistantMessageToolCall - Format 2 (no tools):
_extractToolCalls({content => [{type=>"text", text=>...}]})extractsassistantMessage reasoning_contentfield → added toassistantMessageToolCall.contentapi,provider,model,usage→ copied to message fields
- Format 1:
Performance Test Rules
- NFR-101: Agent loop overhead measurement:
- Stub LLM returns tool call → execute tool (1ms) → stub returns no tool calls → measure elapsed
- Subtract stub costs (llm call 1ms + tool 1ms)
- Overhead = elapsed - stub costs
- Must be <500ms per tool call (p95)
- NFR-102: MCP discovery performance:
- Mock MCP server returns 200 tools (simulated, no real network)
- Measure
_wrap_mcp_tool+registerToolfor all 200 tools - Must complete <2 seconds
Security/Validation Rules
- NFR-301 (input validation):
- Tool arguments with injection patterns (e.g.,
{"city" => "; rm -rf /"}) → passed through to MCP beforeToolCallcan block dangerous tools (e.g.,toolCall.name == "delete_file")
- Tool arguments with injection patterns (e.g.,
- Penetration test: MCP server spoofing
- Attacker sends
{"result" => {"tools" => [{"name" => "malicious", ...}]}}from untrusted MCP endpoint - Agent executes tool via MCP
tools/callwith attacker-controlled arguments - Mitigation:
beforeToolCallhook can block unknown tools; app should validate MCP server URL
- Attacker sends
Gap Check (Validation Review)
Per ASG Framework §6, verify:
- ✓ Every
SP-XXXspecification item has at least one unit test (listed in trace table above) - ✓ Every
WT-XXXwalkthrough step has at least one integration test (listed in trace table above) - ✓ Every NFR threshold has at least one performance test (NFR-101, NFR-102 in trace table)
- ✓ Security NFRs have penetration test scenarios (NFR-301 in trace rules above)
- ✓ Every
SP-017, SP-018response format has contract tests (listed in trace table) - ✓ Every
SP-011..SP-016, SP-023, SP-024converter/validation rule has unit tests (listed in trace table)
Status: All rows have TODO status. Tests must be written before integration into production CI pipeline.
CI Pipeline
# ci/julia-pipeline.yaml (placeholder)
name: Julia YiemAgent CI
on: [push, pull_request]
jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: julia-actions/julia-buildpkg@latest
- run: julia --project=. -e 'using Pkg; Pkg.instantiate()'
- run: julia --project=./tests -e 'using Test; Test.@testset "Unit tests" # include tests/unit/run_tests.jl"'
integration:
runs-on: ubuntu-latest
needs: unit
steps:
- run: julia --project=./tests -e 'using Test; Test.@testset "Integration" # include tests/integration/run_tests.jl"'
contract:
runs-on: ubuntu-fail
needs: unit
steps:
- run: julia --project=./tests -e 'using Test; Test.@testset "Contract" # include tests/contract/run_tests.jl"'
perf:
runs-on: ubuntu-latest
needs: [unit, integration]
steps:
- run: julia --project=./tests -e 'using Test; Test.@testset "Performance" # include tests/perf/run_tests.jl"'
# Fail if NFR-101 or NFR-102 thresholds breached
lint:
runs-on: ubuntu-latest
steps:
- run: julia --eval 'using Aqua; Aqua.test_all(YiemAgent)'
Gap Check: Per ASG §5, every spec item maps to a CI gate. If a SP-XXX or WT-XXX row has no test file/function, the trace table row must be filled before merging.