Files
cgalo5758 41993a2fe2 Refuse any argument but --rebuild in reset-app-db.sh and its neighbours
reset-app-db.sh read one argument, --rebuild, and ignored any other,
so a mistaken argument such as --help restored the application
database from the demo snapshot and flushed the session store.
teardown-stack.sh and reset-test-db.sh take no argument and ignored
any that was given; teardown-stack.sh then removes the stack's
containers and volumes and deletes test/.env, test/secrets/ and
test/testdata/, and reset-test-db.sh drops and recreates the test
databases. Each script now prints its usage line on stderr and exits 2
for an argument it does not take (for reset-app-db.sh, anything but a
single --rebuild), before it reads anything. No argument, and
--rebuild for reset-app-db.sh, behave as before.

TestScriptsRefuseUnknownArguments runs a copy of each script from a
temporary directory with no environment file beside it, with the
database, container and git commands replaced by stubs that record
their name and fail. It has 15 leaf subtests: 7 for reset-app-db.sh and
4 each for reset-test-db.sh and teardown-stack.sh. At the parent
the same test file fails on the 5 refusal rows of reset-app-db.sh and
the 3 of each neighbour (11 in all), each exiting 1 where 2 and the
usage line are wanted, and passes the 4 accepted rows. At the commit
all of them pass.

15 breaks, each caught by the new test (new side only; the test is new,
so it has no parent side). Not probed: the real scripts were not run
with an argument, since a script that accepted one would reset the
application database or delete the stack's keys; and the real
reset-app-db.sh and teardown-stack.sh were not run with no argument or
--rebuild after the change, for the same reason. The accepted path
past the environment-file check is shown only by the copies stopping
at the missing file (for reset-app-db.sh and reset-test-db.sh) or at
their first git call (for teardown-stack.sh), and reset-test-db.sh
with no argument was run once for real and succeeded.

Also not probed: no break moves the guard after the first read in any
of the three scripts, so the guard's position is checked by the
refusal rows but not probed. For reset-app-db.sh and reset-test-db.sh
the shims' record is a backstop that no single-line edit reaches,
because a copy that gets past the guard stops at the missing .env
before it calls any shim; teardown-stack.sh's accepted row does reach
the git shim.
2026-10-11 21:47:51 -05:00
..

Test Environment

This directory contains the Docker Compose setup and test data for local development.

LLM agents: read AGENTS.md first. All commands below are run from this test/ directory. If you are working in a git worktree alongside other worktrees of this repo, you must run ./bootstrap-stack.sh before docker compose up to avoid host port collisions. One exception: the FedWiki HTTPS proxy (Caddy on 443) is a cross-stack singleton among stacks that compose it — see "Running multiple isolated stacks" below.

Quick Start

cd test
./bootstrap-stack.sh
docker compose up -d
set -a; . .env; set +a
go run .. start --config mc-config.yaml

That composes the core stack: Postgres, Valkey, Keycloak, Temporal. Every integration (FedWiki, Discourse) sits behind a compose profile and is off by default, so the console boots without them.

To compose one, set the COMPOSE_PROFILES knob — either at bootstrap time:

COMPOSE_PROFILES=fedwiki ./bootstrap-stack.sh

or by editing that one line in the generated test/.env and re-running docker compose up -d. Then uncomment that integration's MC_* block in the same file so the app is configured for it, not merely running beside it. See AGENTS.md.

