test(docker): admin save persists across container restart (#7819)#7821
Conversation
The OP reports the symptom on the official Docker image specifically. Adds two layers of coverage to docker.yml's build-test job, driven from inside a container started against the same TEST_TAG the existing test-container step uses: 1. New mocha spec adminSettings_7819.ts under tests/container/specs/api — authenticates against /admin, opens the /settings socket, saves an augmented JSON with an ep_oauth-shaped top-level block, and asserts the next load reply contains the marker. Intentionally leaves the marker on disk so the workflow can inspect it. 2. docker.yml now `docker exec test grep`s for the marker after test-container, then `docker restart`s the container, waits for the health probe, and re-greps. Both checks must pass — the first proves the socket-driven save actually touched the file inside the container layer; the second proves an in-place restart doesn't reset it. A recreate (docker rm + docker run) would wipe the file, but that's expected (image layer) and out of scope. Container is started with `-e ADMIN_PASSWORD=changeme1` so the existing settings.json.docker provisions the admin user; pad.js doesn't touch /admin so the existing API specs are unaffected. test-container timeout bumped 5s → 30s to cover socket connect + save round-trip, and the mocha discovery extension list now includes `ts` so the new spec is picked up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
Review Summary by QodoAdd Docker regression test for admin settings persistence across restart
WalkthroughsDescription• Add Docker container regression test for admin settings persistence • New mocha spec validates socket-driven save writes to disk correctly • Verify settings survive in-place container restart via docker restart • Extend test-container timeout and discovery to support new TypeScript spec Diagramflowchart LR
A["New adminSettings_7819.ts spec"] -->|authenticates| B["Admin /settings socket"]
B -->|saves marker| C["settings.json on disk"]
D["docker.yml workflow"] -->|runs spec| A
D -->|greps marker| C
D -->|docker restart| E["Container restart"]
E -->|greps marker again| C
C -->|marker persists| F["Regression test passes"]
File Changes1. src/tests/container/specs/api/adminSettings_7819.ts
|
Code Review by Qodo
1.
|
…ailures fast (ether#7819) CI failed on ether#7821 with a generic 20s mocha timeout because the spec hit GET /admin/ to grab a session cookie. webaccess.ts only treats paths starting with /admin-auth as requireAdmin — and the container runs with REQUIRE_AUTHENTICATION=false (default), so GET /admin/ never issued a Basic challenge and Set-Cookie was empty. The socket then connected unauthenticated, adminsettings.ts's connection handler returned early without binding any listeners, and the load() promise hung until mocha killed the test with no useful diagnostic. Switch to POST /admin-auth/ (always-requireAdmin, regardless of settings.requireAuthentication). Assert a 2xx with at least one Set-Cookie before proceeding. Add an 8s timeout + meaningful error message to load() so the "session was not admin" failure mode reports immediately instead of burning the suite budget. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Last CI failed because the splice-after-last-} approach landed a comma
between an existing trailing-comma-before-comment and the close brace
of settings.json.docker, producing `, /* … */, "ep_oauth"` — invalid
JSON.
settings.json.docker uses jsonc `/* */` and `//` comments and a
trailing-comma-before-comment-before-close shape that's annoying to
patch from the test side, and the existing isJSONClean has zero
backend coverage so the splice is going through Etherpad's lenient
write path anyway.
Switch to a hand-built minimal-but-viable settings document containing
the ep_oauth block. Three properties hold:
- We're testing the WRITE path, not the synthesis path. Whatever
bytes we send, the next `load` must return verbatim.
- The post-save document must survive `docker restart` (the next
step in docker.yml) — minimal-but-viable means port/users/dbType
are present so Etherpad boots back up and HEALTHCHECK passes.
- The next `load` reply must equal the bytes we saved
(`reply.results === augmented`) — stronger than `.includes()`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Follow-up to #7820. Adds the Docker-container layer of regression coverage for #7819, which the OP confirmed they're hitting on the official Docker image.
Two layers:
src/tests/container/specs/api/adminSettings_7819.ts— mocha spec that runs insidepnpm run test-containeragainst the live Docker image. Authenticates against/admin, opens the/settingssocket, saves an augmented JSON with anep_oauth-shaped top-level block, and asserts the nextloadreply contains the marker. Intentionally leaves the marker on disk so the workflow can verify file persistence..github/workflows/docker.yml—build-testjob now passesADMIN_PASSWORD=changeme1so the spec can authenticate, then aftertest-containeritdocker execs into the container andgreps for the marker (proves the socket-driven save actually wrote to/opt/etherpad-lite/settings.json),docker restarts, waits for the health probe, and re-greps (proves an in-place restart doesn't reset the file — the failure mode a docker-compose user withrestart: alwayswould see on host reboot).A recreate (
docker rm+docker run) would wipe the file becausesettings.jsonlives in the image layer, not on a volume — that's expected behavior in the official Dockerfile and explicitly out of scope here.Why not in #7820
#7820 was merged before this could be added. The two halves are independent: #7820 covers the in-process admin flow + Etherpad restart; this covers the Docker layer + container restart.
Test plan
pnpm run ts-check(passes)Docker / build-testjob exercises the full chain (image build → container start → save via socket → grep marker on disk →docker restart→ re-grep)🤖 Generated with Claude Code