Files
computer-playbook/tests/unit/python/roles/web-app-wordpress/test_multisite_usernames.py
T
kevinveenbirkenbachandClaude Opus 5.5 6df5f2be86 fix(web-app-wordpress): give the MCP subscriber a login multisite accepts
On a multisite network WP-CLI's `user create` runs
wpmu_validate_user_signup(), which only accepts lowercase letters and
digits. The MCP subscriber was declared as `mcp-wordpress`, so every
variant with `multisite.enabled: true` (variant 1) stopped at "Create the
subscriber the MCP adapter authenticates as" with "Usernames can only
contain lowercase letters (a-z) and numbers." (WordPress#1 in runs
40625679270 and 40243434327).

meta/users.yml now declares `mcpwordpress`. The users dict is the one
place the login lives: the role tasks, the MCP basic-auth login
(mcp_client_login) and the MCP client configs (mcp_servers) all read
users[owner].username, and the owner key, hence the token-store key,
stays mcp-web-app-wordpress. The email follows the login.

On an existing single-site install the next deploy creates mcpwordpress,
mints its application password and replaces the stored token; the old
mcp-wordpress subscriber is left in place with its now untracked "mcp"
application password. Remove it with `wp user delete mcp-wordpress`.

test_multisite_usernames mirrors the multisite signup checks (charset,
reserved names, 4-60 characters, not all digits): every account the role
declares passes, and mcp-wordpress, `_`, `.`, uppercase, too short, all
digits, admin and too long are refused. With the old login the test
fails with WordPress's own message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:27:36 +01:00

78 lines
2.5 KiB
Python

"""Every account the WordPress role declares passes the multisite signup rule."""
from __future__ import annotations
import re
import unittest
from utils.cache.users import get_user_defaults
from utils.cache.yaml import load_yaml
from . import PROJECT_ROOT
ROLE_USERS = PROJECT_ROOT / "roles/web-app-wordpress/meta/users.yml"
SIGNUP_CHARSET = re.compile(r"[a-z0-9]+")
DIGITS_ONLY = re.compile(r"[0-9]+")
ILLEGAL_NAMES = frozenset(
{"www", "web", "root", "admin", "main", "invite", "administrator", "files", "blog"}
)
def multisite_refusals(username: str) -> list[str]:
"""The errors ``wpmu_validate_user_signup`` raises for a username.
Args:
username: the login ``wp user create`` receives on a multisite network.
Returns:
One WordPress error message per failed check; empty when the name is accepted.
"""
refusals = []
if not SIGNUP_CHARSET.fullmatch(username):
refusals.append(
"Usernames can only contain lowercase letters (a-z) and numbers."
)
if username in ILLEGAL_NAMES:
refusals.append("Sorry, that username is not allowed.")
if len(username) < 4:
refusals.append("Username must be at least 4 characters.")
if len(username) > 60:
refusals.append("Username may not be longer than 60 characters.")
if DIGITS_ONLY.fullmatch(username):
refusals.append("Sorry, usernames must have letters too!")
return refusals
class TestMultisiteUsernames(unittest.TestCase):
def test_every_declared_account_passes_the_multisite_signup_rule(self):
users = get_user_defaults(roles_dir=str(PROJECT_ROOT / "roles"))
offenders = {
key: refusals
for key in load_yaml(str(ROLE_USERS))
if (refusals := multisite_refusals(str(users[key]["username"])))
}
self.assertEqual({}, offenders)
def test_the_rule_refuses_what_a_multisite_network_refuses(self):
for username in (
"mcp-wordpress",
"mcp_wordpress",
"mcp.wordpress",
"McpWordpress",
"mcp",
"1234",
"admin",
"a" * 61,
):
with self.subTest(username=username):
self.assertNotEqual([], multisite_refusals(username))
def test_the_rule_admits_what_a_multisite_network_admits(self):
for username in ("mcpwordpress", "platformadmin", "biber", "a" * 60):
with self.subTest(username=username):
self.assertEqual([], multisite_refusals(username))
if __name__ == "__main__":
unittest.main()