docs(test_framework): add comprehensive integration test guide and fix runner blocking issue

- Document exception handling, zero-value BotResult trap, shared adversary bots
- Add offline parsing example using parseServerOutput
- Skip tests gracefully when JARs missing (guard before suite blocks)
- Fix blocking readLine in runner_process.nim: poll with 50ms sleep + atEnd check
  (was preventing timeout enforcement, now blocks correctly during battle)
- Add test task to QBot.nimble and config.nims setup docs to AGENTS.md
- Add debug logging to TestBattleRunner for bot identity tracking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-13 10:54:56 +02:00
parent 2bfa8eb6a5
commit 7e1f1483a4
19 changed files with 281 additions and 6 deletions
+71
View File
@@ -49,3 +49,74 @@ Single-context layout. See `docs/agents/domain.md`.
### NIM
This repsoitory's main language is Nim, when you need to work with nim use the /nim mcp.
---
## Writing tests for a bot
Framework lives at `common_libs/test_framework/`. Full docs: `common_libs/test_framework/README.md`.
### One-liner
```nim
import test_framework/test_framework
let r = runBattle(@[myBotDir, adversaryDir], rounds = 10)
```
`runBattle` blocks, returns `BattleResult`, raises on failure.
### Types
```
BattleResult → bots, rounds: seq[RoundResult], results: seq[BotResult], winners: seq[string]
BotResult → name, totalScore, rank, firstPlaces, survivalCount
RoundResult → round, results: seq[BotRoundResult]
BotRoundResult→ name, score, rank, survived
```
### Shared adversaries
```nim
const tfAdversaries = currentSourcePath().parentDir.parentDir.parentDir /
"common_libs" / "test_framework" / "adversaries"
const sittingDuck = tfAdversaries / "SittingDuck" # passive — does nothing
const oscillator = tfAdversaries / "OscillatorBot" # fights back
```
### Setup (once per garage)
**`tests/config.nims`**
```nim
--path:"../../common_libs" # adjust depth to match garage location in repo
```
**`YourBot.nimble`** test task:
```nim
task test, "Run integration tests":
exec "nim c -r --path:../common_libs tests/test_basic_battle.nim"
```
### Skip when JARs are absent (put before any suite block)
```nim
import std/os
if not existsEnv("TR_SERVER_JAR") or not existsEnv("TR_BATTLE_RUNNER"):
echo "Skipping: TR_SERVER_JAR / TR_BATTLE_RUNNER not set"
quit(0)
```
### Offline parsing (no Java needed)
```nim
import test_framework/battle_result
let r = parseServerOutput(rawJsonLines)
```
### Exceptions
| Exception | Cause |
|---|---|
| `OSError` | Bot compilation failed |
| `IOError` | Battle runner exited non-zero |
| `TimeoutError` | Server startup (15s), compile (30s/bot), or battle runner timeout |