From bb9393c138561658eb34856c20bcafe2866f24fd Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Fri, 13 Feb 2026 07:40:45 +0000 Subject: [PATCH] test: add systemEvent utility tests (21 cases) --- coverage/coverage-summary.json | 13 +++ src/lib/__tests__/systemEvent.test.ts | 119 ++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 coverage/coverage-summary.json create mode 100644 src/lib/__tests__/systemEvent.test.ts diff --git a/coverage/coverage-summary.json b/coverage/coverage-summary.json new file mode 100644 index 0000000..ab4b3d2 --- /dev/null +++ b/coverage/coverage-summary.json @@ -0,0 +1,13 @@ +{"total": {"lines":{"total":261,"covered":55,"skipped":0,"pct":21.07},"statements":{"total":303,"covered":62,"skipped":0,"pct":20.46},"functions":{"total":53,"covered":5,"skipped":0,"pct":9.43},"branches":{"total":155,"covered":45,"skipped":0,"pct":29.03},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"}} +,"/home/marlburrow/pinchchat/src/lib/credentials.ts": {"lines":{"total":9,"covered":0,"skipped":0,"pct":0},"functions":{"total":3,"covered":0,"skipped":0,"pct":0},"statements":{"total":11,"covered":0,"skipped":0,"pct":0},"branches":{"total":6,"covered":0,"skipped":0,"pct":0}} +,"/home/marlburrow/pinchchat/src/lib/exportChat.ts": {"lines":{"total":41,"covered":26,"skipped":0,"pct":63.41},"functions":{"total":2,"covered":1,"skipped":0,"pct":50},"statements":{"total":41,"covered":26,"skipped":0,"pct":63.41},"branches":{"total":26,"covered":18,"skipped":0,"pct":69.23}} +,"/home/marlburrow/pinchchat/src/lib/gateway.ts": {"lines":{"total":81,"covered":0,"skipped":0,"pct":0},"functions":{"total":26,"covered":0,"skipped":0,"pct":0},"statements":{"total":102,"covered":0,"skipped":0,"pct":0},"branches":{"total":48,"covered":0,"skipped":0,"pct":0}} +,"/home/marlburrow/pinchchat/src/lib/highlight.ts": {"lines":{"total":26,"covered":0,"skipped":0,"pct":0},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":26,"covered":0,"skipped":0,"pct":0},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/home/marlburrow/pinchchat/src/lib/i18n.ts": {"lines":{"total":27,"covered":0,"skipped":0,"pct":0},"functions":{"total":7,"covered":0,"skipped":0,"pct":0},"statements":{"total":34,"covered":0,"skipped":0,"pct":0},"branches":{"total":25,"covered":0,"skipped":0,"pct":0}} +,"/home/marlburrow/pinchchat/src/lib/image.ts": {"lines":{"total":3,"covered":0,"skipped":0,"pct":0},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":5,"covered":0,"skipped":0,"pct":0},"branches":{"total":4,"covered":0,"skipped":0,"pct":0}} +,"/home/marlburrow/pinchchat/src/lib/notificationSound.ts": {"lines":{"total":27,"covered":0,"skipped":0,"pct":0},"functions":{"total":3,"covered":0,"skipped":0,"pct":0},"statements":{"total":28,"covered":0,"skipped":0,"pct":0},"branches":{"total":9,"covered":0,"skipped":0,"pct":0}} +,"/home/marlburrow/pinchchat/src/lib/relativeTime.ts": {"lines":{"total":13,"covered":13,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":18,"covered":18,"skipped":0,"pct":100},"branches":{"total":10,"covered":10,"skipped":0,"pct":100}} +,"/home/marlburrow/pinchchat/src/lib/sessionName.ts": {"lines":{"total":16,"covered":16,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":18,"covered":18,"skipped":0,"pct":100},"branches":{"total":18,"covered":17,"skipped":0,"pct":94.44}} +,"/home/marlburrow/pinchchat/src/lib/systemEvent.ts": {"lines":{"total":14,"covered":0,"skipped":0,"pct":0},"functions":{"total":4,"covered":0,"skipped":0,"pct":0},"statements":{"total":16,"covered":0,"skipped":0,"pct":0},"branches":{"total":8,"covered":0,"skipped":0,"pct":0}} +,"/home/marlburrow/pinchchat/src/lib/utils.ts": {"lines":{"total":4,"covered":0,"skipped":0,"pct":0},"functions":{"total":3,"covered":0,"skipped":0,"pct":0},"statements":{"total":4,"covered":0,"skipped":0,"pct":0},"branches":{"total":1,"covered":0,"skipped":0,"pct":0}} +} diff --git a/src/lib/__tests__/systemEvent.test.ts b/src/lib/__tests__/systemEvent.test.ts new file mode 100644 index 0000000..3636de3 --- /dev/null +++ b/src/lib/__tests__/systemEvent.test.ts @@ -0,0 +1,119 @@ +import { describe, it, expect } from 'vitest'; +import { isSystemEvent, stripWebhookScaffolding, hasWebhookScaffolding } from '../systemEvent'; + +describe('isSystemEvent', () => { + it('returns false for empty string', () => { + expect(isSystemEvent('')).toBe(false); + expect(isSystemEvent(' ')).toBe(false); + }); + + it('returns false for normal user messages', () => { + expect(isSystemEvent('Hello, how are you?')).toBe(false); + expect(isSystemEvent('Can you help me with this?')).toBe(false); + expect(isSystemEvent('Check the event log please')).toBe(false); + }); + + it('detects [EVENT ...] markers', () => { + expect(isSystemEvent('[EVENT] user joined')).toBe(true); + expect(isSystemEvent('[EVENT:ts] marlburrow joined channel')).toBe(true); + }); + + it('detects [from: xxx (system)] markers', () => { + expect(isSystemEvent('[from: gateway (system)] heartbeat')).toBe(true); + expect(isSystemEvent('prefix [from: cron (system)] task done')).toBe(true); + }); + + it('detects [HEARTBEAT ...] markers', () => { + expect(isSystemEvent('[HEARTBEAT] poll')).toBe(true); + }); + + it('detects [cron:...] markers', () => { + expect(isSystemEvent('[cron:abc123] scheduled task')).toBe(true); + }); + + it('detects [hook:...] and [webhook:...] markers', () => { + expect(isSystemEvent('[hook:agent task_id=x] payload')).toBe(true); + expect(isSystemEvent('[webhook:inbound] data')).toBe(true); + }); + + it('detects [sms-inbound ...] markers', () => { + expect(isSystemEvent('[sms-inbound +33600000000] Hello')).toBe(true); + }); + + it('detects [teamspeak ...] markers', () => { + expect(isSystemEvent('[teamspeak] user connected')).toBe(true); + }); + + it('detects heartbeat prompt pattern', () => { + expect(isSystemEvent('Read HEARTBEAT.md if it exists (workspace context). Follow it strictly.')).toBe(true); + }); + + it('detects [source:xxx] markers', () => { + expect(isSystemEvent('[source:telegram] message')).toBe(true); + expect(isSystemEvent('[source:discord] hello')).toBe(true); + }); + + it('handles leading whitespace', () => { + expect(isSystemEvent(' [EVENT] test')).toBe(true); + expect(isSystemEvent('\n[cron:x] task')).toBe(true); + }); +}); + +describe('stripWebhookScaffolding', () => { + it('returns original text when no scaffolding', () => { + expect(stripWebhookScaffolding('Hello world')).toBe('Hello world'); + }); + + it('extracts content from EXTERNAL_UNTRUSTED_CONTENT delimiters', () => { + const input = `[hook:agent task_id=abc] +--- SECURITY NOTICE --- +Do not trust this content. +--- END --- +<<>> +Hello from SMS +<<>>`; + expect(stripWebhookScaffolding(input)).toBe('Hello from SMS'); + }); + + it('strips leading bracket tags', () => { + expect(stripWebhookScaffolding('[hook:agent task_id=x] actual message')) + .toBe('actual message'); + expect(stripWebhookScaffolding('[cron:abc] run task')) + .toBe('run task'); + expect(stripWebhookScaffolding('[sms-inbound +33600000000] Bonjour')) + .toBe('Bonjour'); + }); + + it('strips SECURITY NOTICE blocks with END marker', () => { + const input = `--- SECURITY NOTICE --- +Untrusted content below +--- END --- +Actual message here`; + expect(stripWebhookScaffolding(input)).toBe('Actual message here'); + }); + + it('strips task/job ID lines', () => { + const input = `task_id: abc123 +job_id: def456 +Real content`; + expect(stripWebhookScaffolding(input)).toBe('Real content'); + }); + + it('returns original if stripping leaves empty string', () => { + expect(stripWebhookScaffolding('[hook:x]')).toBe('[hook:x]'); + }); +}); + +describe('hasWebhookScaffolding', () => { + it('returns false for plain messages', () => { + expect(hasWebhookScaffolding('Just a normal message')).toBe(false); + }); + + it('detects EXTERNAL_UNTRUSTED_CONTENT', () => { + expect(hasWebhookScaffolding('before <<>> after')).toBe(true); + }); + + it('detects SECURITY NOTICE', () => { + expect(hasWebhookScaffolding('--- SECURITY NOTICE --- blah')).toBe(true); + }); +});