1- import { describe , expect , it , beforeEach , afterEach , spyOn , mock } from 'bun:test'
2- import { clearMockedModules , mockModule } from '@codebuff/common/testing/mock-modules'
1+ import {
2+ describe ,
3+ expect ,
4+ it ,
5+ beforeEach ,
6+ afterEach ,
7+ spyOn ,
8+ mock ,
9+ } from 'bun:test'
10+ import {
11+ clearMockedModules ,
12+ mockModule ,
13+ } from '@codebuff/common/testing/mock-modules'
314import { getStubProjectFileContext } from '@codebuff/common/util/file'
415
516import {
@@ -89,47 +100,70 @@ const mockStaticTemplates: Record<string, AgentTemplate> = {
89100 } ,
90101}
91102
92- // Mock validation functions
93- mockModule ( '@codebuff/common/templates/agent-validation' , ( ) => ( {
94- validateAgents : ( agentTemplates : Record < string , DynamicAgentTemplate > = { } ) => {
95- const templates : Record < string , AgentTemplate > = { ...mockStaticTemplates }
96- const validationErrors : any [ ] = [ ]
97-
98- for ( const key in agentTemplates ) {
99- const template = agentTemplates [ key ]
100- if ( template . id === 'invalid-agent' ) {
101- validationErrors . push ( {
102- filePath : key ,
103- message : 'Invalid agent configuration' ,
104- } )
105- } else {
106- templates [ template . id ] = template as AgentTemplate
107- }
108- }
109-
110- return { templates, validationErrors }
111- } ,
112- validateSingleAgent : ( template : DynamicAgentTemplate , options ?: any ) => {
113- if ( template . id ?. includes ( 'invalid-db-agent' ) ) {
114- return {
115- success : false ,
116- error : 'Invalid database agent' ,
117- }
118- }
119- return {
120- success : true ,
121- agentTemplate : template as AgentTemplate ,
122- }
123- } ,
103+ // Mock static agent templates
104+ mockModule ( '@codebuff/backend/templates/agent-list' , ( ) => ( {
105+ agentTemplates : mockStaticTemplates ,
124106} ) )
125107
108+ // We'll spy on the validation functions instead of mocking the entire module
109+
126110describe ( 'Agent Registry' , ( ) => {
127111 let mockFileContext : ProjectFileContext
128112
129- beforeEach ( ( ) => {
113+ beforeEach ( async ( ) => {
130114 // Clear cache before each test
131115 clearDatabaseCache ( )
132116 mockFileContext = getStubProjectFileContext ( )
117+
118+ // Spy on validation functions
119+ const validationModule = await import (
120+ '@codebuff/common/templates/agent-validation'
121+ )
122+ spyOn ( validationModule , 'validateAgents' ) . mockImplementation (
123+ ( agentTemplates : Record < string , DynamicAgentTemplate > = { } ) => {
124+ // Start with static templates (simulating the real behavior)
125+ const templates : Record < string , AgentTemplate > = {
126+ ...mockStaticTemplates ,
127+ }
128+ const validationErrors : any [ ] = [ ]
129+
130+ for ( const key in agentTemplates ) {
131+ const template = agentTemplates [ key ]
132+ if ( template . id === 'invalid-agent' ) {
133+ validationErrors . push ( {
134+ filePath : key ,
135+ message : 'Invalid agent configuration' ,
136+ } )
137+ // Don't add invalid agents to templates (this simulates validation failure)
138+ } else {
139+ templates [ template . id ] = template as AgentTemplate
140+ }
141+ }
142+
143+ return { templates, validationErrors }
144+ } ,
145+ )
146+
147+ spyOn ( validationModule , 'validateSingleAgent' ) . mockImplementation (
148+ ( template : DynamicAgentTemplate , options ?: any ) => {
149+ // Check for malformed agents (missing required fields)
150+ if (
151+ template . id === 'malformed-agent' ||
152+ ! template . systemPrompt ||
153+ ! template . instructionsPrompt ||
154+ ! template . stepPrompt
155+ ) {
156+ return {
157+ success : false ,
158+ error : 'Invalid agent configuration - missing required fields' ,
159+ }
160+ }
161+ return {
162+ success : true ,
163+ agentTemplate : template as AgentTemplate ,
164+ }
165+ } ,
166+ )
133167 } )
134168
135169 afterEach ( ( ) => {
@@ -155,7 +189,7 @@ describe('Agent Registry', () => {
155189 inputSchema : { } ,
156190 } as AgentTemplate ,
157191 }
158-
192+
159193 const result = await getAgentTemplate ( 'my-agent' , localAgents )
160194 expect ( result ) . toBeTruthy ( )
161195 expect ( result ?. id ) . toBe ( 'my-agent' )
@@ -172,7 +206,10 @@ describe('Agent Registry', () => {
172206 } )
173207
174208 it ( 'should return null for invalid agent ID formats' , async ( ) => {
175- const result = await getAgentTemplate ( 'invalid/format/with/too/many/slashes' , { } )
209+ const result = await getAgentTemplate (
210+ 'invalid/format/with/too/many/slashes' ,
211+ { } ,
212+ )
176213 expect ( result ) . toBeNull ( )
177214 } )
178215 } )
@@ -207,13 +244,19 @@ describe('Agent Registry', () => {
207244 }
208245
209246 const dbModule = await import ( '@codebuff/common/db' )
210- spyOn ( dbModule . default , 'select' ) . mockImplementation ( ( ) => ( {
211- from : ( ) => ( {
212- where : ( ) => Promise . resolve ( [ mockAgentData ] ) ,
213- } ) ,
214- } ) as any )
215-
216- const result = await getAgentTemplate ( 'test-publisher/test-agent@1.0.0' , { } )
247+ spyOn ( dbModule . default , 'select' ) . mockImplementation (
248+ ( ) =>
249+ ( {
250+ from : ( ) => ( {
251+ where : ( ) => Promise . resolve ( [ mockAgentData ] ) ,
252+ } ) ,
253+ } ) as any ,
254+ )
255+
256+ const result = await getAgentTemplate (
257+ 'test-publisher/test-agent@1.0.0' ,
258+ { } ,
259+ )
217260 expect ( result ) . toBeTruthy ( )
218261 expect ( result ?. id ) . toBe ( 'test-publisher/test-agent@1.0.0' )
219262 } )
@@ -267,19 +310,28 @@ describe('Agent Registry', () => {
267310 }
268311
269312 const dbModule = await import ( '@codebuff/common/db' )
270- const selectSpy = spyOn ( dbModule . default , 'select' ) . mockImplementation ( ( ) => ( {
271- from : ( ) => ( {
272- where : ( ) => Promise . resolve ( [ mockAgentData ] ) ,
273- } ) ,
274- } ) as any )
313+ const selectSpy = spyOn ( dbModule . default , 'select' ) . mockImplementation (
314+ ( ) =>
315+ ( {
316+ from : ( ) => ( {
317+ where : ( ) => Promise . resolve ( [ mockAgentData ] ) ,
318+ } ) ,
319+ } ) as any ,
320+ )
275321
276322 // First call - should hit database
277- const result1 = await getAgentTemplate ( 'test-publisher/cached-agent@1.0.0' , { } )
323+ const result1 = await getAgentTemplate (
324+ 'test-publisher/cached-agent@1.0.0' ,
325+ { } ,
326+ )
278327 expect ( result1 ) . toBeTruthy ( )
279328 expect ( selectSpy ) . toHaveBeenCalledTimes ( 1 )
280329
281330 // Second call - should use cache
282- const result2 = await getAgentTemplate ( 'test-publisher/cached-agent@1.0.0' , { } )
331+ const result2 = await getAgentTemplate (
332+ 'test-publisher/cached-agent@1.0.0' ,
333+ { } ,
334+ )
283335 expect ( result2 ) . toBeTruthy ( )
284336 expect ( result2 ?. displayName ) . toBe ( 'Cached Agent' )
285337 expect ( selectSpy ) . toHaveBeenCalledTimes ( 1 )
@@ -308,11 +360,13 @@ describe('Agent Registry', () => {
308360 }
309361
310362 const result = assembleLocalAgentTemplates ( fileContext )
311-
363+
312364 // Should have dynamic template
313365 expect ( result . agentTemplates ) . toHaveProperty ( 'custom-agent' )
314- expect ( result . agentTemplates [ 'custom-agent' ] . displayName ) . toBe ( 'Custom Agent' )
315-
366+ expect ( result . agentTemplates [ 'custom-agent' ] . displayName ) . toBe (
367+ 'Custom Agent' ,
368+ )
369+
316370 // Should have no validation errors
317371 expect ( result . validationErrors ) . toHaveLength ( 0 )
318372 } )
@@ -330,10 +384,10 @@ describe('Agent Registry', () => {
330384 }
331385
332386 const result = assembleLocalAgentTemplates ( fileContext )
333-
387+
334388 // Should not have invalid template
335389 expect ( result . agentTemplates ) . not . toHaveProperty ( 'invalid-agent' )
336-
390+
337391 // Should have validation errors
338392 expect ( result . validationErrors . length ) . toBeGreaterThan ( 0 )
339393 } )
@@ -345,10 +399,10 @@ describe('Agent Registry', () => {
345399 }
346400
347401 const result = assembleLocalAgentTemplates ( fileContext )
348-
402+
349403 // Should have no validation errors
350404 expect ( result . validationErrors ) . toHaveLength ( 0 )
351-
405+
352406 // Should return some agent templates (static ones from our mock)
353407 expect ( Object . keys ( result . agentTemplates ) . length ) . toBeGreaterThan ( 0 )
354408 } )
@@ -379,11 +433,14 @@ describe('Agent Registry', () => {
379433 }
380434
381435 const dbModule = await import ( '@codebuff/common/db' )
382- const selectSpy = spyOn ( dbModule . default , 'select' ) . mockImplementation ( ( ) => ( {
383- from : ( ) => ( {
384- where : ( ) => Promise . resolve ( [ mockAgentData ] ) ,
385- } ) ,
386- } ) as any )
436+ const selectSpy = spyOn ( dbModule . default , 'select' ) . mockImplementation (
437+ ( ) =>
438+ ( {
439+ from : ( ) => ( {
440+ where : ( ) => Promise . resolve ( [ mockAgentData ] ) ,
441+ } ) ,
442+ } ) as any ,
443+ )
387444
388445 // First call - should hit database and populate cache
389446 await getAgentTemplate ( 'test-publisher/cache-test-agent@1.0.0' , { } )
@@ -430,16 +487,34 @@ describe('Agent Registry', () => {
430487
431488 it ( 'should handle malformed database response' , async ( ) => {
432489 const dbModule = await import ( '@codebuff/common/db' )
433- spyOn ( dbModule . default , 'select' ) . mockImplementation ( ( ) => ( {
434- from : ( ) => ( {
435- where : ( ) => Promise . resolve ( [ {
436- // Missing required fields
437- id : 'malformed-agent' ,
438- } ] ) ,
439- } ) ,
440- } ) as any )
441-
442- const result = await getAgentTemplate ( 'publisher/malformed-agent@1.0.0' , { } )
490+ spyOn ( dbModule . default , 'select' ) . mockImplementation (
491+ ( ) =>
492+ ( {
493+ from : ( ) => ( {
494+ where : ( ) =>
495+ Promise . resolve ( [
496+ {
497+ id : 'malformed-agent' ,
498+ publisher_id : 'publisher' ,
499+ version : '1.0.0' ,
500+ major : 1 ,
501+ minor : 0 ,
502+ patch : 0 ,
503+ data : {
504+ id : 'malformed-agent' ,
505+ displayName : 'Malformed Agent' ,
506+ // Missing required fields like systemPrompt, instructionsPrompt, stepPrompt
507+ } ,
508+ } ,
509+ ] ) ,
510+ } ) ,
511+ } ) as any ,
512+ )
513+
514+ const result = await getAgentTemplate (
515+ 'publisher/malformed-agent@1.0.0' ,
516+ { } ,
517+ )
443518 expect ( result ) . toBeNull ( )
444519 } )
445520 } )
0 commit comments