Skip to content

feat: createAppHarness and a stub admin built only on core's contract (dune#97) - #3

Closed
zumbrunn wants to merge 6 commits into
mainfrom
feat/stub-admin
Closed

zumbrunn wants to merge 6 commits into
mainfrom
feat/stub-admin

Conversation

@zumbrunn

@zumbrunn zumbrunn commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

Part of duneorg/dune#97 (step 5) and #64. Draft until @dune/core 0.36 is published; needs the core capability stack (duneorg/dune#110, #111).

The phase-4 proof that Dune does not depend on @dune/plugin-admin: a replacement admin built only on what core hands every plugin.

  • createStubAdmin() (@dune/testing/stub-admin): keeps users, sessions and password hashing through services.users / services.sessions / services.passwords, provides auth.session and scheduler.executor, seeds authz tuples through services.authz, and offers its own POST /stub-admin/login, GET /stub-admin/pages and POST /stub-admin/pages/edit (the writer, so it fires onPageUpdate itself). It imports nothing from @dune/plugin-admin. Options: users, cookie name, prefix, declareCookie: false to play a provider that does not declare its cookie.
  • createAppHarness() (@dune/testing/app-harness): the in-memory harness plus the full createDuneApp() app, with request(path) going through plugin routes, the response-transform pipeline and the content catch-all.
  • README and CHANGELOG entries; deno.json exports and import-map entries for the new core subpaths.

Tests (tests/stub_admin_test.ts, 8): the site serves with it installed and core sees its capabilities; login accepts the right password and sets its own cookie; core's transform pipeline recognises its session and only its session (anonymous and unrelated cookies get none, markers scrubbed vs kept); a forged cookie is not a session; a provider that does not declare its cookie is still found; it edits a page and fires onPageUpdate exactly once; a role without edit rights is authenticated but cannot edit; and with no admin the site still serves and nothing admin-related exists. deno task check (fmt, lint, type-check) and deno task test (23) pass against the local capability stack.

What it found in core (fixed in duneorg/dune#111): run against core without those fixes, 4 of the 8 fail. Core hard-wired the admin session cookie to dune_session (a replacement admin got no admin bar, and an editor-rendered response could enter the shared page cache), and only created the authz system when admin.enabled was true (a replacement admin could never authorise a session). RequestAuthenticator now declares sessionCookies, and the authz system is also created when a plugin provides auth.session.

Conformance suite (@dune/testing/conformance): runAdminConformance(driver) checks the contract any admin must meet: one active auth.session; core recognises the editor session and only that (no/unrelated/forged cookie get nothing, markers scrubbed); declared sessionCookies includes the cookie sign-in sets; a plugin mounted before the admin can read auth.session; an edit fires onPageUpdate once; admin.pages/admin.routes mounted behind login, CSRF and permission; with no admin the site serves. The stub passes 8; plugin-admin passes all 9 (plugin-admin#17). tests/conformance_negative_test.ts (5) shows broken admins are rejected for the right reason. 36 tests, deno task check clean.

Page-event checks. The driver gained optional createPage / deletePage; the suite then checks that creating fires onPageCreate and deleting fires onPageDelete, each exactly once with a path relative to the content directory. The stub admin has create and delete routes (its events now carry content-relative paths like core's), a new negative test shows an admin that writes pages silently is rejected, and plugin-admin passes both checks. 39 tests.

Runtime. The stub admin's mount() reads MountApi.runtime instead of the bootstrap result (core#114); all 39 tests pass against it. ConformanceApp.bootstrap stays the full BootstrapResult: that is the test harness, not the plugin contract.

Not in this PR: running the suite in CI against core main and the previous minor; the version bump and the @dune/core ^0.36 pin (release work).

🤖 Generated with Claude Code

zumbrunn and others added 5 commits November 5, 2026 13:28
…in as reference

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eDelete (#97)

Optional createPage/deletePage driver hooks. The stub admin gains create and
delete routes; its events now carry content-relative paths like core's.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
0.36.0 is on JSR; the harness, the stub admin and the conformance suite are
written against its contract.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
zumbrunn added a commit that referenced this pull request Nov 10, 2026
… the split contract (#3, #4, #5)

Lands the whole stack at once: the earlier commits were written against intermediate core APIs and only the top type-checks against @dune/core 0.36.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@zumbrunn

Copy link
Copy Markdown
Member Author

Landed as part of #5 (squash). This stack's intermediate commits were written against intermediate core APIs and do not type-check against @dune/core 0.36 on their own, so the three PRs went in as one change through #5.

@zumbrunn zumbrunn closed this Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant