mirror of
https://github.com/BradGroux/veritas-kanban.git
synced 2026-08-28 02:44:59 +00:00
259 lines
7.7 KiB
JavaScript
259 lines
7.7 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
|
|
import {
|
|
affectedWorkspaces,
|
|
classifyCiTestScope,
|
|
coverageWorkspaces,
|
|
diffRangeFor,
|
|
githubOutputLines,
|
|
isDependencyFreeScopeControlPath,
|
|
isDocumentationPath,
|
|
} from './select-ci-test-scope.mjs';
|
|
|
|
test('classifies documentation-only pull requests without unit tests', () => {
|
|
const result = classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
changedFiles: ['README.md', 'docs/AGENTS-TEMPLATE.md'],
|
|
});
|
|
|
|
assert.equal(result.scope, 'none');
|
|
assert.deepEqual(result.packages, []);
|
|
});
|
|
|
|
test('dependency-free cadence controls do not trigger workspace unit tests', () => {
|
|
const result = classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
changedFiles: [
|
|
'CHANGELOG.md',
|
|
'scripts/check-delivery-cadence.mjs',
|
|
'scripts/check-delivery-cadence.test.mjs',
|
|
],
|
|
});
|
|
|
|
assert.equal(result.scope, 'none');
|
|
assert.deepEqual(result.packages, []);
|
|
assert.equal(isDependencyFreeScopeControlPath('scripts/check-delivery-cadence.mjs'), true);
|
|
});
|
|
|
|
test('cadence controls do not turn an ordinary workspace change into a test milestone', () => {
|
|
const result = classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
changedFiles: ['scripts/check-delivery-cadence.mjs', 'server/src/routes/tasks.ts'],
|
|
});
|
|
|
|
assert.equal(result.scope, 'none');
|
|
assert.deepEqual(result.packages, ['server']);
|
|
});
|
|
|
|
test('records affected workspaces without testing an ordinary code change', () => {
|
|
const result = classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
changedFiles: ['web/src/App.tsx', 'server/src/routes/tasks.ts', 'README.md'],
|
|
});
|
|
|
|
assert.equal(result.scope, 'none');
|
|
assert.deepEqual(result.packages, ['server', 'web']);
|
|
});
|
|
|
|
test('selects critical coverage only for the full milestone scope', () => {
|
|
const files = [
|
|
'server/src/storage/file-storage.ts',
|
|
'web/src/components/Board.tsx',
|
|
'mcp/src/tools/tasks.ts',
|
|
];
|
|
|
|
assert.deepEqual(coverageWorkspaces(files), []);
|
|
assert.deepEqual(coverageWorkspaces(files, 'focused'), []);
|
|
assert.deepEqual(coverageWorkspaces(files, 'none'), []);
|
|
assert.deepEqual(coverageWorkspaces(files, 'full'), ['server', 'web', 'cli', 'mcp', 'desktop']);
|
|
});
|
|
|
|
test('defers governed critical-path coverage until a milestone', () => {
|
|
assert.deepEqual(
|
|
coverageWorkspaces([
|
|
'server/src/__tests__/provider-completion-service.test.ts',
|
|
'server/src/schemas/auth-schemas.ts',
|
|
'shared/src/utils/api-permissions.ts',
|
|
'web/src/__tests__/useWebSocket.test.ts',
|
|
'mcp/src/__tests__/task-tools.test.ts',
|
|
]),
|
|
[]
|
|
);
|
|
});
|
|
|
|
test('ci:full overrides a documentation-only pull request', () => {
|
|
const result = classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
labels: ['documentation', 'ci:full'],
|
|
changedFiles: ['docs/V6-GA-CHECKLIST.md'],
|
|
});
|
|
|
|
assert.equal(result.scope, 'full');
|
|
});
|
|
|
|
test('CI control paths require an explicit milestone instead of auto-running the full suite', () => {
|
|
const paths = [
|
|
'.github/workflows/ci.yml',
|
|
'scripts/select-ci-test-scope.mjs',
|
|
'scripts/verify-full-suite-job-evidence.mjs',
|
|
'scripts/run-coverage.mjs',
|
|
'scripts/check-coverage-policy.test.mjs',
|
|
'docs/testing/critical-path-coverage.json',
|
|
'web/vitest.config.ts',
|
|
];
|
|
|
|
for (const file of paths) {
|
|
const result = classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
changedFiles: [file],
|
|
});
|
|
assert.equal(result.scope, 'none', file);
|
|
}
|
|
});
|
|
|
|
test('records shared, storage, desktop, and manifest workspaces without automatic tests', () => {
|
|
const cases = [
|
|
{
|
|
file: 'shared/src/types/task.types.ts',
|
|
packages: ['server', 'web', 'cli', 'mcp', 'desktop'],
|
|
},
|
|
{ file: 'server/src/storage/sqlite/repositories.ts', packages: ['server'] },
|
|
{ file: 'desktop/src/main/index.ts', packages: ['desktop'] },
|
|
{ file: 'server/package.json', packages: ['server'] },
|
|
{ file: 'package.json', packages: ['server', 'web', 'cli', 'mcp', 'desktop'] },
|
|
{ file: 'pnpm-lock.yaml', packages: ['server', 'web', 'cli', 'mcp', 'desktop'] },
|
|
];
|
|
|
|
for (const { file, packages } of cases) {
|
|
const result = classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
changedFiles: [file],
|
|
});
|
|
assert.equal(result.scope, 'none', file);
|
|
assert.deepEqual(result.packages, packages, file);
|
|
}
|
|
});
|
|
|
|
test('scheduled and explicitly full manual runs select the full suite', () => {
|
|
assert.equal(classifyCiTestScope({ eventName: 'schedule', changedFiles: [] }).scope, 'full');
|
|
assert.equal(
|
|
classifyCiTestScope({
|
|
eventName: 'workflow_dispatch',
|
|
manualScope: 'full',
|
|
changedFiles: ['README.md'],
|
|
}).scope,
|
|
'full'
|
|
);
|
|
});
|
|
|
|
test('focused manual runs still classify the selected range by risk', () => {
|
|
assert.equal(
|
|
classifyCiTestScope({
|
|
eventName: 'workflow_dispatch',
|
|
manualScope: 'focused',
|
|
changedFiles: ['server/src/routes/tasks.ts'],
|
|
}).scope,
|
|
'focused'
|
|
);
|
|
assert.equal(
|
|
classifyCiTestScope({
|
|
eventName: 'workflow_dispatch',
|
|
manualScope: 'focused',
|
|
changedFiles: ['pnpm-lock.yaml'],
|
|
}).scope,
|
|
'focused'
|
|
);
|
|
});
|
|
|
|
test('ordinary post-merge pushes defer tests while recording affected packages', () => {
|
|
const result = classifyCiTestScope({
|
|
eventName: 'push',
|
|
changedFiles: ['cli/src/commands/doctor.ts'],
|
|
});
|
|
|
|
assert.equal(result.scope, 'none');
|
|
assert.deepEqual(result.packages, ['cli']);
|
|
});
|
|
|
|
test('unknown non-documentation paths wait for an explicit milestone', () => {
|
|
const result = classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
changedFiles: ['site/src/runtime.ts'],
|
|
});
|
|
|
|
assert.equal(result.scope, 'none');
|
|
});
|
|
|
|
test('deleted source paths are recorded without automatic test escalation', () => {
|
|
assert.equal(
|
|
classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
changedFiles: ['server/src/obsolete.ts'],
|
|
deletedFiles: ['server/src/obsolete.ts'],
|
|
}).scope,
|
|
'none'
|
|
);
|
|
assert.equal(
|
|
classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
changedFiles: ['site/src/obsolete.ts'],
|
|
deletedFiles: ['site/src/obsolete.ts'],
|
|
}).scope,
|
|
'none'
|
|
);
|
|
assert.equal(
|
|
classifyCiTestScope({
|
|
eventName: 'pull_request',
|
|
changedFiles: ['docs/obsolete.md'],
|
|
deletedFiles: ['docs/obsolete.md'],
|
|
}).scope,
|
|
'none'
|
|
);
|
|
});
|
|
|
|
test('empty ranges select no unit-test tier', () => {
|
|
const result = classifyCiTestScope({
|
|
eventName: 'push',
|
|
changedFiles: [],
|
|
});
|
|
|
|
assert.equal(result.scope, 'none');
|
|
});
|
|
|
|
test('documentation and workspace helpers are deterministic', () => {
|
|
assert.equal(isDocumentationPath('prompt-registry/review.md'), true);
|
|
assert.equal(isDocumentationPath('server/src/index.ts'), false);
|
|
assert.deepEqual(
|
|
affectedWorkspaces([
|
|
'desktop/src/main/index.ts',
|
|
'mcp/src/index.ts',
|
|
'server/src/index.ts',
|
|
'server/src/routes/tasks.ts',
|
|
]),
|
|
['server', 'mcp', 'desktop']
|
|
);
|
|
});
|
|
|
|
test('uses two-dot push ranges, three-dot review ranges, and rejects non-SHAs', () => {
|
|
const base = 'a'.repeat(40);
|
|
const head = 'b'.repeat(40);
|
|
|
|
assert.equal(diffRangeFor(base, head, 'push'), `${base}..${head}`);
|
|
assert.equal(diffRangeFor(base, head, 'pull_request'), `${base}...${head}`);
|
|
assert.throws(
|
|
() => diffRangeFor('--output=/tmp/unsafe', head, 'workflow_dispatch'),
|
|
/hexadecimal commit IDs/
|
|
);
|
|
});
|
|
|
|
test('exports the resolved base SHA for downstream coverage gates', () => {
|
|
const baseSha = 'a'.repeat(40);
|
|
const headSha = 'b'.repeat(40);
|
|
const output = githubOutputLines(
|
|
{ scope: 'focused', packages: ['server'], files: [], reason: 'test' },
|
|
{ baseSha, headSha, eventName: 'workflow_dispatch' }
|
|
);
|
|
|
|
assert.match(output, new RegExp(`^base_sha=${baseSha}$`, 'm'));
|
|
});
|