Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion cmd/codeaf/chatv3_local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync/atomic"
Expand Down Expand Up @@ -155,7 +156,7 @@ func drainConsentRoad(t *testing.T, events <-chan session.Event, answer func(ses
// rule.
func shortStateRoot(t *testing.T) string {
t.Helper()
root, err := os.MkdirTemp("", "afl")
root, err := os.MkdirTemp(shortSocketParent(t), "afl")
if err != nil {
t.Fatalf("make a state root: %v", err)
}
Expand All @@ -164,6 +165,23 @@ func shortStateRoot(t *testing.T) string {
return root
}

// shortSocketParent is where a short state root is made. On macOS it is /tmp,
// spelled physically (/private/tmp) like every other fixture root here:
// TestMain gives this package the physical /private/var/folders spelling of the
// temporary folder, and a workspace host's socket under that spelling is a few
// bytes past the socket limit. Other systems keep the temporary folder.
func shortSocketParent(t *testing.T) string {
t.Helper()
if runtime.GOOS != "darwin" {
return ""
}
parent, err := filepath.EvalSymlinks("/tmp")
if err != nil {
t.Fatalf("resolve /tmp: %v", err)
}
return parent
}

// answeringHost is a socket where this workspace's host would be, and nothing
// behind it. The rule asks one question — is anybody there — so a listener is
// the whole of what it takes to be somebody.
Expand Down
2 changes: 1 addition & 1 deletion cmd/codeaf/engine_stale_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ func mustStandInJSON(value any) json.RawMessage {

func shortEngineHome(t *testing.T) {
t.Helper()
root, err := os.MkdirTemp("", "eh")
root, err := os.MkdirTemp(shortSocketParent(t), "eh")
if err != nil {
t.Fatalf("make a state root: %v", err)
}
Expand Down
5 changes: 5 additions & 0 deletions cmd/codeaf/logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/Agent-Field/codeaf/internal/calllog"
"github.com/Agent-Field/codeaf/internal/catalog"
"github.com/Agent-Field/codeaf/internal/config"
"github.com/Agent-Field/codeaf/internal/testutil"
)

// TestMain switches the model-call log OFF for this package and gives the
Expand All @@ -41,6 +42,10 @@ func TestMain(m *testing.M) {
if code, child := runAsCarriedChild(); child {
os.Exit(code)
}
if err := testutil.CanonicalizeTempRoots(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
// C27.7 keeps unrelated CLI fixtures on host execution. Cell wiring tests
// declare availability, and engine-backed tests require the real engine;
// the stamped binary smoke independently proves the production default.
Expand Down
7 changes: 7 additions & 0 deletions docs/changes/unreleased/2033-mac-test-temp-roots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
kind: internal
title: Test fixtures use physical temporary folders on macOS
pr: 2033
surface: [build]
invalidates: []
---
18 changes: 18 additions & 0 deletions internal/cellstore/testmain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package cellstore

import (
"fmt"
"os"
"testing"

"github.com/Agent-Field/codeaf/internal/testutil"
)

// TestMain gives fixtures physical paths so workspace bindings do not traverse host aliases.
func TestMain(m *testing.M) {
if err := testutil.CanonicalizeTempRoots(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(m.Run())
}
18 changes: 18 additions & 0 deletions internal/handoff/testmain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package handoff

import (
"fmt"
"os"
"testing"

"github.com/Agent-Field/codeaf/internal/testutil"
)

// TestMain gives fixtures physical paths so workspace bindings do not traverse host aliases.
func TestMain(m *testing.M) {
if err := testutil.CanonicalizeTempRoots(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(m.Run())
}
7 changes: 7 additions & 0 deletions internal/run/testmain_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package run_test

import (
"fmt"
"os"
"testing"

"github.com/Agent-Field/codeaf/internal/testutil"
)

// TestMain keeps subprocess workers made by this suite out of the plan that
Expand All @@ -16,6 +19,10 @@ func TestMain(m *testing.M) {
if code, child := runAsDelegateChild(); child {
os.Exit(code)
}
if err := testutil.CanonicalizeTempRoots(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
_ = os.Unsetenv("PLANDB_DB")
_ = os.Unsetenv("PLANDB_RUN")
os.Exit(m.Run())
Expand Down
5 changes: 5 additions & 0 deletions internal/session/hermetic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
"testing"

"github.com/Agent-Field/codeaf/internal/home"
"github.com/Agent-Field/codeaf/internal/testutil"
)

func TestMain(m *testing.M) { os.Exit(runTests(m)) }
Expand All @@ -52,6 +53,10 @@ func TestMain(m *testing.M) { os.Exit(runTests(m)) }
// call, and a package that leaks a directory per run to stop leaking files per
// run has not fixed anything.
func runTests(m *testing.M) int {
if err := testutil.CanonicalizeTempRoots(); err != nil {
fmt.Fprintf(os.Stderr, "session tests: %v\n", err)
return 1
}
// The guard watches wherever this machine actually keeps its state: the real
// home, or the CODEAF_HOME a developer had already pointed somewhere else.
// It asks [home.InheritedDir] rather than [home.Dir] because Dir hands a
Expand Down
18 changes: 18 additions & 0 deletions internal/syncsetup/testmain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package syncsetup

import (
"fmt"
"os"
"testing"

"github.com/Agent-Field/codeaf/internal/testutil"
)

// TestMain gives fixtures physical paths so workspace bindings do not traverse host aliases.
func TestMain(m *testing.M) {
if err := testutil.CanonicalizeTempRoots(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(m.Run())
}
18 changes: 18 additions & 0 deletions internal/taskcopy/testmain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package taskcopy

import (
"fmt"
"os"
"testing"

"github.com/Agent-Field/codeaf/internal/testutil"
)

// TestMain gives fixtures physical paths so workspace bindings do not traverse host aliases.
func TestMain(m *testing.M) {
if err := testutil.CanonicalizeTempRoots(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(m.Run())
}
42 changes: 42 additions & 0 deletions internal/testutil/temp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Package testutil keeps fixtures independent of the host's path aliases.
package testutil

import (
"fmt"
"os"
"path/filepath"
"runtime"
)

// CanonicalizeTempRoots makes temporary fixtures agree with canonical workspace paths.
// macOS hands every process a TMPDIR under /var/folders, which is a link to
// /private/var/folders, so a fixture's t.TempDir() spelling differs from the path
// the product records once it resolves links. Empty roots retain Go's defaults,
// relative and physical roots retain their spelling. Other systems keep their
// environment untouched, because their temporary folder is not an alias.
// This belongs before suite setup because testing caches temporary parents per test.
func CanonicalizeTempRoots() error {
if runtime.GOOS != "darwin" {
return nil
}
for _, name := range []string{"TMPDIR", "GOTMPDIR"} {
raw := os.Getenv(name)
// A relative root depends on the working directory's spelling, so it
// is left exactly as given rather than anchored to a guess.
if raw == "" || !filepath.IsAbs(raw) {
continue
}
// Links are followed before any lexical cleaning, so a ".." after a link
// climbs out of the link's target exactly as the kernel would.
resolved, err := filepath.EvalSymlinks(raw)
if err != nil {
return fmt.Errorf("cannot resolve %s: %w", name, err)
}
if resolved != filepath.Clean(raw) {
if err := os.Setenv(name, resolved); err != nil {
return fmt.Errorf("cannot set %s: %w", name, err)
}
}
}
return nil
}
Loading
Loading