Black-Box Testing Architecture
SveltyCMS black-box integration and E2E testing architecture, safety rules, and CI execution model.
On this page
SveltyCMS is standardizing on a Black-Box Testing strategy for integration and E2E suites. The goal is to remove reliance on internal code imports (@src/*) from black-box tests so the CMS is validated the way a real user or API consumer experiences it.
Some legacy tests still need migration. The CI contract, however, is already clear: integration and E2E suites should exercise the app through HTTP, browser automation, and production-like runtime boot paths rather than privileged in-process shortcuts.
'bun run test:integration'] GodMode[/God-Mode API: /api/testing\] Login[/Login API: /api/auth/login\] App[SvelteKit API & Pages] DB[(Database Adapter
SQLite/Mongo/SQL)] Runner -- 1. Orchestrate Setup & Wipe --> GodMode Runner -- 2. Authenticate --> Login Runner -- 3. Fetch/Submit Data --> App GodMode -.-> DB Login -.->|Generates State & Cookie| DB App -.-> DB
Why Black-Box Testing?
1. 🛡️ Maximum Security Verification
By removing “backdoor” imports like dbAdapter or Auth from tests, we guarantee that:
- No Side-Loading: Tests cannot bypass security hooks or permission guards.
- Authentic RBAC: Authentication is performed via real API calls (
/api/auth/login), verifying the entire security handshake. - Production Parity: Tests run against a production-built server entry (
node build/index.js) for integration and most E2E jobs, catching SSR and build-specific issues that unit tests miss. - Network Consistency: Standardization on
127.0.0.1:4173for all local testing avoids IPv6/IPv4 resolution inconsistencies across different OS environments.
2. đź§Ş 100% Database Agnosticism
Internal imports often carry database-specific assumptions (e.g., Mongoose schemas). Black-Box testing treats the CMS as a “black box” that responds to HTTP:
- The same test suite works bit-for-bit across MongoDB, PostgreSQL, MariaDB, and SQLite.
- Tests only care about the JSON response, not the underlying SQL or NoSQL implementation.
3. ⚡ Integrated Performance Auditing
Testing over HTTP allows us to measure real-world latency:
- Baseline: Target < 50ms for core API responses.
- Regressions: Automatic alerts if middle-ware overhead (hooks) increases response times.
The “God Mode” Testing API
To allow automated tests to manage state without internal imports, we expose a secure orchestration endpoint:
Endpoint: /api/testing
Guard: Strictly enabled ONLY when TEST_MODE=true environment variable is set.
đź”’ Triple-Lock Security Guard
To ensure this powerful endpoint never becomes a liability, it is protected by three layers of security:
- Build-Time Stripping: The testing API is physically excluded from production builds. The code literally does not exist in the final artifact.
- Cryptographic Handshake: Requires a
x-test-secretheader matching a unique UUID. This secret is now standardized across the Benchmark Matrix, Integration Runner, and Playwright E2E suites via a central configuration. - Network Lock: Strictly refuses any request not originating from
127.0.0.1or::1.
⚡ Benchmarking Integration (Audit Update)
Our black-box methodology extends to the Enterprise Performance Audit. Individual benchmarks now support:
- Dynamic Port Selection: Tests run on isolated ports to prevent cross-process pollution.
- Trend Detection: Every run automatically compares latency against historical data in
history.jsonlwith visual 🟢/🔴 indicators. - Benchmark Stability:
BENCHMARK_STABLEmode andrefreshContent({ mode: "schemas" })keep API-seeded collections (BenchmarkStable,benchmark_authors) synchronized in the temp DB. Filesystem scan benchmarks use isolated paths under.compiledCollections/test/— see Benchmark Collection Isolation. - Auto-Registration: New benchmark modules automatically register their findings in the MDX technical ledger without manual boilerplate.
Available Actions
| Action | Description |
|---|---|
reset |
Wipes the database (clears all collections/tables). |
seed |
Initializes default roles, permissions, and an Admin user. |
create-user |
Idempotently creates test users with specific roles (Editor, Developer). |
insert |
Inserts a test document into a specific collection for contract checks. |
update |
Updates a test document by _id inside a specific collection. |
delete |
Permanently deletes a test document by _id from a specific collection. |
get-user |
Fetches a user by email for state verification. |
get-user-count |
Returns the total number of registered users. |
cleanup |
Surgically wipes specific users or data without resetting the system. |
Test Execution Flow
- Build / harness: Integration uses
tests/integration/harness.ts, which generates or validatesconfig/private.test.tsand starts the production preview. Preferbun run test:integration(builds withCOMPILE_ALL_ADAPTERS=true) or reusebuild/withbun test --timeout 300000 tests/integration/. Vite aliases@config/privatetoconfig/private.test.tsunderTEST_MODE, so local runs never rename or read the user’sconfig/private.ts. - Preview:
TEST_MODE=true node build/index.jsstarts the production build used by CI black-box jobs. Playwright local runs useplaywright.config.tsweb servers on ports4173(ready state) and4174(setup wizard state). When the suite must mimic production setup gating,STRICT_SETUP_CHECK=trueis enabled so completed/setuproutes redirect the same way they do outside test mode. - Orchestrate: Test helper calls
/api/testing(with secure handshake) to prepare the database. - Login: Test helper calls
/api/auth/loginto obtain a session cookie. - Execute: Bun/Playwright performs HTTP requests against the live endpoints.
- Verify: Assertions are made on JSON responses and HTTP status codes.
Playwright CI Scope
The GitHub Actions E2E matrix covers 17 projects across the full admin surface area:
wizard: setup provisioning and post-setup redirect contractauth-setup: authenticated bootstrap and login/logout state generationsignup: authenticated account/profile smokecontent: collection-builder content management smokesystem: dynamic system settings smokea11y: accessibility compliance auditsrbac: role-based access control enforcementlanguage: i18n and locale switchingbranding: tenant-branded login and theme overridesvisual-regression: screenshot baseline comparisonsusers: user CRUD and profile managementbuilder: collection builder journeyspermissions: access management matrixfirstuser: signup and OAuth flowsconfig-routes: access-management, webhooks, automations, data-management, operationsadmin: multi-tenant managementdashboard: widget grid smoke and add-widget flowappearance: per-user overrides, layout prefs, design-system playgroundmedia: media gallery toolbar, search, upload smoke
All 17 E2E projects run in CI via .github/workflows/ci.yml (job e2e-app). See E2E Coverage Matrix for the complete route-to-test mapping.
Security Matrix Testing
Every core endpoint is now triply-verified:
- Admin: Verifies functionality.
- Restricted Role: Verifies
403 Forbidden(RBAC enforcement). - Public: Verifies
401 Unauthorized(Auth enforcement).
🔄 2026 Redirection Logic
Black-box tests now explicitly verify the “Smart Onboarding” redirection flow:
- Empty CMS: Verifies that Admins are redirected to
/config/collectionbuilderimmediately after login. - Error Recovery: Verifies that any failure during collection detection falls back to the root path
/for a safe user experience.
🛡️ Core Verification Principles
To ensure our 100% black-box integration tests are robust, all current and future endpoint test suites MUST explicitly cover these critical boundaries:
1. Multi-Tenancy Security Boundaries
Endpoints managing sensitive data or authentication must be verified against rigorous multi-tenant isolation scenarios:
- Cross-Tenant Spoofing: Prepare two separate tenant contexts (Tenant-A and Tenant-B). Attempt to execute
PUT/DELETEoperations against Tenant-A’s resources while authenticated as Tenant-B. Assert the API strictly enforces a403 Forbiddenresponse. - Missing Context: Dispatch requests lacking necessary tenant payload headers/locals and assert strict rejection (e.g.
500 TENANT_REQUIREDor400).
2. Cache Invalidation Verification (Preventing Stale UI)
A common pitfall in headless architecture is failing to properly clear caches on mutation.
- Read-After-Write Consistency: After a successful
PUTorDELETErequest, immediately issue aGETrequest to verify the stale cache was correctly bypassed/purged and the updated state is returned. - Failure Gracefulness: When possible via environmental simulation (like halting Redis), verify that cache deletion failures do not crash the primary mutation transaction (e.g., the API should still return
200 Successdespite an internal cache warning).
3. Progressive Degradation & DB-Agnostic Fallbacks
When falling back from unified interfaces to adapter-specific implementations (e.g., TokenAdapter), tests should verify both code paths:
- Tests must pass successfully regardless of whether the primary unified interface handles the action or if the system safely falls back to dynamic adapter imports. This guarantees complete DB-agnostic operations.
4. Direct Handler vs. Network Layer (MSW)
For specialized testing of edge cases (e.g. injecting network failures or verifying ESM mocking with vi.hoisted()), these principles are mirrored in our White-Box Unit Tests. However, in the Black-Box suite, verify the Network Layer explicitly (fetching via HTTP) to guarantee that SvelteKit’s parsing, middleware chain, and payload validation operate as a single harmonious unit.
5. Widget Logic Validation (No-Mount Strategy)
To maintain extreme test velocity, widgets are tested as logic units rather than UI components. By isolating the validationSchema from the Svelte component, we verify the data contract without the overhead of DOM mounting.
- Portable Verification: Tests reside within the widget folder (
src/widgets/custom/.../tests/) and execute via Bun. - Strict Contracts: Every widget must pass a “Boundary Chaos” test, ensuring it rejects invalid data types and out-of-range values.
- Schema Parity: Unit tests use the exact same Valibot schema that the production API uses for server-side validation.
Safety Protocol (live data — non-negotiable): Local automated work must never read or write the developer’s config/private.ts and must never connect to the live user database (e.g. sveltycms.db). Source of truth is config/private.test.ts only (isolated names: sveltycms_test, benchmark_shared, e2e_*). Policy: src/utils/private-config-policy.ts. CI (ci.yml) may create an ephemeral private.ts on the runner as a test mirror — never committed or pushed.
Pre-commit / pre-push run scripts/check-test-db-safety.ts: rejects unsafe test DB_NAME, live config pointing at test DBs, and test DB_NAME equal to live private.ts. Manual: bun run scripts/check-test-db-safety.ts.
Pre-push does not run the 4-DB matrix or benchmarks by default (CI-only). Opt in locally with Docker + DB_TYPE=postgresql|mariadb|mongodb bun test --timeout 300000 tests/integration/. Credentials: src/utils/test-db-credentials.ts.
Shared classifier: src/utils/test-db-safety.ts (server, integration runner, hooks). User live collections under config/collections/ (outside test/) and root .compiledCollections/ must not be written by benchmarks — see Benchmark Collection Isolation.
No Backdoor Policy: SveltyCMS has a zero-backdoor policy for security. Integration tests do NOT bypass authentication via:
- ❌ API keys or bearer tokens
- ❌ Test-only bypass headers (e.g.,
x-test-token) - ❌ Hardcoded credentials
Tests authenticate like real users by calling /api/auth/login with seeded test credentials. The /api/testing endpoint is used only for state orchestration and is removed in production builds.