Skip to content

Commit 299c8f3

Browse files
committed
refactor(internal): extract doStream helpers (Commit 2.9)
Extracts helper logic from two large files: 1. OpenAI-Compatible Chat Model (openai-compatible-chat-language-model.ts): - stream-content-tracker.ts: Text/reasoning delta handling - stream-tool-call-handler.ts: Tool call state management - stream-usage-tracker.ts: Token usage accumulation - doStream function reduced from ~300 to ~120 lines (-60%) 2. CLI Suggestion Engine (use-suggestion-engine.ts): - suggestion-filtering.ts: filterSlashCommands, filterAgentMatches, filterFileMatches - suggestion-parsing.ts: parseSlashContext, parseMentionContext, parseAtInLine - Hook reduced from 527 to 38 lines (-93%) Comprehensive unit tests added: - stream-content-tracker.test.ts: 14 tests - stream-usage-tracker.test.ts: 18 tests - stream-tool-call-handler.test.ts: 26 tests - suggestion-parsing.test.ts: 55 tests (high-value domain logic) - suggestion-parsing.mutation-test.ts: 14 mutation tests - Total: 127 new tests
1 parent f864789 commit 299c8f3

12 files changed

+2398
-750
lines changed

cli/src/hooks/use-suggestion-engine.ts

