@@ -173,14 +173,20 @@ describe('Formatters', () => {
173173 expect ( result . content ) . not . toContain ( '3.' ) ;
174174 } ) ;
175175
176- it ( 'should estimate more tokens than compact' , ( ) => {
176+ it ( 'should estimate more tokens than compact when snippets disabled' , ( ) => {
177+ // When snippets are disabled, verbose still has more metadata
177178 const compactFormatter = new CompactFormatter ( ) ;
178- const verboseFormatter = new VerboseFormatter ( ) ;
179+ const verboseFormatter = new VerboseFormatter ( {
180+ includeSnippets : false ,
181+ includeImports : false ,
182+ } ) ;
179183
180- const compactTokens = compactFormatter . estimateTokens ( mockResults [ 0 ] ) ;
181- const verboseTokens = verboseFormatter . estimateTokens ( mockResults [ 0 ] ) ;
184+ // Use formatResult which includes all the metadata lines
185+ const compactOutput = compactFormatter . formatResult ( mockResults [ 0 ] ) ;
186+ const verboseOutput = verboseFormatter . formatResult ( mockResults [ 0 ] ) ;
182187
183- expect ( verboseTokens ) . toBeGreaterThan ( compactTokens ) ;
188+ // Verbose output should be longer (has Location, Signature, Metadata lines)
189+ expect ( verboseOutput . length ) . toBeGreaterThan ( compactOutput . length ) ;
184190 } ) ;
185191
186192 it ( 'should handle missing metadata gracefully' , ( ) => {
@@ -250,4 +256,215 @@ describe('Formatters', () => {
250256 expect ( result . tokens ) . toBeGreaterThan ( 0 ) ;
251257 } ) ;
252258 } ) ;
259+
260+ describe ( 'Snippet and Import Formatting' , ( ) => {
261+ const resultWithSnippet : SearchResult = {
262+ id : 'src/auth/handler.ts:handleAuth:45' ,
263+ score : 0.85 ,
264+ metadata : {
265+ path : 'src/auth/handler.ts' ,
266+ type : 'function' ,
267+ language : 'typescript' ,
268+ name : 'handleAuth' ,
269+ startLine : 45 ,
270+ endLine : 67 ,
271+ exported : true ,
272+ snippet :
273+ 'export async function handleAuth(req: Request): Promise<Response> {\n const token = extractToken(req);\n return validateToken(token);\n}' ,
274+ imports : [ './service' , '../utils/jwt' , 'express' ] ,
275+ } ,
276+ } ;
277+
278+ const resultWithManyImports : SearchResult = {
279+ id : 'src/index.ts:main:1' ,
280+ score : 0.75 ,
281+ metadata : {
282+ path : 'src/index.ts' ,
283+ type : 'function' ,
284+ name : 'main' ,
285+ startLine : 1 ,
286+ endLine : 10 ,
287+ imports : [ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' ] ,
288+ } ,
289+ } ;
290+
291+ describe ( 'CompactFormatter with snippets' , ( ) => {
292+ it ( 'should not include snippet by default' , ( ) => {
293+ const formatter = new CompactFormatter ( ) ;
294+ const formatted = formatter . formatResult ( resultWithSnippet ) ;
295+
296+ expect ( formatted ) . not . toContain ( 'export async function' ) ;
297+ expect ( formatted ) . not . toContain ( 'Imports:' ) ;
298+ } ) ;
299+
300+ it ( 'should include snippet when enabled' , ( ) => {
301+ const formatter = new CompactFormatter ( { includeSnippets : true } ) ;
302+ const formatted = formatter . formatResult ( resultWithSnippet ) ;
303+
304+ expect ( formatted ) . toContain ( 'export async function handleAuth' ) ;
305+ expect ( formatted ) . toContain ( 'extractToken' ) ;
306+ } ) ;
307+
308+ it ( 'should include imports when enabled' , ( ) => {
309+ const formatter = new CompactFormatter ( { includeImports : true } ) ;
310+ const formatted = formatter . formatResult ( resultWithSnippet ) ;
311+
312+ expect ( formatted ) . toContain ( 'Imports:' ) ;
313+ expect ( formatted ) . toContain ( './service' ) ;
314+ expect ( formatted ) . toContain ( 'express' ) ;
315+ } ) ;
316+
317+ it ( 'should truncate long import lists' , ( ) => {
318+ const formatter = new CompactFormatter ( { includeImports : true } ) ;
319+ const formatted = formatter . formatResult ( resultWithManyImports ) ;
320+
321+ expect ( formatted ) . toContain ( 'Imports:' ) ;
322+ expect ( formatted ) . toContain ( 'a, b, c, d, e' ) ;
323+ expect ( formatted ) . toContain ( '...' ) ;
324+ expect ( formatted ) . not . toContain ( 'f, g' ) ;
325+ } ) ;
326+
327+ it ( 'should truncate long snippets' , ( ) => {
328+ const longSnippet = Array ( 20 ) . fill ( 'const x = 1;' ) . join ( '\n' ) ;
329+ const result : SearchResult = {
330+ id : 'test' ,
331+ score : 0.8 ,
332+ metadata : {
333+ path : 'test.ts' ,
334+ type : 'function' ,
335+ name : 'test' ,
336+ snippet : longSnippet ,
337+ } ,
338+ } ;
339+
340+ const formatter = new CompactFormatter ( { includeSnippets : true , maxSnippetLines : 5 } ) ;
341+ const formatted = formatter . formatResult ( result ) ;
342+
343+ expect ( formatted ) . toContain ( '// ... 15 more lines' ) ;
344+ } ) ;
345+
346+ it ( 'should increase token estimate with snippets' , ( ) => {
347+ const formatterWithout = new CompactFormatter ( ) ;
348+ const formatterWith = new CompactFormatter ( { includeSnippets : true , includeImports : true } ) ;
349+
350+ const tokensWithout = formatterWithout . estimateTokens ( resultWithSnippet ) ;
351+ const tokensWith = formatterWith . estimateTokens ( resultWithSnippet ) ;
352+
353+ expect ( tokensWith ) . toBeGreaterThan ( tokensWithout ) ;
354+ } ) ;
355+ } ) ;
356+
357+ describe ( 'VerboseFormatter with snippets' , ( ) => {
358+ it ( 'should include snippet by default' , ( ) => {
359+ const formatter = new VerboseFormatter ( ) ;
360+ const formatted = formatter . formatResult ( resultWithSnippet ) ;
361+
362+ expect ( formatted ) . toContain ( 'Code:' ) ;
363+ expect ( formatted ) . toContain ( 'export async function handleAuth' ) ;
364+ } ) ;
365+
366+ it ( 'should include imports by default' , ( ) => {
367+ const formatter = new VerboseFormatter ( ) ;
368+ const formatted = formatter . formatResult ( resultWithSnippet ) ;
369+
370+ expect ( formatted ) . toContain ( 'Imports: ./service, ../utils/jwt, express' ) ;
371+ } ) ;
372+
373+ it ( 'should show location with line range' , ( ) => {
374+ const formatter = new VerboseFormatter ( ) ;
375+ const formatted = formatter . formatResult ( resultWithSnippet ) ;
376+
377+ expect ( formatted ) . toContain ( 'Location: src/auth/handler.ts:45-67' ) ;
378+ } ) ;
379+
380+ it ( 'should not truncate imports in verbose mode' , ( ) => {
381+ const formatter = new VerboseFormatter ( ) ;
382+ const formatted = formatter . formatResult ( resultWithManyImports ) ;
383+
384+ expect ( formatted ) . toContain ( 'Imports: a, b, c, d, e, f, g' ) ;
385+ expect ( formatted ) . not . toContain ( '...' ) ;
386+ } ) ;
387+
388+ it ( 'should respect maxSnippetLines option' , ( ) => {
389+ const longSnippet = Array ( 30 ) . fill ( 'const x = 1;' ) . join ( '\n' ) ;
390+ const result : SearchResult = {
391+ id : 'test' ,
392+ score : 0.8 ,
393+ metadata : {
394+ path : 'test.ts' ,
395+ type : 'function' ,
396+ name : 'test' ,
397+ snippet : longSnippet ,
398+ } ,
399+ } ;
400+
401+ const formatter = new VerboseFormatter ( { maxSnippetLines : 10 } ) ;
402+ const formatted = formatter . formatResult ( result ) ;
403+
404+ expect ( formatted ) . toContain ( '// ... 20 more lines' ) ;
405+ } ) ;
406+
407+ it ( 'should be able to disable snippets' , ( ) => {
408+ const formatter = new VerboseFormatter ( { includeSnippets : false } ) ;
409+ const formatted = formatter . formatResult ( resultWithSnippet ) ;
410+
411+ expect ( formatted ) . not . toContain ( 'Code:' ) ;
412+ expect ( formatted ) . not . toContain ( 'export async function' ) ;
413+ } ) ;
414+
415+ it ( 'should be able to disable imports' , ( ) => {
416+ const formatter = new VerboseFormatter ( { includeImports : false } ) ;
417+ const formatted = formatter . formatResult ( resultWithSnippet ) ;
418+
419+ expect ( formatted ) . not . toContain ( 'Imports:' ) ;
420+ } ) ;
421+
422+ it ( 'should increase token estimate with snippets' , ( ) => {
423+ const formatterWithout = new VerboseFormatter ( {
424+ includeSnippets : false ,
425+ includeImports : false ,
426+ } ) ;
427+ const formatterWith = new VerboseFormatter ( ) ;
428+
429+ const tokensWithout = formatterWithout . estimateTokens ( resultWithSnippet ) ;
430+ const tokensWith = formatterWith . estimateTokens ( resultWithSnippet ) ;
431+
432+ expect ( tokensWith ) . toBeGreaterThan ( tokensWithout ) ;
433+ } ) ;
434+ } ) ;
435+
436+ describe ( 'Empty snippets and imports' , ( ) => {
437+ it ( 'should handle missing snippet gracefully' , ( ) => {
438+ const formatter = new VerboseFormatter ( ) ;
439+ const formatted = formatter . formatResult ( mockResults [ 0 ] ) ;
440+
441+ expect ( formatted ) . not . toContain ( 'Code:' ) ;
442+ } ) ;
443+
444+ it ( 'should handle missing imports gracefully' , ( ) => {
445+ const formatter = new VerboseFormatter ( ) ;
446+ const formatted = formatter . formatResult ( mockResults [ 0 ] ) ;
447+
448+ expect ( formatted ) . not . toContain ( 'Imports:' ) ;
449+ } ) ;
450+
451+ it ( 'should handle empty imports array' , ( ) => {
452+ const result : SearchResult = {
453+ id : 'test' ,
454+ score : 0.8 ,
455+ metadata : {
456+ path : 'test.ts' ,
457+ type : 'function' ,
458+ name : 'test' ,
459+ imports : [ ] ,
460+ } ,
461+ } ;
462+
463+ const formatter = new VerboseFormatter ( ) ;
464+ const formatted = formatter . formatResult ( result ) ;
465+
466+ expect ( formatted ) . not . toContain ( 'Imports:' ) ;
467+ } ) ;
468+ } ) ;
469+ } ) ;
253470} ) ;
0 commit comments