fix(2): mattermost functional tests share a deterministic admin bootstrap (_mm.bootstrap_admin) — only ONE unauthenticated first-user creation is allowed, so the multi-user test no longer collides with create_message
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
47
tests/mattermost-lts/functional/_mm.py
Normal file
47
tests/mattermost-lts/functional/_mm.py
Normal file
@ -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
|
||||||
@ -19,43 +19,20 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "runner"))
|
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
|
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):
|
def test_create_message_roundtrip(live_app):
|
||||||
base = f"https://{live_app}/api/v4"
|
base = f"https://{live_app}/api/v4"
|
||||||
uniq = uuid.uuid4().hex[:10]
|
uniq = uuid.uuid4().hex[:10]
|
||||||
|
|
||||||
# 1) Bootstrap first user (system admin on a fresh server). username: lowercase alnum.
|
# 1-2) Bootstrap the shared system admin (first user on a fresh server, else log in as it).
|
||||||
username = f"ccci{uniq}"
|
# mattermost allows only ONE unauthenticated first-user creation, and several functional tests
|
||||||
email = f"{username}@ccci.example.com"
|
# share this deployment — so all bootstrap the same deterministic admin (see _mm.bootstrap_admin).
|
||||||
status, user = harness_http.http_post(
|
auth = _mm.bearer(_mm.bootstrap_admin(base))
|
||||||
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)
|
|
||||||
|
|
||||||
# 3) Create a team, then an open channel in it.
|
# 3) Create a team, then an open channel in it.
|
||||||
status, team = harness_http.http_post(
|
status, team = harness_http.http_post(
|
||||||
|
|||||||
@ -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
|
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:
|
`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.
|
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.
|
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.
|
4. user_b logs in (its own session token) and GETs the channel's posts.
|
||||||
@ -20,19 +20,15 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "runner"))
|
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
|
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:
|
def _login(base: str, login_id: str) -> str:
|
||||||
status, _, hdrs = harness_http.post_with_headers(
|
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}"
|
assert status == 200, f"login {login_id} failed: HTTP {status}"
|
||||||
token = hdrs.get("Token") or hdrs.get("token")
|
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"
|
base = f"https://{live_app}/api/v4"
|
||||||
uniq = uuid.uuid4().hex[:10]
|
uniq = uuid.uuid4().hex[:10]
|
||||||
|
|
||||||
# 1) user_a = first user (system admin), login, team + channel
|
# 1) user_a = shared system admin; create team + open channel
|
||||||
email_a = f"ccci{uniq}@ccci.example.com"
|
auth_a = _mm.bearer(_mm.bootstrap_admin(base))
|
||||||
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))
|
|
||||||
|
|
||||||
status, team = harness_http.http_post(
|
status, team = harness_http.http_post(
|
||||||
f"{base}/teams",
|
f"{base}/teams",
|
||||||
data={"name": f"t{uniq}", "display_name": f"ccci {uniq}", "type": "O"},
|
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}"
|
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"
|
email_b = f"ccci{uniq}b@ccci.example.com"
|
||||||
status, ub = harness_http.http_post(
|
status, ub = harness_http.http_post(
|
||||||
f"{base}/users",
|
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,
|
headers=auth_a,
|
||||||
timeout=30,
|
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}"
|
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
|
# 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(
|
status, posts = harness_http.http_get(
|
||||||
f"{base}/channels/{chan['id']}/posts", headers=auth_b, timeout=30
|
f"{base}/channels/{chan['id']}/posts", headers=auth_b, timeout=30
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user