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/); });