Files
pyMC_Repeater/tests/test_plugin_catalogue.py
T
Rightup 26560fd50a Add comprehensive tests for plugin management and IPC functionality
- Introduced tests for the plugin catalogue installation and update flows in `test_plugin_catalogue_install.py`.
- Added unit tests for the GitHub Releases plugin client in `test_plugin_github_releases.py`.
- Implemented IPC tests for the plugin manager Unix socket protocol in `test_plugin_ipc.py`.
- Created lifecycle and installation tests for the plugin manager in `test_plugin_manager_lifecycle.py`.
- Added tests for plugin manifest parsing in `test_plugin_manifest.py`.
- Developed tests for plugin filesystem layout and state management in `test_plugin_storage.py`.
2026-09-03 22:16:38 +01:00

159 lines
4.1 KiB
Python

"""Unit tests for the curated plugin catalogue client."""
from __future__ import annotations
import io
import json
from urllib.error import HTTPError, URLError
from urllib.request import Request
import pytest
from repeater.plugins.catalogue import (
CatalogueClient,
CatalogueError,
parse_catalogue,
)
VALID = {
"schema": 1,
"plugins": [
{
"id": "openhop.nomad",
"name": "NOMAD Bridge",
"description": "desc",
"repository": "openhop-dev/openhop-nomad-plugin",
"category": "integration",
}
],
}
def test_parse_valid_catalogue():
cat = parse_catalogue(VALID)
assert cat.schema == 1
assert len(cat.plugins) == 1
assert cat.plugins[0].id == "openhop.nomad"
assert cat.get("openhop.nomad").repository == "openhop-dev/openhop-nomad-plugin"
def test_unsupported_schema():
with pytest.raises(CatalogueError, match="unsupported catalogue schema"):
parse_catalogue({"schema": 99, "plugins": []})
def test_invalid_repository():
bad = {
"schema": 1,
"plugins": [
{
"id": "a.b",
"name": "A",
"description": "",
"repository": "not-a-repo",
}
],
}
with pytest.raises(CatalogueError, match="owner/repo"):
parse_catalogue(bad)
def test_duplicate_plugin_id():
data = {
"schema": 1,
"plugins": [
{
"id": "openhop.a",
"name": "A",
"description": "",
"repository": "org/a",
},
{
"id": "openhop.a",
"name": "B",
"description": "",
"repository": "org/b",
},
],
}
with pytest.raises(CatalogueError, match="duplicate plugin id"):
parse_catalogue(data)
def test_duplicate_repository():
data = {
"schema": 1,
"plugins": [
{
"id": "openhop.a",
"name": "A",
"description": "",
"repository": "org/same",
},
{
"id": "openhop.b",
"name": "B",
"description": "",
"repository": "org/same",
},
],
}
with pytest.raises(CatalogueError, match="duplicate repository"):
parse_catalogue(data)
class _FakeResp(io.BytesIO):
def __enter__(self):
return self
def __exit__(self, *args):
return False
def test_fetch_and_cache():
calls = {"n": 0}
def opener(request: Request, timeout: float = 30.0):
calls["n"] += 1
assert "catalogue.json" in request.full_url or request.full_url.endswith("x")
return _FakeResp(json.dumps(VALID).encode())
client = CatalogueClient("https://example.test/catalogue.json", cache_ttl=60, opener=opener)
a = client.fetch()
client.fetch() # cached
assert a.plugins[0].id == "openhop.nomad"
assert calls["n"] == 1
c = client.fetch(force_refresh=True)
assert c.plugins[0].id == "openhop.nomad"
assert calls["n"] == 2
def test_lookup():
def opener(request: Request, timeout: float = 30.0):
return _FakeResp(json.dumps(VALID).encode())
client = CatalogueClient("https://example.test/c.json", opener=opener)
plugin = client.get_plugin("openhop.nomad")
assert plugin.name == "NOMAD Bridge"
with pytest.raises(CatalogueError, match="not in catalogue"):
client.get_plugin("missing.plugin")
def test_catalogue_unavailable():
def opener(request: Request, timeout: float = 30.0):
raise URLError("offline")
client = CatalogueClient("https://example.test/c.json", opener=opener)
with pytest.raises(CatalogueError, match="unavailable"):
client.fetch()
def test_http_error():
def opener(request: Request, timeout: float = 30.0):
raise HTTPError(request.full_url, 500, "err", hdrs=None, fp=None)
client = CatalogueClient("https://example.test/c.json", opener=opener)
with pytest.raises(CatalogueError, match="unavailable"):
client.fetch()