Lines changed: 22 additions & 511 deletions
Large diffs are not rendered by default.
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/**
2+
* Manual Mutation Testing for suggestion-parsing.ts
3+
*
4+
* This file tests whether our unit tests can catch bugs by simulating
5+
* common mutations that could be introduced into the implementation.
6+
*
7+
* Run with: bun test src/utils/__tests__/suggestion-parsing.mutation-test.ts
8+
*/
9+
10+
import { describe, it, expect } from 'bun:test'
11+
12+
/**
13+
* These tests verify that the unit tests are valuable by checking
14+
* edge cases that would break if certain implementation details changed.
15+
*/
16+
17+
describe('Mutation Testing: Would tests catch these bugs?', () => {
18+
describe('parseSlashContext mutations', () => {
19+
it('MUTATION: if startIndex check was removed, second-line slash would activate', async () => {
20+
// This tests that the "first line only" rule is enforced
21+
const { parseSlashContext } = await import('../suggestion-parsing')
22+
23+
// If someone removed: if (startIndex !== 0) return inactive
24+
// This test would catch it:
25+
const result = parseSlashContext('text\n/help')
26+
expect(result.active).toBe(false)
27+
28+
// Verify first line still works
29+
const firstLine = parseSlashContext('/help')
30+
expect(firstLine.active).toBe(true)
31+
})
32+
33+
it('MUTATION: if regex allowed whitespace after slash, "/ help" would activate', async () => {
34+
const { parseSlashContext } = await import('../suggestion-parsing')
35+
36+
// If regex changed from /^(\s*)\/([^\s]*)$/ to /^(\s*)\/(.*)/
37+
// This test would catch it:
38+
const result = parseSlashContext('/ help')
39+
expect(result.active).toBe(false)
40+
})
41+
})
42+
43+
describe('isInsideStringDelimiters mutations', () => {
44+
it('MUTATION: if escape counting was off-by-one, escaped quotes would misbehave', async () => {
45+
const { isInsideStringDelimiters } = await import('../suggestion-parsing')
46+
47+
// The backslash counting logic is: numBackslashes % 2 === 1 means escaped
48+
// If this was changed to === 0, these tests would fail:
49+
50+
// One backslash = escaped quote, still inside
51+
expect(isInsideStringDelimiters('"\\""', 3)).toBe(true)
52+
53+
// Two backslashes = unescaped quote, outside after
54+
expect(isInsideStringDelimiters('"\\\\"', 4)).toBe(false)
55+
})
56+
57+
it('MUTATION: if single quotes were added as delimiters, apostrophes would break', async () => {
58+
const { isInsideStringDelimiters } = await import('../suggestion-parsing')
59+
60+
// If someone added: if (char === "'") inSingleQuote = !inSingleQuote
61+
// Common English text would break:
62+
expect(isInsideStringDelimiters("don't worry", 6)).toBe(false)
63+
expect(isInsideStringDelimiters("it's fine", 5)).toBe(false)
64+
})
65+
66+
it('MUTATION: if backtick nesting was removed, ` inside " would toggle incorrectly', async () => {
67+
const { isInsideStringDelimiters } = await import('../suggestion-parsing')
68+
69+
// If the check `&& !inDoubleQuote` was removed from backtick handling:
70+
// "`code`" - backtick at position 1 would toggle, position 6 would be outside
71+
// But correct behavior: we're inside double quotes the whole time
72+
expect(isInsideStringDelimiters('"`code`"', 6)).toBe(true)
73+
})
74+
})
75+
76+
describe('parseAtInLine mutations', () => {
77+
it('MUTATION: if email check was removed, user@example.com would trigger', async () => {
78+
const { parseAtInLine } = await import('../suggestion-parsing')
79+
80+
// If the regex /[a-zA-Z0-9.:]/ check was removed:
81+
const result = parseAtInLine('user@example.com')
82+
expect(result.active).toBe(false)
83+
})
84+
85+
it('MUTATION: if escape check was removed, \\@user would trigger', async () => {
86+
const { parseAtInLine } = await import('../suggestion-parsing')
87+
88+
// If the beforeChar === '\\' check was removed:
89+
const result = parseAtInLine('\\@user')
90+
expect(result.active).toBe(false)
91+
})
92+
93+
it('MUTATION: if string delimiter check was removed, "@user" would trigger', async () => {
94+
const { parseAtInLine } = await import('../suggestion-parsing')
95+
96+
// If isInsideStringDelimiters check was removed:
97+
const result = parseAtInLine('"hello @user"')
98+
expect(result.active).toBe(false)
99+
})
100+
101+
it('MUTATION: if whitespace requirement was changed to allow any non-alnum', async () => {
102+
const { parseAtInLine } = await import('../suggestion-parsing')
103+
104+
// If the check changed from !/\s/.test(beforeChar) to something looser:
105+
// (@user should NOT trigger because ( is not whitespace
106+
expect(parseAtInLine('(@user').active).toBe(false)
107+
108+
// But ( @user SHOULD trigger because space before @
109+
expect(parseAtInLine('( @user').active).toBe(true)
110+
})
111+
112+
it('MUTATION: if lastIndexOf was changed to indexOf, first @ would be used', async () => {
113+
const { parseAtInLine } = await import('../suggestion-parsing')
114+
115+
// If lastIndexOf('@') was changed to indexOf('@'):
116+
// user@example.com @mention - indexOf would find the email @, not the mention
117+
const result = parseAtInLine('user@example.com @mention')
118+
expect(result.active).toBe(true)
119+
expect(result.query).toBe('mention')
120+
expect(result.atIndex).toBe(17) // Position of second @
121+
})
122+
})
123+
124+
describe('parseMentionContext mutations', () => {
125+
it('MUTATION: if cursor position was ignored, full input would be parsed', async () => {
126+
const { parseMentionContext } = await import('../suggestion-parsing')
127+
128+
// If cursorPosition was ignored (used input.length instead):
129+
// '@username more text' with cursor at 5 would include 'name more text'
130+
const result = parseMentionContext('@username', 5)
131+
expect(result.query).toBe('user') // Not 'username'
132+
})
133+
134+
it('MUTATION: if lineStart calculation was wrong, startIndex would be off', async () => {
135+
const { parseMentionContext } = await import('../suggestion-parsing')
136+
137+
// If lineStart was calculated incorrectly:
138+
const result = parseMentionContext('abc\n@user', 9)
139+
expect(result.startIndex).toBe(4) // Position of @ in full string
140+
})
141+
142+
it('MUTATION: if newline handling was broken, multiline would fail', async () => {
143+
const { parseMentionContext } = await import('../suggestion-parsing')
144+
145+
// First line @ should not be visible when cursor is on second line
146+
const result = parseMentionContext('@first\nsecond', 13)
147+
expect(result.active).toBe(false)
148+
149+
// Second line @ should work
150+
const result2 = parseMentionContext('first\n@second', 13)
151+
expect(result2.active).toBe(true)
152+
})
153+
})
154+
})
155+
156+
describe('Coverage of Critical Paths', () => {
157+
it('all exported functions are tested', async () => {
158+
const module = await import('../suggestion-parsing')
159+
160+
// Verify all exports exist
161+
expect(typeof module.parseSlashContext).toBe('function')
162+
expect(typeof module.parseMentionContext).toBe('function')
163+
expect(typeof module.parseAtInLine).toBe('function')
164+
expect(typeof module.isInsideStringDelimiters).toBe('function')
165+
})
166+
})

0 commit comments

Comments
 (0)