131 lines
5.5 KiB
JavaScript
131 lines
5.5 KiB
JavaScript
import { describe, it, afterEach, mock } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import axios from 'axios';
|
|
import { getPRDiff, filterDiff, postComment, getCommitMessageBySha, getBranchHeadCommitMessage, shouldSkipBotCommit, getBotReviewOutcome } from './gitea.js';
|
|
|
|
afterEach(() => mock.restoreAll());
|
|
|
|
describe('gitea', () => {
|
|
it('getPRDiff calls Gitea diff API with Authorization header', async () => {
|
|
let capturedUrl, capturedOpts;
|
|
mock.method(axios, 'get', async (url, opts) => {
|
|
capturedUrl = url;
|
|
capturedOpts = opts;
|
|
return { data: 'diff content' };
|
|
});
|
|
const result = await getPRDiff();
|
|
assert.equal(result, 'diff content');
|
|
assert.ok(capturedUrl.includes('/api/v1/repos/'));
|
|
assert.ok(capturedUrl.endsWith('.diff'));
|
|
assert.ok(capturedOpts.headers['Authorization'].startsWith('token '));
|
|
assert.equal(capturedOpts.headers['Content-Type'], 'application/json');
|
|
});
|
|
|
|
it('postComment calls Gitea issues comments API with body', async () => {
|
|
let capturedUrl, capturedBody, capturedOpts;
|
|
mock.method(axios, 'post', async (url, body, opts) => {
|
|
capturedUrl = url;
|
|
capturedBody = body;
|
|
capturedOpts = opts;
|
|
return { data: { id: 1 } };
|
|
});
|
|
const result = await postComment('hello world');
|
|
assert.deepEqual(result, { id: 1 });
|
|
assert.ok(capturedUrl.includes('/api/v1/repos/'));
|
|
assert.ok(capturedUrl.endsWith('/comments'));
|
|
assert.equal(capturedBody.body, 'hello world');
|
|
assert.ok(capturedOpts.headers['Authorization'].startsWith('token '));
|
|
});
|
|
|
|
it('does not set httpsAgent by default (GITEA_SKIP_TLS_VERIFY not true)', async () => {
|
|
let capturedOpts;
|
|
mock.method(axios, 'get', async (_url, opts) => {
|
|
capturedOpts = opts;
|
|
return { data: '' };
|
|
});
|
|
await getPRDiff();
|
|
assert.equal(capturedOpts.httpsAgent, undefined);
|
|
});
|
|
|
|
it('getPRDiff propagates axios errors', async () => {
|
|
mock.method(axios, 'get', async () => { throw new Error('network error'); });
|
|
await assert.rejects(() => getPRDiff(), /network error/);
|
|
});
|
|
|
|
it('postComment propagates axios errors', async () => {
|
|
mock.method(axios, 'post', async () => { throw new Error('api error'); });
|
|
await assert.rejects(() => postComment('test'), /api error/);
|
|
});
|
|
|
|
it('getCommitMessageBySha reads commit message from Gitea API', async () => {
|
|
let capturedUrl;
|
|
mock.method(axios, 'get', async (url) => {
|
|
capturedUrl = url;
|
|
return { data: { message: 'chore: update ai-review findings [ai-review-bot]' } };
|
|
});
|
|
const message = await getCommitMessageBySha('abc123');
|
|
assert.ok(capturedUrl.includes('/git/commits/abc123'));
|
|
assert.ok(message.includes('[ai-review-bot]'));
|
|
});
|
|
|
|
it('getBranchHeadCommitMessage reads branch head commit message from Gitea API', async () => {
|
|
const urls = [];
|
|
mock.method(axios, 'get', async (url) => {
|
|
urls.push(url);
|
|
if (url.includes('/branches/feat%2Ftest')) {
|
|
return { data: { commit: { id: 'abc123' } } };
|
|
}
|
|
return { data: { message: 'chore: update ai-review findings [ai-review-bot]' } };
|
|
});
|
|
const message = await getBranchHeadCommitMessage('feat/test');
|
|
assert.ok(urls.some(url => url.includes('/branches/feat%2Ftest')));
|
|
assert.ok(urls.some(url => url.includes('/git/commits/abc123')));
|
|
assert.ok(message.includes('[ai-review-bot]'));
|
|
});
|
|
|
|
it('shouldSkipBotCommit returns true when either sha or branch head is bot commit', async () => {
|
|
mock.method(axios, 'get', async (url) => {
|
|
if (url.includes('/git/commits/sha-bot')) {
|
|
return { data: { message: 'chore: update ai-review findings [ai-review-bot][failure]' } };
|
|
}
|
|
if (url.includes('/branches/feat%2Ftest')) {
|
|
return { data: { commit: { id: 'sha-bot' } } };
|
|
}
|
|
return { data: { message: 'regular commit' } };
|
|
});
|
|
await assert.equal(await shouldSkipBotCommit({ sha: 'sha-bot', branch: 'feat/test' }), true);
|
|
assert.equal(getBotReviewOutcome('chore: update ai-review findings [ai-review-bot][failure]'), 'failure');
|
|
assert.equal(getBotReviewOutcome('chore: update ai-review findings [ai-review-bot][success]'), 'success');
|
|
assert.equal(getBotReviewOutcome('chore: update ai-review findings [ai-review-bot]'), 'unknown');
|
|
});
|
|
});
|
|
|
|
describe('filterDiff', () => {
|
|
const block = (file) => `diff --git a/${file} b/${file}\n--- a/${file}\n+++ b/${file}\n@@ -1 +1 @@\n-old\n+new\n`;
|
|
|
|
it('filters out configured folder blocks', () => {
|
|
const diff = block('.gitea/workflows/review.yaml') + block('.amazonq/rules/triage-findings.md') + block('src/index.js');
|
|
const result = filterDiff(diff, ['.gitea/', '.amazonq/']);
|
|
assert.ok(!result.includes('.gitea/'));
|
|
assert.ok(!result.includes('.amazonq/'));
|
|
assert.ok(result.includes('src/index.js'));
|
|
});
|
|
|
|
it('filters out configured top-level file blocks', () => {
|
|
const diff = block('README.md') + block('src/index.js');
|
|
const result = filterDiff(diff, ['README.md', 'TODO.md']);
|
|
assert.ok(!result.includes('README.md'));
|
|
assert.ok(result.includes('src/index.js'));
|
|
});
|
|
|
|
it('returns empty string when all blocks are excluded', () => {
|
|
const diff = block('.gitea/workflows/review.yaml') + block('.gitea/ai-review/findings.json') + block('.agents/skills/triage-findings/SKILL.md');
|
|
const result = filterDiff(diff, ['.gitea/', '.agents/']);
|
|
assert.equal(result, '');
|
|
});
|
|
|
|
it('returns empty string for empty diff', () => {
|
|
assert.equal(filterDiff('', ['.gitea/']), '');
|
|
});
|
|
});
|