Files
uncle6me-web 87f6de7c9f feat(n8n): KBDB community node 薄殼——對照表 那半的八個動作
leo 2026-08-15「有效才正式開發」下一格:紙上考卷驗過的動作集做成真的 n8n 節點。
詞彙照定案(sheet/field/record,InkStoneCo system-dev/wiki/kbdb-詞彙表.md)。

- 動作 ↔ API 映射收斂在 nodes/Kbdb/actions.ts(純函式),20 個 vitest 釘死
  ——就是動作對照表  那半的機器版;🔴/◐ 的動作刻意不存在,呼叫丟
  「沒有這個能力」(07-thin-shell §3.1:API 沒有的能力不拼裝)
- 對外走 cypher-executor /kbdb/* proxy(X-Arcrun-API-Key),不直連 kbdb worker
- Add a field 不掛:proxy 無 PATCH /kbdb/templates(實測 404);base 端
  PATCH /templates/:id 語意是 slots 整組覆蓋(本地實測),不是加一欄

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 19:42:53 +08:00

110 lines
4.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 動作 → 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/templatesname + 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/searchq 必填、modefilters 透傳)', () => {
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/);
});
});