Files
openchamber/packages/ui/src/lib/giteaPrStatus.test.ts
T
bot-hermes ca91fd7e2d feat(gitea): add Gitea/Forgejo as a git provider
Full parity with the existing GitLab provider:
- Server module packages/web/server/lib/gitea (auth/client/repo/routes + docs + tests)
  with Gitea REST v1 API, PAT + base URL auth, multi-account storage
- Shared GiteaAPI types and web API client
- Provider detection generalized with user-configurable custom domains
  per provider (github/gitlab/gitea), additive with built-in defaults
  (github.com, gitlab.com) and connected-account hosts; precedence
  github -> gitlab -> gitea
- Gitea PR view, issues section, pickers, integration dialog, branch
  PR status helper, settings UI (PAT + base URL + custom domains)
- Magic prompts (gitea.pr.review, gitea.issue.review) and full 11-locale
  i18n parity
2026-08-16 16:27:49 +00:00

121 lines
3.7 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
import type { GiteaPullRequestSummary } from '@/lib/api/types';
const prsListCalls: Array<{ directory: string; options?: { sourceBranch?: string } }> = [];
let prsListResult: GiteaPullRequestSummary[] = [];
let prsListFailure: Error | null = null;
let registryHasGitea = true;
mock.module('@/contexts/runtimeAPIRegistry', () => ({
getRegisteredRuntimeAPIs: () => {
if (!registryHasGitea) return null;
return {
gitea: {
prsList: (directory: string, options?: { sourceBranch?: string }) => {
prsListCalls.push({ directory, options });
if (prsListFailure) {
return Promise.reject(prsListFailure);
}
const all = prsListResult;
const filtered = options?.sourceBranch
? all.filter((item) => item.sourceBranch === options.sourceBranch)
: all;
return Promise.resolve({ prs: filtered });
},
},
};
},
}));
const { resolveGiteaPrForBranch } = await import('./giteaPrStatus');
const pr = (number: number, state: 'open' | 'closed' | 'merged', sourceBranch: string): GiteaPullRequestSummary => ({
number,
title: `PR ${number}`,
url: `https://gitea.example/${number}`,
state,
draft: false,
author: { id: 1, username: 'user' },
labels: [],
sourceBranch,
targetBranch: 'main',
});
describe('resolveGiteaPrForBranch', () => {
beforeEach(() => {
prsListCalls.length = 0;
prsListResult = [];
prsListFailure = null;
registryHasGitea = true;
});
afterEach(() => {
prsListCalls.length = 0;
prsListResult = [];
prsListFailure = null;
registryHasGitea = true;
});
test('prefers the open PR over a merged one for the branch', async () => {
prsListResult = [pr(3, 'merged', 'feat/a'), pr(7, 'open', 'feat/a')];
const result = await resolveGiteaPrForBranch('/repo', 'feat/a');
expect(result?.number).toBe(7);
expect(prsListCalls).toEqual([
{ directory: '/repo', options: { sourceBranch: 'feat/a' } },
]);
});
test('falls back to a merged PR when no open one exists', async () => {
prsListResult = [pr(5, 'merged', 'feat/b'), pr(9, 'closed', 'feat/b')];
const result = await resolveGiteaPrForBranch('/repo', 'feat/b');
expect(result?.number).toBe(5);
});
test('returns null when no PR matches the branch', async () => {
prsListResult = [pr(5, 'merged', 'feat/a')];
const result = await resolveGiteaPrForBranch('/repo', 'feat/c');
expect(result).toBeNull();
expect(prsListCalls).toHaveLength(1);
});
test('returns null without calling the API when the runtime has no Gitea client', async () => {
registryHasGitea = false;
const result = await resolveGiteaPrForBranch('/repo', 'feat/a');
expect(result).toBeNull();
expect(prsListCalls).toHaveLength(0);
});
test('returns null and caches when the request fails', async () => {
prsListFailure = new Error('boom');
const first = await resolveGiteaPrForBranch('/repo', 'fail-branch');
expect(first).toBeNull();
prsListFailure = null;
prsListResult = [pr(1, 'open', 'fail-branch')];
// Same directory+branch within TTL must not re-request.
const second = await resolveGiteaPrForBranch('/repo', 'fail-branch');
expect(second).toBeNull();
expect(prsListCalls).toHaveLength(1);
});
test('serves the second call from cache within the TTL window', async () => {
prsListResult = [pr(7, 'open', 'cache-branch')];
const first = await resolveGiteaPrForBranch('/repo', 'cache-branch');
const second = await resolveGiteaPrForBranch('/repo', 'cache-branch');
expect(first?.number).toBe(7);
expect(second?.number).toBe(7);
expect(prsListCalls).toHaveLength(1);
});
});