Skip to content

Commit 584f1c1

Browse files
committed
fix issue in getPreviouslyReadFiles
1 parent 7e961b8 commit 584f1c1

File tree

2 files changed

+391
-2
lines changed

2 files changed

+391
-2
lines changed

backend/src/util/__tests__/messages.test.ts

Lines changed: 386 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ import {
88
spyOn,
99
} from 'bun:test'
1010

11-
import { trimMessagesToFitTokenLimit, messagesWithSystem } from '../messages'
11+
import { logger } from '../logger'
12+
import {
13+
trimMessagesToFitTokenLimit,
14+
messagesWithSystem,
15+
getPreviouslyReadFiles,
16+
} from '../messages'
1217
import * as tokenCounter from '../token-counter'
1318

1419
import type { Message } from '@codebuff/common/types/messages/codebuff-message'
@@ -417,3 +422,383 @@ describe('trimMessagesToFitTokenLimit', () => {
417422
})
418423
})
419424
})
425+
426+
describe('getPreviouslyReadFiles', () => {
427+
it('returns empty array when no messages provided', () => {
428+
const result = getPreviouslyReadFiles([])
429+
expect(result).toEqual([])
430+
})
431+
432+
it('returns empty array when no tool messages with relevant tool names', () => {
433+
const messages: Message[] = [
434+
{ role: 'user', content: 'hello' },
435+
{ role: 'assistant', content: 'hi' },
436+
{
437+
role: 'tool',
438+
content: {
439+
type: 'tool-result',
440+
toolName: 'write_file',
441+
toolCallId: 'test-id',
442+
output: [{ type: 'json', value: { file: 'test.ts' } }],
443+
},
444+
},
445+
]
446+
447+
const result = getPreviouslyReadFiles(messages)
448+
expect(result).toEqual([])
449+
})
450+
451+
it('extracts files from read_files tool messages', () => {
452+
const messages: Message[] = [
453+
{
454+
role: 'tool',
455+
content: {
456+
type: 'tool-result',
457+
toolName: 'read_files',
458+
toolCallId: 'test-id',
459+
output: [
460+
{
461+
type: 'json',
462+
value: [
463+
{
464+
path: 'src/test.ts',
465+
content: 'export function test() {}',
466+
referencedBy: { 'main.ts': ['line 10'] },
467+
},
468+
{
469+
path: 'src/utils.ts',
470+
content: 'export const utils = {}',
471+
},
472+
],
473+
},
474+
],
475+
},
476+
},
477+
]
478+
479+
const result = getPreviouslyReadFiles(messages)
480+
expect(result).toEqual([
481+
{
482+
path: 'src/test.ts',
483+
content: 'export function test() {}',
484+
referencedBy: { 'main.ts': ['line 10'] },
485+
},
486+
{
487+
path: 'src/utils.ts',
488+
content: 'export const utils = {}',
489+
},
490+
])
491+
})
492+
493+
it('extracts files from find_files tool messages', () => {
494+
const messages: Message[] = [
495+
{
496+
role: 'tool',
497+
content: {
498+
type: 'tool-result',
499+
toolName: 'find_files',
500+
toolCallId: 'test-id',
501+
output: [
502+
{
503+
type: 'json',
504+
value: [
505+
{
506+
path: 'components/Button.tsx',
507+
content: 'export const Button = () => {}',
508+
},
509+
],
510+
},
511+
],
512+
},
513+
},
514+
]
515+
516+
const result = getPreviouslyReadFiles(messages)
517+
expect(result).toEqual([
518+
{
519+
path: 'components/Button.tsx',
520+
content: 'export const Button = () => {}',
521+
},
522+
])
523+
})
524+
525+
it('extracts files from file_updates tool messages', () => {
526+
const messages: Message[] = [
527+
{
528+
role: 'tool',
529+
content: {
530+
type: 'tool-result',
531+
toolName: 'file_updates',
532+
toolCallId: 'test-id',
533+
output: [
534+
{
535+
type: 'json',
536+
value: [
537+
{
538+
path: 'config/database.ts',
539+
content: 'export const dbConfig = {}',
540+
referencedBy: { 'app.ts': ['line 5', 'line 20'] },
541+
},
542+
],
543+
},
544+
],
545+
},
546+
},
547+
]
548+
549+
const result = getPreviouslyReadFiles(messages)
550+
expect(result).toEqual([
551+
{
552+
path: 'config/database.ts',
553+
content: 'export const dbConfig = {}',
554+
referencedBy: { 'app.ts': ['line 5', 'line 20'] },
555+
},
556+
])
557+
})
558+
559+
it('combines files from multiple tool messages', () => {
560+
const messages: Message[] = [
561+
{
562+
role: 'tool',
563+
content: {
564+
type: 'tool-result',
565+
toolName: 'read_files',
566+
toolCallId: 'test-id-1',
567+
output: [
568+
{
569+
type: 'json',
570+
value: [
571+
{
572+
path: 'file1.ts',
573+
content: 'content 1',
574+
},
575+
],
576+
},
577+
],
578+
},
579+
},
580+
{
581+
role: 'tool',
582+
content: {
583+
type: 'tool-result',
584+
toolName: 'find_files',
585+
toolCallId: 'test-id-2',
586+
output: [
587+
{
588+
type: 'json',
589+
value: [
590+
{
591+
path: 'file2.ts',
592+
content: 'content 2',
593+
},
594+
],
595+
},
596+
],
597+
},
598+
},
599+
{
600+
role: 'user',
601+
content: 'Some user message',
602+
},
603+
{
604+
role: 'tool',
605+
content: {
606+
type: 'tool-result',
607+
toolName: 'file_updates',
608+
toolCallId: 'test-id-3',
609+
output: [
610+
{
611+
type: 'json',
612+
value: [
613+
{
614+
path: 'file3.ts',
615+
content: 'content 3',
616+
},
617+
],
618+
},
619+
],
620+
},
621+
},
622+
]
623+
624+
const result = getPreviouslyReadFiles(messages)
625+
expect(result).toEqual([
626+
{ path: 'file1.ts', content: 'content 1' },
627+
{ path: 'file2.ts', content: 'content 2' },
628+
{ path: 'file3.ts', content: 'content 3' },
629+
])
630+
})
631+
632+
it('handles contentOmittedForLength files by filtering them out', () => {
633+
const messages: Message[] = [
634+
{
635+
role: 'tool',
636+
content: {
637+
type: 'tool-result',
638+
toolName: 'read_files',
639+
toolCallId: 'test-id',
640+
output: [
641+
{
642+
type: 'json',
643+
value: [
644+
{
645+
path: 'small-file.ts',
646+
content: 'small content',
647+
},
648+
{
649+
path: 'large-file.ts',
650+
contentOmittedForLength: true,
651+
},
652+
{
653+
path: 'another-small-file.ts',
654+
content: 'another small content',
655+
},
656+
],
657+
},
658+
],
659+
},
660+
},
661+
]
662+
663+
const result = getPreviouslyReadFiles(messages)
664+
expect(result).toEqual([
665+
{ path: 'small-file.ts', content: 'small content' },
666+
{ path: 'another-small-file.ts', content: 'another small content' },
667+
])
668+
})
669+
670+
it('handles malformed tool message output gracefully', () => {
671+
const mockLoggerError = spyOn(logger, 'error').mockImplementation(() => {})
672+
673+
const messages: Message[] = [
674+
{
675+
role: 'tool',
676+
content: {
677+
type: 'tool-result',
678+
toolName: 'read_files',
679+
toolCallId: 'test-id',
680+
output: null, // Invalid output
681+
} as any,
682+
},
683+
]
684+
685+
const result = getPreviouslyReadFiles(messages)
686+
expect(result).toEqual([])
687+
expect(mockLoggerError).toHaveBeenCalled()
688+
689+
mockLoggerError.mockRestore()
690+
})
691+
692+
it('handles find_files tool messages with error message instead of files', () => {
693+
const messages: Message[] = [
694+
{
695+
role: 'tool',
696+
content: {
697+
type: 'tool-result',
698+
toolName: 'find_files',
699+
toolCallId: 'test-id',
700+
output: [
701+
{
702+
type: 'json',
703+
value: {
704+
message: 'No files found matching the criteria',
705+
},
706+
},
707+
],
708+
},
709+
},
710+
]
711+
712+
const result = getPreviouslyReadFiles(messages)
713+
expect(result).toEqual([])
714+
})
715+
716+
it('ignores non-tool messages', () => {
717+
const messages: Message[] = [
718+
{ role: 'user', content: 'hello' },
719+
{ role: 'assistant', content: 'hi there' },
720+
{ role: 'system', content: 'system message' },
721+
{
722+
role: 'tool',
723+
content: {
724+
type: 'tool-result',
725+
toolName: 'read_files',
726+
toolCallId: 'test-id',
727+
output: [
728+
{
729+
type: 'json',
730+
value: [
731+
{
732+
path: 'test.ts',
733+
content: 'test content',
734+
},
735+
],
736+
},
737+
],
738+
},
739+
},
740+
]
741+
742+
const result = getPreviouslyReadFiles(messages)
743+
expect(result).toEqual([{ path: 'test.ts', content: 'test content' }])
744+
})
745+
746+
it('handles empty file arrays in tool output', () => {
747+
const messages: Message[] = [
748+
{
749+
role: 'tool',
750+
content: {
751+
type: 'tool-result',
752+
toolName: 'read_files',
753+
toolCallId: 'test-id',
754+
output: [
755+
{
756+
type: 'json',
757+
value: [], // Empty array
758+
},
759+
],
760+
},
761+
},
762+
]
763+
764+
const result = getPreviouslyReadFiles(messages)
765+
expect(result).toEqual([])
766+
})
767+
768+
it('handles multiple outputs in single tool message', () => {
769+
const messages: Message[] = [
770+
{
771+
role: 'tool',
772+
content: {
773+
type: 'tool-result',
774+
toolName: 'read_files',
775+
toolCallId: 'test-id',
776+
output: [
777+
{
778+
type: 'json',
779+
value: [
780+
{
781+
path: 'file1.ts',
782+
content: 'content 1',
783+
},
784+
],
785+
},
786+
{
787+
type: 'json',
788+
value: [
789+
{
790+
path: 'file2.ts',
791+
content: 'content 2',
792+
},
793+
],
794+
},
795+
],
796+
},
797+
},
798+
]
799+
800+
const result = getPreviouslyReadFiles(messages)
801+
// Function uses output[0], so only first output is processed
802+
expect(result).toEqual([{ path: 'file1.ts', content: 'content 1' }])
803+
})
804+
})

0 commit comments

Comments
 (0)