takt/src/__tests__/github-issue.test.ts

172 lines
4.9 KiB
TypeScript

/**
* Tests for github/issue module
*
* Tests parsing and formatting functions.
* checkGhCli/fetchIssue/resolveIssueTask are integration functions
* that call `gh` CLI, so they are not unit-tested here.
*/
import { describe, it, expect } from 'vitest';
import {
parseIssueNumbers,
isIssueReference,
formatIssueAsTask,
type GitHubIssue,
} from '../infra/github/issue.js';
describe('parseIssueNumbers', () => {
it('should parse single issue reference', () => {
expect(parseIssueNumbers(['#6'])).toEqual([6]);
});
it('should parse multiple issue references', () => {
expect(parseIssueNumbers(['#6', '#7'])).toEqual([6, 7]);
});
it('should parse large issue numbers', () => {
expect(parseIssueNumbers(['#123'])).toEqual([123]);
});
it('should return empty for non-issue args', () => {
expect(parseIssueNumbers(['Fix bug'])).toEqual([]);
});
it('should return empty when mixed issue and non-issue args', () => {
expect(parseIssueNumbers(['#6', 'and', '#7'])).toEqual([]);
});
it('should return empty for empty args', () => {
expect(parseIssueNumbers([])).toEqual([]);
});
it('should not match partial issue patterns', () => {
expect(parseIssueNumbers(['#abc'])).toEqual([]);
expect(parseIssueNumbers(['#'])).toEqual([]);
expect(parseIssueNumbers(['##6'])).toEqual([]);
expect(parseIssueNumbers(['6'])).toEqual([]);
expect(parseIssueNumbers(['issue#6'])).toEqual([]);
});
it('should handle #0', () => {
expect(parseIssueNumbers(['#0'])).toEqual([0]);
});
});
describe('isIssueReference', () => {
it('should return true for #N patterns', () => {
expect(isIssueReference('#6')).toBe(true);
expect(isIssueReference('#123')).toBe(true);
});
it('should return true with whitespace trim', () => {
expect(isIssueReference(' #6 ')).toBe(true);
});
it('should return false for non-issue text', () => {
expect(isIssueReference('Fix bug')).toBe(false);
expect(isIssueReference('#abc')).toBe(false);
expect(isIssueReference('')).toBe(false);
expect(isIssueReference('#')).toBe(false);
expect(isIssueReference('6')).toBe(false);
});
it('should return false for issue number followed by text (issue #32)', () => {
expect(isIssueReference('#32あああ')).toBe(false);
expect(isIssueReference('#10abc')).toBe(false);
expect(isIssueReference('#123text')).toBe(false);
});
it('should return false for multiple issues (single string)', () => {
expect(isIssueReference('#6 #7')).toBe(false);
});
});
describe('formatIssueAsTask', () => {
it('should format issue with all fields', () => {
const issue: GitHubIssue = {
number: 6,
title: 'Fix authentication bug',
body: 'The login flow is broken.',
labels: ['bug', 'priority:high'],
comments: [
{ author: 'user1', body: 'I can reproduce this.' },
{ author: 'user2', body: 'Fixed in PR #7.' },
],
};
const result = formatIssueAsTask(issue);
expect(result).toContain('## GitHub Issue #6: Fix authentication bug');
expect(result).toContain('The login flow is broken.');
expect(result).toContain('### Labels');
expect(result).toContain('bug, priority:high');
expect(result).toContain('### Comments');
expect(result).toContain('**user1**: I can reproduce this.');
expect(result).toContain('**user2**: Fixed in PR #7.');
});
it('should format issue with no body', () => {
const issue: GitHubIssue = {
number: 10,
title: 'Empty issue',
body: '',
labels: [],
comments: [],
};
const result = formatIssueAsTask(issue);
expect(result).toBe('## GitHub Issue #10: Empty issue');
expect(result).not.toContain('### Labels');
expect(result).not.toContain('### Comments');
});
it('should format issue with labels but no comments', () => {
const issue: GitHubIssue = {
number: 5,
title: 'Feature request',
body: 'Add dark mode.',
labels: ['enhancement'],
comments: [],
};
const result = formatIssueAsTask(issue);
expect(result).toContain('### Labels');
expect(result).toContain('enhancement');
expect(result).not.toContain('### Comments');
});
it('should format issue with comments but no labels', () => {
const issue: GitHubIssue = {
number: 3,
title: 'Discussion',
body: 'Thoughts?',
labels: [],
comments: [
{ author: 'dev', body: 'LGTM' },
],
};
const result = formatIssueAsTask(issue);
expect(result).not.toContain('### Labels');
expect(result).toContain('### Comments');
expect(result).toContain('**dev**: LGTM');
});
it('should handle multiline body', () => {
const issue: GitHubIssue = {
number: 1,
title: 'Multi-line',
body: 'Line 1\nLine 2\n\nLine 4',
labels: [],
comments: [],
};
const result = formatIssueAsTask(issue);
expect(result).toContain('Line 1\nLine 2\n\nLine 4');
});
});