@@ -33,17 +33,43 @@ vi.mock('sonner', async () => {
3333 }
3434} )
3535
36+ /**
37+ * Creates reusable mocks for testing the orchestrateRunCustomServer function
38+ */
39+ function createTestMocks ( options ?: {
40+ saveSecretImplementation ?: ( ) => Promise < { key : string } >
41+ getIsServerReadyImplementation ?: ( ) => Promise < boolean >
42+ } ) {
43+ return {
44+ mockSaveSecret : vi
45+ . fn ( )
46+ . mockImplementation (
47+ options ?. saveSecretImplementation ||
48+ ( ( ) => Promise . resolve ( { key : 'test-key' } ) )
49+ ) ,
50+ mockCreateWorkload : vi . fn ( ) ,
51+ mockGetIsServerReady : vi
52+ . fn ( )
53+ . mockImplementation (
54+ options ?. getIsServerReadyImplementation || ( ( ) => Promise . resolve ( true ) )
55+ ) ,
56+ mockQueryClient : {
57+ invalidateQueries : vi . fn ( ) ,
58+ } as unknown as QueryClient ,
59+ }
60+ }
61+
3662beforeEach ( ( ) => {
3763 vi . clearAllMocks ( )
3864} )
3965
4066it ( 'submits without any optional fields' , async ( ) => {
41- const mockSaveSecret = vi . fn ( )
42- const mockCreateWorkload = vi . fn ( )
43- const mockGetIsServerReady = vi . fn ( ) . mockResolvedValue ( true )
44- const mockQueryClient = {
45- invalidateQueries : vi . fn ( ) ,
46- } as unknown as QueryClient
67+ const {
68+ mockSaveSecret ,
69+ mockCreateWorkload ,
70+ mockGetIsServerReady ,
71+ mockQueryClient ,
72+ } = createTestMocks ( )
4773
4874 const DATA : FormSchemaRunMcpCommand = {
4975 image : 'ghcr.io/github/github-mcp-server' ,
@@ -109,12 +135,12 @@ it('handles new secrets properly', async () => {
109135 } )
110136 )
111137
112- const mockSaveSecret = vi . fn ( )
113- const mockCreateWorkload = vi . fn ( )
114- const mockGetIsServerReady = vi . fn ( ) . mockResolvedValue ( true )
115- const mockQueryClient = {
116- invalidateQueries : vi . fn ( ) ,
117- } as unknown as QueryClient
138+ const {
139+ mockSaveSecret ,
140+ mockCreateWorkload ,
141+ mockGetIsServerReady ,
142+ mockQueryClient ,
143+ } = createTestMocks ( )
118144
119145 const DATA = {
120146 image : 'ghcr.io/github/github-mcp-server' ,
@@ -181,12 +207,12 @@ it('handles existing secrets from the store properly', async () => {
181207 } )
182208 )
183209
184- const mockSaveSecret = vi . fn ( )
185- const mockCreateWorkload = vi . fn ( )
186- const mockGetIsServerReady = vi . fn ( ) . mockResolvedValue ( true )
187- const mockQueryClient = {
188- invalidateQueries : vi . fn ( ) ,
189- } as unknown as QueryClient
210+ const {
211+ mockSaveSecret ,
212+ mockCreateWorkload ,
213+ mockGetIsServerReady ,
214+ mockQueryClient ,
215+ } = createTestMocks ( )
190216
191217 const DATA : FormSchemaRunMcpCommand = {
192218 image : 'ghcr.io/github/github-mcp-server' ,
@@ -233,12 +259,12 @@ it('handles naming collisions with secrets from the store', async () => {
233259 } )
234260 )
235261
236- const mockSaveSecret = vi . fn ( )
237- const mockCreateWorkload = vi . fn ( )
238- const mockGetIsServerReady = vi . fn ( ) . mockResolvedValue ( true )
239- const mockQueryClient = {
240- invalidateQueries : vi . fn ( ) ,
241- } as unknown as QueryClient
262+ const {
263+ mockSaveSecret ,
264+ mockCreateWorkload ,
265+ mockGetIsServerReady ,
266+ mockQueryClient ,
267+ } = createTestMocks ( )
242268
243269 const DATA : FormSchemaRunMcpCommand = {
244270 image : 'ghcr.io/github/github-mcp-server' ,
@@ -284,12 +310,14 @@ it('handles naming collisions with secrets from the store', async () => {
284310} )
285311
286312it ( 'handles both new and existing secrets' , async ( ) => {
287- const mockSaveSecret = vi . fn ( ) . mockResolvedValue ( { key : 'new_secret_key' } )
288- const mockCreateWorkload = vi . fn ( )
289- const mockGetIsServerReady = vi . fn ( ) . mockResolvedValue ( true )
290- const mockQueryClient = {
291- invalidateQueries : vi . fn ( ) ,
292- } as unknown as QueryClient
313+ const {
314+ mockSaveSecret,
315+ mockCreateWorkload,
316+ mockGetIsServerReady,
317+ mockQueryClient,
318+ } = createTestMocks ( {
319+ saveSecretImplementation : ( ) => Promise . resolve ( { key : 'new_secret_key' } ) ,
320+ } )
293321
294322 const DATA : FormSchemaRunMcpCommand = {
295323 image : 'ghcr.io/github/github-mcp-server' ,
@@ -338,12 +366,14 @@ it('handles both new and existing secrets', async () => {
338366
339367it ( 'handles error when saving a secret fails' , async ( ) => {
340368 const mockError = new Error ( 'Failed to save secret' )
341- const mockSaveSecret = vi . fn ( ) . mockRejectedValue ( mockError )
342- const mockCreateWorkload = vi . fn ( )
343- const mockGetIsServerReady = vi . fn ( )
344- const mockQueryClient = {
345- invalidateQueries : vi . fn ( ) ,
346- } as unknown as QueryClient
369+ const {
370+ mockSaveSecret,
371+ mockCreateWorkload,
372+ mockGetIsServerReady,
373+ mockQueryClient,
374+ } = createTestMocks ( {
375+ saveSecretImplementation : ( ) => Promise . reject ( mockError ) ,
376+ } )
347377
348378 const DATA : FormSchemaRunMcpCommand = {
349379 image : 'ghcr.io/github/github-mcp-server' ,
@@ -380,12 +410,12 @@ it('handles error when saving a secret fails', async () => {
380410} )
381411
382412it ( 'handles environment variables properly' , async ( ) => {
383- const mockSaveSecret = vi . fn ( )
384- const mockCreateWorkload = vi . fn ( )
385- const mockGetIsServerReady = vi . fn ( ) . mockResolvedValue ( true )
386- const mockQueryClient = {
387- invalidateQueries : vi . fn ( ) ,
388- } as unknown as QueryClient
413+ const {
414+ mockSaveSecret ,
415+ mockCreateWorkload ,
416+ mockGetIsServerReady ,
417+ mockQueryClient ,
418+ } = createTestMocks ( )
389419
390420 const DATA : FormSchemaRunMcpCommand = {
391421 image : 'ghcr.io/github/github-mcp-server' ,
@@ -415,13 +445,63 @@ it('handles environment variables properly', async () => {
415445 } )
416446} )
417447
448+ it ( 'filters out environment variables with empty values' , async ( ) => {
449+ const {
450+ mockSaveSecret,
451+ mockCreateWorkload,
452+ mockGetIsServerReady,
453+ mockQueryClient,
454+ } = createTestMocks ( )
455+
456+ const DATA : FormSchemaRunMcpCommand = {
457+ image : 'ghcr.io/github/github-mcp-server' ,
458+ name : 'env-var-filter-test' ,
459+ transport : 'stdio' ,
460+ type : 'docker_image' ,
461+ envVars : [
462+ { name : 'DEBUG' , value : 'true' } ,
463+ { name : 'PORT' , value : '8080' } ,
464+ { name : 'OPTIONAL_VAR' , value : '' } , // Empty value should be omitted
465+ { name : 'ANOTHER_OPTIONAL' , value : ' ' } , // Whitespace-only should be omitted
466+ { name : 'REQUIRED_VAR' , value : 'some-value' } ,
467+ ] ,
468+ secrets : [ ] ,
469+ cmd_arguments : '' ,
470+ }
471+
472+ await orchestrateRunCustomServer ( {
473+ createWorkload : mockCreateWorkload ,
474+ data : DATA ,
475+ getIsServerReady : mockGetIsServerReady ,
476+ queryClient : mockQueryClient ,
477+ saveSecret : mockSaveSecret ,
478+ } )
479+
480+ expect ( mockCreateWorkload ) . toHaveBeenCalledWith ( {
481+ body : expect . objectContaining ( {
482+ env_vars : [ 'DEBUG=true' , 'PORT=8080' , 'REQUIRED_VAR=some-value' ] ,
483+ } ) ,
484+ } )
485+ // OPTIONAL_VAR and ANOTHER_OPTIONAL should be omitted
486+ expect ( mockCreateWorkload ) . toHaveBeenCalledWith (
487+ expect . not . objectContaining ( {
488+ body : expect . objectContaining ( {
489+ env_vars : expect . arrayContaining ( [
490+ 'OPTIONAL_VAR=' ,
491+ 'ANOTHER_OPTIONAL= ' ,
492+ ] ) ,
493+ } ) ,
494+ } )
495+ )
496+ } )
497+
418498it ( 'handles command arguments properly' , async ( ) => {
419- const mockSaveSecret = vi . fn ( )
420- const mockCreateWorkload = vi . fn ( )
421- const mockGetIsServerReady = vi . fn ( ) . mockResolvedValue ( true )
422- const mockQueryClient = {
423- invalidateQueries : vi . fn ( ) ,
424- } as unknown as QueryClient
499+ const {
500+ mockSaveSecret ,
501+ mockCreateWorkload ,
502+ mockGetIsServerReady ,
503+ mockQueryClient ,
504+ } = createTestMocks ( )
425505
426506 const DATA : FormSchemaRunMcpCommand = {
427507 image : 'ghcr.io/github/github-mcp-server' ,
@@ -449,12 +529,14 @@ it('handles command arguments properly', async () => {
449529} )
450530
451531it ( 'shows warning toast when server is not ready' , async ( ) => {
452- const mockSaveSecret = vi . fn ( )
453- const mockCreateWorkload = vi . fn ( )
454- const mockGetIsServerReady = vi . fn ( ) . mockResolvedValue ( false )
455- const mockQueryClient = {
456- invalidateQueries : vi . fn ( ) ,
457- } as unknown as QueryClient
532+ const {
533+ mockSaveSecret,
534+ mockCreateWorkload,
535+ mockGetIsServerReady,
536+ mockQueryClient,
537+ } = createTestMocks ( {
538+ getIsServerReadyImplementation : ( ) => Promise . resolve ( false ) ,
539+ } )
458540
459541 const DATA : FormSchemaRunMcpCommand = {
460542 image : 'ghcr.io/github/github-mcp-server' ,
@@ -486,12 +568,12 @@ it('shows warning toast when server is not ready', async () => {
486568} )
487569
488570it ( 'handles package manager type properly' , async ( ) => {
489- const mockSaveSecret = vi . fn ( )
490- const mockCreateWorkload = vi . fn ( )
491- const mockGetIsServerReady = vi . fn ( ) . mockResolvedValue ( true )
492- const mockQueryClient = {
493- invalidateQueries : vi . fn ( ) ,
494- } as unknown as QueryClient
571+ const {
572+ mockSaveSecret ,
573+ mockCreateWorkload ,
574+ mockGetIsServerReady ,
575+ mockQueryClient ,
576+ } = createTestMocks ( )
495577
496578 const DATA : FormSchemaRunMcpCommand = {
497579 package_name : 'my-package' ,
0 commit comments