Test code that calls Jev without a network
Status: draft.
Use this recipe when your code takes a SystemOnePort or an
AsyncSystemOnePort and your tests must run without a key or a network.
judgevet.testing ships two fakes in the installed package. No extra is
needed. FakeSystemOnePort satisfies the synchronous port.
AsyncFakeSystemOnePort satisfies the asynchronous port; its system_one
must be awaited.
Each fake takes seed (default 0) and answers, a mapping from question
name to a scripted answer. A scripted name returns its answer unchanged. Every
other typed question receives a seeded answer that passes the
answer validators:
Noulreceives a probability in[0, 1].Choicereceives probabilities over its criteria labels and selects the most probable label.Scorereceives a legend equal to its criteria, keyed from level zero.
The same seed, state and question give the same answer. A raw mapping
question without a scripted answer raises TypeError. The response model
echoes the model argument. Each call appends (state, questions, model) to
calls.
Each fake also takes two keyword-only arguments. usage sets the Usage that
every response carries; the default is Usage(), which has no token counts.
error sets an exception that every call raises. The error covers the whole
call: the fake appends the call to calls first, then raises the instance you
passed. No option fails one question of a call, because the real adapter never
does.
Each fake takes spend_cap and audit too. They behave as the
HTTP adapter options of the same
name. The fake claims one attempt from the SpendCap before each call. A
refused claim raises JevBudgetExceededError and the call is not recorded in
calls. A successful call settles its usage.input_tokens; a failed call
settles nothing. The audit sink receives one JudgmentRecord per call,
whether the call returned, raised or was refused. A successful record carries
status_code=None, because no HTTP response arrived. A sink failure never
changes the result.
Seeded answers are synthetic. They exercise your code paths. They do not predict what the service would answer.
Synchronous code
Save this program as test_route_ticket.py. Run it with pytest or with
your Python interpreter:
from judgevet import Noul, NoulAnswer
from judgevet.ports import SystemOnePort
from judgevet.testing import FakeSystemOnePort
def route(port: SystemOnePort, ticket: str) -> str:
response = port.system_one(
state=ticket,
questions={"billing": Noul(instructions="Is this about billing?")},
model="jev-1.13.0",
)
return "billing" if response.nouls["billing"].noul >= 0.5 else "general"
def test_route_sends_billing_tickets_to_billing() -> None:
fake = FakeSystemOnePort(answers={"billing": NoulAnswer(noul=0.9)})
assert route(fake, "I was charged twice.") == "billing"
assert fake.calls[0][0] == "I was charged twice."
if __name__ == "__main__":
test_route_sends_billing_tickets_to_billing()
Asynchronous code
Save this program as test_route_ticket_async.py. It runs the coroutine with
asyncio.run, so it needs no async test plugin:
import asyncio
from judgevet import Choice
from judgevet.ports import AsyncSystemOnePort
from judgevet.testing import AsyncFakeSystemOnePort
async def queue_for(port: AsyncSystemOnePort, ticket: str) -> str:
response = await port.system_one(
state=ticket,
questions={"queue": Choice(criteria={"billing": "Money", "bugs": "Defects"})},
model="jev-1.13.0",
)
return response.choices["queue"].choice
def test_queue_is_stable_for_a_seed() -> None:
first = asyncio.run(queue_for(AsyncFakeSystemOnePort(seed=4), "Card declined"))
again = asyncio.run(queue_for(AsyncFakeSystemOnePort(seed=4), "Card declined"))
assert first == again
assert first in {"billing", "bugs"}
if __name__ == "__main__":
test_queue_is_stable_for_a_seed()
Scripted errors
Save this program as test_route_ticket_errors.py. It scripts a rate limit
and checks that the caller falls back:
from judgevet import JevRateLimitError, Noul
from judgevet.ports import SystemOnePort
from judgevet.testing import FakeSystemOnePort
def route(port: SystemOnePort, ticket: str) -> str:
try:
response = port.system_one(
state=ticket,
questions={"billing": Noul(instructions="Is this about billing?")},
model="jev-1.13.0",
)
except JevRateLimitError:
return "retry-later"
return "billing" if response.nouls["billing"].noul >= 0.5 else "general"
def test_route_defers_when_rate_limited() -> None:
fake = FakeSystemOnePort(error=JevRateLimitError("rate limit", 429))
assert route(fake, "I was charged twice.") == "retry-later"
assert len(fake.calls) == 1
if __name__ == "__main__":
test_route_defers_when_rate_limited()
Spend cap and audit
Save this program as test_route_ticket_budget.py. It caps the fake at one
attempt and collects each audit record in a list:
from judgevet import JevBudgetExceededError, JudgmentRecord, Noul, SpendCap
from judgevet.testing import FakeSystemOnePort
class ListSink:
def __init__(self) -> None:
self.records: list[JudgmentRecord] = []
def record(self, record: JudgmentRecord) -> None:
self.records.append(record)
def test_second_call_is_refused_by_the_cap() -> None:
sink = ListSink()
fake = FakeSystemOnePort(spend_cap=SpendCap(max_attempts=1), audit=sink)
questions = {"billing": Noul(instructions="Is this about billing?")}
fake.system_one("I was charged twice.", questions, "jev-1.13.0")
try:
fake.system_one("I was charged twice.", questions, "jev-1.13.0")
except JevBudgetExceededError as error:
assert error.limit == "attempts"
else:
raise AssertionError("the cap did not refuse the second call")
assert len(fake.calls) == 1
assert [record.outcome for record in sink.records] == ["success", "error"]
if __name__ == "__main__":
test_second_call_is_refused_by_the_cap()
The refused call writes the second record. The first call wrote one record.
Both fakes import only the domain, the ports and judgevet.diagnostics. An import-linter contract
forbids them from importing an adapter. Use the
HTTP adapters when a test must reach the service.