judgevet.testing._conformance_async
Status: draft.
judgevet.testing._conformance_async
Reusable pytest conformance tests for an AsyncSystemOnePort provider.
BaseAsyncProviderConformance applies the nine rules of the synchronous kit
to an asynchronous provider:
test_port_shape:system_oneis callable with the port's positional and keyword arguments.test_typed_answers_pass_policy:system_onereturns an awaitable, and awaiting it gives aSystemOneResponsewhose answers passjudgevet.policy.evaluate_policy.test_failure_raises_provider_error: awaiting the failing port raises aProviderErrorsubclass.test_scope_entry_yields_usable_port,test_scope_exit_runs_onceandtest_scope_propagates_body_exception: the factory keeps theasync_provider_scopelifecycle rules.test_media_refused_without_media_support: a port from the factory without every media method callable exposes no partial media surface, andasync_judge_with_imagesrefuses it withProviderCapabilityError.test_media_port_refuses_undeclared_media:async_judge_with_imagesrefuses media outside the declared capabilities before any media dispatch.test_media_port_judges_declared_media:system_one_mediareturns an awaitable, and one declared kit image returns answers that passevaluate_policy.
Source: https://github.com/Alberto-Codes/judgevet/issues/251. Source: https://github.com/Alberto-Codes/judgevet/issues/252.
Each rule is an ordinary synchronous test method that drives its coroutine
with anyio.run. The kit adds no pytest plugin, because AnyIO's pytest plugin
and pytest-asyncio interfere when both are installed.
Source: https://anyio.readthedocs.io/en/stable/testing.html.
Source: https://github.com/agronholm/anyio/issues/328.
Source: https://github.com/Alberto-Codes/judgevet/issues/246.
The kit's fixtures are synchronous. pytest-asyncio's auto mode runs a synchronous test that requests an asynchronous fixture, and its strict mode refuses one. Source: https://github.com/pytest-dev/pytest-asyncio/issues/1570.
The kit imports anyio directly, so the conformance extra declares it beside
pytest.
Source: https://github.com/Alberto-Codes/judgevet/issues/253#issuecomment-5904100904.
Examples:
from contextlib import nullcontext
import pytest
from judgevet.providers import ProviderTransportError
from judgevet.testing import AsyncFakeSystemOnePort
from judgevet.testing.conformance import (
VALID_ANSWERS,
BaseAsyncProviderConformance,
)
class TestAsyncFakeProvider(BaseAsyncProviderConformance):
@pytest.fixture
def provider_factory(self):
return lambda: nullcontext(AsyncFakeSystemOnePort(answers=VALID_ANSWERS))
@pytest.fixture
def failing_port(self):
return AsyncFakeSystemOnePort(error=ProviderTransportError("offline"))
See Also
- judgevet.testing.conformance: The synchronous kit and the public names
- judgevet.testing.AsyncFakeSystemOnePort: The offline async fake
- judgevet.ports.AsyncSystemOnePort: The protocol the rules check
- judgevet.testing._conformance_async_scope: The scope rule checks
- judgevet.testing._conformance_async_media: The media rule checks
- judgevet.providers:
async_provider_scopeand the provider errors
BaseAsyncProviderConformance
Conformance rules an AsyncSystemOnePort provider inherits in its tests.
Name the subclass Test... so pytest collects it. Override the fixtures
below as pytest fixtures on the subclass. Each rule enters one
async with provider_factory() scope inside its own anyio.run call. The
scope rules enter it through async_provider_scope and record each
context the factory constructs.
Attributes:
| Name | Type | Description |
|---|---|---|
provider_factory |
fixture
|
Required. A zero-argument callable that returns an async context manager yielding a working port. |
failing_port |
fixture
|
Required. A port configured so that every
awaited |
provider_model |
fixture
|
The model label the kit passes. The default
is |
media_port |
fixture
|
Optional. An |
Examples:
class TestMyAsyncProvider(BaseAsyncProviderConformance):
pass
failing_port()
Fail until the provider supplies a port configured to fail.
Raises:
| Type | Description |
|---|---|
Exception
|
Always. |
media_port()
Supply no dedicated media port, so the kit checks the factory's port.
provider_factory()
Fail until the provider supplies its factory.
Raises:
| Type | Description |
|---|---|
Exception
|
Always. |
provider_model()
Supply the kit's default model label.
Returns:
| Type | Description |
|---|---|
str
|
The model label. |
test_failure_raises_provider_error(failing_port, provider_model)
Rule 3: an awaited failed judgment raises a ProviderError subclass.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
failing_port
|
AsyncSystemOnePort
|
The port configured to fail. |
required |
provider_model
|
str
|
The selected model. |
required |
test_media_port_judges_declared_media(provider_factory, media_port, provider_model)
Rule 6: declared media reaches the port and returns typed answers.
A system_one_media that returns a value that is not awaitable fails
this rule. The rule skips when the declaration admits no kit image.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider_factory
|
AsyncProviderFactory
|
The provider's factory, used without a media port. |
required |
media_port
|
AsyncMediaSystemOnePort | None
|
The dedicated media port, or None. |
required |
provider_model
|
str
|
The selected model. |
required |
test_media_port_refuses_undeclared_media(provider_factory, media_port, provider_model)
Rule 5: media outside the declared capabilities is refused first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider_factory
|
AsyncProviderFactory
|
The provider's factory, used without a media port. |
required |
media_port
|
AsyncMediaSystemOnePort | None
|
The dedicated media port, or None. |
required |
provider_model
|
str
|
The selected model. |
required |
test_media_refused_without_media_support(provider_factory, provider_model)
Rule 5: a text-only port exposes no partial media surface.
A media-capable port from the factory skips this rule, and
test_media_port_refuses_undeclared_media checks it instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider_factory
|
AsyncProviderFactory
|
The provider's factory. |
required |
provider_model
|
str
|
The selected model. |
required |
test_port_shape(provider_factory, provider_model)
Rule 1: system_one is callable as the port declares it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider_factory
|
AsyncProviderFactory
|
The provider's factory. |
required |
provider_model
|
str
|
The selected model. |
required |
test_scope_entry_yields_usable_port(provider_factory, provider_model)
Rule 4: scope entry validates the port and yields one that answers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider_factory
|
AsyncProviderFactory
|
The provider's factory. |
required |
provider_model
|
str
|
The selected model. |
required |
test_scope_exit_runs_once(provider_factory)
Rule 4: each scope gets its own context, and each exits exactly once.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider_factory
|
AsyncProviderFactory
|
The provider's factory. |
required |
test_scope_propagates_body_exception(provider_factory)
Rule 4: a body exception reaches the caller and exits the scope once.
The kit also enters one context directly, as AsyncProviderFactory
callers may, and requires that its exit does not suppress the error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider_factory
|
AsyncProviderFactory
|
The provider's factory. |
required |
test_typed_answers_pass_policy(provider_factory, provider_model)
Rule 2: awaited typed answers per question kind pass the evaluator.
A system_one that returns a value that is not awaitable fails this
rule. A failure message also names every answer whose type does not
match its question kind.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider_factory
|
AsyncProviderFactory
|
The provider's factory. |
required |
provider_model
|
str
|
The selected model. |
required |