Files
computer-playbook/tests/unit/python/roles/web-app-funkwhale/files/test_admin_state.py
T
kevinveenbirkenbachandClaude Opus 5.5 f363c9d075 feat(web-app-funkwhale): theme the instance and converge its administrator
Funkwhale stores its theme in global preferences, so
files/python/design_preferences.py sets the instance name, primary,
accent and background color, ui__custom_css from
templates/custom.css.j2 and the logo and banner as own uploads. It only
writes while ui__custom_css is empty or starts with the role's marker;
custom CSS an administrator wrote reports FOREIGN and stays untouched,
and design off takes back only the role's own values. tasks/main.yml
flushes the compose handlers and includes tasks/01_manager.yml, and
templates/compose.yml.j2 names the api container so container exec can
reach it.

tasks/01_manager.yml probes the administrator with
files/python/admin_state.py (MISSING, DRIFT or OK for active, staff,
superuser and password), creates it with fw users create --superuser
or realigns password and rights with fw users set, the password always
through the task environment. The account is platform_administrator:
meta/users.yml and the ADMIN_* lines of templates/playwright.env.j2 are
identical to feature/store-i18n (3c72c2002a), so the host
administrator's sudo password never becomes a Funkwhale password and
the later merge keeps these hunks clean. store-i18n provisions no
Funkwhale administrator at all.

templates/design.js.j2 follows Funkwhale's own theme switch, and
files/playwright/test-design.js checks the tokens in both modes, the
instance theming with palette, custom CSS and pod name, the start page
hero and sign-in card, the signed-in interface, a theme picked in
Funkwhale, logo, favicon and title, the platform administrator's sign-in
with administration rights and a visitor's refused sign-up, and captures
the gallery. Unit tests cover
both scripts. TODO.md records the Typesense key in the sidecar's argv
and the icons the front fetches from api.iconify.design.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 23:51:09 +02:00

71 lines
2.3 KiB
Python

from __future__ import annotations
import io
import os
import runpy
import sys
import unittest
from contextlib import redirect_stdout
from types import ModuleType
from unittest.mock import MagicMock, patch
from . import PROJECT_ROOT
SCRIPT_PATH = PROJECT_ROOT / "roles/web-app-funkwhale/files/python/admin_state.py"
ENV = {"FUNKWHALE_ADMIN_USERNAME": "alice", "FUNKWHALE_ADMIN_PASSWORD": "s3cret"}
def _fake_modules(user: MagicMock | None) -> dict[str, ModuleType]:
models = ModuleType("funkwhale_api.users.models")
models.User = MagicMock(name="User")
models.User.objects.filter.return_value.first.return_value = user
users = ModuleType("funkwhale_api.users")
users.models = models
api = ModuleType("funkwhale_api")
api.users = users
return {
"funkwhale_api": api,
"funkwhale_api.users": users,
"funkwhale_api.users.models": models,
}
def _user(*, active=True, staff=True, superuser=True, password_ok=True) -> MagicMock:
user = MagicMock(
name="user", is_active=active, is_staff=staff, is_superuser=superuser
)
user.check_password.return_value = password_ok
return user
class TestAdminState(unittest.TestCase):
def _run(self, user: MagicMock | None) -> str:
buf = io.StringIO()
with (
patch.dict(os.environ, ENV, clear=True),
patch.dict(sys.modules, _fake_modules(user)),
redirect_stdout(buf),
):
runpy.run_path(str(SCRIPT_PATH), run_name="__main__")
return buf.getvalue().strip()
def test_reports_a_missing_administrator(self) -> None:
self.assertEqual(self._run(None), "FUNKWHALE_ADMIN=MISSING")
def test_reports_ok_for_an_active_superuser_with_the_current_password(self) -> None:
user = _user()
self.assertEqual(self._run(user), "FUNKWHALE_ADMIN=OK")
user.check_password.assert_called_once_with("s3cret")
def test_reports_drift_on_a_rotated_password(self) -> None:
self.assertEqual(self._run(_user(password_ok=False)), "FUNKWHALE_ADMIN=DRIFT")
def test_reports_drift_on_missing_rights_or_an_inactive_account(self) -> None:
for user in (_user(staff=False), _user(superuser=False), _user(active=False)):
self.assertEqual(self._run(user), "FUNKWHALE_ADMIN=DRIFT")
if __name__ == "__main__":
unittest.main()