feat: plugins and public api (#71)
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import pytest
|
||||
|
||||
from takopi import plugins
|
||||
from tests.plugin_fixtures import FakeEntryPoint, install_entrypoints
|
||||
|
||||
|
||||
def test_list_ids_does_not_load_entrypoints(monkeypatch) -> None:
|
||||
calls = {"count": 0}
|
||||
|
||||
def loader():
|
||||
calls["count"] += 1
|
||||
return object()
|
||||
|
||||
entrypoints = [
|
||||
FakeEntryPoint(
|
||||
"codex",
|
||||
"takopi.runners.codex:BACKEND",
|
||||
plugins.ENGINE_GROUP,
|
||||
loader=loader,
|
||||
)
|
||||
]
|
||||
install_entrypoints(monkeypatch, entrypoints)
|
||||
|
||||
ids = plugins.list_ids(plugins.ENGINE_GROUP)
|
||||
assert ids == ["codex"]
|
||||
assert calls["count"] == 0
|
||||
|
||||
|
||||
def test_load_entrypoint_records_errors(monkeypatch) -> None:
|
||||
def loader():
|
||||
raise RuntimeError("boom")
|
||||
|
||||
entrypoints = [
|
||||
FakeEntryPoint(
|
||||
"broken",
|
||||
"takopi.runners.broken:BACKEND",
|
||||
plugins.ENGINE_GROUP,
|
||||
loader=loader,
|
||||
)
|
||||
]
|
||||
install_entrypoints(monkeypatch, entrypoints)
|
||||
|
||||
with pytest.raises(plugins.PluginLoadFailed):
|
||||
plugins.load_entrypoint(plugins.ENGINE_GROUP, "broken")
|
||||
|
||||
errors = plugins.get_load_errors()
|
||||
assert errors
|
||||
assert errors[0].name == "broken"
|
||||
assert "boom" in errors[0].error
|
||||
|
||||
|
||||
def test_duplicate_entrypoints_are_rejected(monkeypatch) -> None:
|
||||
entrypoints = [
|
||||
FakeEntryPoint(
|
||||
"dup",
|
||||
"takopi.runners.one:BACKEND",
|
||||
plugins.ENGINE_GROUP,
|
||||
dist_name="one",
|
||||
),
|
||||
FakeEntryPoint(
|
||||
"dup",
|
||||
"takopi.runners.two:BACKEND",
|
||||
plugins.ENGINE_GROUP,
|
||||
dist_name="two",
|
||||
),
|
||||
]
|
||||
install_entrypoints(monkeypatch, entrypoints)
|
||||
|
||||
ids = plugins.list_ids(plugins.ENGINE_GROUP)
|
||||
assert ids == []
|
||||
|
||||
with pytest.raises(plugins.PluginLoadFailed):
|
||||
plugins.load_entrypoint(plugins.ENGINE_GROUP, "dup")
|
||||
|
||||
errors = plugins.get_load_errors()
|
||||
assert any("duplicate plugin id" in err.error for err in errors)
|
||||
|
||||
|
||||
def test_allowlist_filters_by_distribution(monkeypatch) -> None:
|
||||
entrypoints = [
|
||||
FakeEntryPoint(
|
||||
"codex",
|
||||
"takopi.runners.codex:BACKEND",
|
||||
plugins.ENGINE_GROUP,
|
||||
dist_name="takopi",
|
||||
),
|
||||
FakeEntryPoint(
|
||||
"thirdparty",
|
||||
"takopi_thirdparty.backend:BACKEND",
|
||||
plugins.ENGINE_GROUP,
|
||||
dist_name="takopi-thirdparty",
|
||||
),
|
||||
]
|
||||
install_entrypoints(monkeypatch, entrypoints)
|
||||
|
||||
ids = plugins.list_ids(plugins.ENGINE_GROUP, allowlist=["takopi"])
|
||||
assert ids == ["codex"]
|
||||
|
||||
|
||||
def test_validator_errors_are_captured(monkeypatch) -> None:
|
||||
entrypoints = [
|
||||
FakeEntryPoint(
|
||||
"bad",
|
||||
"takopi.runners.bad:BACKEND",
|
||||
plugins.ENGINE_GROUP,
|
||||
)
|
||||
]
|
||||
install_entrypoints(monkeypatch, entrypoints)
|
||||
|
||||
def validator(obj, ep):
|
||||
raise TypeError("not valid")
|
||||
|
||||
with pytest.raises(plugins.PluginLoadFailed):
|
||||
plugins.load_entrypoint(plugins.ENGINE_GROUP, "bad", validator=validator)
|
||||
|
||||
errors = plugins.get_load_errors()
|
||||
assert any("not valid" in err.error for err in errors)
|
||||
|
||||
|
||||
def test_reset_plugin_state_clears_cache(monkeypatch) -> None:
|
||||
calls = {"count": 0}
|
||||
|
||||
def loader():
|
||||
calls["count"] += 1
|
||||
return object()
|
||||
|
||||
entrypoints = [
|
||||
FakeEntryPoint(
|
||||
"codex",
|
||||
"takopi.runners.codex:BACKEND",
|
||||
plugins.ENGINE_GROUP,
|
||||
loader=loader,
|
||||
)
|
||||
]
|
||||
install_entrypoints(monkeypatch, entrypoints)
|
||||
|
||||
plugins.load_entrypoint(plugins.ENGINE_GROUP, "codex")
|
||||
plugins.load_entrypoint(plugins.ENGINE_GROUP, "codex")
|
||||
assert calls["count"] == 1
|
||||
|
||||
plugins.reset_plugin_state()
|
||||
plugins.load_entrypoint(plugins.ENGINE_GROUP, "codex")
|
||||
assert calls["count"] == 2
|
||||
|
||||
|
||||
def test_clear_load_errors_filters(monkeypatch) -> None:
|
||||
def loader():
|
||||
raise RuntimeError("boom")
|
||||
|
||||
entrypoints = [
|
||||
FakeEntryPoint(
|
||||
"broken_engine",
|
||||
"takopi.runners.broken:BACKEND",
|
||||
plugins.ENGINE_GROUP,
|
||||
loader=loader,
|
||||
dist_name="engine-dist",
|
||||
),
|
||||
FakeEntryPoint(
|
||||
"broken_transport",
|
||||
"takopi.transports.broken:BACKEND",
|
||||
plugins.TRANSPORT_GROUP,
|
||||
loader=loader,
|
||||
dist_name="transport-dist",
|
||||
),
|
||||
]
|
||||
install_entrypoints(monkeypatch, entrypoints)
|
||||
|
||||
with pytest.raises(plugins.PluginLoadFailed):
|
||||
plugins.load_entrypoint(plugins.ENGINE_GROUP, "broken_engine")
|
||||
with pytest.raises(plugins.PluginLoadFailed):
|
||||
plugins.load_entrypoint(plugins.TRANSPORT_GROUP, "broken_transport")
|
||||
|
||||
errors = plugins.get_load_errors()
|
||||
assert {err.group for err in errors} == {
|
||||
plugins.ENGINE_GROUP,
|
||||
plugins.TRANSPORT_GROUP,
|
||||
}
|
||||
|
||||
plugins.clear_load_errors(group=plugins.ENGINE_GROUP)
|
||||
errors = plugins.get_load_errors()
|
||||
assert {err.group for err in errors} == {plugins.TRANSPORT_GROUP}
|
||||
|
||||
plugins.clear_load_errors(name="broken_transport")
|
||||
assert plugins.get_load_errors() == ()
|
||||
Reference in New Issue
Block a user