diff --git a/cmd/dispatch/testmain_test.go b/cmd/dispatch/testmain_test.go new file mode 100644 index 0000000..76e5b8c --- /dev/null +++ b/cmd/dispatch/testmain_test.go @@ -0,0 +1,48 @@ +package main + +import ( + "os" + "path/filepath" + "runtime" + "testing" +) + +// TestMain isolates the OS user-config directory for the entire cmd/dispatch +// test binary. Some tests exercise commands that read, write, or delete the +// real config file — for example TestHandleArgs_ClearCache runs the +// "--clear-cache" path, which calls config.Reset and deletes config.json. +// Without this redirect that runs against the developer's real config file, so +// "go test ./..." (as "mage install" does) silently wipes the user's +// %APPDATA%\dispatch\config.json (or ~/.config/dispatch/config.json). Pointing +// the config-dir env var at a throwaway temp directory guarantees that no test +// — present or future — can touch the user's real settings. +func TestMain(m *testing.M) { + os.Exit(runWithIsolatedConfig(m)) +} + +// runWithIsolatedConfig redirects the config directory to a temp dir, runs the +// package tests, and returns the exit code. It is a separate function so the +// temp dir cleanup runs before os.Exit (which would skip deferred calls). +func runWithIsolatedConfig(m *testing.M) int { + tmp, err := os.MkdirTemp("", "dispatch-cli-config-*") + if err != nil { + panic("dispatch cli tests: creating temp config dir: " + err.Error()) + } + defer os.RemoveAll(tmp) + + // Redirect the same environment variable os.UserConfigDir() reads on each + // platform so config.Save/Load/Reset resolve inside tmp instead of the real + // user configuration directory. + switch runtime.GOOS { + case "windows": + os.Setenv("APPDATA", tmp) + case "darwin": + // os.UserConfigDir() derives from $HOME on macOS. + os.Setenv("HOME", tmp) + os.MkdirAll(filepath.Join(tmp, "Library", "Application Support"), 0o755) + default: + os.Setenv("XDG_CONFIG_HOME", tmp) + } + + return m.Run() +} diff --git a/internal/config/main_test.go b/internal/config/main_test.go new file mode 100644 index 0000000..6ad67f6 --- /dev/null +++ b/internal/config/main_test.go @@ -0,0 +1,48 @@ +package config + +import ( + "os" + "path/filepath" + "runtime" + "testing" +) + +// TestMain isolates the OS user-config directory for the entire config test +// binary. Save and Reset write to and delete the real config file, and this +// package's tests exercise both. Individual tests already redirect the config +// dir with helpers (withTempConfigDir, setupTempConfig), but this package-level +// baseline guarantees that even a test which forgets that guard can never touch +// the developer's real config file — the failure mode that let "go test ./..." +// (via "mage install") wipe the user's saved settings. Per-test t.Setenv calls +// still override this baseline (and restore back to it), so path and error-path +// tests keep working unchanged. +func TestMain(m *testing.M) { + os.Exit(runWithIsolatedConfigDir(m)) +} + +// runWithIsolatedConfigDir redirects the config directory to a temp dir, runs +// the package tests, and returns the exit code. It is a separate function so +// the temp dir cleanup runs before os.Exit (which would skip deferred calls). +func runWithIsolatedConfigDir(m *testing.M) int { + tmp, err := os.MkdirTemp("", "dispatch-config-test-*") + if err != nil { + panic("dispatch config tests: creating temp config dir: " + err.Error()) + } + defer os.RemoveAll(tmp) + + // Redirect the same environment variable os.UserConfigDir() reads on each + // platform so configPath() resolves inside tmp instead of the real user + // configuration directory. + switch runtime.GOOS { + case "windows": + os.Setenv("APPDATA", tmp) + case "darwin": + // os.UserConfigDir() derives from $HOME on macOS. + os.Setenv("HOME", tmp) + os.MkdirAll(filepath.Join(tmp, "Library", "Application Support"), 0o755) + default: + os.Setenv("XDG_CONFIG_HOME", tmp) + } + + return m.Run() +} diff --git a/internal/tui/main_test.go b/internal/tui/main_test.go new file mode 100644 index 0000000..619c681 --- /dev/null +++ b/internal/tui/main_test.go @@ -0,0 +1,49 @@ +package tui + +import ( + "os" + "path/filepath" + "runtime" + "testing" +) + +// TestMain isolates the OS user-config directory for the entire tui test +// binary. Many tests here build a Model (NewModel, NewModelWithQuery, or +// newTestModel) and persist preferences through config.Save — for example +// recordLaunch stamps a frecency entry on every session launch. Without this +// redirect those writes land in the developer's real config file, so running +// "go test ./..." (as "mage install" does) silently overwrites the user's +// %APPDATA%\dispatch\config.json (or ~/.config/dispatch/config.json) with +// defaults plus test data. Pointing the config-dir env var at a throwaway temp +// directory guarantees that no test — present or future — can clobber the +// developer's real settings. +func TestMain(m *testing.M) { + os.Exit(runIsolated(m)) +} + +// runIsolated redirects the config directory to a temp dir, runs the package +// tests, and returns the exit code. It is a separate function so the temp dir +// cleanup runs before os.Exit (which would skip deferred calls). +func runIsolated(m *testing.M) int { + tmp, err := os.MkdirTemp("", "dispatch-tui-config-*") + if err != nil { + panic("dispatch tui tests: creating temp config dir: " + err.Error()) + } + defer os.RemoveAll(tmp) + + // Redirect the same environment variable os.UserConfigDir() reads on each + // platform so config.Save/Load resolve inside tmp instead of the real user + // configuration directory. + switch runtime.GOOS { + case "windows": + os.Setenv("APPDATA", tmp) + case "darwin": + // os.UserConfigDir() derives from $HOME on macOS. + os.Setenv("HOME", tmp) + os.MkdirAll(filepath.Join(tmp, "Library", "Application Support"), 0o755) + default: + os.Setenv("XDG_CONFIG_HOME", tmp) + } + + return m.Run() +}