From 40684228138303a922ff71a8f39dfe85fad30572 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Sat, 2 May 2026 15:37:28 -0400 Subject: [PATCH] test: use forward slashes in shell commands for Windows compat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows t.TempDir() returns C:\... paths whose backslashes get consumed as escapes when injected into $(cat ...). Convert to forward slashes so the embedded shell resolves the path on every OS. Also accept the Windows coreutils error shape ("cannot find") in addition to POSIX "exit status" when asserting that inner diagnostic detail is surfaced — mvdan/sh runs coreutils in-process on Windows so there is no subprocess exit status to report. --- internal/config/resolve_real_test.go | 32 +++++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/internal/config/resolve_real_test.go b/internal/config/resolve_real_test.go index c840bfc38143eb876c972a756b1cee6c9c8578aa..f33435e9615f44a09c9e2ce1692c3b3aa3ebf276 100644 --- a/internal/config/resolve_real_test.go +++ b/internal/config/resolve_real_test.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "slices" + "strings" "testing" "github.com/charmbracelet/crush/internal/env" @@ -43,8 +44,11 @@ func writeTempFile(t *testing.T, content string) string { func TestResolvedEnv_RealShell_Success(t *testing.T) { t.Parallel() - withNL := writeTempFile(t, "token-with-nl\n") - noNL := writeTempFile(t, "token-no-nl") + // filepath.ToSlash so Windows temp paths (C:\Users\...) survive + // being injected into a shell command string — the embedded shell + // treats backslashes as escapes, forward slashes work on every OS. + withNL := filepath.ToSlash(writeTempFile(t, "token-with-nl\n")) + noNL := filepath.ToSlash(writeTempFile(t, "token-no-nl")) m := MCPConfig{ Env: map[string]string{ @@ -175,14 +179,17 @@ func TestResolvedEnv_RealShell_NounsetRegression(t *testing.T) { } // TestResolvedEnv_RealShell_FailureDetail pins that a failing inner -// command surfaces enough detail (exit code + stderr) to diagnose -// without forcing the user to re-run the command by hand. Also -// verifies the template is included so they know which Env entry -// blew up. +// command surfaces enough detail (exit code + stderr on POSIX, the +// underlying OS error on Windows where coreutils runs in-process) to +// diagnose without forcing the user to re-run the command by hand. +// Also verifies the template is included so they know which Env +// entry blew up. func TestResolvedEnv_RealShell_FailureDetail(t *testing.T) { t.Parallel() - missing := filepath.Join(t.TempDir(), "definitely-not-here") + // Forward slashes so the path survives shell-string injection on + // Windows; see TestResolvedEnv_RealShell_Success for the same note. + missing := filepath.ToSlash(filepath.Join(t.TempDir(), "definitely-not-here")) m := MCPConfig{Env: map[string]string{ "FORGEJO_ACCESS_TOKEN": fmt.Sprintf("$(cat %s)", missing), }} @@ -192,7 +199,16 @@ func TestResolvedEnv_RealShell_FailureDetail(t *testing.T) { msg := err.Error() require.Contains(t, msg, "FORGEJO_ACCESS_TOKEN", "must identify the failing env var") require.Contains(t, msg, missing, "must include the template so users see what failed") - require.Contains(t, msg, "exit status", "must surface the inner exit code") + + // Inner diagnostic detail must survive. POSIX surfaces "exit + // status N" + stderr; Windows' in-process coreutils surfaces the + // Go OS error instead. Accept either shape so the test is + // portable without weakening the intent. + lower := strings.ToLower(msg) + hasDetail := strings.Contains(lower, "exit status") || + strings.Contains(lower, "no such file") || + strings.Contains(lower, "cannot find") + require.True(t, hasDetail, "must surface inner error detail: %s", msg) } // TestResolvedHeaders_RealShell_FailurePreservesOriginal pins two