Skip to main content
Use archal/vitest when you already have a Vitest project and want hosted twins inside your integration tests. Linear and Ramp twins are available via archal run and archal twin start but don’t yet have route-mode manifests for Vitest.

Install

pnpm add -D vitest archal
archal login
In CI, set ARCHAL_TOKEN instead of running archal login.

Add Archal to your existing vitest.config.ts

If your project already has a vitest.config.ts, wrap the value of test: with withArchal. It’s a merge helper — every field you already had (coverage, alias, globalSetup, poolOptions, custom reporters, etc.) is preserved, and Archal composes its setup file, reporter, and session env on top.
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 twins, use a workspace:
import { archalVitestProject } from 'archal/vitest';

export default [
  './vitest.config.ts', // your existing unit-test project untouched
  archalVitestProject(
    {
      name: 'hosted-twins',
      services: {
        github: { mode: 'route', seed: 'small-project' },
        stripe: { mode: 'route' },
      },
    },
    { include: ['__tests__/hosted/**/*.test.ts'] },
  ),
];
Only tests matching the include glob will provision hosted twins; the rest run as normal Vitest.

Write a test

Your test code uses normal SDK clients or direct HTTP calls. Route mode intercepts the HTTP traffic and redirects it to twins. No base URL configuration, no SDK mocking.
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 twin 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');
  });
});

What to expect on the first run

First vitest run: ~30 seconds while the control plane provisions an ECS-hosted twin in AWS. Subsequent runs within the same 30-minute session TTL finish in ~2 seconds — the session is cached by a stable hash of (name, services, seeds).

Seeds

Seeds control the twin’s starting state. Omit seed: to get a twin’s default empty state. Pass a named seed for a known fixture (e.g. small-project on GitHub gives you one repo with a few issues and PRs). Named seeds are documented per twin in Seeds.

Reset state between tests

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

beforeEach(async () => {
  await resetArchalTwins();
});
This restores each twin 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 twin responses — your test file isn’t matched by the project’s include glob.
  • 401 at setupARCHAL_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 resetArchalTwins() in beforeEach, not beforeAll.
  • CI credential race — when multiple jobs run in parallel, export ARCHAL_TOKEN directly instead of relying on ~/.archal/credentials.json.