/** * 動作 → API 的映射測試=「kbdb-動作對照表」✅ 那半的機器版。 * 表若改(路由變動),這裡會先紅——防對照表漂移(表自己的「過期時會教錯」段)。 */ import { describe, expect, it } from 'vitest'; import { buildRequest } from '../nodes/Kbdb/actions'; describe('對照表 ✅ 那半:每個動作恰好對到一支現行 API', () => { it('Append record → POST /kbdb/records', () => { expect(buildRequest('record', 'append', { sheet: 'contact', values: { name: '王小明' } })).toEqual({ method: 'POST', path: '/kbdb/records', body: { template: 'contact', values: { name: '王小明' } }, }); }); it('Get a record → GET /kbdb/records/:recordId', () => { expect(buildRequest('record', 'get', { recordId: 'rec_1' })).toEqual({ method: 'GET', path: '/kbdb/records/rec_1', }); }); it('Get many records → GET /kbdb/records/by-template/:sheet', () => { expect(buildRequest('record', 'getMany', { sheet: 'contact' })).toEqual({ method: 'GET', path: '/kbdb/records/by-template/contact', }); }); it('Update a record → PATCH /kbdb/records/:recordId(只帶要改的欄)', () => { expect(buildRequest('record', 'update', { recordId: 'rec_1', values: { phone: '0912' } })).toEqual({ method: 'PATCH', path: '/kbdb/records/rec_1', body: { values: { phone: '0912' } }, }); }); it('Create a sheet → POST /kbdb/templates(name + slots[])', () => { expect(buildRequest('sheet', 'create', { sheet: 'teacher_list', slots: ['name', 'hired_at'] })).toEqual({ method: 'POST', path: '/kbdb/templates', body: { name: 'teacher_list', slots: ['name', 'hired_at'] }, }); }); it('Get sheet schema → GET /kbdb/templates/:idOrName', () => { expect(buildRequest('sheet', 'getSchema', { sheet: 'contact' })).toEqual({ method: 'GET', path: '/kbdb/templates/contact', }); }); it('List sheets → GET /kbdb/templates', () => { expect(buildRequest('sheet', 'list', {})).toEqual({ method: 'GET', path: '/kbdb/templates' }); }); it('Search → GET /kbdb/search(q 必填、mode/filters 透傳)', () => { expect( buildRequest('search', 'search', { query: '牛肉麵', mode: 'keyword', filters: { library: 'general' }, }), ).toEqual({ method: 'GET', path: '/kbdb/search', qs: { q: '牛肉麵', mode: 'keyword', library: 'general' }, }); }); it('路徑參數會被 URL encode(中文 sheet 名不炸)', () => { expect(buildRequest('record', 'getMany', { sheet: '通訊錄' }).path).toBe( `/kbdb/records/by-template/${encodeURIComponent('通訊錄')}`, ); }); }); describe('對照表 🔴/◐ 那半:沒有的能力要誠實丟錯,不拼裝(07-thin-shell §3.1)', () => { const missing: Array<[string, string]> = [ ['sheet', 'addField'], // ◐→實測:PATCH /templates 是整組覆蓋且 proxy 未暴露 ⇒ 對外沒有這能力 ['sheet', 'archive'], // 🔴 API 連 DELETE 都沒有 ['record', 'archive'], // 🔴 只有 DELETE(真的刪掉),語意不符不掛上來 ['record', 'link'], // 🔴 關係整組沒有 API ['record', 'addToSheet'], // 🔴 同上 ['record', 'getLinked'], // 🔴 同上 ['record', 'unlink'], // 🔴 同上 ]; for (const [resource, operation] of missing) { it(`${resource}.${operation} → 明確丟「沒有這個能力」`, () => { expect(() => buildRequest(resource, operation, {})).toThrow(/沒有.*能力/); }); } }); describe('必要參數缺漏要在薄殼層就講清楚', () => { it('append 缺 values → 錯誤訊息點名 values', () => { expect(() => buildRequest('record', 'append', { sheet: 'contact' })).toThrow(/values/); }); it('create sheet 缺欄位 → 錯誤訊息點名 fields', () => { expect(() => buildRequest('sheet', 'create', { sheet: 'x' })).toThrow(/fields/); }); it('create sheet 空欄位陣列 → 擋', () => { expect(() => buildRequest('sheet', 'create', { sheet: 'x', slots: [] })).toThrow(/至少要有一欄/); }); it('search 缺 query → 錯誤訊息點名 query', () => { expect(() => buildRequest('search', 'search', {})).toThrow(/query/); }); });