@@ -3902,3 +3902,377 @@ func TestLoadStateFunction(t *testing.T) {
39023902 }
39033903 })
39043904}
3905+
3906+ // =============================================================================
3907+ // Tests for PR #338: Token-aware status display and help improvements
3908+ // =============================================================================
3909+
3910+ func TestHibernateCommandIfExists (t * testing.T ) {
3911+ cli , _ , cleanup := setupTestEnvironment (t )
3912+ defer cleanup ()
3913+
3914+ repoCmd , ok := cli .rootCmd .Subcommands ["repo" ]
3915+ if ! ok {
3916+ t .Fatal ("Expected 'repo' command to exist" )
3917+ }
3918+
3919+ hibernateCmd , ok := repoCmd .Subcommands ["hibernate" ]
3920+ if ! ok {
3921+ t .Skip ("hibernate subcommand not yet present" )
3922+ }
3923+
3924+ if hibernateCmd .Description == "" {
3925+ t .Error ("hibernate command should have a description" )
3926+ }
3927+ if hibernateCmd .Usage == "" {
3928+ t .Error ("hibernate command should have usage text" )
3929+ }
3930+ }
3931+
3932+ func TestHibernateHelpRendering (t * testing.T ) {
3933+ cli , _ , cleanup := setupTestEnvironment (t )
3934+ defer cleanup ()
3935+
3936+ repoCmd , ok := cli .rootCmd .Subcommands ["repo" ]
3937+ if ! ok {
3938+ t .Fatal ("Expected 'repo' command to exist" )
3939+ }
3940+
3941+ if _ , ok := repoCmd .Subcommands ["hibernate" ]; ! ok {
3942+ t .Skip ("hibernate subcommand not yet present" )
3943+ }
3944+
3945+ err := cli .Execute ([]string {"repo" , "hibernate" , "--help" })
3946+ if err != nil {
3947+ t .Errorf ("repo hibernate --help should not error: %v" , err )
3948+ }
3949+ }
3950+
3951+ func TestShowCommandHelpBasic (t * testing.T ) {
3952+ cli , _ , cleanup := setupTestEnvironment (t )
3953+ defer cleanup ()
3954+
3955+ cmd := & Command {
3956+ Name : "test-help" ,
3957+ Description : "Test help rendering" ,
3958+ Usage : "test-help [options]" ,
3959+ }
3960+
3961+ err := cli .showCommandHelp (cmd )
3962+ if err != nil {
3963+ t .Errorf ("showCommandHelp() returned error: %v" , err )
3964+ }
3965+ }
3966+
3967+ func TestHandleListReposRichResponseShape (t * testing.T ) {
3968+ _ , d , cleanup := setupTestEnvironment (t )
3969+ defer cleanup ()
3970+
3971+ repo := & state.Repository {
3972+ GithubURL : "https://github.com/test/repo" ,
3973+ TmuxSession : "mc-test-repo" ,
3974+ Agents : make (map [string ]state.Agent ),
3975+ }
3976+ if err := d .GetState ().AddRepo ("test-repo" , repo ); err != nil {
3977+ t .Fatalf ("Failed to add repo: %v" , err )
3978+ }
3979+ d .GetState ().AddAgent ("test-repo" , "supervisor" , state.Agent {
3980+ Type : state .AgentTypeSupervisor ,
3981+ TmuxWindow : "supervisor" ,
3982+ })
3983+ d .GetState ().AddAgent ("test-repo" , "merge-queue" , state.Agent {
3984+ Type : state .AgentTypeMergeQueue ,
3985+ TmuxWindow : "merge-queue" ,
3986+ })
3987+ d .GetState ().AddAgent ("test-repo" , "happy-fox" , state.Agent {
3988+ Type : state .AgentTypeWorker ,
3989+ TmuxWindow : "happy-fox" ,
3990+ Task : "implement feature X" ,
3991+ })
3992+ d .GetState ().AddAgent ("test-repo" , "clever-bear" , state.Agent {
3993+ Type : state .AgentTypeWorker ,
3994+ TmuxWindow : "clever-bear" ,
3995+ Task : "fix bug Y" ,
3996+ })
3997+
3998+ client := socket .NewClient (d .GetPaths ().DaemonSock )
3999+ resp , err := client .Send (socket.Request {
4000+ Command : "list_repos" ,
4001+ Args : map [string ]interface {}{"rich" : true },
4002+ })
4003+ if err != nil {
4004+ t .Fatalf ("Failed to send request: %v" , err )
4005+ }
4006+ if ! resp .Success {
4007+ t .Fatalf ("list_repos failed: %s" , resp .Error )
4008+ }
4009+
4010+ repos , ok := resp .Data .([]interface {})
4011+ if ! ok {
4012+ t .Fatalf ("Expected repos array, got %T" , resp .Data )
4013+ }
4014+ if len (repos ) != 1 {
4015+ t .Fatalf ("Expected 1 repo, got %d" , len (repos ))
4016+ }
4017+
4018+ repoMap , ok := repos [0 ].(map [string ]interface {})
4019+ if ! ok {
4020+ t .Fatalf ("Expected repo map, got %T" , repos [0 ])
4021+ }
4022+
4023+ totalAgents , ok := repoMap ["total_agents" ].(float64 )
4024+ if ! ok {
4025+ t .Fatal ("Expected total_agents field" )
4026+ }
4027+ if int (totalAgents ) != 4 {
4028+ t .Errorf ("total_agents = %v, want 4" , totalAgents )
4029+ }
4030+
4031+ workerCount , ok := repoMap ["worker_count" ].(float64 )
4032+ if ! ok {
4033+ t .Fatal ("Expected worker_count field" )
4034+ }
4035+ if int (workerCount ) != 2 {
4036+ t .Errorf ("worker_count = %v, want 2" , workerCount )
4037+ }
4038+ }
4039+
4040+ func TestHandleListReposRichEmptyAgents (t * testing.T ) {
4041+ _ , d , cleanup := setupTestEnvironment (t )
4042+ defer cleanup ()
4043+
4044+ repo := & state.Repository {
4045+ GithubURL : "https://github.com/test/repo" ,
4046+ TmuxSession : "mc-test-repo" ,
4047+ Agents : make (map [string ]state.Agent ),
4048+ }
4049+ if err := d .GetState ().AddRepo ("test-repo" , repo ); err != nil {
4050+ t .Fatalf ("Failed to add repo: %v" , err )
4051+ }
4052+
4053+ client := socket .NewClient (d .GetPaths ().DaemonSock )
4054+ resp , err := client .Send (socket.Request {
4055+ Command : "list_repos" ,
4056+ Args : map [string ]interface {}{"rich" : true },
4057+ })
4058+ if err != nil {
4059+ t .Fatalf ("Failed to send request: %v" , err )
4060+ }
4061+ if ! resp .Success {
4062+ t .Fatalf ("list_repos failed: %s" , resp .Error )
4063+ }
4064+
4065+ repos , ok := resp .Data .([]interface {})
4066+ if ! ok {
4067+ t .Fatalf ("Expected repos array, got %T" , resp .Data )
4068+ }
4069+ if len (repos ) != 1 {
4070+ t .Fatalf ("Expected 1 repo, got %d" , len (repos ))
4071+ }
4072+
4073+ repoMap := repos [0 ].(map [string ]interface {})
4074+ totalAgents := repoMap ["total_agents" ].(float64 )
4075+ if int (totalAgents ) != 0 {
4076+ t .Errorf ("total_agents = %v, want 0" , totalAgents )
4077+ }
4078+ workerCount := repoMap ["worker_count" ].(float64 )
4079+ if int (workerCount ) != 0 {
4080+ t .Errorf ("worker_count = %v, want 0" , workerCount )
4081+ }
4082+ }
4083+
4084+ // =============================================================================
4085+ // Tests for PR #339: Context-aware refresh with auto-detection
4086+ // =============================================================================
4087+
4088+ func TestRefreshContextDetectionFromWorktreePath (t * testing.T ) {
4089+ tests := []struct {
4090+ name string
4091+ cwdSuffix string
4092+ wantRepo string
4093+ wantAgent string
4094+ wantDetect bool
4095+ }{
4096+ {
4097+ name : "agent worktree path" ,
4098+ cwdSuffix : "my-repo/happy-fox" ,
4099+ wantRepo : "my-repo" ,
4100+ wantAgent : "happy-fox" ,
4101+ wantDetect : true ,
4102+ },
4103+ {
4104+ name : "agent worktree with deep subdirectory" ,
4105+ cwdSuffix : "my-repo/happy-fox/src/main" ,
4106+ wantRepo : "my-repo" ,
4107+ wantAgent : "happy-fox" ,
4108+ wantDetect : true ,
4109+ },
4110+ {
4111+ name : "repo-only path" ,
4112+ cwdSuffix : "my-repo" ,
4113+ wantRepo : "my-repo" ,
4114+ wantAgent : "" ,
4115+ wantDetect : false ,
4116+ },
4117+ }
4118+
4119+ for _ , tt := range tests {
4120+ t .Run (tt .name , func (t * testing.T ) {
4121+ tmpDir , err := os .MkdirTemp ("" , "refresh-ctx-test-*" )
4122+ if err != nil {
4123+ t .Fatalf ("Failed to create temp dir: %v" , err )
4124+ }
4125+ defer os .RemoveAll (tmpDir )
4126+
4127+ tmpDir , _ = filepath .EvalSymlinks (tmpDir )
4128+ wtsDir := filepath .Join (tmpDir , "wts" )
4129+
4130+ testPath := filepath .Join (wtsDir , tt .cwdSuffix )
4131+ if err := os .MkdirAll (testPath , 0755 ); err != nil {
4132+ t .Fatalf ("Failed to create test path: %v" , err )
4133+ }
4134+
4135+ if ! hasPathPrefix (testPath , wtsDir ) {
4136+ t .Fatal ("testPath should have wtsDir as prefix" )
4137+ }
4138+
4139+ rel , err := filepath .Rel (wtsDir , testPath )
4140+ if err != nil {
4141+ t .Fatalf ("filepath.Rel failed: %v" , err )
4142+ }
4143+
4144+ parts := strings .SplitN (rel , string (filepath .Separator ), 2 )
4145+ detected := len (parts ) >= 2 && parts [0 ] != "" && parts [1 ] != ""
4146+
4147+ if detected != tt .wantDetect {
4148+ t .Errorf ("detection = %v, want %v (parts=%v)" , detected , tt .wantDetect , parts )
4149+ }
4150+
4151+ if detected {
4152+ repoName := parts [0 ]
4153+ agentName := strings .SplitN (parts [1 ], string (filepath .Separator ), 2 )[0 ]
4154+ if repoName != tt .wantRepo {
4155+ t .Errorf ("repoName = %q, want %q" , repoName , tt .wantRepo )
4156+ }
4157+ if agentName != tt .wantAgent {
4158+ t .Errorf ("agentName = %q, want %q" , agentName , tt .wantAgent )
4159+ }
4160+ }
4161+ })
4162+ }
4163+ }
4164+
4165+ func TestRefreshParseFlagsAllFlag (t * testing.T ) {
4166+ tests := []struct {
4167+ name string
4168+ args []string
4169+ wantAll bool
4170+ }{
4171+ {"no flags" , []string {}, false },
4172+ {"all flag present" , []string {"--all" }, true },
4173+ {"other flags without all" , []string {"--repo" , "my-repo" }, false },
4174+ {"all flag with other flags" , []string {"--all" , "--repo" , "my-repo" }, true },
4175+ }
4176+
4177+ for _ , tt := range tests {
4178+ t .Run (tt .name , func (t * testing.T ) {
4179+ flags , _ := ParseFlags (tt .args )
4180+ gotAll := flags ["all" ] == "true"
4181+ if gotAll != tt .wantAll {
4182+ t .Errorf ("--all = %v, want %v (flags=%v)" , gotAll , tt .wantAll , flags )
4183+ }
4184+ })
4185+ }
4186+ }
4187+
4188+ func TestRefreshUsageUpdated (t * testing.T ) {
4189+ cli , _ , cleanup := setupTestEnvironment (t )
4190+ defer cleanup ()
4191+
4192+ refreshCmd , ok := cli .rootCmd .Subcommands ["refresh" ]
4193+ if ! ok {
4194+ t .Fatal ("Expected 'refresh' command to exist" )
4195+ }
4196+
4197+ if refreshCmd .Usage == "" {
4198+ t .Error ("refresh command should have a Usage string" )
4199+ }
4200+ if refreshCmd .Description == "" {
4201+ t .Error ("refresh command should have a description" )
4202+ }
4203+ }
4204+
4205+ func TestContextDetectionEdgeCases (t * testing.T ) {
4206+ tests := []struct {
4207+ name string
4208+ path string
4209+ wtsDir string
4210+ wantMatch bool
4211+ }{
4212+ {"path outside worktrees dir" , "/home/user/projects/my-repo" , "/home/user/.multiclaude/wts" , false },
4213+ {"path is exactly the wts dir" , "/home/user/.multiclaude/wts" , "/home/user/.multiclaude/wts" , true },
4214+ {"path with similar prefix" , "/home/user/.multiclaude/wts-backup/repo/agent" , "/home/user/.multiclaude/wts" , false },
4215+ {"path with trailing separator" , "/home/user/.multiclaude/wts/repo/agent" , "/home/user/.multiclaude/wts/" , true },
4216+ {"path with dotfiles" , "/home/user/.multiclaude/wts/.hidden-repo/agent" , "/home/user/.multiclaude/wts" , true },
4217+ {"path with spaces" , "/home/user/.multiclaude/wts/my repo/agent" , "/home/user/.multiclaude/wts" , true },
4218+ }
4219+
4220+ for _ , tt := range tests {
4221+ t .Run (tt .name , func (t * testing.T ) {
4222+ got := hasPathPrefix (tt .path , tt .wtsDir )
4223+ if got != tt .wantMatch {
4224+ t .Errorf ("hasPathPrefix(%q, %q) = %v, want %v" , tt .path , tt .wtsDir , got , tt .wantMatch )
4225+ }
4226+ })
4227+ }
4228+ }
4229+
4230+ func TestRefreshCommandHelp (t * testing.T ) {
4231+ cli , _ , cleanup := setupTestEnvironment (t )
4232+ defer cleanup ()
4233+
4234+ err := cli .Execute ([]string {"refresh" , "--help" })
4235+ if err != nil {
4236+ t .Errorf ("refresh --help should not error: %v" , err )
4237+ }
4238+ }
4239+
4240+ func TestAgentContextPathParsing (t * testing.T ) {
4241+ tests := []struct {
4242+ name string
4243+ rel string
4244+ wantRepo string
4245+ wantAgent string
4246+ wantOK bool
4247+ }{
4248+ {"standard two-component" , "multiclaude/happy-fox" , "multiclaude" , "happy-fox" , true },
4249+ {"path with subdir" , "multiclaude/happy-fox/internal/cli" , "multiclaude" , "happy-fox" , true },
4250+ {"single component" , "multiclaude" , "" , "" , false },
4251+ {"empty relative path" , "." , "" , "" , false },
4252+ {"repo with dots" , "my.repo.name/agent-1" , "my.repo.name" , "agent-1" , true },
4253+ {"repo with hyphens" , "my-cool-repo/lively-otter" , "my-cool-repo" , "lively-otter" , true },
4254+ }
4255+
4256+ for _ , tt := range tests {
4257+ t .Run (tt .name , func (t * testing.T ) {
4258+ parts := strings .SplitN (tt .rel , string (filepath .Separator ), 2 )
4259+ ok := len (parts ) >= 2 && parts [0 ] != "" && parts [1 ] != ""
4260+
4261+ if ok != tt .wantOK {
4262+ t .Errorf ("detection = %v, want %v (parts=%v)" , ok , tt .wantOK , parts )
4263+ return
4264+ }
4265+
4266+ if ok {
4267+ repoName := parts [0 ]
4268+ agentName := strings .SplitN (parts [1 ], string (filepath .Separator ), 2 )[0 ]
4269+ if repoName != tt .wantRepo {
4270+ t .Errorf ("repo = %q, want %q" , repoName , tt .wantRepo )
4271+ }
4272+ if agentName != tt .wantAgent {
4273+ t .Errorf ("agent = %q, want %q" , agentName , tt .wantAgent )
4274+ }
4275+ }
4276+ })
4277+ }
4278+ }
0 commit comments