From eda87020dc4db6101066f71c507fb133b4b57615 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 13 Aug 2026 06:45:57 +0200 Subject: [PATCH 1/2] Add macOS to CI test matrix --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78d82b1..84aceb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,13 @@ jobs: version: latest test: - runs-on: ubuntu-latest + strategy: + matrix: + os: + - ubuntu-latest + - macos-26 + + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v7 From e4667b9236e9eaa2614881329a6bc77e963573fd Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 13 Aug 2026 07:02:09 +0200 Subject: [PATCH 2/2] Set HOME to fix failing macOS test Set HOME alongside XDG_CONFIG_HOME for all tests on macOS, matching the behavior on Linux. --- cmd/config_test.go | 3 ++- config/config_test.go | 4 +++- config/env_test.go | 8 +++++--- internal/testutil/config.go | 13 +++++++++++++ mcp/smoke_test.go | 7 ++++--- 5 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 internal/testutil/config.go diff --git a/cmd/config_test.go b/cmd/config_test.go index f0cb2b7..5fec66d 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/1broseidon/ketch/config" + "github.com/1broseidon/ketch/internal/testutil" "github.com/1broseidon/ketch/scrape" ) @@ -100,7 +101,7 @@ func TestRunConfigSetNeverEchoesSecrets(t *testing.T) { for _, test := range tests { t.Run(test.key, func(t *testing.T) { - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + testutil.SetIsolatedConfigHome(t) output, err := captureStderr(t, func() error { return runConfigSet(nil, []string{test.key, test.value}) }) diff --git a/config/config_test.go b/config/config_test.go index 322e30b..25bb9fe 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -5,6 +5,8 @@ import ( "path/filepath" "reflect" "testing" + + "github.com/1broseidon/ketch/internal/testutil" ) func TestParallelBackendIsAppendedWithoutChangingDefault(t *testing.T) { @@ -50,7 +52,7 @@ func TestEffectiveKeysReturnCopies(t *testing.T) { } func TestSaveEnforcesPrivateMode(t *testing.T) { - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + testutil.SetIsolatedConfigHome(t) path, err := Path() if err != nil { t.Fatal(err) diff --git a/config/env_test.go b/config/env_test.go index aaf4e83..b9ef8dd 100644 --- a/config/env_test.go +++ b/config/env_test.go @@ -6,6 +6,8 @@ import ( "reflect" "strings" "testing" + + "github.com/1broseidon/ketch/internal/testutil" ) // clearKetchEnv unsets every KETCH_* variable for the test so ambient @@ -28,7 +30,7 @@ func TestEnvVarNaming(t *testing.T) { func TestLoadEnvOverlay(t *testing.T) { clearKetchEnv(t) - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + testutil.SetIsolatedConfigHome(t) t.Setenv("KETCH_BACKEND", "ddg") t.Setenv("KETCH_LIMIT", "9") t.Setenv("KETCH_CACHE_TTL", "30m") @@ -94,7 +96,7 @@ func TestLoadEnvOverridesFile(t *testing.T) { func TestLoadInvalidEnvIsLoudButBestEffort(t *testing.T) { clearKetchEnv(t) - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + testutil.SetIsolatedConfigHome(t) t.Setenv("KETCH_LIMIT", "abc") t.Setenv("KETCH_CACHE_TTL", "nope") t.Setenv("KETCH_BACKEND", "ddg") @@ -116,7 +118,7 @@ func TestLoadInvalidEnvIsLoudButBestEffort(t *testing.T) { func TestLoadEmptyEnvValueIsUnset(t *testing.T) { clearKetchEnv(t) - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + testutil.SetIsolatedConfigHome(t) t.Setenv("KETCH_LIMIT", "") res, err := Load() diff --git a/internal/testutil/config.go b/internal/testutil/config.go new file mode 100644 index 0000000..efa01b3 --- /dev/null +++ b/internal/testutil/config.go @@ -0,0 +1,13 @@ +package testutil + +import "testing" + +// SetIsolatedConfigHome prevents tests from using the user's config directory. +func SetIsolatedConfigHome(t testing.TB) string { + t.Helper() + + dir := t.TempDir() + t.Setenv("HOME", dir) + t.Setenv("XDG_CONFIG_HOME", dir) + return dir +} diff --git a/mcp/smoke_test.go b/mcp/smoke_test.go index 2695975..563d2b2 100644 --- a/mcp/smoke_test.go +++ b/mcp/smoke_test.go @@ -7,8 +7,8 @@ // // go test -tags mcpsmoke ./mcp/... -run TestMCPServerSmoke -v // -// The server subprocess runs with XDG_CACHE_HOME/XDG_CONFIG_HOME pointed at -// a temp dir, so it sees default config (grepapp needs no key) and a fresh +// The server subprocess runs with HOME/XDG_CACHE_HOME/XDG_CONFIG_HOME pointed +// at a temp dir, so it sees default config (grepapp needs no key) and a fresh // page cache — which lets the scrape subtest prove shared-cache reuse. package mcp_test @@ -41,6 +41,7 @@ func TestMCPServerSmoke(t *testing.T) { isolated := t.TempDir() serve := exec.Command(bin, "mcp", "serve") serve.Env = append(os.Environ(), + "HOME="+isolated, "XDG_CACHE_HOME="+isolated, "XDG_CONFIG_HOME="+isolated, ) @@ -152,7 +153,7 @@ func TestMCPServerSmoke(t *testing.T) { } // The server writes through its shared, server-lifetime cache handle; - // with XDG_CACHE_HOME isolated, this file exists only if that handle + // with HOME/XDG_CACHE_HOME isolated, this file exists only if that handle // was actually opened and used. cachePath := filepath.Join(isolated, "ketch", "cache.db") info, err := os.Stat(cachePath)