sin-github-issues / test /github_workflow.test.mjs
OpenCode
Deploy latest SIN-GitHub-Issues runtime
cd455f6
Raw
History Blame Contribute Delete
5.86 kB
import assert from 'node:assert/strict';
import { createHmac, generateKeyPairSync } from 'node:crypto';
import { createServer } from 'node:http';
import test from 'node:test';
const DIST_ROOT = new URL('../dist/src/', import.meta.url);
async function importFresh(relativePath) {
return import(new URL(`${relativePath}?t=${Date.now()}-${Math.random()}`, DIST_ROOT).href);
}
function setEnv(updates) {
const previous = new Map();
for (const [key, value] of Object.entries(updates)) {
previous.set(key, process.env[key]);
if (value === undefined || value === null) delete process.env[key];
else process.env[key] = value;
}
return () => {
for (const [key, value] of previous.entries()) {
if (value === undefined) delete process.env[key];
else process.env[key] = value;
}
};
}
test('GitHub Agent help exposes routing actions', async () => {
const { executeGitHubAgentAction } = await importFresh('./runtime.js');
const help = await executeGitHubAgentAction({ action: 'agent.help' });
assert.equal(help.ok, true);
assert.ok(help.actions.includes('sin.github.app.routing.status'));
assert.ok(help.actions.includes('sin.github.webhook.route'));
assert.ok(help.actions.includes('sin.github.issue.comment.as_app'));
});
test('routeGitHubWebhook prefers installation.app_id over shared central path', async () => {
const restoreEnv = setEnv({
SIN_GITHUB_APP_ROUTING_JSON: JSON.stringify({
mode: 'central-endpoint-routing',
defaultWebhookPath: '/api/webhooks/github-apps',
apps: [
{
appIdEnv: 'SIN_HERMES_GITHUB_APP_ID',
appSlug: 'sin-hermes',
preferredAgentSlug: 'sin-hermesbote',
botName: 'SIN-Hermes[bot]',
webhookPath: '/api/webhooks/github-apps',
webhookSecretEnv: 'SIN_HERMES_GITHUB_APP_WEBHOOK_SECRET',
},
{
appIdEnv: 'SIN_BUGBOUNTY_GITHUB_APP_ID',
appSlug: 'sin-bugbounty',
preferredAgentSlug: 'sin-bugbounty',
botName: 'SIN-BugBounty[bot]',
webhookPath: '/api/webhooks/github-apps',
webhookSecretEnv: 'SIN_BUGBOUNTY_GITHUB_APP_WEBHOOK_SECRET',
},
],
}),
SIN_HERMES_GITHUB_APP_ID: '123456',
SIN_HERMES_GITHUB_APP_WEBHOOK_SECRET: 'secret-1',
SIN_BUGBOUNTY_GITHUB_APP_ID: '345678',
SIN_BUGBOUNTY_GITHUB_APP_WEBHOOK_SECRET: 'secret-3',
});
try {
const { routeGitHubWebhook } = await importFresh('./github-app-routing.js');
const rawBody = JSON.stringify({
action: 'opened',
installation: { id: 987654321, app_id: 345678 },
repository: { full_name: 'OpenSIN-AI/OpenSIN-backend' },
});
const signature256 = `sha256=${createHmac('sha256', 'secret-3').update(rawBody).digest('hex')}`;
const result = await routeGitHubWebhook({
rawBody,
routePath: '/api/webhooks/github-apps',
signature256,
eventName: 'pull_request',
deliveryId: 'delivery-central-test',
});
assert.equal(result.ok, true);
assert.equal(result.route.agentSlug, 'sin-bugbounty');
assert.equal(result.route.botName, 'SIN-BugBounty[bot]');
} finally {
restoreEnv();
}
});
test('commentIssueAsGitHubApp exchanges installation token and posts comment', async () => {
const { privateKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
privateKeyEncoding: { type: 'pkcs1', format: 'pem' },
publicKeyEncoding: { type: 'pkcs1', format: 'pem' },
});
let commentRequestBody = '';
const server = createServer((req, res) => {
if (req.method === 'POST' && req.url === '/app/installations/987654321/access_tokens') {
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ token: 'installation-token-1', expires_at: '2030-01-01T00:00:00Z' }));
return;
}
if (req.method === 'POST' && req.url === '/repos/OpenSIN-AI/OpenSIN-backend/issues/42/comments') {
const chunks = [];
req.on('data', (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
req.on('end', () => {
commentRequestBody = Buffer.concat(chunks).toString('utf8');
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ id: 9001, html_url: 'https://github.com/OpenSIN-AI/OpenSIN-backend/issues/42#issuecomment-9001' }));
});
return;
}
res.writeHead(404, { 'content-type': 'application/json' });
res.end(JSON.stringify({ error: 'not_found' }));
});
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
const address = server.address();
const port = typeof address === 'object' && address ? address.port : 0;
const restoreEnv = setEnv({
SIN_GITHUB_API_BASE_URL: `http://127.0.0.1:${port}`,
SIN_GITHUB_APP_ROUTING_JSON: JSON.stringify({
defaultMode: 'hybrid',
defaultWebhookPath: '/github/webhook',
apps: [
{
agentSlug: 'sin-hermesbote',
appSlug: 'sin-hermes',
botName: 'SIN-Hermes[bot]',
appId: 123456,
privateKeyPem: privateKey,
webhookSecret: 'secret-1',
},
],
}),
});
try {
const { commentIssueAsGitHubApp } = await importFresh('./github-app-routing.js');
const result = await commentIssueAsGitHubApp({
repo: 'OpenSIN-AI/OpenSIN-backend',
issueNumber: 42,
body: 'Hello from SIN-Hermes[bot]',
agentSlug: 'sin-hermesbote',
installationId: 987654321,
});
assert.equal(result.ok, true);
assert.equal(result.agentSlug, 'sin-hermesbote');
assert.equal(result.commentId, 9001);
assert.match(commentRequestBody, /Hello from SIN-Hermes\[bot\]/);
} finally {
restoreEnv();
await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
}
});