closes board-viewer-gitea-reader - src/gitea.ts: HTTP client (getFile, getLatestCommitIso, rawUrl); DI'd fetch - src/reader.ts: readBoard(client, repos) → TaskRecord[] - src/config.ts: TOML config loader with board_viewer_repos whitelist - 25 tests total (9 parser incl. 3 real-repo fixtures, 7 gitea, 5 reader, 4 config) - emoji contract ⚪🔴🟡🔵🟢 (open/in_progress/paused/blocked/done) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
113 lines
3.6 KiB
TypeScript
113 lines
3.6 KiB
TypeScript
import { describe, expect, test, vi } from 'vitest';
|
|
import type { GiteaClient } from '../src/gitea.ts';
|
|
import { readBoard } from '../src/reader.ts';
|
|
|
|
function fakeClient(overrides: Partial<GiteaClient> = {}): GiteaClient {
|
|
return {
|
|
getFile: vi.fn().mockResolvedValue(null),
|
|
getLatestCommitIso: vi.fn().mockResolvedValue(null),
|
|
rawUrl: vi.fn().mockReturnValue('http://example/raw/x'),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
const STATUS_MD_ONE = `# Task Board
|
|
_Updated: 2026-05-22_
|
|
|
|
## 🔴 [task-a] — first task
|
|
**Status:** active
|
|
**Where I stopped:** doing A.
|
|
**Next action:** continue A.
|
|
**Branch:** master
|
|
|
|
---
|
|
`;
|
|
|
|
describe('readBoard', () => {
|
|
test('merges parser output with project metadata and Gitea last-commit + raw URL', async () => {
|
|
const client = fakeClient({
|
|
getFile: vi.fn().mockResolvedValue(STATUS_MD_ONE),
|
|
getLatestCommitIso: vi.fn().mockResolvedValue('2026-05-22T09:00:00Z'),
|
|
rawUrl: vi.fn().mockReturnValue(
|
|
'https://git.kzntsv.site/OpeItcLoc03/board-viewer/raw/branch/master/.tasks/task-a.md',
|
|
),
|
|
});
|
|
|
|
const records = await readBoard(client, [{ owner: 'OpeItcLoc03', repo: 'board-viewer' }]);
|
|
|
|
expect(records).toHaveLength(1);
|
|
expect(records[0]).toEqual({
|
|
slug: 'task-a',
|
|
project: 'board-viewer',
|
|
project_owner: 'OpeItcLoc03',
|
|
status: 'in_progress',
|
|
status_emoji: '🔴',
|
|
title: 'first task',
|
|
where_stopped: 'doing A.',
|
|
next_action: 'continue A.',
|
|
blocker: null,
|
|
branch: 'master',
|
|
last_commit_iso: '2026-05-22T09:00:00Z',
|
|
raw_url:
|
|
'https://git.kzntsv.site/OpeItcLoc03/board-viewer/raw/branch/master/.tasks/task-a.md',
|
|
});
|
|
});
|
|
|
|
test('skips repo silently when STATUS.md is missing (returns no records for that repo)', async () => {
|
|
const client = fakeClient({
|
|
getFile: vi.fn().mockResolvedValue(null), // 404
|
|
});
|
|
|
|
const records = await readBoard(client, [{ owner: 'o', repo: 'no-tasks' }]);
|
|
|
|
expect(records).toEqual([]);
|
|
});
|
|
|
|
test('aggregates blocks across multiple repos', async () => {
|
|
const repoFiles: Record<string, string> = {
|
|
'repo-a': STATUS_MD_ONE,
|
|
'repo-b': STATUS_MD_ONE.replace('task-a', 'task-b').replace('first task', 'second task'),
|
|
};
|
|
const client = fakeClient({
|
|
getFile: vi.fn(async (_owner: string, repo: string) => repoFiles[repo] ?? null),
|
|
getLatestCommitIso: vi.fn().mockResolvedValue('2026-05-22T09:00:00Z'),
|
|
});
|
|
|
|
const records = await readBoard(client, [
|
|
{ owner: 'o', repo: 'repo-a' },
|
|
{ owner: 'o', repo: 'repo-b' },
|
|
]);
|
|
|
|
expect(records.map((r) => `${r.project}:${r.slug}`)).toEqual([
|
|
'repo-a:task-a',
|
|
'repo-b:task-b',
|
|
]);
|
|
});
|
|
|
|
test('builds raw_url per-task file using <slug>.md path under .tasks/', async () => {
|
|
const rawUrl = vi.fn((_o: string, _r: string, path: string) => `RAW:${path}`);
|
|
const client = fakeClient({
|
|
getFile: vi.fn().mockResolvedValue(STATUS_MD_ONE),
|
|
getLatestCommitIso: vi.fn().mockResolvedValue(null),
|
|
rawUrl,
|
|
});
|
|
|
|
const records = await readBoard(client, [{ owner: 'o', repo: 'r' }]);
|
|
|
|
expect(records[0]!.raw_url).toBe('RAW:.tasks/task-a.md');
|
|
expect(rawUrl).toHaveBeenCalledWith('o', 'r', '.tasks/task-a.md');
|
|
});
|
|
|
|
test('queries last_commit_iso for the per-task file path', async () => {
|
|
const getLatestCommitIso = vi.fn().mockResolvedValue('2026-05-22T10:00:00Z');
|
|
const client = fakeClient({
|
|
getFile: vi.fn().mockResolvedValue(STATUS_MD_ONE),
|
|
getLatestCommitIso,
|
|
});
|
|
|
|
await readBoard(client, [{ owner: 'o', repo: 'r' }]);
|
|
|
|
expect(getLatestCommitIso).toHaveBeenCalledWith('o', 'r', '.tasks/task-a.md');
|
|
});
|
|
});
|