Skip to main content
Use archal/vitest when you want hosted clones inside an existing Vitest suite. Your tests keep using normal SDK clients; Archal supplies the clone session and routes supported service traffic during the test run.
archal/vitest needs an auth token. Run archal login locally, or set ARCHAL_TOKEN in CI. For CI, use a workspace API key (archal_ws_...); for local dev, a personal token (arc_...) or archal login works. The first hosted session takes about 30 seconds; later runs within the TTL reuse it.

Install

pnpm add -D vitest archal
archal login
In CI, set ARCHAL_TOKEN instead of running archal login. For team CI pipelines, use a workspace API key (archal_ws_...) created by an owner or admin via archal workspace api-key create <label> --scope sessions:write. Workspace keys are already bound to a workspace, so ARCHAL_WORKSPACE_ID is usually unnecessary. See the CI integration guide for full details. Route mode supports Discord, GitHub, Google Workspace, Jira, Linear, Ramp’s primary API domain, Slack, Stripe, and Supabase.

Add Archal to your existing vitest.config.ts

If your project already has a vitest.config.ts, wrap test: with withArchal. It preserves your existing test config and adds Archal’s setup, reporter, and session env.
import { defineConfig } from 'vitest/config';
import { withArchal } from 'archal/vitest';

export default defineConfig({
  test: withArchal(
    {
      // everything you already had in test:, unchanged
      globals: true,
      setupFiles: ['./test/my-setup.ts'],
      coverage: { provider: 'v8' },
    },
    {
      services: {
        github: { mode: 'route', seed: 'small-project' },
        stripe: { mode: 'route' },
      },
    },
  ),
});
If you’re starting from scratch, pass {} as the first argument: withArchal({}, { services: { ... } }).

Or add a separate project in vitest.workspace.ts

For monorepos or when only a subset of tests should hit clones, use a workspace:
// vitest.config.ts - root config required for dashboard uploads
import { defineConfig } from 'vitest/config';
import { archalVitestRootConfig } from 'archal/vitest';

export default defineConfig(archalVitestRootConfig());
// vitest.workspace.ts
import { archalVitestProject } from 'archal/vitest';

export default [
  './vitest.config.ts', // your existing unit-test project untouched
  archalVitestProject(
    {
      name: 'hosted-clones',
      services: {
        github: { mode: 'route', seed: 'small-project' },
        stripe: { mode: 'route' },
      },
    },
    { include: ['__tests__/hosted/**/*.test.ts'] },
  ),
];
Only tests matching the include glob will provision hosted clones; the rest run as normal Vitest.
Workspace mode requires a root vitest.config.ts with archalVitestRootConfig(). Vitest only honors reporters from the root config, so without it route mode still works but dashboard uploads are skipped. If you see no results on /dashboard/tests, check that the root config is present.

Write a test

Your test code uses normal SDK clients or direct HTTP calls. Route mode redirects supported service traffic to clones, so there is no SDK mock or base URL setup.
import { Octokit } from '@octokit/rest';
import { describe, it, expect } from 'vitest';

describe('issue triage', () => {
  it('creates and labels an issue', async () => {
    // route mode redirects api.github.com calls to the clone automatically
    const github = new Octokit();
    const { data: issue } = await github.issues.create({
      owner: 'acme',
      repo: 'webapp',
      title: 'Bug report',
    });
    expect(issue.title).toBe('Bug report');
  });
});
First run takes about 30 seconds. Subsequent runs reuse the cached session.

Seeds

Seeds control starting state. Omit seed: for the default state, or pass a named seed such as small-project. See Seeds.

Reset state between tests

import { beforeEach } from 'vitest';
import { resetArchalClones } from 'archal/vitest';

beforeEach(async () => {
  await resetArchalClones();
});
This restores each clone to its post-seed state and drains the webhook queue. No cold-start - it’s a local state snapshot reapplied to the existing session.

Inspect the session

If you need to check which services, seeds, or versions the backend resolved:
import { getInstalledArchalVitestSession } from 'archal/vitest';

const session = getInstalledArchalVitestSession();
console.log(session?.resolvedRuntime.resolvedServices);
console.log(session?.resolvedRuntime.resolvedSeeds);

Troubleshooting

  • Real API responses instead of clone responses - your test file isn’t matched by the project’s include glob.
  • 401 at setup - ARCHAL_TOKEN isn’t set (or archal login wasn’t run).
  • Test hangs for 30+ seconds on first run - that’s the ECS cold-start, not a hang.
  • Reset isn’t working - call resetArchalClones() in beforeEach, not beforeAll.
  • CI credential race - when multiple jobs run in parallel, export ARCHAL_TOKEN directly instead of relying on ~/.archal/credentials.json.