Files
gastown/internal/cmd/handoff_test.go
morsov 39b1c11bb6 fix(handoff): use env var fallback when town root detection fails
When the repo is in a broken state (wrong branch, detached HEAD, deleted
worktree), gt handoff would fail with "cannot detect town root" error.
This is exactly when handoff is most needed - to recover and hand off
to a fresh session.

Changes:
- detectTownRootFromCwd() now falls back to GT_TOWN_ROOT and GT_ROOT
  environment variables when cwd-based detection fails
- buildRestartCommand() now propagates GT_ROOT to ensure subsequent
  handoffs can also use the fallback
- Added tests for the fallback behavior

Fixes gt-x2q81.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 18:05:00 -08:00

125 lines
3.7 KiB
Go

package cmd
import (
"os"
"path/filepath"
"testing"
"github.com/steveyegge/gastown/internal/workspace"
)
func TestDetectTownRootFromCwd_EnvFallback(t *testing.T) {
// Save original env vars and restore after test
origTownRoot := os.Getenv("GT_TOWN_ROOT")
origRoot := os.Getenv("GT_ROOT")
defer func() {
os.Setenv("GT_TOWN_ROOT", origTownRoot)
os.Setenv("GT_ROOT", origRoot)
}()
// Create a temp directory that looks like a valid town
tmpTown := t.TempDir()
mayorDir := filepath.Join(tmpTown, "mayor")
if err := os.MkdirAll(mayorDir, 0755); err != nil {
t.Fatalf("creating mayor dir: %v", err)
}
townJSON := filepath.Join(mayorDir, "town.json")
if err := os.WriteFile(townJSON, []byte(`{"name": "test-town"}`), 0644); err != nil {
t.Fatalf("creating town.json: %v", err)
}
// Clear both env vars initially
os.Setenv("GT_TOWN_ROOT", "")
os.Setenv("GT_ROOT", "")
t.Run("uses GT_TOWN_ROOT when cwd detection fails", func(t *testing.T) {
// Set GT_TOWN_ROOT to our temp town
os.Setenv("GT_TOWN_ROOT", tmpTown)
os.Setenv("GT_ROOT", "")
// Save cwd, cd to a non-town directory, and restore after
origCwd, _ := os.Getwd()
os.Chdir(os.TempDir())
defer os.Chdir(origCwd)
result := detectTownRootFromCwd()
if result != tmpTown {
t.Errorf("detectTownRootFromCwd() = %q, want %q (should use GT_TOWN_ROOT fallback)", result, tmpTown)
}
})
t.Run("uses GT_ROOT when GT_TOWN_ROOT not set", func(t *testing.T) {
// Set only GT_ROOT
os.Setenv("GT_TOWN_ROOT", "")
os.Setenv("GT_ROOT", tmpTown)
// Save cwd, cd to a non-town directory, and restore after
origCwd, _ := os.Getwd()
os.Chdir(os.TempDir())
defer os.Chdir(origCwd)
result := detectTownRootFromCwd()
if result != tmpTown {
t.Errorf("detectTownRootFromCwd() = %q, want %q (should use GT_ROOT fallback)", result, tmpTown)
}
})
t.Run("prefers GT_TOWN_ROOT over GT_ROOT", func(t *testing.T) {
// Create another temp town for GT_ROOT
anotherTown := t.TempDir()
anotherMayor := filepath.Join(anotherTown, "mayor")
os.MkdirAll(anotherMayor, 0755)
os.WriteFile(filepath.Join(anotherMayor, "town.json"), []byte(`{"name": "other-town"}`), 0644)
// Set both env vars
os.Setenv("GT_TOWN_ROOT", tmpTown)
os.Setenv("GT_ROOT", anotherTown)
// Save cwd, cd to a non-town directory, and restore after
origCwd, _ := os.Getwd()
os.Chdir(os.TempDir())
defer os.Chdir(origCwd)
result := detectTownRootFromCwd()
if result != tmpTown {
t.Errorf("detectTownRootFromCwd() = %q, want %q (should prefer GT_TOWN_ROOT)", result, tmpTown)
}
})
t.Run("ignores invalid GT_TOWN_ROOT", func(t *testing.T) {
// Set GT_TOWN_ROOT to non-existent path, GT_ROOT to valid
os.Setenv("GT_TOWN_ROOT", "/nonexistent/path/to/town")
os.Setenv("GT_ROOT", tmpTown)
// Save cwd, cd to a non-town directory, and restore after
origCwd, _ := os.Getwd()
os.Chdir(os.TempDir())
defer os.Chdir(origCwd)
result := detectTownRootFromCwd()
if result != tmpTown {
t.Errorf("detectTownRootFromCwd() = %q, want %q (should skip invalid GT_TOWN_ROOT and use GT_ROOT)", result, tmpTown)
}
})
t.Run("uses secondary marker when primary missing", func(t *testing.T) {
// Create a temp town with only mayor/ directory (no town.json)
secondaryTown := t.TempDir()
mayorOnlyDir := filepath.Join(secondaryTown, workspace.SecondaryMarker)
os.MkdirAll(mayorOnlyDir, 0755)
os.Setenv("GT_TOWN_ROOT", secondaryTown)
os.Setenv("GT_ROOT", "")
// Save cwd, cd to a non-town directory, and restore after
origCwd, _ := os.Getwd()
os.Chdir(os.TempDir())
defer os.Chdir(origCwd)
result := detectTownRootFromCwd()
if result != secondaryTown {
t.Errorf("detectTownRootFromCwd() = %q, want %q (should accept secondary marker)", result, secondaryTown)
}
})
}