@happyvertical/smrt-vitest

Vitest plugin for SMRT projects -- required for all SMRT tests. Auto-generates manifests, loads cross-package class metadata, and provides transaction-isolated test database utilities.

v0.20.44TestingVitestRequired

Overview

@happyvertical/smrt-vitest provides the smrtVitestPlugin() Vite plugin that every SMRT project must include in its vitest.config.ts. Without it, tests fail with "No field metadata found" or "unregistered class" errors.

The plugin auto-generates manifests at startup by scanning source files for @smrt() classes, discovers @happyvertical/smrt-* dependencies, and loads their manifests into the global ObjectRegistry. The package also provides transaction-isolated test database utilities with automatic DB adapter detection (PostgreSQL or SQLite).

Installation

bash
pnpm add -D @happyvertical/smrt-vitest

Required Plugin Setup

typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import { smrtVitestPlugin } from '@happyvertical/smrt-vitest';

export default defineConfig({
  plugins: [smrtVitestPlugin()],
  test: {
    setupFiles: ['@happyvertical/smrt-vitest/setup'] // optional: globalThis isolation
  }
});

Plugin Options

OptionTypeDefaultDescription
generateManifestbooleantrueAuto-generate manifest at startup
includestring[]['src/**/*.ts']Source patterns to scan
excludestring[]['**/*.d.ts', ...]Patterns to exclude
packagesstring[][]Additional packages beyond auto-discovered
verbosebooleanfalseEnable detailed logging
rootstringprocess.cwd()Root directory

What the Plugin Does

  1. Scans src/**/*.ts for SMRT classes via ManifestBuilder
  2. Discovers @happyvertical/smrt-* dependencies from package.json
  3. Loads external manifests via ManifestManager
  4. Registers all classes in ObjectRegistry

Test Database Utilities

DB adapter auto-detection: if DATABASE_URL is set, uses PostgreSQL; otherwise uses SQLite temp files.

FunctionUse Case
createIsolatedTestDbFromManifest()Multi-table tests -- auto-creates schema from manifest with FK ordering and STI dedup (recommended)
createIsolatedTestDb({ schema })Single-table tests -- pass raw DDL with transaction isolation
createTestDb()No transaction isolation (legacy)
getTestDbConfig()Get DB config for current environment
getTestAdapter()Detect adapter: 'postgres' or 'sqlite'
isPostgresAvailable()Check if DATABASE_URL is set

Transaction Isolation Example

typescript
import { createIsolatedTestDbFromManifest } from '@happyvertical/smrt-vitest';

let db, cleanup;

beforeEach(async () => {
  ({ db, cleanup } = await createIsolatedTestDbFromManifest());
});

afterEach(async () => {
  await cleanup(); // Rolls back transaction
});

it('should insert and query', async () => {
  await db.insert('users', { id: '1', name: 'Alice' });
  const user = await db.get('users', { id: '1' });
  expect(user?.name).toBe('Alice');
});

Raw Schema Example

typescript
import { createIsolatedTestDb } from '@happyvertical/smrt-vitest';

let db, cleanup;

beforeEach(async () => {
  ({ db, cleanup } = await createIsolatedTestDb({
    schema: `CREATE TABLE users (id TEXT PRIMARY KEY, name TEXT NOT NULL)`
  }));
});

afterEach(async () => {
  await cleanup();
});

Imperative Setup

For non-Vite setups (e.g., globalSetup files), use setupSmrtManifests() directly. This loads manifests but does not auto-generate them.

typescript
import { setupSmrtManifests } from '@happyvertical/smrt-vitest';

// In a globalSetup file or custom bootstrap
await setupSmrtManifests({ verbose: true });

Singleton Cache Gotcha

Module-level singleton caches (common in SMRT collections) persist across tests, ignoring new mocks. Fix by using vi.resetModules() in beforeEach and dynamic await import(...) in each test instead of top-level imports.