@@ -412,6 +412,218 @@ func TestCreateCommand_DirectoryOverwrite(t *testing.T) {
412412 assert .FileExists (t , filepath .Join (appPath , "index.ts" ), "new index.ts should exist" )
413413}
414414
415+ // TestCreateCommand_InvalidLanguageTemplateCombinations tests that invalid
416+ // language/template combinations fail with appropriate error messages
417+ func TestCreateCommand_InvalidLanguageTemplateCombinations (t * testing.T ) {
418+ tests := []struct {
419+ name string
420+ language string
421+ template string
422+ errContains string
423+ }{
424+ {
425+ name : "browser-use not available for typescript" ,
426+ language : create .LanguageTypeScript ,
427+ template : create .TemplateBrowserUse ,
428+ errContains : "template not found: typescript/browser-use" ,
429+ },
430+ {
431+ name : "stagehand not available for python" ,
432+ language : create .LanguagePython ,
433+ template : create .TemplateStagehand ,
434+ errContains : "template not found: python/stagehand" ,
435+ },
436+ {
437+ name : "magnitude not available for python" ,
438+ language : create .LanguagePython ,
439+ template : create .TemplateMagnitude ,
440+ errContains : "template not found: python/magnitude" ,
441+ },
442+ {
443+ name : "gemini-cua not available for python" ,
444+ language : create .LanguagePython ,
445+ template : create .TemplateGeminiCUA ,
446+ errContains : "template not found: python/gemini-cua" ,
447+ },
448+ {
449+ name : "invalid language" ,
450+ language : "ruby" ,
451+ template : create .TemplateSampleApp ,
452+ errContains : "template not found: ruby/sample-app" ,
453+ },
454+ {
455+ name : "invalid template" ,
456+ language : create .LanguageTypeScript ,
457+ template : "nonexistent-template" ,
458+ errContains : "template not found: typescript/nonexistent-template" ,
459+ },
460+ }
461+
462+ for _ , tt := range tests {
463+ t .Run (tt .name , func (t * testing.T ) {
464+ tmpDir := t .TempDir ()
465+
466+ orgDir , err := os .Getwd ()
467+ require .NoError (t , err )
468+
469+ err = os .Chdir (tmpDir )
470+ require .NoError (t , err )
471+
472+ t .Cleanup (func () {
473+ os .Chdir (orgDir )
474+ })
475+
476+ c := CreateCmd {}
477+ err = c .Create (context .Background (), CreateInput {
478+ Name : "test-app" ,
479+ Language : tt .language ,
480+ Template : tt .template ,
481+ })
482+
483+ require .Error (t , err , "should fail with invalid language/template combination" )
484+ assert .Contains (t , err .Error (), tt .errContains , "error message should contain expected text" )
485+ })
486+ }
487+ }
488+
489+ // TestCreateCommand_ValidateAllTemplateCombinations validates that only valid
490+ // language/template combinations are defined in the Templates map
491+ func TestCreateCommand_ValidateAllTemplateCombinations (t * testing.T ) {
492+ // This test ensures data consistency between Templates and actual template availability
493+ for templateKey , templateInfo := range create .Templates {
494+ for _ , lang := range templateInfo .Languages {
495+ t .Run (lang + "/" + templateKey , func (t * testing.T ) {
496+ tmpDir := t .TempDir ()
497+ appPath := filepath .Join (tmpDir , "test-app" )
498+
499+ err := os .MkdirAll (appPath , DIR_PERM )
500+ require .NoError (t , err )
501+
502+ // This should succeed for all combinations defined in Templates
503+ err = create .CopyTemplateFiles (appPath , lang , templateKey )
504+ require .NoError (t , err , "Template %s should be available for language %s as defined in Templates map" , templateKey , lang )
505+ })
506+ }
507+ }
508+ }
509+
510+ // TestCreateCommand_InvalidLanguageShorthand tests that invalid language shorthands
511+ // are handled appropriately
512+ func TestCreateCommand_InvalidLanguageShorthand (t * testing.T ) {
513+ tests := []struct {
514+ name string
515+ languageInput string
516+ expectedNormalized string
517+ }{
518+ {
519+ name : "ts shorthand normalizes to typescript" ,
520+ languageInput : "ts" ,
521+ expectedNormalized : create .LanguageTypeScript ,
522+ },
523+ {
524+ name : "py shorthand normalizes to python" ,
525+ languageInput : "py" ,
526+ expectedNormalized : create .LanguagePython ,
527+ },
528+ {
529+ name : "typescript remains typescript" ,
530+ languageInput : "typescript" ,
531+ expectedNormalized : create .LanguageTypeScript ,
532+ },
533+ {
534+ name : "python remains python" ,
535+ languageInput : "python" ,
536+ expectedNormalized : create .LanguagePython ,
537+ },
538+ {
539+ name : "invalid shorthand remains unchanged" ,
540+ languageInput : "js" ,
541+ expectedNormalized : "js" ,
542+ },
543+ }
544+
545+ for _ , tt := range tests {
546+ t .Run (tt .name , func (t * testing.T ) {
547+ normalized := create .NormalizeLanguage (tt .languageInput )
548+ assert .Equal (t , tt .expectedNormalized , normalized )
549+ })
550+ }
551+ }
552+
553+ // TestCreateCommand_TemplateNotAvailableForLanguage tests specific cases where
554+ // templates are not available for certain languages
555+ func TestCreateCommand_TemplateNotAvailableForLanguage (t * testing.T ) {
556+ // Map of templates to languages they should NOT be available for
557+ unavailableCombinations := map [string ][]string {
558+ create .TemplateBrowserUse : {create .LanguageTypeScript },
559+ create .TemplateStagehand : {create .LanguagePython },
560+ create .TemplateMagnitude : {create .LanguagePython },
561+ create .TemplateGeminiCUA : {create .LanguagePython },
562+ }
563+
564+ for template , unavailableLanguages := range unavailableCombinations {
565+ for _ , lang := range unavailableLanguages {
566+ t .Run (template + "/" + lang , func (t * testing.T ) {
567+ // Verify the template info doesn't list this language
568+ templateInfo , exists := create .Templates [template ]
569+ require .True (t , exists , "Template %s should exist in Templates map" , template )
570+
571+ assert .NotContains (t , templateInfo .Languages , lang ,
572+ "Template %s should not list %s as a supported language" , template , lang )
573+
574+ // Verify copying fails
575+ tmpDir := t .TempDir ()
576+ appPath := filepath .Join (tmpDir , "test-app" )
577+ err := os .MkdirAll (appPath , DIR_PERM )
578+ require .NoError (t , err )
579+
580+ err = create .CopyTemplateFiles (appPath , lang , template )
581+ require .Error (t , err , "Should fail to copy %s template for %s" , template , lang )
582+ })
583+ }
584+ }
585+ }
586+
587+ // TestCreateCommand_AllTemplatesHaveDeployCommands ensures that all templates
588+ // have corresponding deploy commands defined
589+ func TestCreateCommand_AllTemplatesHaveDeployCommands (t * testing.T ) {
590+ for templateKey , templateInfo := range create .Templates {
591+ for _ , lang := range templateInfo .Languages {
592+ t .Run (lang + "/" + templateKey , func (t * testing.T ) {
593+ deployCmd := create .GetDeployCommand (lang , templateKey )
594+ assert .NotEmpty (t , deployCmd , "Deploy command should exist for %s/%s" , lang , templateKey )
595+
596+ // Verify deploy command starts with "kernel deploy"
597+ assert .Contains (t , deployCmd , "kernel deploy" , "Deploy command should start with 'kernel deploy'" )
598+
599+ // Verify it contains the entry point
600+ switch lang {
601+ case create .LanguageTypeScript :
602+ assert .Contains (t , deployCmd , "index.ts" , "TypeScript deploy command should contain index.ts" )
603+ case create .LanguagePython :
604+ assert .Contains (t , deployCmd , "main.py" , "Python deploy command should contain main.py" )
605+ }
606+ })
607+ }
608+ }
609+ }
610+
611+ // TestCreateCommand_AllTemplatesHaveInvokeSamples ensures that all templates
612+ // have corresponding invoke samples defined
613+ func TestCreateCommand_AllTemplatesHaveInvokeSamples (t * testing.T ) {
614+ for templateKey , templateInfo := range create .Templates {
615+ for _ , lang := range templateInfo .Languages {
616+ t .Run (lang + "/" + templateKey , func (t * testing.T ) {
617+ invokeCmd := create .GetInvokeSample (lang , templateKey )
618+ assert .NotEmpty (t , invokeCmd , "Invoke sample should exist for %s/%s" , lang , templateKey )
619+
620+ // Verify invoke command starts with "kernel invoke"
621+ assert .Contains (t , invokeCmd , "kernel invoke" , "Invoke command should start with 'kernel invoke'" )
622+ })
623+ }
624+ }
625+ }
626+
415627func getTemplateInfo () []struct {
416628 name string
417629 language string
0 commit comments