From 02bba8e9a641ce14559073db7db0a9335801df3e Mon Sep 17 00:00:00 2001 From: "developerz-ai[bot]" <286432749+developerz-ai[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 06:53:13 +0000 Subject: [PATCH] test(result): pin MAX_LIST_ITEMS, EVIDENCE_KEYS, MIME map, TRUNCATED_KEY, steering note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The integration test in outer-tools.test.ts exercises these constants only behaviorally — a swap of MAX_LIST_ITEMS from 20 to 21 would still pass the existing 25-item array assertion. Export the four module-private constants and assert their values directly so any future edit flips the unit test. Dz-Task-Id: tsk_b9ddded8a73c1263efdb558e24d822c5 --- src/mcp/tools/result.test.ts | 39 ++++++++++++++++++++++++++++++++++++ src/mcp/tools/result.ts | 8 ++++---- 2 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 src/mcp/tools/result.test.ts diff --git a/src/mcp/tools/result.test.ts b/src/mcp/tools/result.test.ts new file mode 100644 index 0000000..8027a5f --- /dev/null +++ b/src/mcp/tools/result.test.ts @@ -0,0 +1,39 @@ +import { expect, test } from 'bun:test'; +import { + EVIDENCE_KEYS, + MAX_LIST_ITEMS, + mimeTypeFor, + steeringNote, + TRUNCATED_KEY, +} from './result.js'; + +test('MAX_LIST_ITEMS is 20', () => { + expect(MAX_LIST_ITEMS).toBe(20); +}); + +test('EVIDENCE_KEYS contains exactly screenshot and evidence', () => { + expect(EVIDENCE_KEYS).toEqual(new Set(['screenshot', 'evidence'])); +}); + +test('mimeTypeFor returns the expected MIME for each known extension', () => { + expect(mimeTypeFor('/tmp/x.png')).toBe('image/png'); + expect(mimeTypeFor('/tmp/x.jpg')).toBe('image/jpeg'); + expect(mimeTypeFor('/tmp/x.jpeg')).toBe('image/jpeg'); + expect(mimeTypeFor('/tmp/x.mp4')).toBe('video/mp4'); + expect(mimeTypeFor('/tmp/x.log')).toBe('text/plain'); +}); + +test('mimeTypeFor returns undefined for an unknown extension', () => { + expect(mimeTypeFor('/tmp/x.txt')).toBeUndefined(); +}); + +test('TRUNCATED_KEY is "truncated"', () => { + expect(TRUNCATED_KEY).toBe('truncated'); +}); + +test('steeringNote contains the literal phrases for the capped fields', () => { + const note = steeringNote(['steps', 'bugs']); + expect(note).toContain('Truncated steps, bugs to the first 20 items'); + expect(note).toContain('see "truncated" for the real totals'); + expect(note).toContain('fields=["steps", "bugs"]'); +}); diff --git a/src/mcp/tools/result.ts b/src/mcp/tools/result.ts index 0ffcbf5..9e8d0d4 100644 --- a/src/mcp/tools/result.ts +++ b/src/mcp/tools/result.ts @@ -26,15 +26,15 @@ function isRecord(value: unknown): value is Record { } /** Cap on a top-level array field before it gets truncated for the response. */ -const MAX_LIST_ITEMS = 20; +export const MAX_LIST_ITEMS = 20; /** Keys whose string value — when an absolute path — is evidence worth linking. */ -const EVIDENCE_KEYS = new Set(['screenshot', 'evidence']); +export const EVIDENCE_KEYS = new Set(['screenshot', 'evidence']); type ResourceLinkContent = Extract; /** Guess a MIME type from an evidence path's extension; omit when unknown. */ -function mimeTypeFor(path: string): string | undefined { +export function mimeTypeFor(path: string): string | undefined { if (path.endsWith('.png')) return 'image/png'; if (path.endsWith('.jpg') || path.endsWith('.jpeg')) return 'image/jpeg'; if (path.endsWith('.mp4')) return 'video/mp4'; @@ -117,7 +117,7 @@ function capLists(data: Record): { } /** Steering text appended when one or more lists got capped. */ -function steeringNote(truncated: string[]): string { +export function steeringNote(truncated: string[]): string { const fields = truncated.map((f) => `"${f}"`).join(', '); return ( `Truncated ${truncated.join(', ')} to the first ${MAX_LIST_ITEMS} items (see "${TRUNCATED_KEY}" ` +