To run the Go test suite against its own pristine databases (not the app's), use make test from the repo root. See AGENTS.md.

Running multiple isolated stacks (parallel worktrees)

compose.yaml accepts per-stack overrides via .env. The bootstrap-stack.sh helper generates one with a deterministic, collision-free port allocation derived from the worktree's directory name. Run the four Quick Start commands above in worktree A and worktree B — the two stacks coexist on disjoint host ports under separate COMPOSE_PROJECT_NAME namespaces.

Exception — the HTTPS farm is a singleton. This applies only to stacks that select the fedwiki profile; core-only stacks never contend for the port. Browser access to FedWiki sites goes through a Caddy TLS proxy on host port 443 (wiki-security-social sets Secure cookies, which browsers only store over HTTPS). That port is fixed, not slot-allocated, so only one composing stack can serve browser login at a time — a second such stack's caddy container fails to bind 443 while the rest of its services come up normally. A slot-allocated port wouldn't help anyway: better-auth scopes cookies to the shared *.localtest.me domain (per-domain, not per-port — RFC 6265), so parallel stacks would clobber each other's session cookies regardless. Everything non-browser stays fully isolated: member-console's farm API calls use plain HTTP over the slot-allocated FEDWIKI_PORT with a bearer token (no cookies). True per-stack browser isolation needs a per-stack farm domain — tracked as forge issue #48.

To return a worktree to a clean state (containers + volumes + .env + secrets/ + testdata/):

./teardown-stack.sh

See AGENTS.md for the full contract, including the Stripe webhook constraint when running stacks in parallel.

Services

URLs below are the single-stack defaults; after bootstrap-stack.sh the host ports and *.localhost hostnames are per-stack (see the generated .env).

Core services, always composed:

Service URL Credentials
Member Console http://localhost:8081 (via Keycloak)
Keycloak http://keycloak.localhost:8080 admin / admin
Temporal UI http://localhost:8233 (via Keycloak)
PostgreSQL localhost:5432 member_console / member_console
Valkey localhost:6379 —

Integration services, composed only when their profile is selected:

Service Profile URL Credentials
FedWiki (browser) fedwiki https://admin.localtest.me (via Keycloak)
FedWiki API fedwiki http://admin.localtest.me (bearer token, see the MC_FEDWIKI_* block in .env)
Discourse discourse http://discourse.localhost:9292 (via Keycloak)

All credentials above are throwaway test-only values — never reuse any of them in a real deployment.

Browser access to FedWiki is HTTPS via the Caddy proxy (Caddy's internal CA — to trust it for manual browsing, export the root once: docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt ./caddy-root.crt). See AGENTS.md for the HTTPS/cert details.

Test Data

Seed identity

The database generates every ID, so a seed never supplies one; it addresses the rows it owns by key (grammar ^[a-z][a-z0-9_]*$) and upserts on that key, which is what makes re-running a seed a no-op. mockshot-seed.sql and mockshot-nuke.sql are the deployment-scale sample-data pair used for screenshots: both work from the mockshot_ organization key prefix and the mockshot: OIDC subject prefix, so the nuke removes exactly what the seed created. The four demo users' Keycloak ids are pinned as literals in seed/keycloak/seed-keycloak.sh, and the demo seeder mirrors them as its persons' OIDC subjects.

The testdata/ directory is named like that so that gopls can ignore it by default. It contains sample data for testing the Member Console, such as:

Keycloak

Keycloak runs with start-dev (an ephemeral H2 database, no persistent volume). It does not import a realm file. Instead, the one-shot keycloak-seed service runs seed/keycloak/seed-keycloak.sh on every docker compose up, which — authenticating as the master-realm bootstrap admin — creates a dedicated test app realm and seeds its clients, roles, protocol mappers, client scopes, and test users over the Keycloak admin API (idempotent). App OIDC runs on test, not Keycloak's master admin realm, so the end-user account console works.

Seeded into the test realm:

  • Clients: member-console, fedwiki, temporal-ui, temporal-authz (plus service accounts)
  • Users: alice (operator), bob, carlos, diana — password password

To change the realm setup, edit seed/keycloak/seed-keycloak.sh — there is no realm export/import step. (An old testdata/keycloak/master-realm.json export remains on disk for reference but is untracked and unused by compose.)

FedWiki

Everything in this section applies only when the fedwiki profile is selected.

FedWiki runtime data lives in data/fedwiki/ (gitignored status/ dirs). Seed data for owner files and access tokens is committed in seed/fedwiki/.

A fedwiki-init container automatically copies seed files into data/fedwiki/ on docker compose up without overwriting existing files (cp -rn). This means:

  • Fresh clone: seed data is copied in automatically, no manual steps needed.
  • Existing setup: your local data is preserved.

To update seed data after making changes (e.g., new tokens or wiki sites):

# Copy the relevant files from data/fedwiki/ to seed/fedwiki/
cp data/fedwiki/<site>/status/owner.json seed/fedwiki/<site>/status/
cp data/fedwiki/<site>/status/user-access-tokens.json seed/fedwiki/<site>/status/