The development environment runs the same Connect server image used by the managed deployment, with disposable or explicitly development-only state. It supports black-box server tests, native Electron tests, and application tests from sibling repositories without reading the operator's normal Connect profile or credentials.
Connect uses one vocabulary from local iteration through release acceptance:
pnpm testruns JavaScript and TypeScript unit/component tests;pnpm test:fastadds the complete Rust workspace tests with an isolated test-only credential backend;pnpm test:integrationruns browser-storage and accessibility boundaries;pnpm test:system -- --listshows the registered black-box suites;pnpm test:system -- --suite local,relayselects system suites; andpnpm test:allruns every local tier, including all system suites.
The existing e2e:* commands remain focused aliases for individual registered
system suites. The system runner owns build prerequisites and deduplicates them
when several suites are selected. CI, which has already built the workspace,
uses --no-prepare and retains one named step per suite for useful failure
diagnostics.
Previous-release server and provider recovery tests live under
test/upgrade/. GitHub Actions supplies disposable PostgreSQL services, while
the versioned scenario programs own image construction, fixtures, polling,
assertions, diagnostics, and cleanup. Workflow YAML contains no embedded SQL
or test-only servers.
Use the narrowest tier that proves an invariant. Real PostgreSQL, NATS, containers, browsers, Electron, and previous images remain system boundaries; ordinary policy and state-machine defects should fail in the fast tier.
Development authentication replaces the external GitHub or Google identity provider. It does not replace Connect credentials: pairing, connector tokens, application access tokens, refresh rotation, grants, and revocation still use the production protocol.
The environment has four isolation boundaries:
- Compose publishes Connect and NATS on host loopback only.
- Automated tests use a unique Compose project, database volume, random ports, passwords, and relay token for every run.
- Electron tests set
MDBASE_CONNECT_USER_DATA_DIRto a temporary directory, socloud.json, agent state, mirrors, sockets, and collection registration never overlap the normal desktop profile. - The connector loopback API receives a separately allocated port.
Automated cleanup removes the containers, network, database volume, Electron profile, connector state, and fixture collection even when an assertion fails.
The shared lifecycle implementation is
scripts/lib/connect-environment.mjs. Both the persistent development command
and disposable tests use it for Compose invocation, readiness, restart,
diagnostics, and cleanup. scripts/lib/connect-test-environment.mjs is a stable
compatibility facade for consumer repositories.
Start the persistent development stack:
pnpm dev:environment:upThe portal is available at http://127.0.0.1:8787. Its development sign-in
accepts a test name and email without contacting an external identity provider.
Inspect or stop the stack with:
pnpm dev:environment:status
pnpm dev:environment:logs
pnpm dev:environment:downpnpm dev:environment:reset deletes only the named development database volume
and starts a clean stack.
To replay the complete invited-account onboarding flow with a fresh database, a beta invitation, the Rust hosted provider, and the local editor:
pnpm dev:onboarding -- onboarding@example.comOpen the invitation_url printed by the command. The account form schedules a
single starter collection, /getting-started provisions it, and the browser
then opens that collection in the editor. Stop the editor with Ctrl+C; stop the
persistent Connect stack separately with pnpm dev:environment:down.
Launch Electron with a persistent development-only profile:
pnpm dev:desktop:isolatedEnter http://127.0.0.1:8787 in the pairing screen and approve the computer in
the portal. This command uses .tmp/desktop-development-profile, not the
platform's normal Electron profile. To delete only that development profile
before launching:
pnpm dev:desktop:freshSet MDBASE_CONNECT_DEV_USER_DATA when a named profile is useful for a
particular application or scenario.
Run the packaged control-plane contract:
pnpm e2e:containerThis builds deploy/docker/Dockerfile.server, starts real PostgreSQL and NATS,
checks health and portal assets, creates a development identity, completes
pairing, rejects an invalid connector credential, restarts the server, and
proves the genuine credential remains valid.
Run the native desktop against that packaged server:
pnpm e2e:desktop:containerOn headless Linux:
xvfb-run -a pnpm e2e:desktop:containerThe desktop suite launches Electron, Chromium, and the real Rust agent outside Docker. It uses a temporary profile, signs into the packaged portal, approves the computer there, checks the connected account, creates a real fixture collection through Electron, and authorizes a consumer through the portal. It then routes an operation through the Docker relay, verifies application access in both Electron and the portal, revokes it in the portal, exercises the local pause boundary, restarts the Docker server, and verifies recovery.
Run both boundaries with:
pnpm e2e:ecosystemThe existing pnpm e2e:provider suite remains the production hosted-authority
boundary. Its browser scenarios live under scripts/system/provider/, while
the top-level program owns shared provider fixtures and lifecycle. Add new
independent provider journeys as scenario modules instead of extending the
orchestrator. Render staging remains responsible for real external OAuth,
HTTPS, proxy, deployment, and multi-service release acceptance.
Consumer tests should start Connect as an external system and keep application-specific assertions in the consumer repository. During local multi-repository development they can import the lifecycle helper from the adjacent Connect checkout:
import { startConnectTestEnvironment } from
"../mdbase-connect/scripts/lib/connect-test-environment.mjs";
const connect = await startConnectTestEnvironment({
// Accept a development manifest served by the application under test.
allowLocalApps: true
});
try {
// Start the application with connect.serverUrl, then drive its real
// authorization and collection workflow.
} finally {
await connect.close();
}The helper accepts build: false, serverImage, and natsImage when CI has
already built or pulled immutable images. compose(arguments) is available for
restart, stop, log, and fault-injection scenarios. Tests must still create a
development session and complete the normal pairing or OAuth flow rather than
seeding bearer tokens directly.
A consumer that needs hosted collections can supply a disposable provider:
const connect = await startConnectTestEnvironment({
allowLocalApps: true,
hostedProvider: {
// Reachable from the Connect container.
url: `http://host.docker.internal:${provider.port}`,
// Returned to the browser or application.
publicUrl: `http://127.0.0.1:${provider.port}`,
internalToken: provider.internalToken
}
});This mode is restricted to loopback development authentication. The Compose
stack does not reuse a production provider token or expose the fake provider as
a production configuration. TaskNotes uses this interface in
scripts/cloud-e2e.mjs, so its browser vertical slice runs against the packaged
Connect server rather than importing an in-process control plane.
Recommended consumer assertions are:
- the bundled manifest and callback identity are accepted;
- the consent screen shows the exact requested contracts and operations;
- normal create, read, query, update, change, and renewal behavior works;
- records outside the declared contract remain inaccessible;
- reload and server restart preserve the expected application state;
- pause, revocation, stale credentials, and reconnection fail or recover visibly and safely.
Keep fast component tests on @mdbase-dev/connect-dev's in-memory sandbox. Use the
container environment for protocol, persistence, networking, packaging, and
cross-process behavior that an in-memory transport cannot represent.