on main help sign in
test(config): tests for the data directory paths

Co-Authored-By: Charm Crush <crush@charm.land>

commita7932c5669e1e1299ff731c0f4e64a6b4c275fa4 authorChristian Rocha <christian@rocha.is> dateTue 12 May 2026, 09:29 (5 months ago) parente112368
6 files changed+49−11
internal/config/config.go+11−7 internal/config/load_test.go+30 internal/swagger/docs.go+1−1 internal/swagger/swagger.json+1−1 internal/swagger/swagger.yaml+5−1 schema.json+1−1
internal/config/config.go+11−7
@@ -259,13 +259,17 @@ func (Attribution) JSONSchemaExtend(schema *jsonschema.Schema) {
259259}
260260
261261type Options struct {
262− ContextPaths []string `json:"context_paths,omitempty" jsonschema:"description=Paths to files containing context information for the AI,example=.cursorrules,example=CRUSH.md"`
263− SkillsPaths []string `json:"skills_paths,omitempty" jsonschema:"description=Paths to directories containing Agent Skills (folders with SKILL.md files),example=~/.config/crush/skills,example=./skills"`
264− TUI *TUIOptions `json:"tui,omitempty" jsonschema:"description=Terminal user interface options"`
265− Debug bool `json:"debug,omitempty" jsonschema:"description=Enable debug logging,default=false"`
266− DebugLSP bool `json:"debug_lsp,omitempty" jsonschema:"description=Enable debug logging for LSP servers,default=false"`
267− DisableAutoSummarize bool `json:"disable_auto_summarize,omitempty" jsonschema:"description=Disable automatic conversation summarization,default=false"`
268− DataDirectory string `json:"data_directory,omitempty" jsonschema:"description=Directory for storing application data (relative to working directory),default=.crush,example=.crush"` // Relative to the cwd
262+ ContextPaths []string `json:"context_paths,omitempty" jsonschema:"description=Paths to files containing context information for the AI,example=.cursorrules,example=CRUSH.md"`
263+ SkillsPaths []string `json:"skills_paths,omitempty" jsonschema:"description=Paths to directories containing Agent Skills (folders with SKILL.md files),example=~/.config/crush/skills,example=./skills"`
264+ TUI *TUIOptions `json:"tui,omitempty" jsonschema:"description=Terminal user interface options"`
265+ Debug bool `json:"debug,omitempty" jsonschema:"description=Enable debug logging,default=false"`
266+ DebugLSP bool `json:"debug_lsp,omitempty" jsonschema:"description=Enable debug logging for LSP servers,default=false"`
267+ DisableAutoSummarize bool `json:"disable_auto_summarize,omitempty" jsonschema:"description=Disable automatic conversation summarization,default=false"`
268+ // DataDirectory is where Crush keeps per-project state such as
269+ // the SQLite database and workspace overrides. Relative paths are
270+ // resolved against the working directory; absolute paths are used
271+ // verbatim. After defaulting the stored value is always absolute.
272+ DataDirectory string `json:"data_directory,omitempty" jsonschema:"description=Directory for storing application data. Relative paths are resolved against the working directory; absolute paths are used as-is.,default=.crush,example=.crush"`
269273 DisabledTools []string `json:"disabled_tools,omitempty" jsonschema:"description=List of built-in tools to disable and hide from the agent,example=bash,example=sourcegraph"`
270274 DisableProviderAutoUpdate bool `json:"disable_provider_auto_update,omitempty" jsonschema:"description=Disable providers auto-update,default=false"`
271275 DisableDefaultProviders bool `json:"disable_default_providers,omitempty" jsonschema:"description=Ignore all default/embedded providers. When enabled\\, providers must be fully specified in the config file with base_url\\, models\\, and api_key - no merging with defaults occurs,default=false"`
internal/config/load_test.go+30
@@ -205,6 +205,36 @@ func TestConfig_setDefaults(t *testing.T) {
205205 require.Equal(t, filepath.Join(workingDir, "state"), cfg.Options.DataDirectory)
206206 })
207207
208+ t.Run("preserves absolute configured data directory", func(t *testing.T) {
209+ // Use a platform-appropriate absolute path so the test runs
210+ // the same way on POSIX and Windows.
211+ absDir := filepath.Join(t.TempDir(), "data")
212+ cfg := &Config{Options: &Options{DataDirectory: absDir}}
213+
214+ cfg.setDefaults(filepath.Join(t.TempDir(), "worktree"), "")
215+
216+ require.Equal(t, absDir, cfg.Options.DataDirectory)
217+ })
218+
219+ t.Run("workspace merge re-entry keeps an absolute data directory", func(t *testing.T) {
220+ // Simulate the load and reload paths: defaults are applied
221+ // twice with the data directory potentially carried through
222+ // from an earlier merge as a relative string.
223+ workingDir := filepath.Join(t.TempDir(), "worktree")
224+ cfg := &Config{}
225+ cfg.setDefaults(workingDir, "")
226+
227+ // Workspace JSON sets data_directory to a relative value; the
228+ // merge replaces the struct, then setDefaults runs again.
229+ cfg.Options.DataDirectory = "./state"
230+ cfg.setDefaults(workingDir, "")
231+
232+ require.True(t, filepath.IsAbs(cfg.Options.DataDirectory),
233+ "data directory must remain absolute after re-merge, got %q",
234+ cfg.Options.DataDirectory)
235+ require.Equal(t, filepath.Join(workingDir, "state"), cfg.Options.DataDirectory)
236+ })
237+
208238 t.Run("does not adopt .crush from a parent project", func(t *testing.T) {
209239 parent := t.TempDir()
210240
internal/swagger/docs.go+1−1
@@ -2939,7 +2939,7 @@ const docTemplate = `{
29392939 }
29402940 },
29412941 "data_directory": {
2942− "description": "Relative to the cwd",
2942+ "description": "DataDirectory is where Crush keeps per-project state such as the SQLite database and workspace overrides. Relative paths are resolved against the working directory; absolute paths are used verbatim. After defaulting the stored value is always absolute.",
29432943 "type": "string"
29442944 },
29452945 "debug": {
internal/swagger/swagger.json+1−1
@@ -2932,7 +2932,7 @@
29322932 }
29332933 },
29342934 "data_directory": {
2935− "description": "Relative to the cwd",
2935+ "description": "DataDirectory is where Crush keeps per-project state such as the SQLite database and workspace overrides. Relative paths are resolved against the working directory; absolute paths are used verbatim. After defaulting the stored value is always absolute.",
29362936 "type": "string"
29372937 },
29382938 "debug": {
internal/swagger/swagger.yaml+5−1
@@ -287,7 +287,11 @@ definitions:
287287 type: string
288288 type: array
289289 data_directory:
290− description: Relative to the cwd
290+ description: |-
291+ DataDirectory is where Crush keeps per-project state such as the SQLite
292+ database and workspace overrides. Relative paths are resolved against
293+ the working directory; absolute paths are used verbatim. After
294+ defaulting the stored value is always absolute.
291295 type: string
292296 debug:
293297 type: boolean
schema.json+1−1
@@ -423,7 +423,7 @@
423423 },
424424 "data_directory": {
425425 "type": "string",
426− "description": "Directory for storing application data (relative to working directory)",
426+ "description": "Directory for storing application data. Relative paths are resolved against the working directory; absolute paths are used as-is.",
427427 "default": ".crush",
428428 "examples": [
429429 ".crush"