|
13 | 13 |
|
14 | 14 | import { |
15 | 15 | group, test, runPhase, |
16 | | - get, post, del, |
| 16 | + get, post, put, del, |
17 | 17 | mcpCall, |
18 | 18 | assert, assertEqual, assertExists, assertOk, assertStatus, assertMcpOk, |
19 | 19 | printSummary, runStandalone, wait, |
@@ -680,6 +680,83 @@ test('Cleanup cross-graph file-link fixtures', async () => { |
680 | 680 | await del(`/tasks/${linkedTaskId}`); |
681 | 681 | }); |
682 | 682 |
|
| 683 | +// ─── 19.14 Tasks assignee end-to-end (numeric assigneeId) ──────── |
| 684 | +// |
| 685 | +// Verifies the full pipeline: |
| 686 | +// .team/{slug}.md → GET /team (upserts into team_members table, returns numeric id) |
| 687 | +// POST /tasks { assigneeId } → store |
| 688 | +// GET /tasks/:id → assigneeId round-trips |
| 689 | +// GET /tasks?assigneeId=N → filter |
| 690 | +// Replaces the historically broken `assignee: <slug>` REST field which was |
| 691 | +// silently dropped because the validation schema accepted it but the handler |
| 692 | +// read a different key. |
| 693 | + |
| 694 | +group('19.14 Tasks assignee end-to-end (numeric assigneeId)'); |
| 695 | + |
| 696 | +let assigneeMemberId: number; |
| 697 | +let assigneeTaskId: number; |
| 698 | +const TEAM_FILE = projectPath('.team', 'qa-bot.md'); |
| 699 | + |
| 700 | +test('Setup: write .team/qa-bot.md and trigger /team upsert', async () => { |
| 701 | + // Standalone mode reads team members from `.team/{slug}.md` in the project dir. |
| 702 | + const { mkdirSync } = await import('fs'); |
| 703 | + mkdirSync(projectPath('.team'), { recursive: true }); |
| 704 | + writeFileSync(TEAM_FILE, '---\nname: QA Bot\nemail: qa@test.dev\n---\n# QA Bot\n', 'utf-8'); |
| 705 | + |
| 706 | + const res = await get('/team'); |
| 707 | + assertOk(res); |
| 708 | + const members = res.data.results ?? res.data; |
| 709 | + assert(Array.isArray(members), 'team is array'); |
| 710 | + const qa = members.find((m: any) => m.slug === 'qa-bot'); |
| 711 | + assertExists(qa, 'qa-bot in team listing'); |
| 712 | + assert(typeof qa.id === 'number' && qa.id > 0, 'team member id is positive number'); |
| 713 | + assertEqual(qa.name, 'QA Bot', 'name'); |
| 714 | + assigneeMemberId = qa.id; |
| 715 | +}); |
| 716 | + |
| 717 | +test('POST /tasks with numeric assigneeId persists the assignment', async () => { |
| 718 | + const res = await post('/tasks', { |
| 719 | + title: 'Assigned Task', |
| 720 | + description: 'has an assignee', |
| 721 | + priority: 'medium', |
| 722 | + assigneeId: assigneeMemberId, |
| 723 | + }); |
| 724 | + assertOk(res); |
| 725 | + assertEqual(res.data.assigneeId, assigneeMemberId, 'assigneeId in create response'); |
| 726 | + assigneeTaskId = res.data.id; |
| 727 | +}); |
| 728 | + |
| 729 | +test('GET /tasks/:id round-trips assigneeId', async () => { |
| 730 | + const res = await get(`/tasks/${assigneeTaskId}`); |
| 731 | + assertOk(res); |
| 732 | + assertEqual(res.data.assigneeId, assigneeMemberId, 'assigneeId on read'); |
| 733 | +}); |
| 734 | + |
| 735 | +test('GET /tasks?assigneeId=N filters by numeric assigneeId', async () => { |
| 736 | + const res = await get(`/tasks?assigneeId=${assigneeMemberId}`); |
| 737 | + assertOk(res); |
| 738 | + const items = res.data.results ?? res.data; |
| 739 | + assert(Array.isArray(items) && items.length > 0, 'at least one task in filter'); |
| 740 | + assert(items.every((t: any) => t.assigneeId === assigneeMemberId), 'all results have matching assigneeId'); |
| 741 | +}); |
| 742 | + |
| 743 | +test('PUT /tasks/:id with assigneeId=null unassigns', async () => { |
| 744 | + const res = await put(`/tasks/${assigneeTaskId}`, { assigneeId: null }); |
| 745 | + assertOk(res); |
| 746 | + assertEqual(res.data.assigneeId, null, 'assigneeId cleared on update'); |
| 747 | +}); |
| 748 | + |
| 749 | +test('PUT /tasks/:id with assigneeId restores assignment', async () => { |
| 750 | + const res = await put(`/tasks/${assigneeTaskId}`, { assigneeId: assigneeMemberId }); |
| 751 | + assertOk(res); |
| 752 | + assertEqual(res.data.assigneeId, assigneeMemberId, 'assigneeId restored'); |
| 753 | +}); |
| 754 | + |
| 755 | +test('Cleanup assignee fixtures', async () => { |
| 756 | + await del(`/tasks/${assigneeTaskId}`); |
| 757 | + try { unlinkSync(TEAM_FILE); } catch { /* ignore */ } |
| 758 | +}); |
| 759 | + |
683 | 760 | // ─── Run ───────────────────────────────────────────────────────── |
684 | 761 |
|
685 | 762 | export async function run() { |
|
0 commit comments