Testing and validation
Every behavior that changes authorization, rendering mode, schemas, document persistence, theme migration, plugin declaration, or MCP visibility needs an automated test in its owning package.
Test count is not a quality target. Add a focused test for both the expected path and the security/failure boundary whenever a behavior changes.
Required checks
Run these commands from the repository root before committing:
pnpm test
pnpm check
pnpm build
pnpm test runs Vitest contract/adapter/security tests, real-workerd Cloudflare suites, and Chromium E2E. pnpm check runs TypeScript checks and astro check; pnpm build proves every workspace package type-checks and that the Morning Bell Astro example builds for the Cloudflare adapter. See Security validation for the binding and browser coverage.
The build also bundles every dedicated Worker with wrangler deploy --dry-run, including tenant workspace, plugin sandbox, and the physically separate public/private MCP services. The Astro deployment can be dry-run from its generated dist/server/wrangler.json, proving that static assets, D1, R2 media, session secrets, and remote service/Object namespaces do not collide.
Test boundaries
- Contracts: schema limits, the complete built-in marketing component catalog, page collection invariants, theme migration reports, role permissions, plugin manifests, and sensitive data-scope rules.
- Runtime: published requests bypass authentication; preview/edit require authorization and are private/no-store.
- Content and hosting adapter: draft-only changes, schema validation, section identity, optimistic versions, actor attribution, and publish delegation.
- Theme migration: explicit loss/addition decisions, schema-approved default sections, and whole-collection migration that preserves stable page identities and routes.
- Theme persistence: validated site pins, paired migrated-draft calls, paired publication, cross-tenant rejection, and real-SQLite rollback/commit of a matching multi-page resource and theme pin.
- Forms: published-definition field allowlists, type checks, required values, and bounded text input.
- Site resources: navigation identity/limits, deep settings patches, and duplicate/cyclic redirect rejection.
- Page lifecycle: every approved template, stable duplicate identities, route/SEO updates, navigation synchronization, redirect creation/retargeting, total-order validation, and protected home/unique slug invariants.
- Resource persistence: schema revalidation, optimistic resource versions, independent publish state, actor attribution, and tenant-scope rejection.
- Atomic page bundle persistence: real SQLite transaction commit for pages/navigation/redirects, rollback on any stale version, and atomic publication of all three resources. The tenant-user workerd drill performs this through the private managed-dispatch command while a second tenant uses colliding site identity.
- Guarded commands: tenant scope, permission denial, explicit confirmations, exclusive concurrent claims, failure release, idempotent replay, tenant-scoped ledger/audit persistence, shared-D1 platform claim/replay/input-digest rejection/failure audit, digest-bound AI dry-run plans, stale/tampered-plan denial, atomic provenance, and confirmed reversible restore.
- Editor reference flow: content, approved variant and structural drafts, idempotent replay, confirmed publication, and published-snapshot isolation.
- MCP gateways: public tool/resource/prompt filtering, generated component/document JSON Schema resources, discoverability filtering, stable public/private page/slot search citations, private typed navigation/settings/form-definition/version resources, sensitive-store exclusion, real protocol discovery, public rate limits, and private tools routed through guarded commands.
- MCP OAuth: supported-scope filtering, write-scope removal for viewer roles, one-time upstream state, same-origin/CSRF consent, grant ownership checks, and revocation.
- Structural editing: schema-default insertion, component/section/row identity, placement policy, destination validity, move/remove/duplicate policy, one-level responsive row creation/configuration/movement/unwrapping, nested placement denial, and the tighter structural/placement instance limit.
- Rich text: legacy string compatibility, bounded portable spans, escaped rendering, safe-link protocols, approved marks only, mutation revalidation, and browser formatting persistence.
- Assets: tenant/site-scoped keys, per-kind MIME/size policy, image/video/document signatures, exact streamed-byte validation through a real-R2-compatible fixed-length stream, attachment disposition, immutable metadata, opaque public delivery, idempotent deletion, and cross-tenant workerd denial.
- External embeds: approved YouTube URL/iframe normalization, exact-host and HTTPS enforcement, malformed/lookalike rejection, raw-HTML elimination, canonical privacy-enhanced rendering, and upload/embed mutual exclusion.
- Auth: signed session integrity and expiration.
- Browser session boundary: signed-cookie identity conversion, strict cookie attributes, missing/cross-origin mutation rejection.
- OAuth and provisioning: S256 PKCE/state hashing, one-time/expired state, bounded token/UserInfo exchange, external identity mapping, revocable sessions, verified idempotent site creation, and conflict detection.
- Media pipeline: tenant-scoped allocation, streaming byte limits, image-signature mismatch quarantine, transformation bounds, cross-tenant rejection, schema-validated replacement, and immutable delivery metadata.
- CMS management: bounded JSON bodies, D1 invitation conflict checks, typed resource limits, form-definition validation, guarded resource publishing/restore, and reserved-path public-cache exclusion.
- Sensitive managed queries: viewer draft grants remain read-only; submission, audit, and plugin queries require their exact role permission inside the tenant Worker and cannot accept tenant/site selectors.
- Managed public forms: fixed same-origin form paths, 32 KiB request bounds, pseudonymous subject validation, published-schema validation, tenant-local rate/quota enforcement, and two-tenant workerd isolation for stored values.
- Publish operations: atomic page/navigation/redirect/theme publication, four-version scheduled snapshots, tenant-local managed scheduling, version-addressed public indexing, cache invalidation, after-publish hooks, schedule version conflicts, supersession, bounded backoff, retry/dead-letter state, and confirmed status actions.
- Operations: real-R2 checksummed tenant backup/restore, managed tenant-Worker recovery dispatch, cross-tenant backup denial, per-user editor mutation quotas, usage accounting, read-only routing state, fixed retention tables, and private asynchronous cross-store erasure boundaries.
- Managed lifecycle: checksum-pinned operator R2 releases, trusted deploy/upgrade/rollback artifacts, service-token and namespace pinning, immediate suspension, exact confirmation, export-before-erase ordering, bounded tenant-export streaming, independent archive checksum/tenant/Worker verification, operator-only restore and erase grants, empty-target transactional restore, provider identity preflight, foreign-consumer denial, exact script/Queue/R2 deletion, retry-safe 404 handling, and failed tombstones after partial cleanup.
- Hosted service: host-neutral orchestration success/failure, exact DNS challenges, private deployer-binding responses, D1 environment/deployment/domain state, secret-reference-only persistence, grant role/expiry checks, and support-session denial without an active grant.
- Tenant execution isolation: immutable tenant-to-Worker mappings, ready-state-only hostname resolution, reserved-header stripping, platform-owned public/private/event contexts, revocable-session and authoritative-role grants, platform-cookie filtering in both directions, exact Queue/tenant/site tuple resolution, operation/version claims, CPU/subrequest limits, no invocation for unresolved tenants, and provider-detail-free dispatch failures.
- Tenant user Workers: deployment-time tenant pinning, operator-only complete site initialization with exact-retry/conflict behavior, public-audience-only published reads, query-site spoofing resistance, fixed local DO selection, no public draft mutations, guarded browser section/variant/structure/resource/media commands, permission- and OAuth-scope-pinned MCP reads and idempotent mutations, publication confirmation, declarative SQLite export/restore, cross-tenant operator-grant rejection, verified backup streaming, SQLite and tenant-local-R2 erasure, exact-name idempotent R2/Queue creation, sole-consumer enforcement, unique binding metadata, bounded provider/MCP responses, exact provider identity checks, trusted-artifact gating, and failed-provision recording. A real-workerd drill seeds two distinct tenant workspace identities with the same site/plugin IDs, uploads/serves/deletes tenant A media, edits and publishes tenant A, and confirms public, private editor, private plugin MCP, operator export, and forged cross-tenant archive restore cannot expose, mutate, or import tenant B data.
- Managed Astro bridge: fixed internal data/command/platform/auth paths, original-host preservation, minimal cookie/origin/idempotency/confirmation forwarding, caller tenant-header removal, dispatcher-owned PKCE/session lifecycle, no-D1 SSR mode selection, unauthorized-hint published fallback, dispatcher-generated tenant-scoped form subjects, interception before tenant code, private no-store responses, higher private CPU ceiling, and generated-config denial of shared D1/R2/KV/DO/sandbox/operator/secrets capabilities.
- Plugin supply chain: normalized release signatures, signer allowlists, artifact digest binding/recomputation, immutable idempotent artifact installation, tamper/conflict rejection, declarative migration limits, explicit sandbox service calls, clean validation result handling, and a real-workerd non-public sandbox lifecycle.
- Cloudflare private boundary: host-derived scope, permission checks, cross-origin rejection before session work, cross-tenant non-disclosure, and private/no-store failures.
- MCP: public/private catalog filtering and role-gated engagement tools.
- Plugin SDK: manifest validation at plugin definition time, including MCP tool/resource/prompt access policies.
- Plugin runtime: declared-capability checks, declared/implemented hook matching, bounded hook execution, declared storage namespaces, full tenant-local install/upgrade/rollback/disable/enable/uninstall behavior, first-class public/private MCP resource and prompt discovery, exact resource collection allowlists, current-role authorization, and personal-data denial.
- CLI scaffolds and deployment profiles: portable names, path containment, build/secret exclusion, atomic five-page site creation, built-in component/theme seeds, EmDash-compatible plugin plus AI/MCP sidecar generation, dry-run and overwrite controls, command parsing, two-to-five self-hosted Worker profile selection, optional service-binding removal, diagnostics, every generated artifact shape, and a read-only managed-isolated v2 preflight with reserved tenant names, clean release pinning, fail-closed binding checks, stable entitlement codes, private evidence files, and raw-output redaction.
- Agency five-page browser flow: all five public routes, shared navigation, no anonymous edit metadata, theme marker, valid contact submission, browser workspace entry, responsive canvas sizing, direct inline and rich-text formatting, schema-limited palette state, drag/drop insertion and nested responsive rows with server-rendered replacement, selection/inspection, non-home draft isolation, preview, and publication. Browser acceptance also creates a Services page, verifies its four typed components and draft navigation, renames and publishes it, then deletes it and verifies both former paths redirect to the chosen surviving page.
- Project sites: the documentation and marketing sites define explicit desktop, tablet, and mobile layouts; navigation remains keyboard- and touch-operable; primary targets meet a 44-pixel minimum; hover enhancements are limited to hover-capable pointers; and reduced-motion preferences disable decorative movement.
The real-workerd gate now covers D1 migrations/routing, SQLite Durable Object identity isolation, R2 key separation and checksummed backup restore, Cache API invalidation, queue delivery, public KV indexing, mutation quotas, and public MCP Streamable HTTP startup. Full upstream OAuth exchange against an installation’s chosen identity provider, destructive disposable-tenant drills, and deployed Cloudflare account smoke tests remain environment-specific release checks; local tests cover their cryptographic, state, mapping, scope, authorization, and binding boundaries.