judgevet.testing
Status: draft.
judgevet.testing
Offline fakes of the System One ports for caller tests.
FakeSystemOnePort satisfies judgevet.ports.SystemOnePort and
AsyncFakeSystemOnePort satisfies judgevet.ports.AsyncSystemOnePort. Both
make no network call. A scripted answer is returned for its question name.
Every other typed question receives a seeded answer that passes the domain
answer validators. The same seed, state and question give the same answer.
A scripted Choice answer whose option is outside its question's criteria
raises JevResponseError with status 200, as the HTTP adapters do.
Seeded values are synthetic. They exercise caller code paths; they do not
predict what the service would answer. The returned model echoes the model
argument.
Each fake takes six keyword-only arguments. Two script the whole call and four match the HTTP adapter arguments of the same name:
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
usage
|
Usage | None
|
The |
required |
error
|
BaseException | None
|
An exception every call raises. The fake
records the call in |
required |
spend_cap
|
SpendCap | None
|
A cap the fake claims one attempt from before
each call. A refused claim raises |
required |
audit
|
AuditSink | None
|
A sink that receives one |
required |
fingerprint_key
|
bytes | None
|
A caller-held key. With it, each record
carries the keyed |
required |
profile
|
ProviderProfile | None
|
Opt-in server limits. A breach raises
|
required |
Examples:
from judgevet.domain.answers import NoulAnswer
from judgevet.domain.questions import Choice, Noul
from judgevet.testing import FakeSystemOnePort
fake = FakeSystemOnePort(seed=3, answers={"billing": NoulAnswer(noul=0.9)})
response = fake.system_one(
state="I was charged twice.",
questions={
"billing": Noul(instructions="Is this about billing?"),
"queue": Choice(criteria={"billing": "Money", "technical": "Bugs"}),
},
model="jev-1.13.0",
)
assert response.nouls["billing"].noul == 0.9
assert response.choices["queue"].choice in {"billing", "technical"}
assert len(fake.calls) == 1
from judgevet.domain.errors import JevRateLimitError
from judgevet.domain.usage import Usage
metered = FakeSystemOnePort(usage=Usage(input_tokens=12, output_tokens=3))
assert metered.system_one("text", {"q": Noul()}, "m").usage.input_tokens == 12
limited = FakeSystemOnePort(error=JevRateLimitError("slow down", 429))
try:
limited.system_one("text", {"q": Noul()}, "m")
except JevRateLimitError as error:
assert error.status_code == 429
else:
raise AssertionError("the scripted error was not raised")
assert len(limited.calls) == 1
from judgevet.domain.errors import JevBudgetExceededError
from judgevet.domain.spend import SpendCap
class ListSink:
def __init__(self) -> None:
self.records = []
def record(self, record) -> None:
self.records.append(record)
sink = ListSink()
capped = FakeSystemOnePort(spend_cap=SpendCap(max_attempts=1), audit=sink)
capped.system_one("text", {"q": Noul()}, "m")
try:
capped.system_one("text", {"q": Noul()}, "m")
except JevBudgetExceededError as error:
assert error.limit == "attempts"
else:
raise AssertionError("the spend cap did not refuse the second call")
assert [r.outcome for r in sink.records] == ["success", "error"]
assert len(capped.calls) == 1
See Also
- judgevet.ports: The protocols these fakes satisfy
- judgevet.domain.answers: The answer types and their validators
- judgevet.domain.choice_options: The off-list Choice check both fakes run
- judgevet.domain.questions: The typed questions the fakes answer
- judgevet.domain.spend: The cap both fakes accept as
spend_cap= - judgevet.domain.audit: The record both fakes write to
audit= - judgevet.testing.conformance: The provider conformance kit, which
needs the
conformanceextra
Attributes:
| Name | Type | Description |
|---|---|---|
FakeSystemOnePort |
type
|
Synchronous offline fake of |
AsyncFakeSystemOnePort |
type
|
Asynchronous offline fake of
|
AsyncFakeSystemOnePort
Bases: _FakeCore
Asynchronous offline fake of judgevet.ports.AsyncSystemOnePort.
Attributes:
| Name | Type | Description |
|---|---|---|
seed |
int
|
The seed mixed into every generated answer. |
answers |
dict[str, Answer]
|
Scripted answers keyed by question name. |
usage |
Usage
|
The usage every response carries. |
error |
BaseException | None
|
The exception every call raises after it is recorded, or None to answer normally. |
spend_cap |
SpendCap | None
|
The cap claimed before each call. |
audit |
AuditSink | None
|
The sink that receives one record per call. |
fingerprint_key |
bytes | None
|
The key for each record's state fingerprint, or None for no fingerprint. |
profile |
ProviderProfile | None
|
The limits checked before each call, or None for no check. |
calls |
list[Call]
|
Each call's (state, questions, model) in order. The questions mapping is copied, so later caller edits do not change the record. |
Examples:
import anyio
from judgevet.domain.questions import Noul
async def main() -> float:
fake = AsyncFakeSystemOnePort(seed=1)
response = await fake.system_one("text", {"q": Noul()}, "jev-1.13.0")
return response.nouls["q"].noul
assert 0.0 <= anyio.run(main) <= 1.0
See Also
- judgevet.testing.FakeSystemOnePort: The synchronous twin
system_one(state, questions, model)
async
Record the call and answer every question without a network call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
str | dict[str, Any] | list[Any]
|
The content the caller would judge. |
required |
questions
|
Mapping[str, Question | Mapping[str, Any]]
|
Question names mapped to typed or raw questions. |
required |
model
|
str
|
The model name, echoed into the response. |
required |
Returns:
| Type | Description |
|---|---|
SystemOneResponse
|
A response with one answer per question name and the scripted |
SystemOneResponse
|
usage. |
Raises:
| Type | Description |
|---|---|
ProviderRequestError
|
If the profile refuses the questions. |
JevBudgetExceededError
|
If the spend cap refuses the call. |
BaseException
|
The scripted error, after the call is recorded. |
FakeSystemOnePort
Bases: _FakeCore
Synchronous offline fake of judgevet.ports.SystemOnePort.
Attributes:
| Name | Type | Description |
|---|---|---|
seed |
int
|
The seed mixed into every generated answer. |
answers |
dict[str, Answer]
|
Scripted answers keyed by question name. |
usage |
Usage
|
The usage every response carries. |
error |
BaseException | None
|
The exception every call raises after it is recorded, or None to answer normally. |
spend_cap |
SpendCap | None
|
The cap claimed before each call. |
audit |
AuditSink | None
|
The sink that receives one record per call. |
fingerprint_key |
bytes | None
|
The key for each record's state fingerprint, or None for no fingerprint. |
profile |
ProviderProfile | None
|
The limits checked before each call, or None for no check. |
calls |
list[Call]
|
Each call's (state, questions, model) in order. The questions mapping is copied, so later caller edits do not change the record. |
Examples:
from judgevet.domain.questions import Score
from judgevet.ports import SystemOnePort
port: SystemOnePort = FakeSystemOnePort(seed=1)
response = port.system_one(
"text", {"tone": Score(criteria=["cold", "warm"])}, "jev-1.13.0"
)
assert response.scores["tone"].legend == {0: "cold", 1: "warm"}
See Also
- judgevet.testing.AsyncFakeSystemOnePort: The awaitable twin
system_one(state, questions, model)
Record the call and answer every question without a network call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
str | dict[str, Any] | list[Any]
|
The content the caller would judge. |
required |
questions
|
Mapping[str, Question | Mapping[str, Any]]
|
Question names mapped to typed or raw questions. |
required |
model
|
str
|
The model name, echoed into the response. |
required |
Returns:
| Type | Description |
|---|---|
SystemOneResponse
|
A response with one answer per question name and the scripted |
SystemOneResponse
|
usage. |
Raises:
| Type | Description |
|---|---|
ProviderRequestError
|
If the profile refuses the questions. |
JevBudgetExceededError
|
If the spend cap refuses the call. |
BaseException
|
The scripted error, after the call is recorded. |