Add advanced Git and deployment workflows, secure backups and auditing, live Gitea integration, desktop notifications, connection validation, and the premium responsive UX refresh.
110 lines
5.3 KiB
JavaScript
110 lines
5.3 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import giteaModule from '../src/main/gitea-service.cjs';
|
|
|
|
const { GiteaService } = giteaModule;
|
|
|
|
function makeStore() {
|
|
return { data: { gitea: { baseUrl: 'https://gitea.example.test' } }, getToken: () => 'demo-token' };
|
|
}
|
|
|
|
test('normalizes run payloads from different Actions API shapes', () => {
|
|
const service = new GiteaService(makeStore());
|
|
const run = service.normalizeRun({
|
|
task_id: 42,
|
|
index: 7,
|
|
workflow_name: 'Deploy',
|
|
status: 'success',
|
|
commit: { sha: 'a'.repeat(40) },
|
|
ref: 'refs/heads/main',
|
|
workflow_file: '.gitea/workflows/deploy.yml',
|
|
start_time: '2026-07-24T12:00:00Z'
|
|
});
|
|
assert.equal(run.id, 42);
|
|
assert.equal(run.runNumber, 7);
|
|
assert.equal(run.conclusion, 'success');
|
|
assert.equal(run.headSha, 'a'.repeat(40));
|
|
assert.equal(run.headBranch, 'refs/heads/main');
|
|
});
|
|
|
|
test('retries Actions runs without optional filters when a server rejects them', async () => {
|
|
const service = new GiteaService(makeStore());
|
|
const calls = [];
|
|
service.request = async (pathname) => {
|
|
calls.push(pathname);
|
|
if (calls.length === 1) {
|
|
const error = new Error('Unsupported query');
|
|
error.status = 422;
|
|
throw error;
|
|
}
|
|
return { data: { workflow_runs: [{ id: 11, run_number: 11, status: 'queued', head_sha: 'b'.repeat(40), head_branch: 'main' }] } };
|
|
};
|
|
const result = await service.listWorkflowRuns({ owner: 'jens', repo: 'app', sha: 'b'.repeat(40), branch: 'main' });
|
|
assert.equal(calls.length, 2);
|
|
assert.match(calls[0], /head_sha=/);
|
|
assert.doesNotMatch(calls[1], /head_sha=/);
|
|
assert.equal(result.source, 'runs');
|
|
assert.equal(result.runs[0].runNumber, 11);
|
|
});
|
|
|
|
test('falls back to legacy Actions tasks endpoint when runs is unavailable', async () => {
|
|
const service = new GiteaService(makeStore());
|
|
const calls = [];
|
|
service.request = async (pathname) => {
|
|
calls.push(pathname);
|
|
if (pathname.includes('/runs?')) {
|
|
const error = new Error('Not found');
|
|
error.status = 404;
|
|
throw error;
|
|
}
|
|
return { data: [{ task_id: 9, index: 3, status: 'running', commit_sha: 'c'.repeat(40), branch: 'main' }] };
|
|
};
|
|
const result = await service.listWorkflowRuns({ owner: 'jens', repo: 'app' });
|
|
assert.equal(result.source, 'tasks');
|
|
assert.equal(result.runs[0].id, 9);
|
|
assert.ok(calls.some((pathname) => pathname.includes('/tasks?')));
|
|
});
|
|
|
|
test('selects the newest matching workflow run', async () => {
|
|
const service = new GiteaService(makeStore());
|
|
service.listWorkflowRuns = async () => ({ source: 'runs', runs: [
|
|
{ id: 1, headSha: 'd'.repeat(40), headBranch: 'main', workflowPath: 'deploy.yml', createdAt: '2026-07-24T10:00:00Z' },
|
|
{ id: 2, headSha: 'd'.repeat(40), headBranch: 'main', workflowPath: '.gitea/workflows/deploy.yml', createdAt: '2026-07-24T11:00:00Z' },
|
|
{ id: 3, headSha: 'e'.repeat(40), headBranch: 'main', workflowPath: 'deploy.yml', createdAt: '2026-07-24T12:00:00Z' }
|
|
] });
|
|
const found = await service.findWorkflowRun({ owner: 'jens', repo: 'app', sha: 'd'.repeat(40), branch: 'main', workflowFile: 'deploy.yml' });
|
|
assert.equal(found.run.id, 2);
|
|
const excludingNewest = await service.findWorkflowRun({ owner: 'jens', repo: 'app', sha: 'd'.repeat(40), branch: 'main', workflowFile: 'deploy.yml', excludeRunIds: [2] });
|
|
assert.equal(excludingNewest.run.id, 1);
|
|
});
|
|
|
|
test('checks repository workflow files through the contents API', async () => {
|
|
const service = new GiteaService(makeStore());
|
|
const calls = [];
|
|
service.request = async (pathname) => { calls.push(pathname); return { data: { type: 'file' } }; };
|
|
assert.equal(await service.repositoryFileExists({ owner: 'jens', repo: 'app', filePath: '.gitea/workflows/deploy.yml', ref: 'main' }), true);
|
|
assert.match(calls[0], /contents\/\.gitea\/workflows\/deploy\.yml\?ref=main/);
|
|
|
|
service.request = async () => { const error = new Error('missing'); error.status = 404; throw error; };
|
|
assert.equal(await service.repositoryFileExists({ owner: 'jens', repo: 'app', filePath: '.gitea/workflows/missing.yml', ref: 'main' }), false);
|
|
});
|
|
|
|
test('creates controlled pull requests and reads branch protection', async () => {
|
|
const service = new GiteaService(makeStore());
|
|
const calls = [];
|
|
service.request = async (pathname, options = {}) => {
|
|
calls.push({ pathname, options });
|
|
if (pathname.includes('/branches/main')) return { data: { name: 'main', protected: true } };
|
|
if (pathname.endsWith('/branch_protections')) return { data: [{ branch_name: 'main', required_approvals: 2, require_signed_commits: true }] };
|
|
return { data: { number: 12, html_url: 'https://gitea.test/owner/app/pulls/12' } };
|
|
};
|
|
const protection = await service.getBranchProtection('owner', 'app', 'main');
|
|
assert.equal(protection.protected, true);
|
|
assert.equal(protection.requiredApprovals, 2);
|
|
const pull = await service.createPullRequest({ owner: 'owner', repo: 'app', head: 'feature', base: 'main', title: 'Release feature', body: 'Summary' });
|
|
assert.equal(pull.number, 12);
|
|
const create = calls.find((call) => call.options.method === 'POST');
|
|
assert.deepEqual(create.options.body, { head: 'feature', base: 'main', title: 'Release feature', body: 'Summary' });
|
|
await assert.rejects(() => service.createPullRequest({ owner: 'owner', repo: 'app', head: 'main', base: 'main', title: 'Invalid' }), /different/);
|
|
});
|