@@ -37,6 +37,98 @@ func TestConfig_LoadFromBytes(t *testing.T) {
37 require.Equal(t, "https://api.openai.com/v2", pc.BaseURL)
37 require.Equal(t, "https://api.openai.com/v2", pc.BaseURL)
40+func TestLookupConfigs_BoundedByProject(t *testing.T) {
41+ // Force GlobalConfig and GlobalConfigData to point at locations we
42+ // control so they can be present in the result without polluting
43+ // the developer's real config.
44+ globalDir := t.TempDir()
45+ t.Setenv("CRUSH_GLOBAL_CONFIG", globalDir)
46+ t.Setenv("CRUSH_GLOBAL_DATA", globalDir)
48+ t.Run("does not pick up crush.json above non-git project", func(t *testing.T) {
49+ parent := t.TempDir()
51+ // crush.json above the project must not be adopted.
52+ require.NoError(t, os.WriteFile(
53+ filepath.Join(parent, "crush.json"),
58+ project := filepath.Join(parent, "project")
59+ require.NoError(t, os.Mkdir(project, 0o755))
61+ got := lookupConfigs(project)
62+ for _, p := range got {
63+ require.NotEqual(t, filepath.Join(parent, "crush.json"), p)
67+ t.Run("does not climb out of git worktree to find crush.json", func(t *testing.T) {
68+ if _, err := exec.LookPath("git"); err != nil {
69+ t.Skip("git not available")
72+ parent := t.TempDir()
74+ require.NoError(t, os.WriteFile(
75+ filepath.Join(parent, "crush.json"),
80+ worktree := filepath.Join(parent, "worktree")
81+ require.NoError(t, os.Mkdir(worktree, 0o755))
82+ gitInit := exec.CommandContext(t.Context(), "git", "init", "-q")
83+ gitInit.Dir = worktree
84+ require.NoError(t, gitInit.Run())
86+ got := lookupConfigs(worktree)
87+ strayEval, err := filepath.EvalSymlinks(filepath.Join(parent, "crush.json"))
88+ require.NoError(t, err)
89+ for _, p := range got {
90+ pEval, err := filepath.EvalSymlinks(p)
94+ require.NotEqual(t, strayEval, pEval, "must not adopt parent crush.json")
98+ t.Run("picks up crush.json inside the project", func(t *testing.T) {
99+ project := t.TempDir()
100+ local := filepath.Join(project, "crush.json")
101+ require.NoError(t, os.WriteFile(local, []byte(`{}`), 0o644))
103+ got := lookupConfigs(project)
105+ localEval, err := filepath.EvalSymlinks(local)
106+ require.NoError(t, err)
108+ for _, p := range got {
109+ pEval, err := filepath.EvalSymlinks(p)
113+ if pEval == localEval {
118+ require.True(t, foundLocal, "expected project crush.json to be in lookup result: %v", got)
121+ t.Run("global config is always included regardless of boundary", func(t *testing.T) {
122+ project := t.TempDir()
124+ got := lookupConfigs(project)
125+ // Global config and global data path are always prepended,
126+ // even when no project file exists.
127+ require.Contains(t, got, GlobalConfig())
128+ require.Contains(t, got, GlobalConfigData())
40func TestLoadFromConfigPaths_InvalidJSON(t *testing.T) {
132func TestLoadFromConfigPaths_InvalidJSON(t *testing.T) {
41 t.Parallel()
133 t.Parallel()