@@ -396,6 +396,222 @@ func TestParse(t *testing.T) {
396396 err := Parse (cmd , nil )
397397 require .NoError (t , err )
398398 })
399+ t .Run ("underscore in command name" , func (t * testing.T ) {
400+ t .Parallel ()
401+ cmd := & Command {
402+ Name : "root" ,
403+ Exec : func (ctx context.Context , s * State ) error { return nil },
404+ SubCommands : []* Command {
405+ {Name : "sub_command" , Exec : func (ctx context.Context , s * State ) error { return nil }},
406+ },
407+ }
408+ err := Parse (cmd , []string {"sub_command" })
409+ require .NoError (t , err )
410+ })
411+ t .Run ("command name starting with number" , func (t * testing.T ) {
412+ t .Parallel ()
413+ cmd := & Command {
414+ Name : "root" ,
415+ SubCommands : []* Command {
416+ {Name : "1command" },
417+ },
418+ }
419+ err := Parse (cmd , nil )
420+ require .Error (t , err )
421+ require .ErrorContains (t , err , `name must start with a letter` )
422+ })
423+ t .Run ("command name with special characters" , func (t * testing.T ) {
424+ t .Parallel ()
425+ cmd := & Command {
426+ Name : "root" ,
427+ SubCommands : []* Command {
428+ {Name : "sub@command" },
429+ },
430+ }
431+ err := Parse (cmd , nil )
432+ require .Error (t , err )
433+ require .ErrorContains (t , err , `name must start with a letter and contain only letters, numbers, dashes (-) or underscores (_)` )
434+ })
435+ t .Run ("very long command name" , func (t * testing.T ) {
436+ t .Parallel ()
437+ longName := "very-long-command-name-that-exceeds-normal-expectations-and-continues-for-a-while-to-test-edge-cases"
438+ cmd := & Command {
439+ Name : "root" ,
440+ Exec : func (ctx context.Context , s * State ) error { return nil },
441+ SubCommands : []* Command {
442+ {Name : longName , Exec : func (ctx context.Context , s * State ) error { return nil }},
443+ },
444+ }
445+ err := Parse (cmd , []string {longName })
446+ require .NoError (t , err )
447+ })
448+ t .Run ("empty args list" , func (t * testing.T ) {
449+ t .Parallel ()
450+ cmd := & Command {
451+ Name : "root" ,
452+ Exec : func (ctx context.Context , s * State ) error { return nil },
453+ }
454+ err := Parse (cmd , []string {})
455+ require .NoError (t , err )
456+ require .Len (t , cmd .state .Args , 0 )
457+ })
458+ t .Run ("args with whitespace only" , func (t * testing.T ) {
459+ t .Parallel ()
460+ cmd := & Command {
461+ Name : "root" ,
462+ Exec : func (ctx context.Context , s * State ) error { return nil },
463+ }
464+ err := Parse (cmd , []string {" " , "\t " , "" })
465+ require .NoError (t , err )
466+ require .Equal (t , []string {" " , "\t " , "" }, cmd .state .Args )
467+ })
468+ t .Run ("flag with empty value" , func (t * testing.T ) {
469+ t .Parallel ()
470+ cmd := & Command {
471+ Name : "root" ,
472+ Flags : FlagsFunc (func (fset * flag.FlagSet ) {
473+ fset .String ("config" , "" , "config file" )
474+ }),
475+ Exec : func (ctx context.Context , s * State ) error { return nil },
476+ }
477+ err := Parse (cmd , []string {"--config=" })
478+ require .NoError (t , err )
479+ require .Equal (t , "" , GetFlag [string ](cmd .state , "config" ))
480+ })
481+ t .Run ("boolean flag with explicit false" , func (t * testing.T ) {
482+ t .Parallel ()
483+ cmd := & Command {
484+ Name : "root" ,
485+ Flags : FlagsFunc (func (fset * flag.FlagSet ) {
486+ fset .Bool ("verbose" , true , "verbose mode" )
487+ }),
488+ Exec : func (ctx context.Context , s * State ) error { return nil },
489+ }
490+ err := Parse (cmd , []string {"--verbose=false" })
491+ require .NoError (t , err )
492+ require .False (t , GetFlag [bool ](cmd .state , "verbose" ))
493+ })
494+ t .Run ("deeply nested command hierarchy" , func (t * testing.T ) {
495+ t .Parallel ()
496+ level5 := & Command {
497+ Name : "level5" ,
498+ Exec : func (ctx context.Context , s * State ) error { return nil },
499+ }
500+ level4 := & Command {
501+ Name : "level4" ,
502+ SubCommands : []* Command {level5 },
503+ }
504+ level3 := & Command {
505+ Name : "level3" ,
506+ SubCommands : []* Command {level4 },
507+ }
508+ level2 := & Command {
509+ Name : "level2" ,
510+ SubCommands : []* Command {level3 },
511+ }
512+ level1 := & Command {
513+ Name : "level1" ,
514+ SubCommands : []* Command {level2 },
515+ }
516+ root := & Command {
517+ Name : "root" ,
518+ SubCommands : []* Command {level1 },
519+ }
520+ err := Parse (root , []string {"level1" , "level2" , "level3" , "level4" , "level5" })
521+ require .NoError (t , err )
522+ terminal := root .terminal ()
523+ require .Equal (t , level5 , terminal )
524+ })
525+ t .Run ("many subcommands" , func (t * testing.T ) {
526+ t .Parallel ()
527+ var subcommands []* Command
528+ for i := 0 ; i < 25 ; i ++ {
529+ subcommands = append (subcommands , & Command {
530+ Name : "cmd" + string (rune ('a' + i % 26 )),
531+ Exec : func (ctx context.Context , s * State ) error { return nil },
532+ })
533+ }
534+ root := & Command {
535+ Name : "root" ,
536+ SubCommands : subcommands ,
537+ }
538+ err := Parse (root , []string {"cmda" })
539+ require .NoError (t , err )
540+ terminal := root .terminal ()
541+ require .Equal (t , "cmda" , terminal .Name )
542+ })
543+ t .Run ("duplicate subcommand names" , func (t * testing.T ) {
544+ t .Parallel ()
545+ cmd := & Command {
546+ Name : "root" ,
547+ SubCommands : []* Command {
548+ {Name : "duplicate" , Exec : func (ctx context.Context , s * State ) error { return nil }},
549+ {Name : "duplicate" , Exec : func (ctx context.Context , s * State ) error { return nil }},
550+ },
551+ }
552+ // This library may not check for duplicate names, so just verify it works
553+ err := Parse (cmd , []string {"duplicate" })
554+ require .NoError (t , err )
555+ // Just ensure it doesn't crash and can parse the first match
556+ })
557+ t .Run ("flag metadata for non-existent flag" , func (t * testing.T ) {
558+ t .Parallel ()
559+ cmd := & Command {
560+ Name : "root" ,
561+ Flags : FlagsFunc (func (fset * flag.FlagSet ) {
562+ fset .String ("existing" , "" , "existing flag" )
563+ }),
564+ FlagsMetadata : []FlagMetadata {
565+ {Name : "existing" , Required : true },
566+ {Name : "nonexistent" , Required : true },
567+ },
568+ Exec : func (ctx context.Context , s * State ) error { return nil },
569+ }
570+ err := Parse (cmd , []string {"--existing=value" })
571+ require .Error (t , err )
572+ require .ErrorContains (t , err , "required flag -nonexistent not found in flag set" )
573+ })
574+ t .Run ("args with special characters" , func (t * testing.T ) {
575+ t .Parallel ()
576+ cmd := & Command {
577+ Name : "root" ,
578+ Exec : func (ctx context.Context , s * State ) error { return nil },
579+ }
580+ specialArgs := []string {"file with spaces.txt" , "file@symbol.txt" , "file\" quote.txt" , "file'apostrophe.txt" }
581+ err := Parse (cmd , specialArgs )
582+ require .NoError (t , err )
583+ require .Equal (t , specialArgs , cmd .state .Args )
584+ })
585+ t .Run ("very long argument list" , func (t * testing.T ) {
586+ t .Parallel ()
587+ cmd := & Command {
588+ Name : "root" ,
589+ Exec : func (ctx context.Context , s * State ) error { return nil },
590+ }
591+ var longArgList []string
592+ for i := 0 ; i < 100 ; i ++ {
593+ longArgList = append (longArgList , "arg" + string (rune ('0' + i % 10 )))
594+ }
595+ err := Parse (cmd , longArgList )
596+ require .NoError (t , err )
597+ require .Equal (t , longArgList , cmd .state .Args )
598+ })
599+ t .Run ("mixed flags and args in various orders" , func (t * testing.T ) {
600+ t .Parallel ()
601+ cmd := & Command {
602+ Name : "root" ,
603+ Flags : FlagsFunc (func (fset * flag.FlagSet ) {
604+ fset .String ("flag1" , "" , "first flag" )
605+ fset .String ("flag2" , "" , "second flag" )
606+ }),
607+ Exec : func (ctx context.Context , s * State ) error { return nil },
608+ }
609+ err := Parse (cmd , []string {"arg1" , "--flag1=val1" , "arg2" , "--flag2" , "val2" , "arg3" })
610+ require .NoError (t , err )
611+ require .Equal (t , "val1" , GetFlag [string ](cmd .state , "flag1" ))
612+ require .Equal (t , "val2" , GetFlag [string ](cmd .state , "flag2" ))
613+ require .Equal (t , []string {"arg1" , "arg2" , "arg3" }, cmd .state .Args )
614+ })
399615}
400616
401617func getCommand (t * testing.T , c * Command ) * Command {
0 commit comments