ariadne/tests/test_services.py

901 lines
32 KiB
Python

from __future__ import annotations
import time
import types
import pytest
from ariadne.services.firefly import FireflyService
from ariadne.services.mailu import MailuService
from ariadne.services.nextcloud import NextcloudService
from ariadne.services.wger import WgerService
from ariadne.services.vaultwarden import VaultwardenService
class DummyExecutor:
def __init__(self, stdout: str = "ok", stderr: str = "", exit_code: int = 0):
self.calls = []
self._stdout = stdout
self._stderr = stderr
self._exit_code = exit_code
def exec(self, command, env=None, timeout_sec=None, check=True):
self.calls.append((command, env, timeout_sec, check))
return types.SimpleNamespace(
stdout=self._stdout,
stderr=self._stderr,
exit_code=self._exit_code,
ok=self._exit_code == 0,
)
class DummyClient:
def __init__(self):
self.url = ""
self.payload = None
self.status_code = 200
def __enter__(self):
return self
def __exit__(self, exc_type, exc, tb):
return False
def post(self, url, json=None):
self.url = url
self.payload = json
return types.SimpleNamespace(status_code=self.status_code)
class DummyResponse:
def __init__(self, status_code=200, text=""):
self.status_code = status_code
self.text = text
def raise_for_status(self):
return None
class DummyVaultwardenClient:
def __init__(self):
self.calls = []
self.responses = {}
def post(self, path, json=None, data=None):
self.calls.append((path, json, data))
resp = self.responses.get(path)
if resp is None:
resp = DummyResponse(200, "")
return resp
def close(self):
return None
def test_nextcloud_sync_mail_no_user(monkeypatch) -> None:
dummy = types.SimpleNamespace(
nextcloud_namespace="nextcloud",
nextcloud_mail_sync_cronjob="nextcloud-mail-sync",
nextcloud_mail_sync_wait_timeout_sec=90.0,
nextcloud_mail_sync_job_ttl_sec=3600,
nextcloud_pod_label="app=nextcloud",
nextcloud_container="nextcloud",
nextcloud_exec_timeout_sec=30.0,
nextcloud_db_host="",
nextcloud_db_port=5432,
nextcloud_db_name="nextcloud",
nextcloud_db_user="nextcloud",
nextcloud_db_password="",
mailu_domain="bstein.dev",
mailu_host="mail.bstein.dev",
)
monkeypatch.setattr("ariadne.services.nextcloud.settings", dummy)
monkeypatch.setattr("ariadne.services.nextcloud.keycloak_admin.ready", lambda: True)
monkeypatch.setattr("ariadne.services.nextcloud.keycloak_admin.find_user", lambda *_args, **_kwargs: None)
svc = NextcloudService()
result = svc.sync_mail("alice", wait=True)
assert result["status"] == "ok"
def test_wger_sync_user_exec(monkeypatch) -> None:
dummy = types.SimpleNamespace(
wger_namespace="health",
wger_user_sync_cronjob="wger-user-sync",
wger_admin_cronjob="wger-admin-ensure",
wger_user_sync_wait_timeout_sec=60.0,
wger_pod_label="app=wger",
wger_container="wger",
wger_admin_username="admin",
wger_admin_password="pw",
wger_admin_email="admin@bstein.dev",
)
monkeypatch.setattr("ariadne.services.wger.settings", dummy)
calls: list[dict[str, str]] = []
class DummyExecutor:
def exec(self, _cmd, env=None, timeout_sec=None, check=True):
calls.append(env or {})
return types.SimpleNamespace(stdout="ok", stderr="", exit_code=0, ok=True)
monkeypatch.setattr("ariadne.services.wger.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
svc = WgerService()
result = svc.sync_user("alice", "alice@bstein.dev", "pw", wait=True)
assert result["status"] == "ok"
assert calls[0]["WGER_USERNAME"] == "alice"
def test_wger_ensure_admin_exec(monkeypatch) -> None:
dummy = types.SimpleNamespace(
wger_namespace="health",
wger_user_sync_cronjob="wger-user-sync",
wger_admin_cronjob="wger-admin-ensure",
wger_user_sync_wait_timeout_sec=60.0,
wger_pod_label="app=wger",
wger_container="wger",
wger_admin_username="admin",
wger_admin_password="pw",
wger_admin_email="admin@bstein.dev",
)
monkeypatch.setattr("ariadne.services.wger.settings", dummy)
calls: list[dict[str, str]] = []
class DummyExecutor:
def exec(self, _cmd, env=None, timeout_sec=None, check=True):
calls.append(env or {})
return types.SimpleNamespace(stdout="ok", stderr="", exit_code=0, ok=True)
monkeypatch.setattr("ariadne.services.wger.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
svc = WgerService()
result = svc.ensure_admin(wait=False)
assert result["status"] == "ok"
assert calls[0]["WGER_ADMIN_USERNAME"] == "admin"
def test_firefly_sync_user_exec(monkeypatch) -> None:
dummy = types.SimpleNamespace(
firefly_namespace="finance",
firefly_user_sync_cronjob="firefly-user-sync",
firefly_user_sync_wait_timeout_sec=60.0,
firefly_pod_label="app=firefly",
firefly_container="firefly",
firefly_cron_base_url="http://firefly/cron",
firefly_cron_token="token",
firefly_cron_timeout_sec=10.0,
)
monkeypatch.setattr("ariadne.services.firefly.settings", dummy)
class DummyExecutor:
def exec(self, _cmd, env=None, timeout_sec=None, check=True):
return types.SimpleNamespace(stdout="ok", stderr="", exit_code=0, ok=True)
monkeypatch.setattr("ariadne.services.firefly.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
svc = FireflyService()
result = svc.sync_user("alice@bstein.dev", "pw", wait=True)
assert result["status"] == "ok"
def test_firefly_sync_missing_inputs(monkeypatch) -> None:
dummy = types.SimpleNamespace(
firefly_namespace="finance",
firefly_user_sync_cronjob="firefly-user-sync",
firefly_user_sync_wait_timeout_sec=60.0,
firefly_pod_label="app=firefly",
firefly_container="firefly",
firefly_cron_base_url="http://firefly/cron",
firefly_cron_token="token",
firefly_cron_timeout_sec=10.0,
)
monkeypatch.setattr("ariadne.services.firefly.settings", dummy)
monkeypatch.setattr("ariadne.services.firefly.PodExecutor", lambda *_args, **_kwargs: None)
svc = FireflyService()
with pytest.raises(RuntimeError):
svc.sync_user("", "pw", wait=True)
with pytest.raises(RuntimeError):
svc.sync_user("alice@bstein.dev", "", wait=True)
def test_firefly_sync_missing_config(monkeypatch) -> None:
dummy = types.SimpleNamespace(
firefly_namespace="",
firefly_user_sync_cronjob="",
firefly_user_sync_wait_timeout_sec=60.0,
firefly_pod_label="app=firefly",
firefly_container="firefly",
firefly_cron_base_url="http://firefly/cron",
firefly_cron_token="token",
firefly_cron_timeout_sec=10.0,
)
monkeypatch.setattr("ariadne.services.firefly.settings", dummy)
svc = FireflyService()
with pytest.raises(RuntimeError):
svc.sync_user("alice@bstein.dev", "pw", wait=True)
def test_firefly_run_cron(monkeypatch) -> None:
dummy = types.SimpleNamespace(
firefly_namespace="finance",
firefly_user_sync_cronjob="firefly-user-sync",
firefly_user_sync_wait_timeout_sec=60.0,
firefly_pod_label="app=firefly",
firefly_container="firefly",
firefly_cron_base_url="http://firefly/cron",
firefly_cron_token="token",
firefly_cron_timeout_sec=10.0,
)
monkeypatch.setattr("ariadne.services.firefly.settings", dummy)
monkeypatch.setattr("ariadne.services.firefly.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
class DummyHTTP:
def __init__(self):
self.calls = []
def __enter__(self):
return self
def __exit__(self, exc_type, exc, tb):
return False
def get(self, url):
self.calls.append(url)
return types.SimpleNamespace(status_code=200)
monkeypatch.setattr("ariadne.services.firefly.httpx.Client", lambda *args, **kwargs: DummyHTTP())
svc = FireflyService()
result = svc.run_cron()
assert result["status"] == "ok"
def test_mailu_sync_includes_force(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
mailu_sync_url="http://mailu",
mailu_sync_wait_timeout_sec=10.0,
mailu_db_host="localhost",
mailu_db_port=5432,
mailu_db_name="mailu",
mailu_db_user="mailu",
mailu_db_password="secret",
)
client = DummyClient()
monkeypatch.setattr("ariadne.services.mailu.settings", dummy_settings)
monkeypatch.setattr("ariadne.services.mailu.httpx.Client", lambda *args, **kwargs: client)
svc = MailuService()
svc.sync("provision", force=True)
assert client.url == "http://mailu"
assert client.payload["wait"] is True
assert client.payload["force"] is True
def test_mailu_sync_skips_without_url(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
mailu_sync_url="",
mailu_sync_wait_timeout_sec=10.0,
mailu_db_host="localhost",
mailu_db_port=5432,
mailu_db_name="mailu",
mailu_db_user="mailu",
mailu_db_password="secret",
mailu_domain="bstein.dev",
)
monkeypatch.setattr("ariadne.services.mailu.settings", dummy_settings)
svc = MailuService()
assert svc.sync("provision") is None
def test_mailu_sync_raises_on_error(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
mailu_sync_url="http://mailu",
mailu_sync_wait_timeout_sec=10.0,
mailu_db_host="localhost",
mailu_db_port=5432,
mailu_db_name="mailu",
mailu_db_user="mailu",
mailu_db_password="secret",
)
client = DummyClient()
client.status_code = 500
monkeypatch.setattr("ariadne.services.mailu.settings", dummy_settings)
monkeypatch.setattr("ariadne.services.mailu.httpx.Client", lambda *args, **kwargs: client)
svc = MailuService()
with pytest.raises(RuntimeError):
svc.sync("provision")
def test_vaultwarden_invite_uses_admin_session(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
vaultwarden_namespace="vaultwarden",
vaultwarden_admin_secret_name="vaultwarden-admin",
vaultwarden_admin_secret_key="ADMIN_TOKEN",
vaultwarden_admin_rate_limit_backoff_sec=600,
vaultwarden_admin_session_ttl_sec=900,
vaultwarden_service_host="vaultwarden-service.vaultwarden.svc.cluster.local",
vaultwarden_pod_label="app=vaultwarden",
vaultwarden_pod_port=80,
)
client = DummyVaultwardenClient()
monkeypatch.setattr("ariadne.services.vaultwarden.settings", dummy_settings)
monkeypatch.setattr("ariadne.services.vaultwarden.get_secret_value", lambda *args, **kwargs: "token")
monkeypatch.setattr("ariadne.services.vaultwarden.httpx.Client", lambda *args, **kwargs: client)
monkeypatch.setattr(
"ariadne.services.vaultwarden.VaultwardenService._find_pod_ip",
staticmethod(lambda *args, **kwargs: "127.0.0.1"),
)
svc = VaultwardenService()
result = svc.invite_user("alice@bstein.dev")
assert result.ok is True
assert any(call[0] == "/admin" for call in client.calls)
assert any(call[0] == "/admin/invite" for call in client.calls)
def test_vaultwarden_invite_handles_rate_limit(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
vaultwarden_namespace="vaultwarden",
vaultwarden_admin_secret_name="vaultwarden-admin",
vaultwarden_admin_secret_key="ADMIN_TOKEN",
vaultwarden_admin_rate_limit_backoff_sec=600,
vaultwarden_admin_session_ttl_sec=900,
vaultwarden_service_host="vaultwarden-service.vaultwarden.svc.cluster.local",
vaultwarden_pod_label="app=vaultwarden",
vaultwarden_pod_port=80,
)
client = DummyVaultwardenClient()
client.responses["/admin/invite"] = DummyResponse(429, "rate limited")
monkeypatch.setattr("ariadne.services.vaultwarden.settings", dummy_settings)
monkeypatch.setattr("ariadne.services.vaultwarden.get_secret_value", lambda *args, **kwargs: "token")
monkeypatch.setattr("ariadne.services.vaultwarden.httpx.Client", lambda *args, **kwargs: client)
monkeypatch.setattr(
"ariadne.services.vaultwarden.VaultwardenService._find_pod_ip",
staticmethod(lambda *args, **kwargs: "127.0.0.1"),
)
svc = VaultwardenService()
result = svc.invite_user("alice@bstein.dev")
assert result.status == "rate_limited"
def test_vaultwarden_invite_existing_user(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
vaultwarden_namespace="vaultwarden",
vaultwarden_admin_secret_name="vaultwarden-admin",
vaultwarden_admin_secret_key="ADMIN_TOKEN",
vaultwarden_admin_rate_limit_backoff_sec=600,
vaultwarden_admin_session_ttl_sec=900,
vaultwarden_service_host="vaultwarden-service.vaultwarden.svc.cluster.local",
vaultwarden_pod_label="app=vaultwarden",
vaultwarden_pod_port=80,
)
client = DummyVaultwardenClient()
client.responses["/admin/invite"] = DummyResponse(409, "user already exists")
monkeypatch.setattr("ariadne.services.vaultwarden.settings", dummy_settings)
monkeypatch.setattr("ariadne.services.vaultwarden.get_secret_value", lambda *args, **kwargs: "token")
monkeypatch.setattr("ariadne.services.vaultwarden.httpx.Client", lambda *args, **kwargs: client)
monkeypatch.setattr(
"ariadne.services.vaultwarden.VaultwardenService._find_pod_ip",
staticmethod(lambda *args, **kwargs: "127.0.0.1"),
)
svc = VaultwardenService()
result = svc.invite_user("alice@bstein.dev")
assert result.status == "already_present"
def test_vaultwarden_invite_rejects_invalid_email() -> None:
svc = VaultwardenService()
result = svc.invite_user("bad-email")
assert result.status == "invalid_email"
def test_vaultwarden_invite_rate_limited_short_circuit() -> None:
svc = VaultwardenService()
svc._rate_limited_until = time.time() + 60
result = svc.invite_user("alice@bstein.dev")
assert result.status == "rate_limited"
def test_vaultwarden_invite_handles_admin_exception(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
vaultwarden_namespace="vaultwarden",
vaultwarden_admin_secret_name="vaultwarden-admin",
vaultwarden_admin_secret_key="ADMIN_TOKEN",
vaultwarden_admin_rate_limit_backoff_sec=600,
vaultwarden_admin_session_ttl_sec=900,
vaultwarden_service_host="vaultwarden-service.vaultwarden.svc.cluster.local",
vaultwarden_pod_label="app=vaultwarden",
vaultwarden_pod_port=80,
)
monkeypatch.setattr("ariadne.services.vaultwarden.settings", dummy_settings)
monkeypatch.setattr(
"ariadne.services.vaultwarden.VaultwardenService._find_pod_ip",
staticmethod(lambda *args, **kwargs: (_ for _ in ()).throw(RuntimeError("boom"))),
)
svc = VaultwardenService()
monkeypatch.setattr(
svc,
"_admin_session",
lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("rate limited")),
)
result = svc.invite_user("alice@bstein.dev")
assert result.status == "rate_limited"
def test_vaultwarden_invite_handles_bad_body(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
vaultwarden_namespace="vaultwarden",
vaultwarden_admin_secret_name="vaultwarden-admin",
vaultwarden_admin_secret_key="ADMIN_TOKEN",
vaultwarden_admin_rate_limit_backoff_sec=600,
vaultwarden_admin_session_ttl_sec=900,
vaultwarden_service_host="vaultwarden-service.vaultwarden.svc.cluster.local",
vaultwarden_pod_label="app=vaultwarden",
vaultwarden_pod_port=80,
)
class BadTextResponse:
def __init__(self, status_code=500):
self.status_code = status_code
def raise_for_status(self):
return None
@property
def text(self):
raise RuntimeError("boom")
class BadTextClient(DummyVaultwardenClient):
def post(self, path, json=None, data=None):
self.calls.append((path, json, data))
return BadTextResponse(500)
monkeypatch.setattr("ariadne.services.vaultwarden.settings", dummy_settings)
monkeypatch.setattr("ariadne.services.vaultwarden.get_secret_value", lambda *args, **kwargs: "token")
monkeypatch.setattr("ariadne.services.vaultwarden.httpx.Client", lambda *args, **kwargs: BadTextClient())
monkeypatch.setattr(
"ariadne.services.vaultwarden.VaultwardenService._find_pod_ip",
staticmethod(lambda *args, **kwargs: "127.0.0.1"),
)
svc = VaultwardenService()
result = svc.invite_user("alice@bstein.dev")
assert result.status == "error"
def test_vaultwarden_invite_handles_fallback_skip(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
vaultwarden_namespace="vaultwarden",
vaultwarden_admin_secret_name="vaultwarden-admin",
vaultwarden_admin_secret_key="ADMIN_TOKEN",
vaultwarden_admin_rate_limit_backoff_sec=600,
vaultwarden_admin_session_ttl_sec=900,
vaultwarden_service_host="vaultwarden-service.vaultwarden.svc.cluster.local",
vaultwarden_pod_label="app=vaultwarden",
vaultwarden_pod_port=80,
)
monkeypatch.setattr("ariadne.services.vaultwarden.settings", dummy_settings)
monkeypatch.setattr(
"ariadne.services.vaultwarden.VaultwardenService._find_pod_ip",
staticmethod(lambda *args, **kwargs: (_ for _ in ()).throw(RuntimeError("boom"))),
)
svc = VaultwardenService()
monkeypatch.setattr(svc, "_admin_session", lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("nope")))
result = svc.invite_user("alice@bstein.dev")
assert result.status == "error"
def test_vaultwarden_find_pod_ip(monkeypatch) -> None:
monkeypatch.setattr(
"ariadne.services.vaultwarden.get_json",
lambda *args, **kwargs: {
"items": [
{
"status": {
"phase": "Running",
"podIP": "10.0.0.1",
"conditions": [{"type": "Ready", "status": "True"}],
}
}
]
},
)
assert VaultwardenService._find_pod_ip("ns", "app=vaultwarden") == "10.0.0.1"
def test_vaultwarden_find_pod_ip_skips_missing_ip(monkeypatch) -> None:
monkeypatch.setattr(
"ariadne.services.vaultwarden.get_json",
lambda *args, **kwargs: {
"items": [
{"status": {"phase": "Running", "podIP": ""}},
{"status": {"phase": "Running", "podIP": "10.0.0.2", "conditions": []}},
]
},
)
assert VaultwardenService._find_pod_ip("ns", "app=vaultwarden") == "10.0.0.2"
def test_vaultwarden_find_pod_ip_conditions_default_ready(monkeypatch) -> None:
monkeypatch.setattr(
"ariadne.services.vaultwarden.get_json",
lambda *args, **kwargs: {
"items": [
{"status": {"phase": "Running", "podIP": "10.0.0.3", "conditions": ["bad"]}},
]
},
)
assert VaultwardenService._find_pod_ip("ns", "app=vaultwarden") == "10.0.0.3"
def test_vaultwarden_find_pod_ip_no_pods(monkeypatch) -> None:
monkeypatch.setattr("ariadne.services.vaultwarden.get_json", lambda *args, **kwargs: {"items": []})
with pytest.raises(RuntimeError):
VaultwardenService._find_pod_ip("ns", "app=vaultwarden")
def test_vaultwarden_find_pod_ip_missing_ip(monkeypatch) -> None:
monkeypatch.setattr(
"ariadne.services.vaultwarden.get_json",
lambda *args, **kwargs: {
"items": [
{"status": {"phase": "Pending", "conditions": ["bad"]}},
]
},
)
with pytest.raises(RuntimeError):
VaultwardenService._find_pod_ip("ns", "app=vaultwarden")
def test_vaultwarden_admin_session_rate_limit(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
vaultwarden_namespace="vaultwarden",
vaultwarden_admin_secret_name="vaultwarden-admin",
vaultwarden_admin_secret_key="ADMIN_TOKEN",
vaultwarden_admin_rate_limit_backoff_sec=1,
vaultwarden_admin_session_ttl_sec=900,
vaultwarden_service_host="vaultwarden-service.vaultwarden.svc.cluster.local",
vaultwarden_pod_label="app=vaultwarden",
vaultwarden_pod_port=80,
)
client = DummyVaultwardenClient()
client.responses["/admin"] = DummyResponse(429, "")
monkeypatch.setattr("ariadne.services.vaultwarden.settings", dummy_settings)
monkeypatch.setattr("ariadne.services.vaultwarden.get_secret_value", lambda *args, **kwargs: "token")
monkeypatch.setattr("ariadne.services.vaultwarden.httpx.Client", lambda *args, **kwargs: client)
svc = VaultwardenService()
with pytest.raises(RuntimeError):
svc._admin_session("http://vaultwarden")
def test_vaultwarden_admin_session_reuses_client() -> None:
svc = VaultwardenService()
svc._admin_client = DummyVaultwardenClient()
svc._admin_session_expires_at = time.time() + 60
svc._admin_session_base_url = "http://vaultwarden"
client = svc._admin_session("http://vaultwarden")
assert client is svc._admin_client
def test_vaultwarden_admin_session_rate_limited_until() -> None:
svc = VaultwardenService()
svc._rate_limited_until = time.time() + 60
with pytest.raises(RuntimeError):
svc._admin_session("http://vaultwarden")
def test_vaultwarden_admin_session_closes_existing(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
vaultwarden_namespace="vaultwarden",
vaultwarden_admin_secret_name="vaultwarden-admin",
vaultwarden_admin_secret_key="ADMIN_TOKEN",
vaultwarden_admin_rate_limit_backoff_sec=600,
vaultwarden_admin_session_ttl_sec=900,
vaultwarden_service_host="vaultwarden-service.vaultwarden.svc.cluster.local",
vaultwarden_pod_label="app=vaultwarden",
vaultwarden_pod_port=80,
)
class CloseFail:
def close(self):
raise RuntimeError("boom")
monkeypatch.setattr("ariadne.services.vaultwarden.settings", dummy_settings)
monkeypatch.setattr("ariadne.services.vaultwarden.get_secret_value", lambda *args, **kwargs: "token")
monkeypatch.setattr("ariadne.services.vaultwarden.httpx.Client", lambda *args, **kwargs: DummyVaultwardenClient())
svc = VaultwardenService()
svc._admin_client = CloseFail()
svc._admin_session_expires_at = time.time() - 10
svc._admin_session_base_url = "http://old"
assert svc._admin_session("http://vaultwarden") is not None
def test_nextcloud_missing_config(monkeypatch) -> None:
dummy = types.SimpleNamespace(
nextcloud_namespace="",
nextcloud_mail_sync_cronjob="",
nextcloud_mail_sync_wait_timeout_sec=90.0,
nextcloud_mail_sync_job_ttl_sec=3600,
nextcloud_pod_label="app=nextcloud",
nextcloud_container="nextcloud",
nextcloud_exec_timeout_sec=30.0,
nextcloud_db_host="",
nextcloud_db_port=5432,
nextcloud_db_name="nextcloud",
nextcloud_db_user="nextcloud",
nextcloud_db_password="",
mailu_domain="bstein.dev",
mailu_host="mail.bstein.dev",
)
monkeypatch.setattr("ariadne.services.nextcloud.settings", dummy)
svc = NextcloudService()
with pytest.raises(RuntimeError):
svc.sync_mail("alice")
def test_wger_sync_missing_inputs(monkeypatch) -> None:
dummy = types.SimpleNamespace(
wger_namespace="health",
wger_user_sync_cronjob="wger-user-sync",
wger_admin_cronjob="wger-admin-ensure",
wger_user_sync_wait_timeout_sec=60.0,
wger_pod_label="app=wger",
wger_container="wger",
wger_admin_username="admin",
wger_admin_password="pw",
wger_admin_email="admin@bstein.dev",
)
monkeypatch.setattr("ariadne.services.wger.settings", dummy)
monkeypatch.setattr("ariadne.services.wger.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
svc = WgerService()
with pytest.raises(RuntimeError):
svc.sync_user("", "email", "pw", wait=True)
with pytest.raises(RuntimeError):
svc.sync_user("alice", "email", "", wait=True)
def test_wger_sync_missing_config(monkeypatch) -> None:
dummy = types.SimpleNamespace(
wger_namespace="",
wger_user_sync_cronjob="",
wger_admin_cronjob="wger-admin-ensure",
wger_user_sync_wait_timeout_sec=60.0,
wger_pod_label="app=wger",
wger_container="wger",
wger_admin_username="admin",
wger_admin_password="pw",
wger_admin_email="admin@bstein.dev",
)
monkeypatch.setattr("ariadne.services.wger.settings", dummy)
monkeypatch.setattr("ariadne.services.wger.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
svc = WgerService()
with pytest.raises(RuntimeError):
svc.sync_user("alice", "email", "pw", wait=True)
def test_wger_ensure_admin(monkeypatch) -> None:
dummy = types.SimpleNamespace(
wger_namespace="health",
wger_user_sync_cronjob="wger-user-sync",
wger_admin_cronjob="wger-admin-ensure",
wger_user_sync_wait_timeout_sec=60.0,
wger_pod_label="app=wger",
wger_container="wger",
wger_admin_username="admin",
wger_admin_password="pw",
wger_admin_email="admin@bstein.dev",
)
monkeypatch.setattr("ariadne.services.wger.settings", dummy)
monkeypatch.setattr("ariadne.services.wger.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
svc = WgerService()
result = svc.ensure_admin(wait=True)
assert result["status"] == "ok"
def test_wger_ensure_admin_missing_creds(monkeypatch) -> None:
dummy = types.SimpleNamespace(
wger_namespace="health",
wger_user_sync_cronjob="wger-user-sync",
wger_admin_cronjob="wger-admin-ensure",
wger_user_sync_wait_timeout_sec=60.0,
wger_pod_label="app=wger",
wger_container="wger",
wger_admin_username="",
wger_admin_password="",
wger_admin_email="",
)
monkeypatch.setattr("ariadne.services.wger.settings", dummy)
monkeypatch.setattr("ariadne.services.wger.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
svc = WgerService()
result = svc.ensure_admin(wait=True)
assert result["status"] == "error"
def test_wger_ensure_admin_missing_config(monkeypatch) -> None:
dummy = types.SimpleNamespace(
wger_namespace="",
wger_user_sync_cronjob="wger-user-sync",
wger_admin_cronjob="",
wger_user_sync_wait_timeout_sec=60.0,
wger_pod_label="app=wger",
wger_container="wger",
wger_admin_username="admin",
wger_admin_password="pw",
wger_admin_email="admin@bstein.dev",
)
monkeypatch.setattr("ariadne.services.wger.settings", dummy)
monkeypatch.setattr("ariadne.services.wger.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
svc = WgerService()
with pytest.raises(RuntimeError):
svc.ensure_admin(wait=True)
def test_mailu_mailbox_exists_handles_error(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
mailu_sync_url="",
mailu_sync_wait_timeout_sec=10.0,
mailu_db_host="localhost",
mailu_db_port=5432,
mailu_db_name="mailu",
mailu_db_user="mailu",
mailu_db_password="secret",
mailu_domain="bstein.dev",
)
monkeypatch.setattr("ariadne.services.mailu.settings", dummy_settings)
monkeypatch.setattr("ariadne.services.mailu.psycopg.connect", lambda *args, **kwargs: (_ for _ in ()).throw(RuntimeError("boom")))
svc = MailuService()
assert svc.mailbox_exists("alice@bstein.dev") is False
def test_mailu_mailbox_exists_success(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
mailu_sync_url="",
mailu_sync_wait_timeout_sec=10.0,
mailu_db_host="localhost",
mailu_db_port=5432,
mailu_db_name="mailu",
mailu_db_user="mailu",
mailu_db_password="secret",
mailu_domain="bstein.dev",
)
monkeypatch.setattr("ariadne.services.mailu.settings", dummy_settings)
class DummyCursor:
def execute(self, *_args, **_kwargs):
return None
def fetchone(self):
return {"id": 1}
def __enter__(self):
return self
def __exit__(self, *_args):
return False
class DummyConn:
def cursor(self):
return DummyCursor()
def __enter__(self):
return self
def __exit__(self, *_args):
return False
monkeypatch.setattr("ariadne.services.mailu.psycopg.connect", lambda *args, **kwargs: DummyConn())
svc = MailuService()
assert svc.mailbox_exists("alice@bstein.dev") is True
def test_mailu_wait_for_mailbox(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
mailu_sync_url="",
mailu_sync_wait_timeout_sec=10.0,
mailu_db_host="localhost",
mailu_db_port=5432,
mailu_db_name="mailu",
mailu_db_user="mailu",
mailu_db_password="secret",
mailu_domain="bstein.dev",
)
monkeypatch.setattr("ariadne.services.mailu.settings", dummy_settings)
monkeypatch.setattr(MailuService, "mailbox_exists", lambda self, email: True)
svc = MailuService()
assert svc.wait_for_mailbox("alice@bstein.dev", timeout_sec=1.0) is True
def test_mailu_mailbox_exists_empty_email(monkeypatch) -> None:
dummy_settings = types.SimpleNamespace(
mailu_sync_url="",
mailu_sync_wait_timeout_sec=10.0,
mailu_db_host="localhost",
mailu_db_port=5432,
mailu_db_name="mailu",
mailu_db_user="mailu",
mailu_db_password="secret",
mailu_domain="bstein.dev",
)
monkeypatch.setattr("ariadne.services.mailu.settings", dummy_settings)
svc = MailuService()
assert svc.mailbox_exists("") is False
def test_nextcloud_sync_missing_username(monkeypatch) -> None:
dummy = types.SimpleNamespace(
nextcloud_namespace="nextcloud",
nextcloud_mail_sync_cronjob="nextcloud-mail-sync",
nextcloud_mail_sync_wait_timeout_sec=90.0,
nextcloud_mail_sync_job_ttl_sec=3600,
nextcloud_pod_label="app=nextcloud",
nextcloud_container="nextcloud",
nextcloud_exec_timeout_sec=30.0,
nextcloud_db_host="",
nextcloud_db_port=5432,
nextcloud_db_name="nextcloud",
nextcloud_db_user="nextcloud",
nextcloud_db_password="",
mailu_domain="bstein.dev",
mailu_host="mail.bstein.dev",
)
monkeypatch.setattr("ariadne.services.nextcloud.settings", dummy)
monkeypatch.setattr("ariadne.services.nextcloud.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
svc = NextcloudService()
with pytest.raises(RuntimeError):
svc.sync_mail(" ", wait=True)
def test_nextcloud_sync_no_match(monkeypatch) -> None:
dummy = types.SimpleNamespace(
nextcloud_namespace="nextcloud",
nextcloud_mail_sync_cronjob="nextcloud-mail-sync",
nextcloud_mail_sync_wait_timeout_sec=90.0,
nextcloud_mail_sync_job_ttl_sec=3600,
nextcloud_pod_label="app=nextcloud",
nextcloud_container="nextcloud",
nextcloud_exec_timeout_sec=30.0,
nextcloud_db_host="",
nextcloud_db_port=5432,
nextcloud_db_name="nextcloud",
nextcloud_db_user="nextcloud",
nextcloud_db_password="",
mailu_domain="bstein.dev",
mailu_host="mail.bstein.dev",
)
monkeypatch.setattr("ariadne.services.nextcloud.settings", dummy)
monkeypatch.setattr("ariadne.services.nextcloud.keycloak_admin.ready", lambda: True)
monkeypatch.setattr("ariadne.services.nextcloud.keycloak_admin.find_user", lambda *_args, **_kwargs: None)
monkeypatch.setattr("ariadne.services.nextcloud.PodExecutor", lambda *_args, **_kwargs: DummyExecutor())
svc = NextcloudService()
result = svc.sync_mail("alice", wait=False)
assert result["status"] == "ok"