|
| 1 | +#!/usr/bin/env tsx |
| 2 | + |
| 3 | +/* |
| 4 | + * Live list endpoints smoke test. |
| 5 | + * Requires real Basecamp OAuth env vars: |
| 6 | + * - BASECAMP_CLIENT_ID |
| 7 | + * - BASECAMP_CLIENT_SECRET |
| 8 | + * - BASECAMP_REDIRECT_URI (e.g. http://localhost:8787/callback) |
| 9 | + * - BASECAMP_USER_AGENT (e.g. "Your App (you@example.com)") |
| 10 | + * |
| 11 | + * Optional IDs to test deeper list endpoints: |
| 12 | + * - BASECAMP_PROJECT_ID |
| 13 | + * - BASECAMP_TODOLIST_ID |
| 14 | + * - BASECAMP_MESSAGE_BOARD_ID |
| 15 | + * - BASECAMP_CARD_TABLE_ID |
| 16 | + */ |
| 17 | + |
| 18 | +import { BasecampClient } from '../src/sdk/client.js'; |
| 19 | +import { ProjectsResource } from '../src/sdk/resources/projects.js'; |
| 20 | +import { PeopleResource } from '../src/sdk/resources/people.js'; |
| 21 | +import { TodosResource } from '../src/sdk/resources/todos.js'; |
| 22 | +import { MessagesResource } from '../src/sdk/resources/messages.js'; |
| 23 | +import { CardTablesResource } from '../src/sdk/resources/cardTables.js'; |
| 24 | + |
| 25 | +type TestResult = { name: string; ok: boolean; detail?: string }; |
| 26 | + |
| 27 | +async function main() { |
| 28 | + const required = [ |
| 29 | + 'BASECAMP_CLIENT_ID', |
| 30 | + 'BASECAMP_CLIENT_SECRET', |
| 31 | + 'BASECAMP_REDIRECT_URI', |
| 32 | + 'BASECAMP_USER_AGENT', |
| 33 | + ]; |
| 34 | + const missing = required.filter(k => !process.env[k]); |
| 35 | + if (missing.length) { |
| 36 | + console.error(`Missing required env: ${missing.join(', ')}`); |
| 37 | + process.exit(1); |
| 38 | + } |
| 39 | + |
| 40 | + // Ensure protocol logs, if any, go to stderr (safety) |
| 41 | + process.env['BASECAMP_MCP_STDERR'] = '1'; |
| 42 | + |
| 43 | + const client = new BasecampClient(); |
| 44 | + const results: TestResult[] = []; |
| 45 | + |
| 46 | + async function check(name: string, fn: () => Promise<unknown>) { |
| 47 | + try { |
| 48 | + const r = await fn(); |
| 49 | + let detail: string | undefined; |
| 50 | + if (Array.isArray(r)) detail = `count=${r.length}`; |
| 51 | + results.push({ name, ok: true, detail }); |
| 52 | + } catch (e) { |
| 53 | + results.push({ name, ok: false, detail: e instanceof Error ? e.message : String(e) }); |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + const projects = new ProjectsResource(client); |
| 58 | + const people = new PeopleResource(client); |
| 59 | + const todos = new TodosResource(client); |
| 60 | + const messages = new MessagesResource(client); |
| 61 | + const cards = new CardTablesResource(client); |
| 62 | + |
| 63 | + // Always-run list endpoints |
| 64 | + await check('projects.list (/projects.json)', () => projects.list()); |
| 65 | + await check('people.list (/people.json)', () => people.list()); |
| 66 | + |
| 67 | + // Optional: deeper lists requiring IDs |
| 68 | + const projectId = process.env['BASECAMP_PROJECT_ID'] |
| 69 | + ? Number(process.env['BASECAMP_PROJECT_ID']) |
| 70 | + : undefined; |
| 71 | + const todoListId = process.env['BASECAMP_TODOLIST_ID'] |
| 72 | + ? Number(process.env['BASECAMP_TODOLIST_ID']) |
| 73 | + : undefined; |
| 74 | + const boardId = process.env['BASECAMP_MESSAGE_BOARD_ID'] |
| 75 | + ? Number(process.env['BASECAMP_MESSAGE_BOARD_ID']) |
| 76 | + : undefined; |
| 77 | + const tableId = process.env['BASECAMP_CARD_TABLE_ID'] |
| 78 | + ? Number(process.env['BASECAMP_CARD_TABLE_ID']) |
| 79 | + : undefined; |
| 80 | + |
| 81 | + if (projectId && todoListId) { |
| 82 | + await check( |
| 83 | + 'todos.list (/buckets/:projectId/todolists/:listId/todos.json)', |
| 84 | + () => todos.list(projectId, todoListId) |
| 85 | + ); |
| 86 | + } |
| 87 | + |
| 88 | + if (projectId && boardId) { |
| 89 | + await check( |
| 90 | + 'messages.list (/buckets/:projectId/message_boards/:boardId/messages.json)', |
| 91 | + () => messages.list(projectId, boardId) |
| 92 | + ); |
| 93 | + } |
| 94 | + |
| 95 | + if (projectId && tableId) { |
| 96 | + await check( |
| 97 | + 'card_tables.get (/buckets/:projectId/card_tables/:tableId.json)', |
| 98 | + () => cards.get(projectId, tableId) |
| 99 | + ); |
| 100 | + } |
| 101 | + |
| 102 | + // Report |
| 103 | + const ok = results.filter(r => r.ok); |
| 104 | + const fail = results.filter(r => !r.ok); |
| 105 | + |
| 106 | + console.error('\nLive list checks summary:'); |
| 107 | + for (const r of results) { |
| 108 | + if (r.ok) console.error(`✓ ${r.name}${r.detail ? ` (${r.detail})` : ''}`); |
| 109 | + else console.error(`✗ ${r.name} -> ${r.detail}`); |
| 110 | + } |
| 111 | + console.error(`\nPassed: ${ok.length}, Failed: ${fail.length}`); |
| 112 | + |
| 113 | + process.exit(fail.length ? 1 : 0); |
| 114 | +} |
| 115 | + |
| 116 | +main().catch(err => { |
| 117 | + console.error('Fatal error:', err?.message || String(err)); |
| 118 | + process.exit(1); |
| 119 | +}); |
| 120 | + |
0 commit comments