portal 搜尋濾殘影(#46 治標)+四件套單測
/portal/data/search 拿到 KBDB 回應後 server-side 過濾 metadata_json.status= 'deprecated' 與 content 以「(舊管線產物」開頭的 entries(metadata parse 失敗 視為保留,不誤殺),count 重算。這是 Arcrun#46 上游修好前的 portal 端治標, 上游清完資料後整段(含 filterDeprecatedEntries)可拔。 單測:sanitizeUploadFilename(路徑穿越/副檔名/長度)、filterDeprecatedEntries (濾殘影/壞 metadata 保留)、mapGraphWorkflowOutput(#57 輸出映射); /portal/session 補 upload_enabled=false 斷言。另修 portal-ui 一處註解字樣 (誤用產品名,撞零 Mira 斷言)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BH7LdhuCdVUHfHXbM7N8r5
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
import { SELF, env, fetchMock } from 'cloudflare:test';
|
||||
import { beforeAll, afterEach, describe, it, expect } from 'vitest';
|
||||
import { workflowsVisible } from '../src/routes/portal';
|
||||
import { entryLibrary } from '../src/routes/portal-data';
|
||||
import { entryLibrary, sanitizeUploadFilename, filterDeprecatedEntries, mapGraphWorkflowOutput } from '../src/routes/portal-data';
|
||||
import type { Bindings } from '../src/types';
|
||||
|
||||
const KBDB = 'https://kbdb.test';
|
||||
@@ -353,5 +353,58 @@ describe('GET /portal/session(P3 能力欄位)', () => {
|
||||
const data = (await res.json()) as Record<string, unknown>;
|
||||
expect(data.graph_allowed).toBe(true);
|
||||
expect(data.workflows_visible).toBe(true);
|
||||
// portal-demo-suite:測試環境未設 upload bindings → upload_enabled=false(Mira 零影響預設)
|
||||
expect(data.upload_enabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════ 7. portal-demo-suite 純函式 ═══════════════
|
||||
|
||||
describe('sanitizeUploadFilename(上傳檔名驗證)', () => {
|
||||
it('去路徑分隔(擋穿越)、.txt 改 .md、無副檔名補 .md', () => {
|
||||
expect(sanitizeUploadFilename('notes.md')).toBe('notes.md');
|
||||
expect(sanitizeUploadFilename('memo.txt')).toBe('memo.md');
|
||||
expect(sanitizeUploadFilename('README')).toBe('README.md');
|
||||
expect(sanitizeUploadFilename('../../etc/passwd')).toBe('passwd.md'); // 只取最後一段,穿越失效
|
||||
expect(sanitizeUploadFilename('a\\b\\c.md')).toBe('c.md');
|
||||
expect(sanitizeUploadFilename('中文筆記.txt')).toBe('中文筆記.md');
|
||||
});
|
||||
it('空名/純路徑/隱藏檔/超過 100 字/非字串 → null', () => {
|
||||
expect(sanitizeUploadFilename('')).toBe(null);
|
||||
expect(sanitizeUploadFilename(' ')).toBe(null);
|
||||
expect(sanitizeUploadFilename('docs/')).toBe(null);
|
||||
expect(sanitizeUploadFilename('.env')).toBe(null);
|
||||
expect(sanitizeUploadFilename('a'.repeat(120) + '.md')).toBe(null);
|
||||
expect(sanitizeUploadFilename(42)).toBe(null);
|
||||
expect(sanitizeUploadFilename(undefined)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterDeprecatedEntries(Arcrun#46 搜尋殘影治標)', () => {
|
||||
it('濾 status=deprecated 與「(舊管線產物」開頭;metadata parse 失敗保留', () => {
|
||||
const keepNormal = { metadata_json: '{"library":"general"}', content: '正常內容' };
|
||||
const keepBadMeta = { metadata_json: 'not-json{{', content: '壞 metadata 不誤殺' };
|
||||
const keepNullMeta = { metadata_json: null, content: '無 metadata' };
|
||||
const dropByStatus = { metadata_json: '{"status":"deprecated"}', content: '看起來正常但已標廢' };
|
||||
const dropByContent = { metadata_json: '{}', content: '(舊管線產物)殘影條目' };
|
||||
const out = filterDeprecatedEntries([keepNormal, dropByStatus, keepBadMeta, dropByContent, keepNullMeta]);
|
||||
expect(out).toEqual([keepNormal, keepBadMeta, keepNullMeta]);
|
||||
});
|
||||
it('空陣列 → 空陣列', () => {
|
||||
expect(filterDeprecatedEntries([])).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapGraphWorkflowOutput(#57 workflow 輸出 → plugin 形狀)', () => {
|
||||
it('本體有 neighbors → 直取;count 重算不信自報', () => {
|
||||
const out = mapGraphWorkflowOutput({ neighbors: ['a', 'b'], edges: [{ subject: 'a', predicate: 'rel', object: 'b' }], count: 99 });
|
||||
expect(out.neighbors).toEqual(['a', 'b']);
|
||||
expect(out.edges.length).toBe(1);
|
||||
expect(out.count).toBe(2);
|
||||
});
|
||||
it('包一層 data(http_request 慣例)→ 取內層;非物件/缺欄位 → 誠實空集合', () => {
|
||||
expect(mapGraphWorkflowOutput({ data: { neighbors: ['x'], edges: [] } })).toEqual({ neighbors: ['x'], edges: [], count: 1 });
|
||||
expect(mapGraphWorkflowOutput(null)).toEqual({ neighbors: [], edges: [], count: 0 });
|
||||
expect(mapGraphWorkflowOutput('oops')).toEqual({ neighbors: [], edges: [], count: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user