@@ -314,6 +314,218 @@ func TestCreateCommand_DirectoryOverwrite(t *testing.T) {
314314 assert .FileExists (t , filepath .Join (appPath , "index.ts" ), "new index.ts should exist" )
315315}
316316
317+ // TestCreateCommand_InvalidLanguageTemplateCombinations tests that invalid
318+ // language/template combinations fail with appropriate error messages
319+ func TestCreateCommand_InvalidLanguageTemplateCombinations (t * testing.T ) {
320+ tests := []struct {
321+ name string
322+ language string
323+ template string
324+ errContains string
325+ }{
326+ {
327+ name : "browser-use not available for typescript" ,
328+ language : create .LanguageTypeScript ,
329+ template : create .TemplateBrowserUse ,
330+ errContains : "template not found: typescript/browser-use" ,
331+ },
332+ {
333+ name : "stagehand not available for python" ,
334+ language : create .LanguagePython ,
335+ template : create .TemplateStagehand ,
336+ errContains : "template not found: python/stagehand" ,
337+ },
338+ {
339+ name : "magnitude not available for python" ,
340+ language : create .LanguagePython ,
341+ template : create .TemplateMagnitude ,
342+ errContains : "template not found: python/magnitude" ,
343+ },
344+ {
345+ name : "gemini-cua not available for python" ,
346+ language : create .LanguagePython ,
347+ template : create .TemplateGeminiCUA ,
348+ errContains : "template not found: python/gemini-cua" ,
349+ },
350+ {
351+ name : "invalid language" ,
352+ language : "ruby" ,
353+ template : create .TemplateSampleApp ,
354+ errContains : "template not found: ruby/sample-app" ,
355+ },
356+ {
357+ name : "invalid template" ,
358+ language : create .LanguageTypeScript ,
359+ template : "nonexistent-template" ,
360+ errContains : "template not found: typescript/nonexistent-template" ,
361+ },
362+ }
363+
364+ for _ , tt := range tests {
365+ t .Run (tt .name , func (t * testing.T ) {
366+ tmpDir := t .TempDir ()
367+
368+ orgDir , err := os .Getwd ()
369+ require .NoError (t , err )
370+
371+ err = os .Chdir (tmpDir )
372+ require .NoError (t , err )
373+
374+ t .Cleanup (func () {
375+ os .Chdir (orgDir )
376+ })
377+
378+ c := CreateCmd {}
379+ err = c .Create (context .Background (), CreateInput {
380+ Name : "test-app" ,
381+ Language : tt .language ,
382+ Template : tt .template ,
383+ })
384+
385+ require .Error (t , err , "should fail with invalid language/template combination" )
386+ assert .Contains (t , err .Error (), tt .errContains , "error message should contain expected text" )
387+ })
388+ }
389+ }
390+
391+ // TestCreateCommand_ValidateAllTemplateCombinations validates that only valid
392+ // language/template combinations are defined in the Templates map
393+ func TestCreateCommand_ValidateAllTemplateCombinations (t * testing.T ) {
394+ // This test ensures data consistency between Templates and actual template availability
395+ for templateKey , templateInfo := range create .Templates {
396+ for _ , lang := range templateInfo .Languages {
397+ t .Run (lang + "/" + templateKey , func (t * testing.T ) {
398+ tmpDir := t .TempDir ()
399+ appPath := filepath .Join (tmpDir , "test-app" )
400+
401+ err := os .MkdirAll (appPath , DIR_PERM )
402+ require .NoError (t , err )
403+
404+ // This should succeed for all combinations defined in Templates
405+ err = create .CopyTemplateFiles (appPath , lang , templateKey )
406+ require .NoError (t , err , "Template %s should be available for language %s as defined in Templates map" , templateKey , lang )
407+ })
408+ }
409+ }
410+ }
411+
412+ // TestCreateCommand_InvalidLanguageShorthand tests that invalid language shorthands
413+ // are handled appropriately
414+ func TestCreateCommand_InvalidLanguageShorthand (t * testing.T ) {
415+ tests := []struct {
416+ name string
417+ languageInput string
418+ expectedNormalized string
419+ }{
420+ {
421+ name : "ts shorthand normalizes to typescript" ,
422+ languageInput : "ts" ,
423+ expectedNormalized : create .LanguageTypeScript ,
424+ },
425+ {
426+ name : "py shorthand normalizes to python" ,
427+ languageInput : "py" ,
428+ expectedNormalized : create .LanguagePython ,
429+ },
430+ {
431+ name : "typescript remains typescript" ,
432+ languageInput : "typescript" ,
433+ expectedNormalized : create .LanguageTypeScript ,
434+ },
435+ {
436+ name : "python remains python" ,
437+ languageInput : "python" ,
438+ expectedNormalized : create .LanguagePython ,
439+ },
440+ {
441+ name : "invalid shorthand remains unchanged" ,
442+ languageInput : "js" ,
443+ expectedNormalized : "js" ,
444+ },
445+ }
446+
447+ for _ , tt := range tests {
448+ t .Run (tt .name , func (t * testing.T ) {
449+ normalized := create .NormalizeLanguage (tt .languageInput )
450+ assert .Equal (t , tt .expectedNormalized , normalized )
451+ })
452+ }
453+ }
454+
455+ // TestCreateCommand_TemplateNotAvailableForLanguage tests specific cases where
456+ // templates are not available for certain languages
457+ func TestCreateCommand_TemplateNotAvailableForLanguage (t * testing.T ) {
458+ // Map of templates to languages they should NOT be available for
459+ unavailableCombinations := map [string ][]string {
460+ create .TemplateBrowserUse : {create .LanguageTypeScript },
461+ create .TemplateStagehand : {create .LanguagePython },
462+ create .TemplateMagnitude : {create .LanguagePython },
463+ create .TemplateGeminiCUA : {create .LanguagePython },
464+ }
465+
466+ for template , unavailableLanguages := range unavailableCombinations {
467+ for _ , lang := range unavailableLanguages {
468+ t .Run (template + "/" + lang , func (t * testing.T ) {
469+ // Verify the template info doesn't list this language
470+ templateInfo , exists := create .Templates [template ]
471+ require .True (t , exists , "Template %s should exist in Templates map" , template )
472+
473+ assert .NotContains (t , templateInfo .Languages , lang ,
474+ "Template %s should not list %s as a supported language" , template , lang )
475+
476+ // Verify copying fails
477+ tmpDir := t .TempDir ()
478+ appPath := filepath .Join (tmpDir , "test-app" )
479+ err := os .MkdirAll (appPath , DIR_PERM )
480+ require .NoError (t , err )
481+
482+ err = create .CopyTemplateFiles (appPath , lang , template )
483+ require .Error (t , err , "Should fail to copy %s template for %s" , template , lang )
484+ })
485+ }
486+ }
487+ }
488+
489+ // TestCreateCommand_AllTemplatesHaveDeployCommands ensures that all templates
490+ // have corresponding deploy commands defined
491+ func TestCreateCommand_AllTemplatesHaveDeployCommands (t * testing.T ) {
492+ for templateKey , templateInfo := range create .Templates {
493+ for _ , lang := range templateInfo .Languages {
494+ t .Run (lang + "/" + templateKey , func (t * testing.T ) {
495+ deployCmd := create .GetDeployCommand (lang , templateKey )
496+ assert .NotEmpty (t , deployCmd , "Deploy command should exist for %s/%s" , lang , templateKey )
497+
498+ // Verify deploy command starts with "kernel deploy"
499+ assert .Contains (t , deployCmd , "kernel deploy" , "Deploy command should start with 'kernel deploy'" )
500+
501+ // Verify it contains the entry point
502+ switch lang {
503+ case create .LanguageTypeScript :
504+ assert .Contains (t , deployCmd , "index.ts" , "TypeScript deploy command should contain index.ts" )
505+ case create .LanguagePython :
506+ assert .Contains (t , deployCmd , "main.py" , "Python deploy command should contain main.py" )
507+ }
508+ })
509+ }
510+ }
511+ }
512+
513+ // TestCreateCommand_AllTemplatesHaveInvokeSamples ensures that all templates
514+ // have corresponding invoke samples defined
515+ func TestCreateCommand_AllTemplatesHaveInvokeSamples (t * testing.T ) {
516+ for templateKey , templateInfo := range create .Templates {
517+ for _ , lang := range templateInfo .Languages {
518+ t .Run (lang + "/" + templateKey , func (t * testing.T ) {
519+ invokeCmd := create .GetInvokeSample (lang , templateKey )
520+ assert .NotEmpty (t , invokeCmd , "Invoke sample should exist for %s/%s" , lang , templateKey )
521+
522+ // Verify invoke command starts with "kernel invoke"
523+ assert .Contains (t , invokeCmd , "kernel invoke" , "Invoke command should start with 'kernel invoke'" )
524+ })
525+ }
526+ }
527+ }
528+
317529func getTemplateInfo () []struct {
318530 name string
319531 language string
0 commit comments