Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions docs/contributing/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,21 @@ Use fake `RuntimePort`/`RegistryPort`/`CachePort` fixtures (`tests/conftest.py`)

## Port-Contract Tests

A shared test suite parameterized over every `RuntimePort`/`DownloaderPort` implementation. Guarantees each adapter honors the interface.
A shared test suite (`tests/unit/test_port_contract.py`) parameterized over every `RuntimePort`, `CachePort`, and `DownloaderPort` implementation. Guarantees each adapter honors its interface.

```bash
pytest tests/unit -k contract
```

New runtimes MUST pass this suite.
The suite is split by adapter capability rather than one flat parametrization:

- **Universal** — properties every `RuntimePort` must satisfy regardless of what it supports (backend identity, `status()`, `default_tag_for()`, category/capability lookups). Runs against all seven adapters, including the planned ones.
- **Lifecycle** — adapters that actually pull/install/list/remove models in-process (`FakeRuntime`, `OllamaRuntime`, `LMStudioRuntime`), exercised with fake/mocked clients so no real daemon is required.
- **Server-bound** — `LlamaCppRuntime`, which binds exactly one GGUF model per server process and must fail informatively (not silently) on `pull`/`remove`.
- **Planned/offline** — `JanRuntime`, `Gpt4AllRuntime`, `VllmRuntime`, which aren't shipped yet and must consistently raise `RuntimeUnavailableError`.
- **Registry coverage** — `test_registry_backend_coverage` asserts every backend in `RuntimeRegistry` has a corresponding adapter under contract test, so a new adapter that's registered but never added to the suite fails loudly instead of shipping untested.

New runtimes MUST pass this suite, and MUST be added to `_all_runtime_implementations()` in `test_port_contract.py` so registry coverage stays enforced.

---

Expand Down
3 changes: 3 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ def clean(self, force: bool = False) -> List[str]:
def status(self) -> List[dict]:
return list(self.entries.values())

def path(self) -> str:
return "/fake/cache"

def get_model_config(self, ref: ModelRef) -> Optional[dict]:
entry = self.entries.get(self._key(ref))
return entry.get("user_config") if entry else None
Expand Down
Loading
Loading