diff --git a/tests/mattermost-lts/functional/_mm.py b/tests/mattermost-lts/functional/_mm.py new file mode 100644 index 0000000..a8db99e --- /dev/null +++ b/tests/mattermost-lts/functional/_mm.py @@ -0,0 +1,47 @@ +"""Shared mattermost-lts test helpers. + +mattermost lets the FIRST user be created unauthenticated (and makes them system admin); after that, +open signups are disabled (`api.user.create_user.no_open_server`). Several functional tests share one +per-run deployment (the custom tier), so they cannot each create "the first user." Instead they all +bootstrap ONE deterministic admin: whichever test runs first creates it (201, as the first user); the +rest log in as the same admin. Subsequent (non-first) users are created via the admin token, which +works regardless of the open-signup setting. +""" + +from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "runner")) +from harness import http as harness_http # noqa: E402 + +ADMIN_EMAIL = "ccci-admin@ccci.example.com" +ADMIN_USERNAME = "ccciadmin" +ADMIN_PW = "Ccci-Test-Pw-2026!" + + +def bearer(token: str) -> dict[str, str]: + return {"Authorization": f"Bearer {token}"} + + +def bootstrap_admin(base: str) -> str: + """Return a system-admin session token for the shared deployment. + + Create the deterministic admin as the first user if the server is fresh (201); otherwise it + already exists (400/403) — log in as it. Either way we end with a valid admin token. RAISES if + neither create nor login yields a usable session (a genuinely broken auth path).""" + # Try to create the first user (unauthenticated; only succeeds on a fresh server). + harness_http.http_post( + f"{base}/users", + data={"email": ADMIN_EMAIL, "username": ADMIN_USERNAME, "password": ADMIN_PW}, + timeout=30, + ) + # Whether or not creation succeeded (it 4xxs if the admin already exists / signups closed), log in. + status, _, hdrs = harness_http.post_with_headers( + f"{base}/users/login", data={"login_id": ADMIN_EMAIL, "password": ADMIN_PW}, timeout=30 + ) + assert status == 200, f"admin login failed: HTTP {status}" + token = hdrs.get("Token") or hdrs.get("token") + assert token, f"admin login returned no Token header; headers={list(hdrs.keys())}" + return token diff --git a/tests/mattermost-lts/functional/test_create_message.py b/tests/mattermost-lts/functional/test_create_message.py index 9174964..a3efdd0 100644 --- a/tests/mattermost-lts/functional/test_create_message.py +++ b/tests/mattermost-lts/functional/test_create_message.py @@ -19,43 +19,20 @@ import os import sys import uuid +sys.path.insert(0, os.path.dirname(__file__)) sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "runner")) +import _mm # noqa: E402 from harness import http as harness_http # noqa: E402 -# Password must satisfy mattermost's default policy (>=5 chars; use a clearly strong one). -_ADMIN_PW = "Ccci-Test-Pw-2026!" - - -def _bearer(token: str) -> dict[str, str]: - return {"Authorization": f"Bearer {token}"} - def test_create_message_roundtrip(live_app): base = f"https://{live_app}/api/v4" uniq = uuid.uuid4().hex[:10] - # 1) Bootstrap first user (system admin on a fresh server). username: lowercase alnum. - username = f"ccci{uniq}" - email = f"{username}@ccci.example.com" - status, user = harness_http.http_post( - f"{base}/users", - data={"email": email, "username": username, "password": _ADMIN_PW}, - timeout=30, - ) - assert status in (200, 201) and isinstance(user, dict) and user.get("id"), ( - f"first-user creation failed: HTTP {status}, body={user!r}" - ) - - # 2) Login → token in the `Token` response header. - status, _, hdrs = harness_http.post_with_headers( - f"{base}/users/login", - data={"login_id": email, "password": _ADMIN_PW}, - timeout=30, - ) - assert status == 200, f"login failed: HTTP {status}" - token = hdrs.get("Token") or hdrs.get("token") - assert token, f"login returned no Token header; headers={list(hdrs.keys())}" - auth = _bearer(token) + # 1-2) Bootstrap the shared system admin (first user on a fresh server, else log in as it). + # mattermost allows only ONE unauthenticated first-user creation, and several functional tests + # share this deployment — so all bootstrap the same deterministic admin (see _mm.bootstrap_admin). + auth = _mm.bearer(_mm.bootstrap_admin(base)) # 3) Create a team, then an open channel in it. status, team = harness_http.http_post( diff --git a/tests/mattermost-lts/functional/test_multiuser_message.py b/tests/mattermost-lts/functional/test_multiuser_message.py index c619612..b9903dc 100644 --- a/tests/mattermost-lts/functional/test_multiuser_message.py +++ b/tests/mattermost-lts/functional/test_multiuser_message.py @@ -4,7 +4,7 @@ The defining behaviour of a team-chat platform is that a message one user posts readable by *another* user in the same channel — not just round-tripped by its own author (that is `test_create_message.py`). This exercises the real membership + post-delivery path end-to-end: - 1. Bootstrap user_a (first user = system admin) → login → create team + open channel. + 1. Bootstrap the shared system admin (user_a) → create team + open channel. 2. user_a posts a unique marker message to the channel. 3. Create a SECOND user (user_b) via the admin API; add user_b to the team + the channel. 4. user_b logs in (its own session token) and GETs the channel's posts. @@ -20,19 +20,15 @@ import os import sys import uuid +sys.path.insert(0, os.path.dirname(__file__)) sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "runner")) +import _mm # noqa: E402 from harness import http as harness_http # noqa: E402 -_PW = "Ccci-Test-Pw-2026!" - - -def _bearer(token: str) -> dict[str, str]: - return {"Authorization": f"Bearer {token}"} - def _login(base: str, login_id: str) -> str: status, _, hdrs = harness_http.post_with_headers( - f"{base}/users/login", data={"login_id": login_id, "password": _PW}, timeout=30 + f"{base}/users/login", data={"login_id": login_id, "password": _mm.ADMIN_PW}, timeout=30 ) assert status == 200, f"login {login_id} failed: HTTP {status}" token = hdrs.get("Token") or hdrs.get("token") @@ -44,16 +40,8 @@ def test_second_user_reads_first_users_message(live_app): base = f"https://{live_app}/api/v4" uniq = uuid.uuid4().hex[:10] - # 1) user_a = first user (system admin), login, team + channel - email_a = f"ccci{uniq}@ccci.example.com" - status, ua = harness_http.http_post( - f"{base}/users", - data={"email": email_a, "username": f"ccci{uniq}", "password": _PW}, - timeout=30, - ) - assert status in (200, 201) and ua.get("id"), f"user_a create HTTP {status}: {ua!r}" - auth_a = _bearer(_login(base, email_a)) - + # 1) user_a = shared system admin; create team + open channel + auth_a = _mm.bearer(_mm.bootstrap_admin(base)) status, team = harness_http.http_post( f"{base}/teams", data={"name": f"t{uniq}", "display_name": f"ccci {uniq}", "type": "O"}, @@ -76,11 +64,11 @@ def test_second_user_reads_first_users_message(live_app): ) assert status in (200, 201) and post.get("id"), f"post create HTTP {status}: {post!r}" - # 3) create user_b (admin API) + add to team + channel + # 3) create user_b (admin API — works with open-signup off) + add to team + channel email_b = f"ccci{uniq}b@ccci.example.com" status, ub = harness_http.http_post( f"{base}/users", - data={"email": email_b, "username": f"ccci{uniq}b", "password": _PW}, + data={"email": email_b, "username": f"ccci{uniq}b", "password": _mm.ADMIN_PW}, headers=auth_a, timeout=30, ) @@ -101,7 +89,7 @@ def test_second_user_reads_first_users_message(live_app): assert status in (200, 201), f"add user_b to channel HTTP {status}" # 4) user_b logs in (own session) and reads the channel posts - auth_b = _bearer(_login(base, email_b)) + auth_b = _mm.bearer(_login(base, email_b)) status, posts = harness_http.http_get( f"{base}/channels/{chan['id']}/posts", headers=auth_b, timeout=30 )