"""Plugin SDK facade behaviour (SPEC ยง11).""" from __future__ import annotations import uuid import pytest from app.events import get_event_bus from app.plugins.sdk import sdk async def test_publish_namespaces_under_plugin_name() -> None: received: list[dict[str, object]] = [] async def handler(payload: dict[str, object]) -> None: received.append(payload) event = f"plugin.demo.thing_{uuid.uuid4().hex[:6]}" suffix = event.removeprefix("plugin.demo.") get_event_bus().subscribe(event, handler) await sdk("demo").publish(suffix, {"user_id": "u1", "x": 1}) assert received == [{"user_id": "u1", "x": 1}] async def test_publish_requires_user_id() -> None: with pytest.raises(ValueError, match="user_id"): await sdk("demo").publish("thing", {"x": 1}) async def test_http_get_uses_ssrf_guard(monkeypatch: pytest.MonkeyPatch) -> None: calls: list[str] = [] async def fake_guarded_get(url: str) -> str | None: calls.append(url) return f"body:{url}" monkeypatch.setattr("app.plugins.sdk.guarded_get", fake_guarded_get) out = await sdk("demo").http_get("http://example.test/x") assert out == "body:http://example.test/x" assert calls == ["http://example.test/x"] def test_sdk_exposes_shared_services() -> None: s = sdk("demo") assert s.name == "demo" assert s.settings is not None # blob store + outbox helpers are reachable through the SDK namespace. assert hasattr(s.blob, "store") and hasattr(s.blob, "load") assert hasattr(s.outbox, "upsert_draft")