Testing the AI subsystem
AI tests must be deterministic and offline. The package ships a testing kit in src so
you never call a live model in CI.
FakeAiResearchProvider
Returns a recorded ResearchResult per counterparty fingerprint (a “cassette”), and counts
calls so you can assert caching.
use Gawrys\Counterparty\Ai\Testing\FakeAiResearchProvider;
use Gawrys\Counterparty\Ai\Research\{ResearchResult, Finding};
$provider = new FakeAiResearchProvider();
$provider->record($counterparty->fingerprint(), new ResearchResult([
new Finding('Named in a 2023 enforcement notice.', 'https://regulator.example/114', 0.82, adverse: true),
new Finding('Unverified forum rumour.', null, 0.3, adverse: false), // ungrounded -> ignored as evidence
], summary: 'One grounded adverse finding.'));
InMemoryCache
A minimal PSR-16 cache for asserting the strategy’s caching behaviour:
use Gawrys\Counterparty\Ai\Testing\InMemoryCache;
$cache = new InMemoryCache();
$strategy = new AiRiskStrategy($provider, new RiskPromptBuilder(), [], $cache);
$strategy->assess($cp, $report);
$strategy->assess($cp, $report);
self::assertSame(1, $provider->calls); // second call served from cache
self::assertSame(1, $cache->writes);
Cassettes from JSON
Record a model response as JSON and load it through the same parser used in production, so a cassette that would not parse in prod cannot silently pass in tests:
use Gawrys\Counterparty\Ai\Testing\Cassette;
$result = (new Cassette())->fromFile(__DIR__ . '/cassettes/adverse-media.json');
{
"summary": "One adverse-media hit located.",
"findings": [
{ "claim": "Named in a regulatory notice.", "source_url": "https://regulator.example/114", "confidence": 0.82, "adverse": true }
]
}
Testing a real provider offline
To test AnthropicResearchProvider / OpenAiResearchProvider (or your own) without the
network, back the JsonHttpClient with a mock PSR-18 client and queue the API’s JSON
response - including tool_use / tool_calls turns to exercise the tool loop. See the
provider tests in counterparty-ai for ready-made examples.