Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions packages/cli/test/vitest-tiers-partition.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* The two tiers of this package's suite stay a PARTITION, and the integration
* list stays equal to what the files DO (#13504).
*
* `vitest.config.ts` splits the suite into two named projects — `unit` (the
* local default) and `integration` (spawns the real CLI or boots a real
* kernel/driver; CI-mandatory, local on demand). Two things can rot under a
* split like that, and both rot silently, which is why this pin exists:
*
* 1. A test file that matches NO project is not run by `vitest run` at all —
* not by the fast tier AND not by `pnpm test` in CI, because with
* `projects` configured the root run IS the union of the projects. A file
* matching BOTH runs twice and reports twice. So the first two cases hold
* `unit ⊎ integration = every test file on disk`, read from vitest's own
* resolution (`vitest list --filesOnly`, with and without `--project`)
* against a filesystem walk — the config's spelling is judged by what
* vitest actually collects, never by re-reading the config.
*
* 2. `INTEGRATION_FILES` is an explicit list, and the `*.e2e.test.ts` NAME is
* not the predicate (the ACCEPT on #13504 measured 18 of 220 files where
* name and behaviour disagree). So the third case re-derives the tier of
* every file from its comment-masked SOURCE and fails when the list and
* the derivation disagree — a new spawner cannot land in the fast tier
* unnoticed, and a stale entry cannot linger. The predicate, in code
* position (comments masked by `scripts/js-comment-mask.mjs`):
*
* SPAWN = calls `runServe(` (the helper in `test/helpers/serve-process.ts`
* whose body spawns the source entry), OR value-imports
* `node:child_process` AND (names an entry basename — the
* `run-dev` / `run` scripts under `bin/` — OR imports `CLI` /
* `TSX` from that helper OR names the `tsx` binary under
* `node_modules/.bin`);
* KERNEL = value-imports `bootSchemaStack` from `schema-migrate`, OR
* value-imports `better-sqlite3`, OR value-imports any
* `@objectstack/driver-*` package, OR constructs `new ObjectQL(`.
* INTEGRATION = SPAWN ∨ KERNEL.
*
* Value imports only: `import type { … } from '@objectstack/driver-sql'`
* loads nothing, a spelling list that SAYS `'better-sqlite3'` opens no
* database, and `expect(deps).toContain('better-sqlite3')` boots nothing —
* every one of those was a false positive of the text-match census this
* predicate replaced. An import statement is one `import … from '<spec>'`
* span containing neither `;` nor another `from` (every import in this
* package's tests ends in `;`, measured on 00ff228fe0).
*
* The fourth case classifies THIS file: it imports `node:child_process` (to
* ask vitest for its file lists) and must still read as `unit`, which is the
* predicate's own regression test against matching its own source.
*
* Runs in the `unit` tier and needs no built `dist/`: it spawns `vitest list`,
* which only globs, and reads sources.
*/

import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
import { childEnv } from './helpers/serve-process.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const PKG = resolve(HERE, '..');
const THIS_FILE = relative(PKG, fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const VITEST_ENTRY = resolve(dirname(require.resolve('vitest/package.json')), 'vitest.mjs');

// ---------------------------------------------------------------------------
// The predicate
// ---------------------------------------------------------------------------

interface ValueImport {
clause: string;
spec: string;
}

/** One `import … from '<spec>'` statement; `import type` is skipped. */
const IMPORT_RE = /\bimport\s+(?!type\b)((?:(?!\bfrom\b)[^;])*?)\bfrom\s+['"]([^'"]+)['"]/g;

function valueImports(code: string): ValueImport[] {
const out: ValueImport[] = [];
for (const m of code.matchAll(IMPORT_RE)) out.push({ clause: m[1], spec: m[2] });
return out;
}

/** Inline `type X` specifiers do not make a value import of `X`. */
function importsValue(imports: ValueImport[], spec: RegExp, name?: RegExp): boolean {
return imports.some((i) => spec.test(i.spec) && (!name || name.test(i.clause.replace(/\btype\s+\w+/g, ''))));
}

export interface TierSignals {
runServe: boolean;
childProcess: boolean;
entryBasename: boolean;
helperCliOrTsx: boolean;
tsxBin: boolean;
bootSchemaStack: boolean;
betterSqlite3: boolean;
driverPackage: boolean;
objectQLCtor: boolean;
}

export function tierSignals(maskedCode: string): TierSignals {
const imports = valueImports(maskedCode);
return {
runServe: /\brunServe\s*[(]/.test(maskedCode),
childProcess: importsValue(imports, /^(?:node:)?child_process$/),
entryBasename: /\brun(?:-dev)?[.]js\b/.test(maskedCode),
helperCliOrTsx: importsValue(imports, /helpers\/serve-process(?:\.js)?$/, /\b(?:CLI|TSX)\b/),
tsxBin: /[.]bin[/]tsx\b/.test(maskedCode),
bootSchemaStack: importsValue(imports, /schema-migrate(?:\.js)?$/, /\bbootSchemaStack\b/),
betterSqlite3: importsValue(imports, /^better-sqlite3$/) || /require[(]\s*['"]better-sqlite3['"]\s*[)]/.test(maskedCode),
driverPackage: importsValue(imports, /^@objectstack\/driver-/),
objectQLCtor: /new\s+ObjectQL\s*[(]/.test(maskedCode),
};
}

export function isIntegration(s: TierSignals): boolean {
const spawn = s.runServe || (s.childProcess && (s.entryBasename || s.helperCliOrTsx || s.tsxBin));
const kernel = s.bootSchemaStack || s.betterSqlite3 || s.driverPackage || s.objectQLCtor;
return spawn || kernel;
}

function firedSignals(s: TierSignals): string {
return (Object.keys(s) as Array<keyof TierSignals>).filter((k) => s[k]).join(', ') || 'none';
}

// ---------------------------------------------------------------------------
// The two readings: the filesystem, and vitest's own resolution
// ---------------------------------------------------------------------------

const TEST_FILE_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', 'coverage']);

function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (SKIP_DIRS.has(entry.name)) continue;
const abs = join(dir, entry.name);
if (entry.isDirectory()) walk(abs, out);
else if (TEST_FILE_RE.test(entry.name)) out.push(relative(PKG, abs));
}
return out;
}

/** `vitest list --filesOnly [--project NAME]`, one relative path per line. */
function vitestFiles(project?: string): string[] {
const args = [VITEST_ENTRY, 'list', '--filesOnly', ...(project ? ['--project', project] : [])];
const out = execFileSync(process.execPath, args, {
cwd: PKG,
env: childEnv(),
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return out
.split('\n')
.map((line) => line.trim())
.filter(Boolean)
.map((line) => line.replace(/^\[[^\]]+\]\s+/, ''));
}

/** The explicit list as `vitest.config.ts` declares it — read as text, not imported. */
function declaredIntegrationFiles(): string[] {
const masked = maskComments(readFileSync(join(PKG, 'vitest.config.ts'), 'utf8'));
const block = /INTEGRATION_FILES\s*=\s*\[([^\]]*)\]/.exec(masked);
if (!block) throw new Error('vitest.config.ts no longer declares `INTEGRATION_FILES = [ … ]`');
return Array.from(block[1].matchAll(/['"]([^'"]+)['"]/g), (m) => m[1]);
}

const sorted = (xs: Iterable<string>): string[] => [...xs].sort();

describe('the two tiers of packages/cli (#13504)', () => {
const onDisk = sorted(walk(PKG));
const all = sorted(vitestFiles());
const unit = sorted(vitestFiles('unit'));
const integration = sorted(vitestFiles('integration'));

it('every test file on disk is one vitest collects with no --project (what `pnpm test` runs)', () => {
expect(onDisk.length).toBeGreaterThan(100);
expect(all, 'vitest run collects a different population than the filesystem holds').toEqual(onDisk);
});

it('unit and integration partition that population — no file in both, none in neither', () => {
const inBoth = unit.filter((f) => integration.includes(f));
expect(inBoth, 'files matched by BOTH projects (they would run and report twice)').toEqual([]);
const union = sorted([...unit, ...integration]);
expect(union, 'files matched by NEITHER project fall out of every tier, including CI').toEqual(all);
});

it('INTEGRATION_FILES equals the behavioural predicate over every file on disk', () => {
const declared = declaredIntegrationFiles();
expect(sorted(new Set(declared)), 'INTEGRATION_FILES carries a duplicate').toEqual(sorted(declared));
expect(sorted(declared), 'an INTEGRATION_FILES entry names no file vitest can find').toEqual(integration);

const missing: string[] = [];
const stale: string[] = [];
for (const file of onDisk) {
const signals = tierSignals(maskComments(readFileSync(join(PKG, file), 'utf8')));
const predicted = isIntegration(signals);
const listed = integration.includes(file);
if (predicted && !listed) missing.push(`${file} [${firedSignals(signals)}]`);
if (!predicted && listed) stale.push(file);
}
expect(
missing,
'files that spawn the CLI or boot a kernel/driver but are NOT in INTEGRATION_FILES (add them)',
).toEqual([]);
expect(stale, 'INTEGRATION_FILES entries that neither spawn nor boot (remove them)').toEqual([]);
});

it('this pin is itself unit-tier: importing child_process to ask vitest is not spawning the CLI', () => {
const signals = tierSignals(maskComments(readFileSync(join(PKG, THIS_FILE), 'utf8')));
expect(signals.childProcess).toBe(true);
expect(isIntegration(signals), `fired: ${firedSignals(signals)}`).toBe(false);
expect(unit).toContain(THIS_FILE);
});
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions packages/cli/test/vitest-tiers-partition.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* The two tiers of this package's suite stay a PARTITION, and the integration
* list stays equal to what the files DO (#13504).
*
* `vitest.config.ts` splits the suite into two named projects — `unit` (the
* local default) and `integration` (spawns the real CLI or boots a real
* kernel/driver; CI-mandatory, local on demand). Two things can rot under a
* split like that, and both rot silently, which is why this pin exists:
*
* 1. A test file that matches NO project is not run by `vitest run` at all —
* not by the fast tier AND not by `pnpm test` in CI, because with
* `projects` configured the root run IS the union of the projects. A file
* matching BOTH runs twice and reports twice. So the first two cases hold
* `unit ⊎ integration = every test file on disk`, read from vitest's own
* resolution (`vitest list --filesOnly`, with and without `--project`)
* against a filesystem walk — the config's spelling is judged by what
* vitest actually collects, never by re-reading the config.
*
* 2. `INTEGRATION_FILES` is an explicit list, and the `*.e2e.test.ts` NAME is
* not the predicate (the ACCEPT on #13504 measured 18 of 220 files where
* name and behaviour disagree). So the third case re-derives the tier of
* every file from its comment-masked SOURCE and fails when the list and
* the derivation disagree — a new spawner cannot land in the fast tier
* unnoticed, and a stale entry cannot linger. The predicate, in code
* position (comments masked by `scripts/js-comment-mask.mjs`):
*
* SPAWN = calls `runServe(` (the helper in `test/helpers/serve-process.ts`
* whose body spawns the source entry), OR value-imports
* `node:child_process` AND (names an entry basename — the
* `run-dev` / `run` scripts under `bin/` — OR imports `CLI` /
* `TSX` from that helper OR names the `tsx` binary under
* `node_modules/.bin`);
* KERNEL = value-imports `bootSchemaStack` from `schema-migrate`, OR
* value-imports `better-sqlite3`, OR value-imports any
* `@objectstack/driver-*` package, OR constructs `new ObjectQL(`.
* INTEGRATION = SPAWN ∨ KERNEL.
*
* Value imports only: `import type { … } from '@objectstack/driver-sql'`
* loads nothing, a spelling list that SAYS `'better-sqlite3'` opens no
* database, and `expect(deps).toContain('better-sqlite3')` boots nothing —
* every one of those was a false positive of the text-match census this
* predicate replaced. An import statement is one `import … from '<spec>'`
* span containing neither `;` nor another `from` (every import in this
* package's tests ends in `;`, measured on 00ff228fe0).
*
* The fourth case classifies THIS file: it imports `node:child_process` (to
* ask vitest for its file lists) and must still read as `unit`, which is the
* predicate's own regression test against matching its own source.
*
* Runs in the `unit` tier and needs no built `dist/`: it spawns `vitest list`,
* which only globs, and reads sources.
*/

import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
import { childEnv } from './helpers/serve-process.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const PKG = resolve(HERE, '..');
const THIS_FILE = relative(PKG, fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const VITEST_ENTRY = resolve(dirname(require.resolve('vitest/package.json')), 'vitest.mjs');

// ---------------------------------------------------------------------------
// The predicate
// ---------------------------------------------------------------------------

interface ValueImport {
clause: string;
spec: string;
}

/** One `import … from '<spec>'` statement; `import type` is skipped. */
const IMPORT_RE = /\bimport\s+(?!type\b)((?:(?!\bfrom\b)[^;])*?)\bfrom\s+['"]([^'"]+)['"]/g;

function valueImports(code: string): ValueImport[] {
const out: ValueImport[] = [];
for (const m of code.matchAll(IMPORT_RE)) out.push({ clause: m[1], spec: m[2] });
return out;
}

/** Inline `type X` specifiers do not make a value import of `X`. */
function importsValue(imports: ValueImport[], spec: RegExp, name?: RegExp): boolean {
return imports.some((i) => spec.test(i.spec) && (!name || name.test(i.clause.replace(/\btype\s+\w+/g, ''))));
}

export interface TierSignals {
runServe: boolean;
childProcess: boolean;
entryBasename: boolean;
helperCliOrTsx: boolean;
tsxBin: boolean;
bootSchemaStack: boolean;
betterSqlite3: boolean;
driverPackage: boolean;
objectQLCtor: boolean;
}

export function tierSignals(maskedCode: string): TierSignals {
const imports = valueImports(maskedCode);
return {
runServe: /\brunServe\s*[(]/.test(maskedCode),
childProcess: importsValue(imports, /^(?:node:)?child_process$/),
entryBasename: /\brun(?:-dev)?[.]js\b/.test(maskedCode),
helperCliOrTsx: importsValue(imports, /helpers\/serve-process(?:\.js)?$/, /\b(?:CLI|TSX)\b/),
tsxBin: /[.]bin[/]tsx\b/.test(maskedCode),
bootSchemaStack: importsValue(imports, /schema-migrate(?:\.js)?$/, /\bbootSchemaStack\b/),
betterSqlite3: importsValue(imports, /^better-sqlite3$/) || /require[(]\s*['"]better-sqlite3['"]\s*[)]/.test(maskedCode),
driverPackage: importsValue(imports, /^@objectstack\/driver-/),
objectQLCtor: /new\s+ObjectQL\s*[(]/.test(maskedCode),
};
}

export function isIntegration(s: TierSignals): boolean {
const spawn = s.runServe || (s.childProcess && (s.entryBasename || s.helperCliOrTsx || s.tsxBin));
const kernel = s.bootSchemaStack || s.betterSqlite3 || s.driverPackage || s.objectQLCtor;
return spawn || kernel;
}

function firedSignals(s: TierSignals): string {
return (Object.keys(s) as Array<keyof TierSignals>).filter((k) => s[k]).join(', ') || 'none';
}

// ---------------------------------------------------------------------------
// The two readings: the filesystem, and vitest's own resolution
// ---------------------------------------------------------------------------

const TEST_FILE_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', 'coverage']);

function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (SKIP_DIRS.has(entry.name)) continue;
const abs = join(dir, entry.name);
if (entry.isDirectory()) walk(abs, out);
else if (TEST_FILE_RE.test(entry.name)) out.push(relative(PKG, abs));
}
return out;
}

/** `vitest list --filesOnly [--project NAME]`, one relative path per line. */
function vitestFiles(project?: string): string[] {
const args = [VITEST_ENTRY, 'list', '--filesOnly', ...(project ? ['--project', project] : [])];
const out = execFileSync(process.execPath, args, {
cwd: PKG,
env: childEnv(),
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return out
.split('\n')
.map((line) => line.trim())
.filter(Boolean)
.map((line) => line.replace(/^\[[^\]]+\]\s+/, ''));
}

/** The explicit list as `vitest.config.ts` declares it — read as text, not imported. */
function declaredIntegrationFiles(): string[] {
const masked = maskComments(readFileSync(join(PKG, 'vitest.config.ts'), 'utf8'));
const block = /INTEGRATION_FILES\s*=\s*\[([^\]]*)\]/.exec(masked);
if (!block) throw new Error('vitest.config.ts no longer declares `INTEGRATION_FILES = [ … ]`');
return Array.from(block[1].matchAll(/['"]([^'"]+)['"]/g), (m) => m[1]);
}

const sorted = (xs: Iterable<string>): string[] => [...xs].sort();

describe('the two tiers of packages/cli (#13504)', () => {
const onDisk = sorted(walk(PKG));
const all = sorted(vitestFiles());
const unit = sorted(vitestFiles('unit'));
const integration = sorted(vitestFiles('integration'));

it('every test file on disk is one vitest collects with no --project (what `pnpm test` runs)', () => {
expect(onDisk.length).toBeGreaterThan(100);
expect(all, 'vitest run collects a different population than the filesystem holds').toEqual(onDisk);
});

it('unit and integration partition that population — no file in both, none in neither', () => {
const inBoth = unit.filter((f) => integration.includes(f));
expect(inBoth, 'files matched by BOTH projects (they would run and report twice)').toEqual([]);
const union = sorted([...unit, ...integration]);
expect(union, 'files matched by NEITHER project fall out of every tier, including CI').toEqual(all);
});

it('INTEGRATION_FILES equals the behavioural predicate over every file on disk', () => {
const declared = declaredIntegrationFiles();
expect(sorted(new Set(declared)), 'INTEGRATION_FILES carries a duplicate').toEqual(sorted(declared));
expect(sorted(declared), 'an INTEGRATION_FILES entry names no file vitest can find').toEqual(integration);

const missing: string[] = [];
const stale: string[] = [];
for (const file of onDisk) {
const signals = tierSignals(maskComments(readFileSync(join(PKG, file), 'utf8')));
const predicted = isIntegration(signals);
const listed = integration.includes(file);
if (predicted && !listed) missing.push(`${file} [${firedSignals(signals)}]`);
if (!predicted && listed) stale.push(file);
}
expect(
missing,
'files that spawn the CLI or boot a kernel/driver but are NOT in INTEGRATION_FILES (add them)',
).toEqual([]);
expect(stale, 'INTEGRATION_FILES entries that neither spawn nor boot (remove them)').toEqual([]);
});

it('this pin is itself unit-tier: importing child_process to ask vitest is not spawning the CLI', () => {
const signals = tierSignals(maskComments(readFileSync(join(PKG, THIS_FILE), 'utf8')));
expect(signals.childProcess).toBe(true);
expect(isIntegration(signals), `fired: ${firedSignals(signals)}`).toBe(false);
expect(unit).toContain(THIS_FILE);
});
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions packages/cli/test/vitest-tiers-partition.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* The two tiers of this package's suite stay a PARTITION, and the integration
* list stays equal to what the files DO (#13504).
*
* `vitest.config.ts` splits the suite into two named projects — `unit` (the
* local default) and `integration` (spawns the real CLI or boots a real
* kernel/driver; CI-mandatory, local on demand). Two things can rot under a
* split like that, and both rot silently, which is why this pin exists:
*
* 1. A test file that matches NO project is not run by `vitest run` at all —
* not by the fast tier AND not by `pnpm test` in CI, because with
* `projects` configured the root run IS the union of the projects. A file
* matching BOTH runs twice and reports twice. So the first two cases hold
* `unit ⊎ integration = every test file on disk`, read from vitest's own
* resolution (`vitest list --filesOnly`, with and without `--project`)
* against a filesystem walk — the config's spelling is judged by what
* vitest actually collects, never by re-reading the config.
*
* 2. `INTEGRATION_FILES` is an explicit list, and the `*.e2e.test.ts` NAME is
* not the predicate (the ACCEPT on #13504 measured 18 of 220 files where
* name and behaviour disagree). So the third case re-derives the tier of
* every file from its comment-masked SOURCE and fails when the list and
* the derivation disagree — a new spawner cannot land in the fast tier
* unnoticed, and a stale entry cannot linger. The predicate, in code
* position (comments masked by `scripts/js-comment-mask.mjs`):
*
* SPAWN = calls `runServe(` (the helper in `test/helpers/serve-process.ts`
* whose body spawns the source entry), OR value-imports
* `node:child_process` AND (names an entry basename — the
* `run-dev` / `run` scripts under `bin/` — OR imports `CLI` /
* `TSX` from that helper OR names the `tsx` binary under
* `node_modules/.bin`);
* KERNEL = value-imports `bootSchemaStack` from `schema-migrate`, OR
* value-imports `better-sqlite3`, OR value-imports any
* `@objectstack/driver-*` package, OR constructs `new ObjectQL(`.
* INTEGRATION = SPAWN ∨ KERNEL.
*
* Value imports only: `import type { … } from '@objectstack/driver-sql'`
* loads nothing, a spelling list that SAYS `'better-sqlite3'` opens no
* database, and `expect(deps).toContain('better-sqlite3')` boots nothing —
* every one of those was a false positive of the text-match census this
* predicate replaced. An import statement is one `import … from '<spec>'`
* span containing neither `;` nor another `from` (every import in this
* package's tests ends in `;`, measured on 00ff228fe0).
*
* The fourth case classifies THIS file: it imports `node:child_process` (to
* ask vitest for its file lists) and must still read as `unit`, which is the
* predicate's own regression test against matching its own source.
*
* Runs in the `unit` tier and needs no built `dist/`: it spawns `vitest list`,
* which only globs, and reads sources.
*/

import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
import { childEnv } from './helpers/serve-process.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const PKG = resolve(HERE, '..');
const THIS_FILE = relative(PKG, fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const VITEST_ENTRY = resolve(dirname(require.resolve('vitest/package.json')), 'vitest.mjs');

// ---------------------------------------------------------------------------
// The predicate
// ---------------------------------------------------------------------------

interface ValueImport {
clause: string;
spec: string;
}

/** One `import … from '<spec>'` statement; `import type` is skipped. */
const IMPORT_RE = /\bimport\s+(?!type\b)((?:(?!\bfrom\b)[^;])*?)\bfrom\s+['"]([^'"]+)['"]/g;

function valueImports(code: string): ValueImport[] {
const out: ValueImport[] = [];
for (const m of code.matchAll(IMPORT_RE)) out.push({ clause: m[1], spec: m[2] });
return out;
}

/** Inline `type X` specifiers do not make a value import of `X`. */
function importsValue(imports: ValueImport[], spec: RegExp, name?: RegExp): boolean {
return imports.some((i) => spec.test(i.spec) && (!name || name.test(i.clause.replace(/\btype\s+\w+/g, ''))));
}

export interface TierSignals {
runServe: boolean;
childProcess: boolean;
entryBasename: boolean;
helperCliOrTsx: boolean;
tsxBin: boolean;
bootSchemaStack: boolean;
betterSqlite3: boolean;
driverPackage: boolean;
objectQLCtor: boolean;
}

export function tierSignals(maskedCode: string): TierSignals {
const imports = valueImports(maskedCode);
return {
runServe: /\brunServe\s*[(]/.test(maskedCode),
childProcess: importsValue(imports, /^(?:node:)?child_process$/),
entryBasename: /\brun(?:-dev)?[.]js\b/.test(maskedCode),
helperCliOrTsx: importsValue(imports, /helpers\/serve-process(?:\.js)?$/, /\b(?:CLI|TSX)\b/),
tsxBin: /[.]bin[/]tsx\b/.test(maskedCode),
bootSchemaStack: importsValue(imports, /schema-migrate(?:\.js)?$/, /\bbootSchemaStack\b/),
betterSqlite3: importsValue(imports, /^better-sqlite3$/) || /require[(]\s*['"]better-sqlite3['"]\s*[)]/.test(maskedCode),
driverPackage: importsValue(imports, /^@objectstack\/driver-/),
objectQLCtor: /new\s+ObjectQL\s*[(]/.test(maskedCode),
};
}

export function isIntegration(s: TierSignals): boolean {
const spawn = s.runServe || (s.childProcess && (s.entryBasename || s.helperCliOrTsx || s.tsxBin));
const kernel = s.bootSchemaStack || s.betterSqlite3 || s.driverPackage || s.objectQLCtor;
return spawn || kernel;
}

function firedSignals(s: TierSignals): string {
return (Object.keys(s) as Array<keyof TierSignals>).filter((k) => s[k]).join(', ') || 'none';
}

// ---------------------------------------------------------------------------
// The two readings: the filesystem, and vitest's own resolution
// ---------------------------------------------------------------------------

const TEST_FILE_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', 'coverage']);

function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (SKIP_DIRS.has(entry.name)) continue;
const abs = join(dir, entry.name);
if (entry.isDirectory()) walk(abs, out);
else if (TEST_FILE_RE.test(entry.name)) out.push(relative(PKG, abs));
}
return out;
}

/** `vitest list --filesOnly [--project NAME]`, one relative path per line. */
function vitestFiles(project?: string): string[] {
const args = [VITEST_ENTRY, 'list', '--filesOnly', ...(project ? ['--project', project] : [])];
const out = execFileSync(process.execPath, args, {
cwd: PKG,
env: childEnv(),
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return out
.split('\n')
.map((line) => line.trim())
.filter(Boolean)
.map((line) => line.replace(/^\[[^\]]+\]\s+/, ''));
}

/** The explicit list as `vitest.config.ts` declares it — read as text, not imported. */
function declaredIntegrationFiles(): string[] {
const masked = maskComments(readFileSync(join(PKG, 'vitest.config.ts'), 'utf8'));
const block = /INTEGRATION_FILES\s*=\s*\[([^\]]*)\]/.exec(masked);
if (!block) throw new Error('vitest.config.ts no longer declares `INTEGRATION_FILES = [ … ]`');
return Array.from(block[1].matchAll(/['"]([^'"]+)['"]/g), (m) => m[1]);
}

const sorted = (xs: Iterable<string>): string[] => [...xs].sort();

describe('the two tiers of packages/cli (#13504)', () => {
const onDisk = sorted(walk(PKG));
const all = sorted(vitestFiles());
const unit = sorted(vitestFiles('unit'));
const integration = sorted(vitestFiles('integration'));

it('every test file on disk is one vitest collects with no --project (what `pnpm test` runs)', () => {
expect(onDisk.length).toBeGreaterThan(100);
expect(all, 'vitest run collects a different population than the filesystem holds').toEqual(onDisk);
});

it('unit and integration partition that population — no file in both, none in neither', () => {
const inBoth = unit.filter((f) => integration.includes(f));
expect(inBoth, 'files matched by BOTH projects (they would run and report twice)').toEqual([]);
const union = sorted([...unit, ...integration]);
expect(union, 'files matched by NEITHER project fall out of every tier, including CI').toEqual(all);
});

it('INTEGRATION_FILES equals the behavioural predicate over every file on disk', () => {
const declared = declaredIntegrationFiles();
expect(sorted(new Set(declared)), 'INTEGRATION_FILES carries a duplicate').toEqual(sorted(declared));
expect(sorted(declared), 'an INTEGRATION_FILES entry names no file vitest can find').toEqual(integration);

const missing: string[] = [];
const stale: string[] = [];
for (const file of onDisk) {
const signals = tierSignals(maskComments(readFileSync(join(PKG, file), 'utf8')));
const predicted = isIntegration(signals);
const listed = integration.includes(file);
if (predicted && !listed) missing.push(`${file} [${firedSignals(signals)}]`);
if (!predicted && listed) stale.push(file);
}
expect(
missing,
'files that spawn the CLI or boot a kernel/driver but are NOT in INTEGRATION_FILES (add them)',
).toEqual([]);
expect(stale, 'INTEGRATION_FILES entries that neither spawn nor boot (remove them)').toEqual([]);
});

it('this pin is itself unit-tier: importing child_process to ask vitest is not spawning the CLI', () => {
const signals = tierSignals(maskComments(readFileSync(join(PKG, THIS_FILE), 'utf8')));
expect(signals.childProcess).toBe(true);
expect(isIntegration(signals), `fired: ${firedSignals(signals)}`).toBe(false);
expect(unit).toContain(THIS_FILE);
});
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions packages/cli/test/vitest-tiers-partition.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* The two tiers of this package's suite stay a PARTITION, and the integration
* list stays equal to what the files DO (#13504).
*
* `vitest.config.ts` splits the suite into two named projects — `unit` (the
* local default) and `integration` (spawns the real CLI or boots a real
* kernel/driver; CI-mandatory, local on demand). Two things can rot under a
* split like that, and both rot silently, which is why this pin exists:
*
* 1. A test file that matches NO project is not run by `vitest run` at all —
* not by the fast tier AND not by `pnpm test` in CI, because with
* `projects` configured the root run IS the union of the projects. A file
* matching BOTH runs twice and reports twice. So the first two cases hold
* `unit ⊎ integration = every test file on disk`, read from vitest's own
* resolution (`vitest list --filesOnly`, with and without `--project`)
* against a filesystem walk — the config's spelling is judged by what
* vitest actually collects, never by re-reading the config.
*
* 2. `INTEGRATION_FILES` is an explicit list, and the `*.e2e.test.ts` NAME is
* not the predicate (the ACCEPT on #13504 measured 18 of 220 files where
* name and behaviour disagree). So the third case re-derives the tier of
* every file from its comment-masked SOURCE and fails when the list and
* the derivation disagree — a new spawner cannot land in the fast tier
* unnoticed, and a stale entry cannot linger. The predicate, in code
* position (comments masked by `scripts/js-comment-mask.mjs`):
*
* SPAWN = calls `runServe(` (the helper in `test/helpers/serve-process.ts`
* whose body spawns the source entry), OR value-imports
* `node:child_process` AND (names an entry basename — the
* `run-dev` / `run` scripts under `bin/` — OR imports `CLI` /
* `TSX` from that helper OR names the `tsx` binary under
* `node_modules/.bin`);
* KERNEL = value-imports `bootSchemaStack` from `schema-migrate`, OR
* value-imports `better-sqlite3`, OR value-imports any
* `@objectstack/driver-*` package, OR constructs `new ObjectQL(`.
* INTEGRATION = SPAWN ∨ KERNEL.
*
* Value imports only: `import type { … } from '@objectstack/driver-sql'`
* loads nothing, a spelling list that SAYS `'better-sqlite3'` opens no
* database, and `expect(deps).toContain('better-sqlite3')` boots nothing —
* every one of those was a false positive of the text-match census this
* predicate replaced. An import statement is one `import … from '<spec>'`
* span containing neither `;` nor another `from` (every import in this
* package's tests ends in `;`, measured on 00ff228fe0).
*
* The fourth case classifies THIS file: it imports `node:child_process` (to
* ask vitest for its file lists) and must still read as `unit`, which is the
* predicate's own regression test against matching its own source.
*
* Runs in the `unit` tier and needs no built `dist/`: it spawns `vitest list`,
* which only globs, and reads sources.
*/

import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
import { childEnv } from './helpers/serve-process.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const PKG = resolve(HERE, '..');
const THIS_FILE = relative(PKG, fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const VITEST_ENTRY = resolve(dirname(require.resolve('vitest/package.json')), 'vitest.mjs');

// ---------------------------------------------------------------------------
// The predicate
// ---------------------------------------------------------------------------

interface ValueImport {
clause: string;
spec: string;
}

/** One `import … from '<spec>'` statement; `import type` is skipped. */
const IMPORT_RE = /\bimport\s+(?!type\b)((?:(?!\bfrom\b)[^;])*?)\bfrom\s+['"]([^'"]+)['"]/g;

function valueImports(code: string): ValueImport[] {
const out: ValueImport[] = [];
for (const m of code.matchAll(IMPORT_RE)) out.push({ clause: m[1], spec: m[2] });
return out;
}

/** Inline `type X` specifiers do not make a value import of `X`. */
function importsValue(imports: ValueImport[], spec: RegExp, name?: RegExp): boolean {
return imports.some((i) => spec.test(i.spec) && (!name || name.test(i.clause.replace(/\btype\s+\w+/g, ''))));
}

export interface TierSignals {
runServe: boolean;
childProcess: boolean;
entryBasename: boolean;
helperCliOrTsx: boolean;
tsxBin: boolean;
bootSchemaStack: boolean;
betterSqlite3: boolean;
driverPackage: boolean;
objectQLCtor: boolean;
}

export function tierSignals(maskedCode: string): TierSignals {
const imports = valueImports(maskedCode);
return {
runServe: /\brunServe\s*[(]/.test(maskedCode),
childProcess: importsValue(imports, /^(?:node:)?child_process$/),
entryBasename: /\brun(?:-dev)?[.]js\b/.test(maskedCode),
helperCliOrTsx: importsValue(imports, /helpers\/serve-process(?:\.js)?$/, /\b(?:CLI|TSX)\b/),
tsxBin: /[.]bin[/]tsx\b/.test(maskedCode),
bootSchemaStack: importsValue(imports, /schema-migrate(?:\.js)?$/, /\bbootSchemaStack\b/),
betterSqlite3: importsValue(imports, /^better-sqlite3$/) || /require[(]\s*['"]better-sqlite3['"]\s*[)]/.test(maskedCode),
driverPackage: importsValue(imports, /^@objectstack\/driver-/),
objectQLCtor: /new\s+ObjectQL\s*[(]/.test(maskedCode),
};
}

export function isIntegration(s: TierSignals): boolean {
const spawn = s.runServe || (s.childProcess && (s.entryBasename || s.helperCliOrTsx || s.tsxBin));
const kernel = s.bootSchemaStack || s.betterSqlite3 || s.driverPackage || s.objectQLCtor;
return spawn || kernel;
}

function firedSignals(s: TierSignals): string {
return (Object.keys(s) as Array<keyof TierSignals>).filter((k) => s[k]).join(', ') || 'none';
}

// ---------------------------------------------------------------------------
// The two readings: the filesystem, and vitest's own resolution
// ---------------------------------------------------------------------------

const TEST_FILE_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', 'coverage']);

function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (SKIP_DIRS.has(entry.name)) continue;
const abs = join(dir, entry.name);
if (entry.isDirectory()) walk(abs, out);
else if (TEST_FILE_RE.test(entry.name)) out.push(relative(PKG, abs));
}
return out;
}

/** `vitest list --filesOnly [--project NAME]`, one relative path per line. */
function vitestFiles(project?: string): string[] {
const args = [VITEST_ENTRY, 'list', '--filesOnly', ...(project ? ['--project', project] : [])];
const out = execFileSync(process.execPath, args, {
cwd: PKG,
env: childEnv(),
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return out
.split('\n')
.map((line) => line.trim())
.filter(Boolean)
.map((line) => line.replace(/^\[[^\]]+\]\s+/, ''));
}

/** The explicit list as `vitest.config.ts` declares it — read as text, not imported. */
function declaredIntegrationFiles(): string[] {
const masked = maskComments(readFileSync(join(PKG, 'vitest.config.ts'), 'utf8'));
const block = /INTEGRATION_FILES\s*=\s*\[([^\]]*)\]/.exec(masked);
if (!block) throw new Error('vitest.config.ts no longer declares `INTEGRATION_FILES = [ … ]`');
return Array.from(block[1].matchAll(/['"]([^'"]+)['"]/g), (m) => m[1]);
}

const sorted = (xs: Iterable<string>): string[] => [...xs].sort();

describe('the two tiers of packages/cli (#13504)', () => {
const onDisk = sorted(walk(PKG));
const all = sorted(vitestFiles());
const unit = sorted(vitestFiles('unit'));
const integration = sorted(vitestFiles('integration'));

it('every test file on disk is one vitest collects with no --project (what `pnpm test` runs)', () => {
expect(onDisk.length).toBeGreaterThan(100);
expect(all, 'vitest run collects a different population than the filesystem holds').toEqual(onDisk);
});

it('unit and integration partition that population — no file in both, none in neither', () => {
const inBoth = unit.filter((f) => integration.includes(f));
expect(inBoth, 'files matched by BOTH projects (they would run and report twice)').toEqual([]);
const union = sorted([...unit, ...integration]);
expect(union, 'files matched by NEITHER project fall out of every tier, including CI').toEqual(all);
});

it('INTEGRATION_FILES equals the behavioural predicate over every file on disk', () => {
const declared = declaredIntegrationFiles();
expect(sorted(new Set(declared)), 'INTEGRATION_FILES carries a duplicate').toEqual(sorted(declared));
expect(sorted(declared), 'an INTEGRATION_FILES entry names no file vitest can find').toEqual(integration);

const missing: string[] = [];
const stale: string[] = [];
for (const file of onDisk) {
const signals = tierSignals(maskComments(readFileSync(join(PKG, file), 'utf8')));
const predicted = isIntegration(signals);
const listed = integration.includes(file);
if (predicted && !listed) missing.push(`${file} [${firedSignals(signals)}]`);
if (!predicted && listed) stale.push(file);
}
expect(
missing,
'files that spawn the CLI or boot a kernel/driver but are NOT in INTEGRATION_FILES (add them)',
).toEqual([]);
expect(stale, 'INTEGRATION_FILES entries that neither spawn nor boot (remove them)').toEqual([]);
});

it('this pin is itself unit-tier: importing child_process to ask vitest is not spawning the CLI', () => {
const signals = tierSignals(maskComments(readFileSync(join(PKG, THIS_FILE), 'utf8')));
expect(signals.childProcess).toBe(true);
expect(isIntegration(signals), `fired: ${firedSignals(signals)}`).toBe(false);
expect(unit).toContain(THIS_FILE);
});
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions packages/cli/test/vitest-tiers-partition.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* The two tiers of this package's suite stay a PARTITION, and the integration
* list stays equal to what the files DO (#13504).
*
* `vitest.config.ts` splits the suite into two named projects — `unit` (the
* local default) and `integration` (spawns the real CLI or boots a real
* kernel/driver; CI-mandatory, local on demand). Two things can rot under a
* split like that, and both rot silently, which is why this pin exists:
*
* 1. A test file that matches NO project is not run by `vitest run` at all —
* not by the fast tier AND not by `pnpm test` in CI, because with
* `projects` configured the root run IS the union of the projects. A file
* matching BOTH runs twice and reports twice. So the first two cases hold
* `unit ⊎ integration = every test file on disk`, read from vitest's own
* resolution (`vitest list --filesOnly`, with and without `--project`)
* against a filesystem walk — the config's spelling is judged by what
* vitest actually collects, never by re-reading the config.
*
* 2. `INTEGRATION_FILES` is an explicit list, and the `*.e2e.test.ts` NAME is
* not the predicate (the ACCEPT on #13504 measured 18 of 220 files where
* name and behaviour disagree). So the third case re-derives the tier of
* every file from its comment-masked SOURCE and fails when the list and
* the derivation disagree — a new spawner cannot land in the fast tier
* unnoticed, and a stale entry cannot linger. The predicate, in code
* position (comments masked by `scripts/js-comment-mask.mjs`):
*
* SPAWN = calls `runServe(` (the helper in `test/helpers/serve-process.ts`
* whose body spawns the source entry), OR value-imports
* `node:child_process` AND (names an entry basename — the
* `run-dev` / `run` scripts under `bin/` — OR imports `CLI` /
* `TSX` from that helper OR names the `tsx` binary under
* `node_modules/.bin`);
* KERNEL = value-imports `bootSchemaStack` from `schema-migrate`, OR
* value-imports `better-sqlite3`, OR value-imports any
* `@objectstack/driver-*` package, OR constructs `new ObjectQL(`.
* INTEGRATION = SPAWN ∨ KERNEL.
*
* Value imports only: `import type { … } from '@objectstack/driver-sql'`
* loads nothing, a spelling list that SAYS `'better-sqlite3'` opens no
* database, and `expect(deps).toContain('better-sqlite3')` boots nothing —
* every one of those was a false positive of the text-match census this
* predicate replaced. An import statement is one `import … from '<spec>'`
* span containing neither `;` nor another `from` (every import in this
* package's tests ends in `;`, measured on 00ff228fe0).
*
* The fourth case classifies THIS file: it imports `node:child_process` (to
* ask vitest for its file lists) and must still read as `unit`, which is the
* predicate's own regression test against matching its own source.
*
* Runs in the `unit` tier and needs no built `dist/`: it spawns `vitest list`,
* which only globs, and reads sources.
*/

import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
import { childEnv } from './helpers/serve-process.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const PKG = resolve(HERE, '..');
const THIS_FILE = relative(PKG, fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const VITEST_ENTRY = resolve(dirname(require.resolve('vitest/package.json')), 'vitest.mjs');

// ---------------------------------------------------------------------------
// The predicate
// ---------------------------------------------------------------------------

interface ValueImport {
clause: string;
spec: string;
}

/** One `import … from '<spec>'` statement; `import type` is skipped. */
const IMPORT_RE = /\bimport\s+(?!type\b)((?:(?!\bfrom\b)[^;])*?)\bfrom\s+['"]([^'"]+)['"]/g;

function valueImports(code: string): ValueImport[] {
const out: ValueImport[] = [];
for (const m of code.matchAll(IMPORT_RE)) out.push({ clause: m[1], spec: m[2] });
return out;
}

/** Inline `type X` specifiers do not make a value import of `X`. */
function importsValue(imports: ValueImport[], spec: RegExp, name?: RegExp): boolean {
return imports.some((i) => spec.test(i.spec) && (!name || name.test(i.clause.replace(/\btype\s+\w+/g, ''))));
}

export interface TierSignals {
runServe: boolean;
childProcess: boolean;
entryBasename: boolean;
helperCliOrTsx: boolean;
tsxBin: boolean;
bootSchemaStack: boolean;
betterSqlite3: boolean;
driverPackage: boolean;
objectQLCtor: boolean;
}

export function tierSignals(maskedCode: string): TierSignals {
const imports = valueImports(maskedCode);
return {
runServe: /\brunServe\s*[(]/.test(maskedCode),
childProcess: importsValue(imports, /^(?:node:)?child_process$/),
entryBasename: /\brun(?:-dev)?[.]js\b/.test(maskedCode),
helperCliOrTsx: importsValue(imports, /helpers\/serve-process(?:\.js)?$/, /\b(?:CLI|TSX)\b/),
tsxBin: /[.]bin[/]tsx\b/.test(maskedCode),
bootSchemaStack: importsValue(imports, /schema-migrate(?:\.js)?$/, /\bbootSchemaStack\b/),
betterSqlite3: importsValue(imports, /^better-sqlite3$/) || /require[(]\s*['"]better-sqlite3['"]\s*[)]/.test(maskedCode),
driverPackage: importsValue(imports, /^@objectstack\/driver-/),
objectQLCtor: /new\s+ObjectQL\s*[(]/.test(maskedCode),
};
}

export function isIntegration(s: TierSignals): boolean {
const spawn = s.runServe || (s.childProcess && (s.entryBasename || s.helperCliOrTsx || s.tsxBin));
const kernel = s.bootSchemaStack || s.betterSqlite3 || s.driverPackage || s.objectQLCtor;
return spawn || kernel;
}

function firedSignals(s: TierSignals): string {
return (Object.keys(s) as Array<keyof TierSignals>).filter((k) => s[k]).join(', ') || 'none';
}

// ---------------------------------------------------------------------------
// The two readings: the filesystem, and vitest's own resolution
// ---------------------------------------------------------------------------

const TEST_FILE_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', 'coverage']);

function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (SKIP_DIRS.has(entry.name)) continue;
const abs = join(dir, entry.name);
if (entry.isDirectory()) walk(abs, out);
else if (TEST_FILE_RE.test(entry.name)) out.push(relative(PKG, abs));
}
return out;
}

/** `vitest list --filesOnly [--project NAME]`, one relative path per line. */
function vitestFiles(project?: string): string[] {
const args = [VITEST_ENTRY, 'list', '--filesOnly', ...(project ? ['--project', project] : [])];
const out = execFileSync(process.execPath, args, {
cwd: PKG,
env: childEnv(),
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return out
.split('\n')
.map((line) => line.trim())
.filter(Boolean)
.map((line) => line.replace(/^\[[^\]]+\]\s+/, ''));
}

/** The explicit list as `vitest.config.ts` declares it — read as text, not imported. */
function declaredIntegrationFiles(): string[] {
const masked = maskComments(readFileSync(join(PKG, 'vitest.config.ts'), 'utf8'));
const block = /INTEGRATION_FILES\s*=\s*\[([^\]]*)\]/.exec(masked);
if (!block) throw new Error('vitest.config.ts no longer declares `INTEGRATION_FILES = [ … ]`');
return Array.from(block[1].matchAll(/['"]([^'"]+)['"]/g), (m) => m[1]);
}

const sorted = (xs: Iterable<string>): string[] => [...xs].sort();

describe('the two tiers of packages/cli (#13504)', () => {
const onDisk = sorted(walk(PKG));
const all = sorted(vitestFiles());
const unit = sorted(vitestFiles('unit'));
const integration = sorted(vitestFiles('integration'));

it('every test file on disk is one vitest collects with no --project (what `pnpm test` runs)', () => {
expect(onDisk.length).toBeGreaterThan(100);
expect(all, 'vitest run collects a different population than the filesystem holds').toEqual(onDisk);
});

it('unit and integration partition that population — no file in both, none in neither', () => {
const inBoth = unit.filter((f) => integration.includes(f));
expect(inBoth, 'files matched by BOTH projects (they would run and report twice)').toEqual([]);
const union = sorted([...unit, ...integration]);
expect(union, 'files matched by NEITHER project fall out of every tier, including CI').toEqual(all);
});

it('INTEGRATION_FILES equals the behavioural predicate over every file on disk', () => {
const declared = declaredIntegrationFiles();
expect(sorted(new Set(declared)), 'INTEGRATION_FILES carries a duplicate').toEqual(sorted(declared));
expect(sorted(declared), 'an INTEGRATION_FILES entry names no file vitest can find').toEqual(integration);

const missing: string[] = [];
const stale: string[] = [];
for (const file of onDisk) {
const signals = tierSignals(maskComments(readFileSync(join(PKG, file), 'utf8')));
const predicted = isIntegration(signals);
const listed = integration.includes(file);
if (predicted && !listed) missing.push(`${file} [${firedSignals(signals)}]`);
if (!predicted && listed) stale.push(file);
}
expect(
missing,
'files that spawn the CLI or boot a kernel/driver but are NOT in INTEGRATION_FILES (add them)',
).toEqual([]);
expect(stale, 'INTEGRATION_FILES entries that neither spawn nor boot (remove them)').toEqual([]);
});

it('this pin is itself unit-tier: importing child_process to ask vitest is not spawning the CLI', () => {
const signals = tierSignals(maskComments(readFileSync(join(PKG, THIS_FILE), 'utf8')));
expect(signals.childProcess).toBe(true);
expect(isIntegration(signals), `fired: ${firedSignals(signals)}`).toBe(false);
expect(unit).toContain(THIS_FILE);
});
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions packages/cli/test/vitest-tiers-partition.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* The two tiers of this package's suite stay a PARTITION, and the integration
* list stays equal to what the files DO (#13504).
*
* `vitest.config.ts` splits the suite into two named projects — `unit` (the
* local default) and `integration` (spawns the real CLI or boots a real
* kernel/driver; CI-mandatory, local on demand). Two things can rot under a
* split like that, and both rot silently, which is why this pin exists:
*
* 1. A test file that matches NO project is not run by `vitest run` at all —
* not by the fast tier AND not by `pnpm test` in CI, because with
* `projects` configured the root run IS the union of the projects. A file
* matching BOTH runs twice and reports twice. So the first two cases hold
* `unit ⊎ integration = every test file on disk`, read from vitest's own
* resolution (`vitest list --filesOnly`, with and without `--project`)
* against a filesystem walk — the config's spelling is judged by what
* vitest actually collects, never by re-reading the config.
*
* 2. `INTEGRATION_FILES` is an explicit list, and the `*.e2e.test.ts` NAME is
* not the predicate (the ACCEPT on #13504 measured 18 of 220 files where
* name and behaviour disagree). So the third case re-derives the tier of
* every file from its comment-masked SOURCE and fails when the list and
* the derivation disagree — a new spawner cannot land in the fast tier
* unnoticed, and a stale entry cannot linger. The predicate, in code
* position (comments masked by `scripts/js-comment-mask.mjs`):
*
* SPAWN = calls `runServe(` (the helper in `test/helpers/serve-process.ts`
* whose body spawns the source entry), OR value-imports
* `node:child_process` AND (names an entry basename — the
* `run-dev` / `run` scripts under `bin/` — OR imports `CLI` /
* `TSX` from that helper OR names the `tsx` binary under
* `node_modules/.bin`);
* KERNEL = value-imports `bootSchemaStack` from `schema-migrate`, OR
* value-imports `better-sqlite3`, OR value-imports any
* `@objectstack/driver-*` package, OR constructs `new ObjectQL(`.
* INTEGRATION = SPAWN ∨ KERNEL.
*
* Value imports only: `import type { … } from '@objectstack/driver-sql'`
* loads nothing, a spelling list that SAYS `'better-sqlite3'` opens no
* database, and `expect(deps).toContain('better-sqlite3')` boots nothing —
* every one of those was a false positive of the text-match census this
* predicate replaced. An import statement is one `import … from '<spec>'`
* span containing neither `;` nor another `from` (every import in this
* package's tests ends in `;`, measured on 00ff228fe0).
*
* The fourth case classifies THIS file: it imports `node:child_process` (to
* ask vitest for its file lists) and must still read as `unit`, which is the
* predicate's own regression test against matching its own source.
*
* Runs in the `unit` tier and needs no built `dist/`: it spawns `vitest list`,
* which only globs, and reads sources.
*/

import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
import { childEnv } from './helpers/serve-process.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const PKG = resolve(HERE, '..');
const THIS_FILE = relative(PKG, fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const VITEST_ENTRY = resolve(dirname(require.resolve('vitest/package.json')), 'vitest.mjs');

// ---------------------------------------------------------------------------
// The predicate
// ---------------------------------------------------------------------------

interface ValueImport {
clause: string;
spec: string;
}

/** One `import … from '<spec>'` statement; `import type` is skipped. */
const IMPORT_RE = /\bimport\s+(?!type\b)((?:(?!\bfrom\b)[^;])*?)\bfrom\s+['"]([^'"]+)['"]/g;

function valueImports(code: string): ValueImport[] {
const out: ValueImport[] = [];
for (const m of code.matchAll(IMPORT_RE)) out.push({ clause: m[1], spec: m[2] });
return out;
}

/** Inline `type X` specifiers do not make a value import of `X`. */
function importsValue(imports: ValueImport[], spec: RegExp, name?: RegExp): boolean {
return imports.some((i) => spec.test(i.spec) && (!name || name.test(i.clause.replace(/\btype\s+\w+/g, ''))));
}

export interface TierSignals {
runServe: boolean;
childProcess: boolean;
entryBasename: boolean;
helperCliOrTsx: boolean;
tsxBin: boolean;
bootSchemaStack: boolean;
betterSqlite3: boolean;
driverPackage: boolean;
objectQLCtor: boolean;
}

export function tierSignals(maskedCode: string): TierSignals {
const imports = valueImports(maskedCode);
return {
runServe: /\brunServe\s*[(]/.test(maskedCode),
childProcess: importsValue(imports, /^(?:node:)?child_process$/),
entryBasename: /\brun(?:-dev)?[.]js\b/.test(maskedCode),
helperCliOrTsx: importsValue(imports, /helpers\/serve-process(?:\.js)?$/, /\b(?:CLI|TSX)\b/),
tsxBin: /[.]bin[/]tsx\b/.test(maskedCode),
bootSchemaStack: importsValue(imports, /schema-migrate(?:\.js)?$/, /\bbootSchemaStack\b/),
betterSqlite3: importsValue(imports, /^better-sqlite3$/) || /require[(]\s*['"]better-sqlite3['"]\s*[)]/.test(maskedCode),
driverPackage: importsValue(imports, /^@objectstack\/driver-/),
objectQLCtor: /new\s+ObjectQL\s*[(]/.test(maskedCode),
};
}

export function isIntegration(s: TierSignals): boolean {
const spawn = s.runServe || (s.childProcess && (s.entryBasename || s.helperCliOrTsx || s.tsxBin));
const kernel = s.bootSchemaStack || s.betterSqlite3 || s.driverPackage || s.objectQLCtor;
return spawn || kernel;
}

function firedSignals(s: TierSignals): string {
return (Object.keys(s) as Array<keyof TierSignals>).filter((k) => s[k]).join(', ') || 'none';
}

// ---------------------------------------------------------------------------
// The two readings: the filesystem, and vitest's own resolution
// ---------------------------------------------------------------------------

const TEST_FILE_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', 'coverage']);

function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (SKIP_DIRS.has(entry.name)) continue;
const abs = join(dir, entry.name);
if (entry.isDirectory()) walk(abs, out);
else if (TEST_FILE_RE.test(entry.name)) out.push(relative(PKG, abs));
}
return out;
}

/** `vitest list --filesOnly [--project NAME]`, one relative path per line. */
function vitestFiles(project?: string): string[] {
const args = [VITEST_ENTRY, 'list', '--filesOnly', ...(project ? ['--project', project] : [])];
const out = execFileSync(process.execPath, args, {
cwd: PKG,
env: childEnv(),
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return out
.split('\n')
.map((line) => line.trim())
.filter(Boolean)
.map((line) => line.replace(/^\[[^\]]+\]\s+/, ''));
}

/** The explicit list as `vitest.config.ts` declares it — read as text, not imported. */
function declaredIntegrationFiles(): string[] {
const masked = maskComments(readFileSync(join(PKG, 'vitest.config.ts'), 'utf8'));
const block = /INTEGRATION_FILES\s*=\s*\[([^\]]*)\]/.exec(masked);
if (!block) throw new Error('vitest.config.ts no longer declares `INTEGRATION_FILES = [ … ]`');
return Array.from(block[1].matchAll(/['"]([^'"]+)['"]/g), (m) => m[1]);
}

const sorted = (xs: Iterable<string>): string[] => [...xs].sort();

describe('the two tiers of packages/cli (#13504)', () => {
const onDisk = sorted(walk(PKG));
const all = sorted(vitestFiles());
const unit = sorted(vitestFiles('unit'));
const integration = sorted(vitestFiles('integration'));

it('every test file on disk is one vitest collects with no --project (what `pnpm test` runs)', () => {
expect(onDisk.length).toBeGreaterThan(100);
expect(all, 'vitest run collects a different population than the filesystem holds').toEqual(onDisk);
});

it('unit and integration partition that population — no file in both, none in neither', () => {
const inBoth = unit.filter((f) => integration.includes(f));
expect(inBoth, 'files matched by BOTH projects (they would run and report twice)').toEqual([]);
const union = sorted([...unit, ...integration]);
expect(union, 'files matched by NEITHER project fall out of every tier, including CI').toEqual(all);
});

it('INTEGRATION_FILES equals the behavioural predicate over every file on disk', () => {
const declared = declaredIntegrationFiles();
expect(sorted(new Set(declared)), 'INTEGRATION_FILES carries a duplicate').toEqual(sorted(declared));
expect(sorted(declared), 'an INTEGRATION_FILES entry names no file vitest can find').toEqual(integration);

const missing: string[] = [];
const stale: string[] = [];
for (const file of onDisk) {
const signals = tierSignals(maskComments(readFileSync(join(PKG, file), 'utf8')));
const predicted = isIntegration(signals);
const listed = integration.includes(file);
if (predicted && !listed) missing.push(`${file} [${firedSignals(signals)}]`);
if (!predicted && listed) stale.push(file);
}
expect(
missing,
'files that spawn the CLI or boot a kernel/driver but are NOT in INTEGRATION_FILES (add them)',
).toEqual([]);
expect(stale, 'INTEGRATION_FILES entries that neither spawn nor boot (remove them)').toEqual([]);
});

it('this pin is itself unit-tier: importing child_process to ask vitest is not spawning the CLI', () => {
const signals = tierSignals(maskComments(readFileSync(join(PKG, THIS_FILE), 'utf8')));
expect(signals.childProcess).toBe(true);
expect(isIntegration(signals), `fired: ${firedSignals(signals)}`).toBe(false);
expect(unit).toContain(THIS_FILE);
});
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions packages/cli/test/vitest-tiers-partition.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* The two tiers of this package's suite stay a PARTITION, and the integration
* list stays equal to what the files DO (#13504).
*
* `vitest.config.ts` splits the suite into two named projects — `unit` (the
* local default) and `integration` (spawns the real CLI or boots a real
* kernel/driver; CI-mandatory, local on demand). Two things can rot under a
* split like that, and both rot silently, which is why this pin exists:
*
* 1. A test file that matches NO project is not run by `vitest run` at all —
* not by the fast tier AND not by `pnpm test` in CI, because with
* `projects` configured the root run IS the union of the projects. A file
* matching BOTH runs twice and reports twice. So the first two cases hold
* `unit ⊎ integration = every test file on disk`, read from vitest's own
* resolution (`vitest list --filesOnly`, with and without `--project`)
* against a filesystem walk — the config's spelling is judged by what
* vitest actually collects, never by re-reading the config.
*
* 2. `INTEGRATION_FILES` is an explicit list, and the `*.e2e.test.ts` NAME is
* not the predicate (the ACCEPT on #13504 measured 18 of 220 files where
* name and behaviour disagree). So the third case re-derives the tier of
* every file from its comment-masked SOURCE and fails when the list and
* the derivation disagree — a new spawner cannot land in the fast tier
* unnoticed, and a stale entry cannot linger. The predicate, in code
* position (comments masked by `scripts/js-comment-mask.mjs`):
*
* SPAWN = calls `runServe(` (the helper in `test/helpers/serve-process.ts`
* whose body spawns the source entry), OR value-imports
* `node:child_process` AND (names an entry basename — the
* `run-dev` / `run` scripts under `bin/` — OR imports `CLI` /
* `TSX` from that helper OR names the `tsx` binary under
* `node_modules/.bin`);
* KERNEL = value-imports `bootSchemaStack` from `schema-migrate`, OR
* value-imports `better-sqlite3`, OR value-imports any
* `@objectstack/driver-*` package, OR constructs `new ObjectQL(`.
* INTEGRATION = SPAWN ∨ KERNEL.
*
* Value imports only: `import type { … } from '@objectstack/driver-sql'`
* loads nothing, a spelling list that SAYS `'better-sqlite3'` opens no
* database, and `expect(deps).toContain('better-sqlite3')` boots nothing —
* every one of those was a false positive of the text-match census this
* predicate replaced. An import statement is one `import … from '<spec>'`
* span containing neither `;` nor another `from` (every import in this
* package's tests ends in `;`, measured on 00ff228fe0).
*
* The fourth case classifies THIS file: it imports `node:child_process` (to
* ask vitest for its file lists) and must still read as `unit`, which is the
* predicate's own regression test against matching its own source.
*
* Runs in the `unit` tier and needs no built `dist/`: it spawns `vitest list`,
* which only globs, and reads sources.
*/

import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
import { childEnv } from './helpers/serve-process.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const PKG = resolve(HERE, '..');
const THIS_FILE = relative(PKG, fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const VITEST_ENTRY = resolve(dirname(require.resolve('vitest/package.json')), 'vitest.mjs');

// ---------------------------------------------------------------------------
// The predicate
// ---------------------------------------------------------------------------

interface ValueImport {
clause: string;
spec: string;
}

/** One `import … from '<spec>'` statement; `import type` is skipped. */
const IMPORT_RE = /\bimport\s+(?!type\b)((?:(?!\bfrom\b)[^;])*?)\bfrom\s+['"]([^'"]+)['"]/g;

function valueImports(code: string): ValueImport[] {
const out: ValueImport[] = [];
for (const m of code.matchAll(IMPORT_RE)) out.push({ clause: m[1], spec: m[2] });
return out;
}

/** Inline `type X` specifiers do not make a value import of `X`. */
function importsValue(imports: ValueImport[], spec: RegExp, name?: RegExp): boolean {
return imports.some((i) => spec.test(i.spec) && (!name || name.test(i.clause.replace(/\btype\s+\w+/g, ''))));
}

export interface TierSignals {
runServe: boolean;
childProcess: boolean;
entryBasename: boolean;
helperCliOrTsx: boolean;
tsxBin: boolean;
bootSchemaStack: boolean;
betterSqlite3: boolean;
driverPackage: boolean;
objectQLCtor: boolean;
}

export function tierSignals(maskedCode: string): TierSignals {
const imports = valueImports(maskedCode);
return {
runServe: /\brunServe\s*[(]/.test(maskedCode),
childProcess: importsValue(imports, /^(?:node:)?child_process$/),
entryBasename: /\brun(?:-dev)?[.]js\b/.test(maskedCode),
helperCliOrTsx: importsValue(imports, /helpers\/serve-process(?:\.js)?$/, /\b(?:CLI|TSX)\b/),
tsxBin: /[.]bin[/]tsx\b/.test(maskedCode),
bootSchemaStack: importsValue(imports, /schema-migrate(?:\.js)?$/, /\bbootSchemaStack\b/),
betterSqlite3: importsValue(imports, /^better-sqlite3$/) || /require[(]\s*['"]better-sqlite3['"]\s*[)]/.test(maskedCode),
driverPackage: importsValue(imports, /^@objectstack\/driver-/),
objectQLCtor: /new\s+ObjectQL\s*[(]/.test(maskedCode),
};
}

export function isIntegration(s: TierSignals): boolean {
const spawn = s.runServe || (s.childProcess && (s.entryBasename || s.helperCliOrTsx || s.tsxBin));
const kernel = s.bootSchemaStack || s.betterSqlite3 || s.driverPackage || s.objectQLCtor;
return spawn || kernel;
}

function firedSignals(s: TierSignals): string {
return (Object.keys(s) as Array<keyof TierSignals>).filter((k) => s[k]).join(', ') || 'none';
}

// ---------------------------------------------------------------------------
// The two readings: the filesystem, and vitest's own resolution
// ---------------------------------------------------------------------------

const TEST_FILE_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', 'coverage']);

function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (SKIP_DIRS.has(entry.name)) continue;
const abs = join(dir, entry.name);
if (entry.isDirectory()) walk(abs, out);
else if (TEST_FILE_RE.test(entry.name)) out.push(relative(PKG, abs));
}
return out;
}

/** `vitest list --filesOnly [--project NAME]`, one relative path per line. */
function vitestFiles(project?: string): string[] {
const args = [VITEST_ENTRY, 'list', '--filesOnly', ...(project ? ['--project', project] : [])];
const out = execFileSync(process.execPath, args, {
cwd: PKG,
env: childEnv(),
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return out
.split('\n')
.map((line) => line.trim())
.filter(Boolean)
.map((line) => line.replace(/^\[[^\]]+\]\s+/, ''));
}

/** The explicit list as `vitest.config.ts` declares it — read as text, not imported. */
function declaredIntegrationFiles(): string[] {
const masked = maskComments(readFileSync(join(PKG, 'vitest.config.ts'), 'utf8'));
const block = /INTEGRATION_FILES\s*=\s*\[([^\]]*)\]/.exec(masked);
if (!block) throw new Error('vitest.config.ts no longer declares `INTEGRATION_FILES = [ … ]`');
return Array.from(block[1].matchAll(/['"]([^'"]+)['"]/g), (m) => m[1]);
}

const sorted = (xs: Iterable<string>): string[] => [...xs].sort();

describe('the two tiers of packages/cli (#13504)', () => {
const onDisk = sorted(walk(PKG));
const all = sorted(vitestFiles());
const unit = sorted(vitestFiles('unit'));
const integration = sorted(vitestFiles('integration'));

it('every test file on disk is one vitest collects with no --project (what `pnpm test` runs)', () => {
expect(onDisk.length).toBeGreaterThan(100);
expect(all, 'vitest run collects a different population than the filesystem holds').toEqual(onDisk);
});

it('unit and integration partition that population — no file in both, none in neither', () => {
const inBoth = unit.filter((f) => integration.includes(f));
expect(inBoth, 'files matched by BOTH projects (they would run and report twice)').toEqual([]);
const union = sorted([...unit, ...integration]);
expect(union, 'files matched by NEITHER project fall out of every tier, including CI').toEqual(all);
});

it('INTEGRATION_FILES equals the behavioural predicate over every file on disk', () => {
const declared = declaredIntegrationFiles();
expect(sorted(new Set(declared)), 'INTEGRATION_FILES carries a duplicate').toEqual(sorted(declared));
expect(sorted(declared), 'an INTEGRATION_FILES entry names no file vitest can find').toEqual(integration);

const missing: string[] = [];
const stale: string[] = [];
for (const file of onDisk) {
const signals = tierSignals(maskComments(readFileSync(join(PKG, file), 'utf8')));
const predicted = isIntegration(signals);
const listed = integration.includes(file);
if (predicted && !listed) missing.push(`${file} [${firedSignals(signals)}]`);
if (!predicted && listed) stale.push(file);
}
expect(
missing,
'files that spawn the CLI or boot a kernel/driver but are NOT in INTEGRATION_FILES (add them)',
).toEqual([]);
expect(stale, 'INTEGRATION_FILES entries that neither spawn nor boot (remove them)').toEqual([]);
});

it('this pin is itself unit-tier: importing child_process to ask vitest is not spawning the CLI', () => {
const signals = tierSignals(maskComments(readFileSync(join(PKG, THIS_FILE), 'utf8')));
expect(signals.childProcess).toBe(true);
expect(isIntegration(signals), `fired: ${firedSignals(signals)}`).toBe(false);
expect(unit).toContain(THIS_FILE);
});
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions packages/cli/test/vitest-tiers-partition.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* The two tiers of this package's suite stay a PARTITION, and the integration
* list stays equal to what the files DO (#13504).
*
* `vitest.config.ts` splits the suite into two named projects — `unit` (the
* local default) and `integration` (spawns the real CLI or boots a real
* kernel/driver; CI-mandatory, local on demand). Two things can rot under a
* split like that, and both rot silently, which is why this pin exists:
*
* 1. A test file that matches NO project is not run by `vitest run` at all —
* not by the fast tier AND not by `pnpm test` in CI, because with
* `projects` configured the root run IS the union of the projects. A file
* matching BOTH runs twice and reports twice. So the first two cases hold
* `unit ⊎ integration = every test file on disk`, read from vitest's own
* resolution (`vitest list --filesOnly`, with and without `--project`)
* against a filesystem walk — the config's spelling is judged by what
* vitest actually collects, never by re-reading the config.
*
* 2. `INTEGRATION_FILES` is an explicit list, and the `*.e2e.test.ts` NAME is
* not the predicate (the ACCEPT on #13504 measured 18 of 220 files where
* name and behaviour disagree). So the third case re-derives the tier of
* every file from its comment-masked SOURCE and fails when the list and
* the derivation disagree — a new spawner cannot land in the fast tier
* unnoticed, and a stale entry cannot linger. The predicate, in code
* position (comments masked by `scripts/js-comment-mask.mjs`):
*
* SPAWN = calls `runServe(` (the helper in `test/helpers/serve-process.ts`
* whose body spawns the source entry), OR value-imports
* `node:child_process` AND (names an entry basename — the
* `run-dev` / `run` scripts under `bin/` — OR imports `CLI` /
* `TSX` from that helper OR names the `tsx` binary under
* `node_modules/.bin`);
* KERNEL = value-imports `bootSchemaStack` from `schema-migrate`, OR
* value-imports `better-sqlite3`, OR value-imports any
* `@objectstack/driver-*` package, OR constructs `new ObjectQL(`.
* INTEGRATION = SPAWN ∨ KERNEL.
*
* Value imports only: `import type { … } from '@objectstack/driver-sql'`
* loads nothing, a spelling list that SAYS `'better-sqlite3'` opens no
* database, and `expect(deps).toContain('better-sqlite3')` boots nothing —
* every one of those was a false positive of the text-match census this
* predicate replaced. An import statement is one `import … from '<spec>'`
* span containing neither `;` nor another `from` (every import in this
* package's tests ends in `;`, measured on 00ff228fe0).
*
* The fourth case classifies THIS file: it imports `node:child_process` (to
* ask vitest for its file lists) and must still read as `unit`, which is the
* predicate's own regression test against matching its own source.
*
* Runs in the `unit` tier and needs no built `dist/`: it spawns `vitest list`,
* which only globs, and reads sources.
*/

import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { maskComments } from '../../../scripts/js-comment-mask.mjs';
import { childEnv } from './helpers/serve-process.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const PKG = resolve(HERE, '..');
const THIS_FILE = relative(PKG, fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const VITEST_ENTRY = resolve(dirname(require.resolve('vitest/package.json')), 'vitest.mjs');

// ---------------------------------------------------------------------------
// The predicate
// ---------------------------------------------------------------------------

interface ValueImport {
clause: string;
spec: string;
}

/** One `import … from '<spec>'` statement; `import type` is skipped. */
const IMPORT_RE = /\bimport\s+(?!type\b)((?:(?!\bfrom\b)[^;])*?)\bfrom\s+['"]([^'"]+)['"]/g;

function valueImports(code: string): ValueImport[] {
const out: ValueImport[] = [];
for (const m of code.matchAll(IMPORT_RE)) out.push({ clause: m[1], spec: m[2] });
return out;
}

/** Inline `type X` specifiers do not make a value import of `X`. */
function importsValue(imports: ValueImport[], spec: RegExp, name?: RegExp): boolean {
return imports.some((i) => spec.test(i.spec) && (!name || name.test(i.clause.replace(/\btype\s+\w+/g, ''))));
}

export interface TierSignals {
runServe: boolean;
childProcess: boolean;
entryBasename: boolean;
helperCliOrTsx: boolean;
tsxBin: boolean;
bootSchemaStack: boolean;
betterSqlite3: boolean;
driverPackage: boolean;
objectQLCtor: boolean;
}

export function tierSignals(maskedCode: string): TierSignals {
const imports = valueImports(maskedCode);
return {
runServe: /\brunServe\s*[(]/.test(maskedCode),
childProcess: importsValue(imports, /^(?:node:)?child_process$/),
entryBasename: /\brun(?:-dev)?[.]js\b/.test(maskedCode),
helperCliOrTsx: importsValue(imports, /helpers\/serve-process(?:\.js)?$/, /\b(?:CLI|TSX)\b/),
tsxBin: /[.]bin[/]tsx\b/.test(maskedCode),
bootSchemaStack: importsValue(imports, /schema-migrate(?:\.js)?$/, /\bbootSchemaStack\b/),
betterSqlite3: importsValue(imports, /^better-sqlite3$/) || /require[(]\s*['"]better-sqlite3['"]\s*[)]/.test(maskedCode),
driverPackage: importsValue(imports, /^@objectstack\/driver-/),
objectQLCtor: /new\s+ObjectQL\s*[(]/.test(maskedCode),
};
}

export function isIntegration(s: TierSignals): boolean {
const spawn = s.runServe || (s.childProcess && (s.entryBasename || s.helperCliOrTsx || s.tsxBin));
const kernel = s.bootSchemaStack || s.betterSqlite3 || s.driverPackage || s.objectQLCtor;
return spawn || kernel;
}

function firedSignals(s: TierSignals): string {
return (Object.keys(s) as Array<keyof TierSignals>).filter((k) => s[k]).join(', ') || 'none';
}

// ---------------------------------------------------------------------------
// The two readings: the filesystem, and vitest's own resolution
// ---------------------------------------------------------------------------

const TEST_FILE_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', 'coverage']);

function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (SKIP_DIRS.has(entry.name)) continue;
const abs = join(dir, entry.name);
if (entry.isDirectory()) walk(abs, out);
else if (TEST_FILE_RE.test(entry.name)) out.push(relative(PKG, abs));
}
return out;
}

/** `vitest list --filesOnly [--project NAME]`, one relative path per line. */
function vitestFiles(project?: string): string[] {
const args = [VITEST_ENTRY, 'list', '--filesOnly', ...(project ? ['--project', project] : [])];
const out = execFileSync(process.execPath, args, {
cwd: PKG,
env: childEnv(),
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return out
.split('\n')
.map((line) => line.trim())
.filter(Boolean)
.map((line) => line.replace(/^\[[^\]]+\]\s+/, ''));
}

/** The explicit list as `vitest.config.ts` declares it — read as text, not imported. */
function declaredIntegrationFiles(): string[] {
const masked = maskComments(readFileSync(join(PKG, 'vitest.config.ts'), 'utf8'));
const block = /INTEGRATION_FILES\s*=\s*\[([^\]]*)\]/.exec(masked);
if (!block) throw new Error('vitest.config.ts no longer declares `INTEGRATION_FILES = [ … ]`');
return Array.from(block[1].matchAll(/['"]([^'"]+)['"]/g), (m) => m[1]);
}

const sorted = (xs: Iterable<string>): string[] => [...xs].sort();

describe('the two tiers of packages/cli (#13504)', () => {
const onDisk = sorted(walk(PKG));
const all = sorted(vitestFiles());
const unit = sorted(vitestFiles('unit'));
const integration = sorted(vitestFiles('integration'));

it('every test file on disk is one vitest collects with no --project (what `pnpm test` runs)', () => {
expect(onDisk.length).toBeGreaterThan(100);
expect(all, 'vitest run collects a different population than the filesystem holds').toEqual(onDisk);
});

it('unit and integration partition that population — no file in both, none in neither', () => {
const inBoth = unit.filter((f) => integration.includes(f));
expect(inBoth, 'files matched by BOTH projects (they would run and report twice)').toEqual([]);
const union = sorted([...unit, ...integration]);
expect(union, 'files matched by NEITHER project fall out of every tier, including CI').toEqual(all);
});

it('INTEGRATION_FILES equals the behavioural predicate over every file on disk', () => {
const declared = declaredIntegrationFiles();
expect(sorted(new Set(declared)), 'INTEGRATION_FILES carries a duplicate').toEqual(sorted(declared));
expect(sorted(declared), 'an INTEGRATION_FILES entry names no file vitest can find').toEqual(integration);

const missing: string[] = [];
const stale: string[] = [];
for (const file of onDisk) {
const signals = tierSignals(maskComments(readFileSync(join(PKG, file), 'utf8')));
const predicted = isIntegration(signals);
const listed = integration.includes(file);
if (predicted && !listed) missing.push(`${file} [${firedSignals(signals)}]`);
if (!predicted && listed) stale.push(file);
}
expect(
missing,
'files that spawn the CLI or boot a kernel/driver but are NOT in INTEGRATION_FILES (add them)',
).toEqual([]);
expect(stale, 'INTEGRATION_FILES entries that neither spawn nor boot (remove them)').toEqual([]);
});

it('this pin is itself unit-tier: importing child_process to ask vitest is not spawning the CLI', () => {
const signals = tierSignals(maskComments(readFileSync(join(PKG, THIS_FILE), 'utf8')));
expect(signals.childProcess).toBe(true);
expect(isIntegration(signals), `fired: ${firedSignals(signals)}`).toBe(false);
expect(unit).toContain(THIS_FILE);
});
});
Loading
Loading