stoke/test/governance.test.js
codex-bot-andresmgsl e86ce95180
All checks were successful
ci / test (pull_request) Successful in 24s
Add governance config validation
2026-08-19 19:23:21 +00:00

97 lines
4.3 KiB
JavaScript

const { test } = require('node:test');
const assert = require('node:assert/strict');
const { spawn } = require('node:child_process');
const fs = require('node:fs');
const http = require('node:http');
const os = require('node:os');
const path = require('node:path');
const SCRIPT = path.join(__dirname, '..', 'scripts', 'check-governance.js');
const REPOSITORY_CONFIG = path.join(__dirname, '..', '.github', 'labels.conf');
const cleanups = [];
process.on('exit', () => {
for (const dir of cleanups) fs.rmSync(dir, { recursive: true, force: true });
});
function writeConfig(contents) {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'stoke-governance-test-'));
cleanups.push(dir);
const config = path.join(dir, 'labels.conf');
fs.writeFileSync(config, contents);
return config;
}
function runValidator(config, apiUrl) {
return new Promise((resolve) => {
const child = spawn(process.execPath, [SCRIPT, '--config', config, '--api-url', apiUrl], {
encoding: 'utf8',
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (chunk) => { stdout += chunk; });
child.stderr.on('data', (chunk) => { stderr += chunk; });
child.on('close', (status) => resolve({ status, stdout, stderr }));
});
}
async function withIdentityServer(logins, callback) {
const server = http.createServer((request, response) => {
const login = decodeURIComponent(request.url.replace('/api/v1/users/', ''));
response.writeHead(logins.has(login) ? 200 : 404, { 'content-type': 'application/json' });
response.end(JSON.stringify(logins.has(login) ? { login } : { message: 'not found' }));
});
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
const { port } = server.address();
try {
await callback(`http://127.0.0.1:${port}/api/v1`);
} finally {
await new Promise((resolve) => server.close(resolve));
}
}
const validConfig = [
'panel=codex-bot-andresmgsl glm-bot-andresmgsl cluade-bot-andresmgsl kimi-bot-andresmgsl',
'triage-actors=cluade-bot-andresmgsl',
'scope:cli|C5DEF5|src/ — the command surface (cli.js, api.js, config.js)',
'scope:packaging|C5DEF5|scripts/ and the release workflow — deb build, registry publish, apt install path',
'scope:manifests|C5DEF5|manifests/ — the fleet repo registry data',
'scope:ci|C5DEF5|.forgejo/workflows/ — the test and label gates',
'scope:docs|C5DEF5|README and docs/ — the prose contract',
].join('\n');
test('governance validator accepts the configured roster when every identity resolves', async () => {
const config = writeConfig(`${validConfig}\n`);
const logins = new Set(['codex-bot-andresmgsl', 'glm-bot-andresmgsl', 'cluade-bot-andresmgsl', 'kimi-bot-andresmgsl']);
await withIdentityServer(logins, async (apiUrl) => {
const result = await runValidator(config, apiUrl);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /4 identities resolved; 5 scope rows valid/);
});
});
test('governance validator fails when a roster identity does not resolve', async () => {
const config = writeConfig(`${validConfig.replace('kimi-bot-andresmgsl', 'kimi-bto-andresmgsl')}\n`);
const logins = new Set(['codex-bot-andresmgsl', 'glm-bot-andresmgsl', 'cluade-bot-andresmgsl', 'kimi-bot-andresmgsl']);
await withIdentityServer(logins, async (apiUrl) => {
const result = await runValidator(config, apiUrl);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /kimi-bto-andresmgsl.*HTTP 404/);
});
});
test('governance validator rejects malformed scope rows before identity requests', async () => {
const config = writeConfig(`${validConfig.replace('|C5DEF5|', '|not-a-color|')}\n`);
const result = await runValidator(config, 'http://127.0.0.1:1/api/v1');
assert.notEqual(result.status, 0);
assert.match(result.stderr, /malformed label row/);
assert.doesNotMatch(result.stderr, /fetch failed/);
});
test('repository governance config resolves the four-member panel and five scopes', async () => {
const logins = new Set(['codex-bot-andresmgsl', 'glm-bot-andresmgsl', 'cluade-bot-andresmgsl', 'kimi-bot-andresmgsl']);
await withIdentityServer(logins, async (apiUrl) => {
const result = await runValidator(REPOSITORY_CONFIG, apiUrl);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /4 identities resolved; 5 scope rows valid/);
});
});