Push builds have been RED on the lint step since ~build 209 from accumulated formatting drift. This is the mechanical cleanup: ruff format + ruff --fix (UP038 isinstance unions, SIM105 contextlib.suppress, UP031 f-strings, SIM115 tempfile context manager), shfmt -i 2 -ci, nixpkgs-fmt/statix/deadnix (merged attrsets, dropped unused lib args), yamllint, and shell quoting fixes in tests/lasuite-docs/setup_custom_tests.sh. No behaviour changes intended; lint: PASS, unit tests: 138 passed.
74 lines
3.7 KiB
Python
74 lines
3.7 KiB
Python
"""Shared discourse test helpers — admin user + API key + JSON HTTP.
|
|
|
|
The bitnamilegacy/discourse recipe (compose app env) sets ONLY the DB + SMTP vars — it does NOT seed
|
|
an admin user, and Discourse's Admin API requires `Api-Key` + `Api-Username` headers (there is no
|
|
auto-created API key). So the functional tests bootstrap their own admin by running Rails inside the
|
|
`app` container (Discourse ships its Rails env at /opt/bitnami/discourse): find-or-create an admin
|
|
user, then create an ApiKey and print its plaintext `.key` (Discourse returns the plaintext only at
|
|
create time; it's stored hashed). Both the admin user and the key are class-B run-scoped — they live
|
|
only in the per-run app and are destroyed at teardown.
|
|
|
|
`mint_admin(domain)` returns (api_key, api_username). Each call creates a fresh ApiKey (cheap;
|
|
idempotent enough — the shared deployment's functional tests each mint their own), reusing the same
|
|
admin user across calls. Uses `lifecycle.exec_in_app` (hardened exec with retry).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "runner"))
|
|
from harness import lifecycle # noqa: E402
|
|
|
|
# Rails snippet (single line): find-or-create an admin, create an ApiKey, print key + username as the
|
|
# last two lines. SecureRandom is available in the Rails runtime. We mark the user active + approved
|
|
# so the API accepts it. created_by_id must be set (ApiKey validates it).
|
|
#
|
|
# We also enable `allow_uncategorized_topics` (a standard Discourse feature, off by default since 3.x):
|
|
# without it, POST /posts.json with no category 422s "Category can't be blank". This is config parity
|
|
# with a real forum (the operator would either enable uncategorized or pick a category), not a test
|
|
# weakening — the create-topic round-trip still posts a real topic and asserts a unique marker survives.
|
|
_BOOTSTRAP_RB = (
|
|
"SiteSetting.allow_uncategorized_topics = true; "
|
|
"u = User.where(admin: true).order(:id).first; "
|
|
"if u.nil?; "
|
|
"u = User.create!(username: 'ccciadmin', name: 'CCCI Admin', "
|
|
"email: 'ccciadmin@ccci.example.com', password: SecureRandom.hex(20), "
|
|
"active: true, approved: true, trust_level: 1); "
|
|
"u.update!(admin: true); u.activate; "
|
|
"end; "
|
|
"k = ApiKey.create!(description: 'ccci-run', created_by_id: u.id); "
|
|
"puts 'CCCI_API_KEY=' + k.key; "
|
|
"puts 'CCCI_API_USER=' + u.username"
|
|
)
|
|
|
|
|
|
def mint_admin(domain: str) -> tuple[str, str]:
|
|
"""Bootstrap an admin + fresh API key via Rails in the app container. Returns (api_key, username)."""
|
|
# `bin/rails` is `#!/usr/bin/env ruby`; the bitnami discourse image keeps ruby at
|
|
# /opt/bitnami/ruby/bin, which is NOT on a login shell's PATH (`bash -lc` resets PATH from
|
|
# /etc/profile → `env: 'ruby': No such file or directory`, rc=127). Use a non-login shell, discover
|
|
# ruby (image-ENV PATH first, bitnami fallback), and invoke it explicitly so the shebang is moot.
|
|
cmd = (
|
|
"cd /opt/bitnami/discourse && "
|
|
"RUBY=$(command -v ruby || echo /opt/bitnami/ruby/bin/ruby) && "
|
|
f'RAILS_ENV=production "$RUBY" bin/rails runner "{_BOOTSTRAP_RB}"'
|
|
)
|
|
out = lifecycle.exec_in_app(domain, ["bash", "-c", cmd], service="app", timeout=240)
|
|
key = user = None
|
|
for line in out.splitlines():
|
|
line = line.strip()
|
|
if line.startswith("CCCI_API_KEY="):
|
|
key = line.split("=", 1)[1].strip()
|
|
elif line.startswith("CCCI_API_USER="):
|
|
user = line.split("=", 1)[1].strip()
|
|
assert (
|
|
key and user
|
|
), f"could not bootstrap discourse admin/API key; rails output tail:\n{out[-1000:]}"
|
|
return key, user
|
|
|
|
|
|
def admin_headers(api_key: str, api_username: str) -> dict[str, str]:
|
|
return {"Api-Key": api_key, "Api-Username": api_username}
|