chore: import upstream snapshot with attribution
CI / Test (ubuntu-latest, Node 18.x, bun) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, npm) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, pnpm) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, yarn) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 20.x, bun) (push) Failing after 17m13s
CI / Test (ubuntu-latest, Node 20.x, npm) (push) Failing after 18m42s
CI / Test (ubuntu-latest, Node 20.x, pnpm) (push) Failing after 15m0s
CI / Test (ubuntu-latest, Node 20.x, yarn) (push) Failing after 49m44s
CI / Test (ubuntu-latest, Node 22.x, bun) (push) Failing after 51m55s
CI / Test (ubuntu-latest, Node 22.x, pnpm) (push) Failing after 21m57s
CI / Test (ubuntu-latest, Node 22.x, npm) (push) Failing after 37m39s
CI / Test (ubuntu-latest, Node 22.x, yarn) (push) Failing after 34m7s
CI / Validate Components (push) Failing after 37m15s
CI / Python Tests (push) Failing after 10m1s
CI / Security Scan (push) Failing after 10m1s
CI / Lint (push) Failing after 17m12s
CI / Coverage (push) Failing after 20m19s
CI / Test (macos-latest, Node 18.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, yarn) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, yarn) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, yarn) (push) Has been cancelled
CI / Test (ubuntu-latest, Node 18.x, bun) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, npm) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, pnpm) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, yarn) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 20.x, bun) (push) Failing after 17m13s
CI / Test (ubuntu-latest, Node 20.x, npm) (push) Failing after 18m42s
CI / Test (ubuntu-latest, Node 20.x, pnpm) (push) Failing after 15m0s
CI / Test (ubuntu-latest, Node 20.x, yarn) (push) Failing after 49m44s
CI / Test (ubuntu-latest, Node 22.x, bun) (push) Failing after 51m55s
CI / Test (ubuntu-latest, Node 22.x, pnpm) (push) Failing after 21m57s
CI / Test (ubuntu-latest, Node 22.x, npm) (push) Failing after 37m39s
CI / Test (ubuntu-latest, Node 22.x, yarn) (push) Failing after 34m7s
CI / Validate Components (push) Failing after 37m15s
CI / Python Tests (push) Failing after 10m1s
CI / Security Scan (push) Failing after 10m1s
CI / Lint (push) Failing after 17m12s
CI / Coverage (push) Failing after 20m19s
CI / Test (macos-latest, Node 18.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, yarn) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, yarn) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, yarn) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,461 @@
|
||||
/**
|
||||
* Tests for scripts/platform-audit.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { execFileSync, spawnSync } = require('child_process');
|
||||
|
||||
const SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'platform-audit.js');
|
||||
const {
|
||||
DISCUSSION_ENABLED_QUERY,
|
||||
DISCUSSION_QUERY
|
||||
} = require(path.join(__dirname, '..', '..', 'scripts', 'lib', 'github-discussions'));
|
||||
|
||||
function createTempDir(prefix) {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
}
|
||||
|
||||
function cleanup(dirPath) {
|
||||
fs.rmSync(dirPath, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function writeFile(rootDir, relativePath, content) {
|
||||
const targetPath = path.join(rootDir, relativePath);
|
||||
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
||||
fs.writeFileSync(targetPath, content);
|
||||
}
|
||||
|
||||
function seedRepo(rootDir, overrides = {}) {
|
||||
const files = {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'everything-claude-code',
|
||||
scripts: {
|
||||
'platform:audit': 'node scripts/platform-audit.js',
|
||||
'discussion:audit': 'node scripts/discussion-audit.js',
|
||||
'operator:dashboard': 'node scripts/operator-readiness-dashboard.js',
|
||||
'observability:ready': 'node scripts/observability-readiness.js',
|
||||
'security:ioc-scan': 'node scripts/ci/scan-supply-chain-iocs.js',
|
||||
'security:advisory-sources': 'node scripts/ci/supply-chain-advisory-sources.js',
|
||||
'harness:audit': 'node scripts/harness-audit.js'
|
||||
}
|
||||
}, null, 2),
|
||||
'docs/ECC-2.0-GA-ROADMAP.md': [
|
||||
'ECC Platform Roadmap',
|
||||
'https://linear.app/itomarkets/project/ecc-platform-roadmap-52b328ee03e1',
|
||||
'ITO-44',
|
||||
'ITO-59'
|
||||
].join('\n'),
|
||||
'docs/architecture/progress-sync-contract.md': [
|
||||
'GitHub PRs/issues/discussions',
|
||||
'Linear project',
|
||||
'local handoff',
|
||||
'repo roadmap',
|
||||
'scripts/work-items.js'
|
||||
].join('\n'),
|
||||
'docs/security/supply-chain-incident-response.md': [
|
||||
'TanStack',
|
||||
'Mini Shai-Hulud',
|
||||
'node-ipc',
|
||||
'scan-supply-chain-iocs.js',
|
||||
'supply-chain-advisory-sources.js'
|
||||
].join('\n'),
|
||||
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-19.md': [
|
||||
'Release video suite',
|
||||
'growth outreach',
|
||||
'Operator dashboard',
|
||||
'GitGuardian',
|
||||
'macOS/Ubuntu/Windows test matrix',
|
||||
'2568 passed'
|
||||
].join('\n'),
|
||||
'docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-20.md': [
|
||||
'This dashboard is generated by `npm run operator:dashboard`',
|
||||
'Growth Baseline',
|
||||
'hypergrowth release command center',
|
||||
'Prompt-To-Artifact Checklist',
|
||||
'ITO-44',
|
||||
'ITO-59',
|
||||
'PR queue',
|
||||
'Not complete',
|
||||
'operator:dashboard',
|
||||
'Next Work Order'
|
||||
].join('\n'),
|
||||
'scripts/operator-readiness-dashboard.js': 'operator dashboard generator'
|
||||
};
|
||||
|
||||
for (const [relativePath, content] of Object.entries({ ...files, ...overrides })) {
|
||||
if (content === null) {
|
||||
continue;
|
||||
}
|
||||
writeFile(rootDir, relativePath, content);
|
||||
}
|
||||
}
|
||||
|
||||
function discussionGhKey(owner, name, first = 100) {
|
||||
return `api graphql -f owner=${owner} -f name=${name} -F first=${first} -f query=${DISCUSSION_QUERY}`;
|
||||
}
|
||||
|
||||
function discussionEnabledGhKey(owner, name) {
|
||||
return `api graphql -f owner=${owner} -f name=${name} -f query=${DISCUSSION_ENABLED_QUERY}`;
|
||||
}
|
||||
|
||||
function writeGhShim(rootDir, responses) {
|
||||
const shimPath = path.join(rootDir, 'gh-shim.js');
|
||||
fs.writeFileSync(shimPath, `
|
||||
const responses = ${JSON.stringify(responses)};
|
||||
const args = process.argv.slice(2);
|
||||
const key = args.join(' ');
|
||||
if (process.env.GITHUB_TOKEN) {
|
||||
console.error('GITHUB_TOKEN should be unset by default');
|
||||
process.exit(42);
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(responses, key)) {
|
||||
console.error('Unexpected gh args: ' + key);
|
||||
process.exit(3);
|
||||
}
|
||||
process.stdout.write(JSON.stringify(responses[key]));
|
||||
`);
|
||||
return shimPath;
|
||||
}
|
||||
|
||||
function run(args = [], options = {}) {
|
||||
const env = {
|
||||
...process.env,
|
||||
...(options.env || {})
|
||||
};
|
||||
|
||||
return execFileSync('node', [SCRIPT, ...args], {
|
||||
cwd: options.cwd || path.join(__dirname, '..', '..'),
|
||||
env,
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 10000
|
||||
});
|
||||
}
|
||||
|
||||
function runProcess(args = [], options = {}) {
|
||||
const env = {
|
||||
...process.env,
|
||||
...(options.env || {})
|
||||
};
|
||||
|
||||
return spawnSync('node', [SCRIPT, ...args], {
|
||||
cwd: options.cwd || path.join(__dirname, '..', '..'),
|
||||
env,
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 10000
|
||||
});
|
||||
}
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` PASS ${name}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(` FAIL ${name}`);
|
||||
console.log(` Error: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing platform-audit.js ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
if (test('parseArgs accepts supported flags and rejects invalid values', () => {
|
||||
const { parseArgs } = require(SCRIPT);
|
||||
const rootDir = createTempDir('platform-audit-args-');
|
||||
|
||||
try {
|
||||
const parsed = parseArgs([
|
||||
'node',
|
||||
'script',
|
||||
'--format=json',
|
||||
`--root=${rootDir}`,
|
||||
'--json',
|
||||
'--repo',
|
||||
'affaan-m/ECC',
|
||||
'--max-open-prs',
|
||||
'5',
|
||||
'--max-open-issues',
|
||||
'6',
|
||||
'--allow-untracked',
|
||||
'docs/drafts/'
|
||||
]);
|
||||
|
||||
assert.strictEqual(parsed.format, 'json');
|
||||
assert.strictEqual(parsed.root, path.resolve(rootDir));
|
||||
assert.deepStrictEqual(parsed.repos, ['affaan-m/ECC']);
|
||||
assert.strictEqual(parsed.thresholds.maxOpenPrs, 5);
|
||||
assert.strictEqual(parsed.thresholds.maxOpenIssues, 6);
|
||||
assert.deepStrictEqual(parsed.allowUntracked, ['docs/drafts/']);
|
||||
|
||||
assert.throws(() => parseArgs(['node', 'script', '--format', 'xml']), /Invalid format/);
|
||||
assert.throws(() => parseArgs(['node', 'script', '--write', 'audit.md']), /--write requires/);
|
||||
assert.throws(() => parseArgs(['node', 'script', '--repo']), /--repo requires a value/);
|
||||
assert.throws(() => parseArgs(['node', 'script', '--max-open-prs', 'x']), /Invalid --max-open-prs/);
|
||||
assert.throws(() => parseArgs(['node', 'script', '--unknown']), /Unknown argument/);
|
||||
} finally {
|
||||
cleanup(rootDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('skip-github report checks local release and security evidence', () => {
|
||||
const projectRoot = createTempDir('platform-audit-local-');
|
||||
|
||||
try {
|
||||
seedRepo(projectRoot);
|
||||
const parsed = JSON.parse(run(['--format=json', `--root=${projectRoot}`, '--skip-github'], { cwd: projectRoot }));
|
||||
|
||||
assert.strictEqual(parsed.schema_version, 'ecc.platform-audit.v1');
|
||||
assert.strictEqual(parsed.ready, true);
|
||||
assert.strictEqual(parsed.github.skipped, true);
|
||||
assert.ok(parsed.checks.some(check => check.id === 'roadmap-linear-mirror' && check.status === 'pass'));
|
||||
assert.ok(parsed.checks.some(check => check.id === 'supply-chain-runbook' && check.status === 'pass'));
|
||||
assert.ok(parsed.checks.some(check => check.id === 'operator-dashboard-command' && check.status === 'pass'));
|
||||
assert.ok(parsed.checks.some(check => check.id === 'operator-readiness-dashboard' && check.status === 'pass'));
|
||||
assert.ok(parsed.checks.some(check => check.id === 'release-evidence-current' && check.status === 'pass'));
|
||||
assert.deepStrictEqual(parsed.top_actions, []);
|
||||
} finally {
|
||||
cleanup(projectRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('release evidence gate rejects stale root suite counts', () => {
|
||||
const projectRoot = createTempDir('platform-audit-stale-release-evidence-');
|
||||
|
||||
try {
|
||||
seedRepo(projectRoot, {
|
||||
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-19.md': [
|
||||
'Release video suite',
|
||||
'growth outreach',
|
||||
'Operator dashboard',
|
||||
'GitGuardian',
|
||||
'macOS/Ubuntu/Windows test matrix',
|
||||
'2560 passed'
|
||||
].join('\n')
|
||||
});
|
||||
|
||||
const parsed = JSON.parse(run(['--format=json', `--root=${projectRoot}`, '--skip-github'], { cwd: projectRoot }));
|
||||
const releaseEvidence = parsed.checks.find(check => check.id === 'release-evidence-current');
|
||||
|
||||
assert.strictEqual(releaseEvidence.status, 'fail');
|
||||
assert.ok(parsed.top_actions.some(action => action.id === 'release-evidence-current'));
|
||||
} finally {
|
||||
cleanup(projectRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('markdown output can be written as an operator artifact', () => {
|
||||
const projectRoot = createTempDir('platform-audit-markdown-');
|
||||
const outputPath = path.join(projectRoot, 'artifacts', 'platform-audit.md');
|
||||
|
||||
try {
|
||||
seedRepo(projectRoot);
|
||||
const stdout = run([
|
||||
'--markdown',
|
||||
'--write',
|
||||
outputPath,
|
||||
`--root=${projectRoot}`,
|
||||
'--skip-github'
|
||||
], { cwd: projectRoot });
|
||||
const written = fs.readFileSync(outputPath, 'utf8');
|
||||
|
||||
assert.strictEqual(stdout, written);
|
||||
assert.ok(written.includes('# ECC Platform Audit'));
|
||||
assert.ok(written.includes('## Queue Summary'));
|
||||
assert.ok(written.includes('| Open PRs | 0 | 20 | PASS |'));
|
||||
assert.ok(written.includes('`roadmap-linear-mirror`'));
|
||||
assert.ok(written.includes('## Top Actions'));
|
||||
assert.ok(written.includes('- none'));
|
||||
} finally {
|
||||
cleanup(projectRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('github queue and discussion budgets pass with maintainer touch', () => {
|
||||
const projectRoot = createTempDir('platform-audit-github-pass-');
|
||||
|
||||
try {
|
||||
seedRepo(projectRoot);
|
||||
const shimPath = writeGhShim(projectRoot, {
|
||||
'pr list --repo affaan-m/ECC --state open --json number,title,isDraft,mergeStateStatus,updatedAt,url,author': [],
|
||||
'issue list --repo affaan-m/ECC --state open --json number,title,updatedAt,url,author,labels': [],
|
||||
[discussionEnabledGhKey('affaan-m', 'ECC')]: {
|
||||
data: { repository: { hasDiscussionsEnabled: true } }
|
||||
},
|
||||
[discussionGhKey('affaan-m', 'ECC')]: {
|
||||
data: {
|
||||
repository: {
|
||||
hasDiscussionsEnabled: true,
|
||||
discussions: {
|
||||
totalCount: 1,
|
||||
nodes: [
|
||||
{
|
||||
number: 73,
|
||||
title: 'Compacting during workflow',
|
||||
url: 'https://github.com/example/discussions/73',
|
||||
updatedAt: '2026-05-15T00:00:00Z',
|
||||
authorAssociation: 'NONE',
|
||||
category: { name: 'General', isAnswerable: false },
|
||||
answer: null,
|
||||
comments: { nodes: [{ authorAssociation: 'OWNER' }] }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const parsed = JSON.parse(run([
|
||||
'--format=json',
|
||||
`--root=${projectRoot}`,
|
||||
'--repo',
|
||||
'affaan-m/ECC'
|
||||
], {
|
||||
cwd: projectRoot,
|
||||
env: {
|
||||
ECC_GH_SHIM: shimPath,
|
||||
GITHUB_TOKEN: 'must-be-removed'
|
||||
}
|
||||
}));
|
||||
|
||||
assert.strictEqual(parsed.ready, true);
|
||||
assert.strictEqual(parsed.github.totals.openPrs, 0);
|
||||
assert.strictEqual(parsed.github.totals.openIssues, 0);
|
||||
assert.strictEqual(parsed.github.totals.discussionsNeedingMaintainerTouch, 0);
|
||||
assert.strictEqual(parsed.github.totals.discussionsMissingAcceptedAnswer, 0);
|
||||
assert.ok(parsed.checks.some(check => check.id === 'github-discussion-touch' && check.status === 'pass'));
|
||||
assert.ok(parsed.checks.some(check => check.id === 'github-discussion-answers' && check.status === 'pass'));
|
||||
} finally {
|
||||
cleanup(projectRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('threshold failures and untouched discussions become top actions', () => {
|
||||
const projectRoot = createTempDir('platform-audit-github-fail-');
|
||||
|
||||
try {
|
||||
seedRepo(projectRoot);
|
||||
const prs = Array.from({ length: 3 }, (_, index) => ({
|
||||
number: index + 1,
|
||||
title: `PR ${index + 1}`,
|
||||
isDraft: false,
|
||||
mergeStateStatus: 'CLEAN',
|
||||
updatedAt: '2026-05-15T00:00:00Z',
|
||||
url: `https://github.com/example/pull/${index + 1}`,
|
||||
author: { login: 'contributor' }
|
||||
}));
|
||||
const shimPath = writeGhShim(projectRoot, {
|
||||
'pr list --repo affaan-m/ECC --state open --json number,title,isDraft,mergeStateStatus,updatedAt,url,author': prs,
|
||||
'issue list --repo affaan-m/ECC --state open --json number,title,updatedAt,url,author,labels': [],
|
||||
[discussionEnabledGhKey('affaan-m', 'ECC')]: {
|
||||
data: { repository: { hasDiscussionsEnabled: true } }
|
||||
},
|
||||
[discussionGhKey('affaan-m', 'ECC')]: {
|
||||
data: {
|
||||
repository: {
|
||||
hasDiscussionsEnabled: true,
|
||||
discussions: {
|
||||
totalCount: 1,
|
||||
nodes: [
|
||||
{
|
||||
number: 1239,
|
||||
title: 'Losing context',
|
||||
url: 'https://github.com/example/discussions/1239',
|
||||
updatedAt: '2026-05-15T00:00:00Z',
|
||||
authorAssociation: 'NONE',
|
||||
category: { name: 'Q&A', isAnswerable: true },
|
||||
answer: null,
|
||||
comments: { nodes: [] }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const parsed = JSON.parse(run([
|
||||
'--format=json',
|
||||
`--root=${projectRoot}`,
|
||||
'--repo',
|
||||
'affaan-m/ECC',
|
||||
'--max-open-prs',
|
||||
'2'
|
||||
], {
|
||||
cwd: projectRoot,
|
||||
env: { ECC_GH_SHIM: shimPath }
|
||||
}));
|
||||
|
||||
assert.strictEqual(parsed.ready, false);
|
||||
assert.ok(parsed.top_actions.some(action => action.id === 'github-open-pr-budget'));
|
||||
assert.ok(parsed.top_actions.some(action => action.id === 'github-discussion-touch'));
|
||||
assert.ok(parsed.top_actions.some(action => action.id === 'github-discussion-answers'));
|
||||
assert.strictEqual(parsed.github.totals.discussionsNeedingMaintainerTouch, 1);
|
||||
assert.strictEqual(parsed.github.totals.discussionsMissingAcceptedAnswer, 1);
|
||||
} finally {
|
||||
cleanup(projectRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('discussion-disabled repos skip the heavy discussion query', () => {
|
||||
const projectRoot = createTempDir('platform-audit-discussions-disabled-');
|
||||
|
||||
try {
|
||||
seedRepo(projectRoot);
|
||||
const shimPath = writeGhShim(projectRoot, {
|
||||
'pr list --repo ECC-Tools/ECC-website --state open --json number,title,isDraft,mergeStateStatus,updatedAt,url,author': [],
|
||||
'issue list --repo ECC-Tools/ECC-website --state open --json number,title,updatedAt,url,author,labels': [],
|
||||
[discussionEnabledGhKey('ECC-Tools', 'ECC-website')]: {
|
||||
data: { repository: { hasDiscussionsEnabled: false } }
|
||||
}
|
||||
});
|
||||
|
||||
const parsed = JSON.parse(run([
|
||||
'--format=json',
|
||||
`--root=${projectRoot}`,
|
||||
'--repo',
|
||||
'ECC-Tools/ECC-website'
|
||||
], {
|
||||
cwd: projectRoot,
|
||||
env: { ECC_GH_SHIM: shimPath }
|
||||
}));
|
||||
|
||||
assert.strictEqual(parsed.ready, true);
|
||||
assert.strictEqual(parsed.github.repos[0].discussions.enabled, false);
|
||||
assert.strictEqual(parsed.github.repos[0].discussions.totalCount, 0);
|
||||
assert.strictEqual(parsed.github.totals.errors, 0);
|
||||
} finally {
|
||||
cleanup(projectRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('cli help and invalid args exit cleanly', () => {
|
||||
const help = runProcess(['--help']);
|
||||
assert.strictEqual(help.status, 0);
|
||||
assert.ok(help.stdout.includes('Usage: node scripts/platform-audit.js'));
|
||||
|
||||
const invalid = runProcess(['--format', 'xml']);
|
||||
assert.strictEqual(invalid.status, 1);
|
||||
assert.ok(invalid.stderr.includes('Invalid format'));
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nPassed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
|
||||
if (failed > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
runTests();
|
||||
}
|
||||
Reference in New Issue
Block a user