diff --git a/.Jules/vantage.md b/.Jules/vantage.md index 6e9850b5..988e5166 100644 --- a/.Jules/vantage.md +++ b/.Jules/vantage.md @@ -15,3 +15,5 @@ ## 2026-05-20 - Hardening Telemetry Verification and Refining Stream Types | **Learning:** Index-based assertions in telemetry tests are brittle; using `.find()` ensures robust verification. Standardizing on `String(error)` across all API catch blocks prevents context loss during failure events. | **Action:** Refactored `chat-api.test.ts` and `github-releases.test.ts` to use search-based spy assertions, and added explicit return types to `chat-stream.ts` to resolve Astro compiler hints. ## 2026-05-24 - Comprehensive Unit Testing for Visitor Changelog Formatting | **Learning:** Single-pass Edge transformations require dedicated unit tests covering edge cases like empty inputs, internal agent filtering, and fallback formatting to prevent silent regressions in SSR release streams. | **Action:** Added `src/utils/visitor-changelog.test.ts` to test `toVisitorChangelogTitle`, `stripChangelogChrome`, `toVisitorReleaseBody`, and `toVisitorRelease`. + +## 2026-09-13 - Hardening Chat Explore Card Question Matching & Expanding Edge-Case Tests | **Learning:** Helper functions operating on user inputs in Cloudflare Workers edge runtimes must explicitly guard against non-string arguments to prevent unexpected runtime errors. | **Action:** Added runtime type check in `exploreCardForQuestion` in `src/utils/chat-explore.ts` and added unit test cases covering non-string, empty whitespace, and uppercase inputs in `src/utils/chat-explore.test.ts`. diff --git a/src/utils/chat-explore.test.ts b/src/utils/chat-explore.test.ts index b1020201..6a14d22e 100644 --- a/src/utils/chat-explore.test.ts +++ b/src/utils/chat-explore.test.ts @@ -31,6 +31,21 @@ describe('exploreCardForQuestion', () => { ); }); + it('handles edge cases gracefully such as non-string values or empty whitespace', () => { + // @ts-expect-error testing runtime guard against non-string input + expect(exploreCardForQuestion(null)).toBeNull(); + // @ts-expect-error testing runtime guard against non-string input + expect(exploreCardForQuestion(undefined)).toBeNull(); + expect(exploreCardForQuestion('')).toBeNull(); + expect(exploreCardForQuestion(' ')).toBeNull(); + }); + + it('handles uppercase and mixed-case queries', () => { + expect(exploreCardForQuestion('TELL ME ABOUT YOUR WORK')).toEqual(EXPLORE_CARDS.work); + expect(exploreCardForQuestion('DESIGN SYSTEM')).toEqual(EXPLORE_CARDS.designSystem); + expect(exploreCardForQuestion('LINKEDIN')).toEqual(LINKEDIN_CONFIRM); + }); + it('keeps action cards on published routes and LinkedIn confirm on the hire path', () => { expect(EXPLORE_CARDS.designSystem.href).toBe('/work/ifs-design-system/'); expect(EXPLORE_CARDS.designSystem.actionLabel).toBe('View the case'); diff --git a/src/utils/chat-explore.ts b/src/utils/chat-explore.ts index 4e266baf..9f704d7c 100644 --- a/src/utils/chat-explore.ts +++ b/src/utils/chat-explore.ts @@ -63,6 +63,7 @@ export const EXPLORE_CARDS = { } as const satisfies Record; export function exploreCardForQuestion(lastUserMessage: string): ExploreCard | null { + if (typeof lastUserMessage !== 'string') return null; const question = lastUserMessage.trim().toLowerCase(); if (!question) return null;