Repository URLs, version strings, and example creds normalized for the public git.sulkta.com endpoint. No code-behavior change. Audit-applied by the public-flip rolling-audit pass — see kayos/openclaw-workspace memory/2026-05-27 logs for the campaign context.
281 lines
10 KiB
Python
281 lines
10 KiB
Python
"""Live read-only smoke test against a real ISPConfig panel.
|
|
|
|
Gated on env vars: ``ISPCONFIG_TEST_URL``, ``ISPCONFIG_TEST_USER``,
|
|
``ISPCONFIG_TEST_PASS``. These tests are skipped if any is unset.
|
|
|
|
They are read-only — no ``_add`` / ``_update`` / ``_delete`` calls. Safe to
|
|
run against production.
|
|
|
|
Two further env vars target a known record on your panel so the hand-audited
|
|
helpers have something to fetch:
|
|
|
|
* ``ISPCONFIG_TEST_DOMAIN_ID`` — a ``web_domain`` primary id (int).
|
|
* ``ISPCONFIG_TEST_DOMAIN_NAME`` — the matching ``domain`` string + DNS zone
|
|
origin (e.g. ``example.com``).
|
|
|
|
If either is missing, the domain/dns/mail tests below are skipped — only
|
|
the auto-generated module probes run, since those don't need a known record.
|
|
Methods that the API user lacks permission for (admin-only, etc.) skip
|
|
cleanly — see the README's "Known admin-only" list.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from collections.abc import Iterator
|
|
|
|
import pytest
|
|
|
|
from ispconfig import ISPConfigClient, NotFoundError, PermissionError
|
|
|
|
|
|
@pytest.fixture()
|
|
def client(live_creds: dict[str, str]) -> Iterator[ISPConfigClient]:
|
|
verify = live_creds.get("verify", "1") not in ("0", "false", "False")
|
|
with ISPConfigClient(
|
|
live_creds["url"],
|
|
live_creds["user"],
|
|
live_creds["password"],
|
|
verify_ssl=verify,
|
|
) as c:
|
|
yield c
|
|
|
|
|
|
def _known_domain_id() -> int:
|
|
val = os.environ.get("ISPCONFIG_TEST_DOMAIN_ID")
|
|
if not val:
|
|
pytest.skip("set ISPCONFIG_TEST_DOMAIN_ID to a real web_domain primary id")
|
|
return int(val)
|
|
|
|
|
|
def _known_domain_name() -> str:
|
|
val = os.environ.get("ISPCONFIG_TEST_DOMAIN_NAME")
|
|
if not val:
|
|
pytest.skip("set ISPCONFIG_TEST_DOMAIN_NAME to the matching domain/zone origin")
|
|
return val
|
|
|
|
|
|
# ---- hand-audited modules (first pass) -----------------------------------
|
|
|
|
|
|
def test_login_returns_session(live_creds: dict[str, str]) -> None:
|
|
verify = live_creds.get("verify", "1") not in ("0", "false", "False")
|
|
c = ISPConfigClient(live_creds["url"], live_creds["user"], live_creds["password"], verify_ssl=verify)
|
|
c.login()
|
|
assert c.session_id and len(c.session_id) > 10
|
|
assert c.logout() is True
|
|
|
|
|
|
def test_get_known_domain(client: ISPConfigClient) -> None:
|
|
domain_id = _known_domain_id()
|
|
expected_name = _known_domain_name()
|
|
site = client.sites.web_domain_get(domain_id)
|
|
assert site["domain"] == expected_name
|
|
|
|
|
|
def test_dns_zone_lookup(client: ISPConfigClient) -> None:
|
|
expected_name = _known_domain_name()
|
|
# Pass with a trailing dot to exercise the wrapper's stripping behavior.
|
|
zone_id = client.dns.zone_get_id(expected_name + ".")
|
|
assert zone_id > 0, "zone_get_id returned 0 — is the zone present?"
|
|
|
|
|
|
def test_mail_users_filter_returns_list(client: ISPConfigClient) -> None:
|
|
# mail_user_get accepts a filter-dict and returns a list.
|
|
expected_name = _known_domain_name()
|
|
users = client.mail.user_get({"email": f"%@{expected_name}"})
|
|
assert isinstance(users, list)
|
|
# Don't assert count — just shape. Zero mailboxes is a valid state.
|
|
for u in users:
|
|
assert "email" in u
|
|
|
|
|
|
# ---- auto-generated modules: one read-only probe each -------------------
|
|
#
|
|
# These prove the wrappers encode/decode correctly against a real panel.
|
|
# Each test tolerates the method being restricted to admin — reseller logins
|
|
# fault with "permission denied" on those, and we skip with a clear reason.
|
|
|
|
|
|
def test_raw_call_list_functions(client: ISPConfigClient) -> None:
|
|
"""``list_functions`` is the introspection escape hatch — sanity check it."""
|
|
funcs = client.list_functions()
|
|
assert "sites_web_domain_get" in funcs
|
|
assert "mail_user_get" in funcs
|
|
# 300+ is typical for a modern ISPConfig.
|
|
assert len(funcs) > 200
|
|
|
|
|
|
def test_raw_call_escape_hatch(client: ISPConfigClient) -> None:
|
|
"""``raw_call`` must route through the same retry/fault-mapping pipeline."""
|
|
# ``client_get_all`` is a no-arg read.
|
|
ids = client.raw_call("client_get_all")
|
|
assert ids is None or isinstance(ids, (list, dict))
|
|
|
|
|
|
def test_clients_client_get_all(client: ISPConfigClient) -> None:
|
|
ids = client.clients.get_all()
|
|
assert isinstance(ids, list)
|
|
# Don't assert count — panel may be empty of managed clients.
|
|
|
|
|
|
def test_clients_templates_get_all(client: ISPConfigClient) -> None:
|
|
try:
|
|
tpls = client.clients.client_templates_get_all()
|
|
except PermissionError:
|
|
pytest.skip("client_templates_get_all: admin-only on this panel")
|
|
assert tpls is None or isinstance(tpls, (list, dict))
|
|
|
|
|
|
def test_server_get_all(client: ISPConfigClient) -> None:
|
|
try:
|
|
servers = client.server.server_get_all()
|
|
except PermissionError:
|
|
pytest.skip("server_get_all: admin-only on this panel")
|
|
assert servers is None or isinstance(servers, (list, dict))
|
|
|
|
|
|
def test_server_get_functions(client: ISPConfigClient) -> None:
|
|
"""Pick the first server id and ask which modules it runs."""
|
|
try:
|
|
servers = client.server.server_get_all()
|
|
except PermissionError:
|
|
pytest.skip("server_get_all: admin-only on this panel")
|
|
if not servers:
|
|
pytest.skip("no servers visible to this API user")
|
|
# ``server_get_all`` returns a list of server records. Grab the first id.
|
|
first = servers[0] if isinstance(servers, list) else next(iter(servers.values()))
|
|
server_id = int(first.get("server_id") if isinstance(first, dict) else first)
|
|
try:
|
|
fns = client.server.server_get_functions(server_id)
|
|
except PermissionError:
|
|
pytest.skip("server_get_functions: admin-only on this panel")
|
|
assert fns is None or isinstance(fns, (list, dict))
|
|
|
|
|
|
def test_monitor_jobqueue_count(client: ISPConfigClient) -> None:
|
|
try:
|
|
n = client.monitor.monitor_jobqueue_count()
|
|
except PermissionError:
|
|
pytest.skip("monitor_jobqueue_count: admin-only on this panel")
|
|
# Returns an int-ish; ISPConfig may stringify.
|
|
assert n is None or isinstance(n, (int, str, dict, list))
|
|
|
|
|
|
def test_admin_system_config_get(client: ISPConfigClient) -> None:
|
|
try:
|
|
cfg = client.admin.system_config_get("mail")
|
|
except PermissionError:
|
|
pytest.skip("system_config_get: admin-only on this panel")
|
|
assert cfg is None or isinstance(cfg, (dict, list, str))
|
|
|
|
|
|
def test_ftp_user_get_missing(client: ISPConfigClient) -> None:
|
|
"""Nonexistent primary_id → ``NotFoundError`` via the fault-map path."""
|
|
try:
|
|
result = client.ftp.sites_ftp_user_get(999_999_999)
|
|
except NotFoundError:
|
|
return # expected
|
|
except PermissionError:
|
|
pytest.skip("sites_ftp_user_get: admin-only on this panel")
|
|
# Some panels return None/empty instead of a fault.
|
|
assert result in (None, {}, "", [])
|
|
|
|
|
|
def test_shell_user_get_missing(client: ISPConfigClient) -> None:
|
|
try:
|
|
result = client.shell.sites_shell_user_get(999_999_999)
|
|
except NotFoundError:
|
|
return
|
|
except PermissionError:
|
|
pytest.skip("sites_shell_user_get: admin-only on this panel")
|
|
assert result in (None, {}, "", [])
|
|
|
|
|
|
def test_webdav_user_get_missing(client: ISPConfigClient) -> None:
|
|
try:
|
|
result = client.webdav.sites_webdav_user_get(999_999_999)
|
|
except NotFoundError:
|
|
return
|
|
except PermissionError:
|
|
pytest.skip("sites_webdav_user_get: admin-only on this panel")
|
|
assert result in (None, {}, "", [])
|
|
|
|
|
|
def test_cron_get_missing(client: ISPConfigClient) -> None:
|
|
try:
|
|
result = client.cron.sites_cron_get(999_999_999)
|
|
except NotFoundError:
|
|
return
|
|
except PermissionError:
|
|
pytest.skip("sites_cron_get: admin-only on this panel")
|
|
assert result in (None, {}, "", [])
|
|
|
|
|
|
def test_backups_list(client: ISPConfigClient) -> None:
|
|
"""``sites_web_domain_backup_list`` on a known domain."""
|
|
domain_id = _known_domain_id()
|
|
try:
|
|
result = client.backups.sites_web_domain_backup_list(domain_id)
|
|
except PermissionError:
|
|
pytest.skip("sites_web_domain_backup_list: admin-only on this panel")
|
|
except NotFoundError:
|
|
pytest.skip(f"no backups configured for domain {domain_id}")
|
|
assert result is None or isinstance(result, (list, dict))
|
|
|
|
|
|
def test_aps_available_packages_list(client: ISPConfigClient) -> None:
|
|
try:
|
|
pkgs = client.aps.sites_aps_available_packages_list()
|
|
except PermissionError:
|
|
pytest.skip("sites_aps_available_packages_list: admin-only on this panel")
|
|
except NotFoundError:
|
|
pytest.skip("APS not initialized on this panel")
|
|
assert pkgs is None or isinstance(pkgs, (list, dict))
|
|
|
|
|
|
def test_domains_get_all_by_user(client: ISPConfigClient) -> None:
|
|
"""``domains`` is the (optional) domain-module — may not be installed."""
|
|
try:
|
|
# group 1 = admin
|
|
domains = client.domains.domains_get_all_by_user(1)
|
|
except PermissionError:
|
|
pytest.skip("domains_get_all_by_user: admin-only or module disabled")
|
|
except NotFoundError:
|
|
pytest.skip("domains module not installed")
|
|
assert domains is None or isinstance(domains, (list, dict))
|
|
|
|
|
|
def test_openvz_get_free_ip(client: ISPConfigClient) -> None:
|
|
"""OpenVZ module may not be installed — skip cleanly if so."""
|
|
try:
|
|
ip = client.openvz.openvz_get_free_ip()
|
|
except PermissionError:
|
|
pytest.skip("openvz_get_free_ip: admin-only or OpenVZ not installed")
|
|
except NotFoundError:
|
|
pytest.skip("OpenVZ not installed / no free IPs")
|
|
assert ip is None or isinstance(ip, (str, dict, list))
|
|
|
|
|
|
def test_misc_quota_get_by_user(client: ISPConfigClient) -> None:
|
|
"""``quota_get_by_user`` — look up one visible client, query its quota."""
|
|
try:
|
|
ids = client.clients.get_all()
|
|
except PermissionError:
|
|
pytest.skip("client_get_all: admin-only on this panel")
|
|
if not ids:
|
|
pytest.skip("no clients visible to this API user")
|
|
client_id = int(ids[0])
|
|
try:
|
|
group_id = client.clients.get_groupid(client_id)
|
|
except (PermissionError, NotFoundError):
|
|
pytest.skip("client_get_groupid: unavailable")
|
|
if not group_id:
|
|
pytest.skip("couldn't resolve group id for first visible client")
|
|
try:
|
|
quota = client.misc.quota_get_by_user(group_id)
|
|
except PermissionError:
|
|
pytest.skip("quota_get_by_user: admin-only on this panel")
|
|
except NotFoundError:
|
|
pytest.skip("no quota record for this user")
|
|
assert quota is None or isinstance(quota, (list, dict))
|