@@ -147,36 +147,84 @@ func TestConfigCmd(t *testing.T) {
147147 t .Parallel ()
148148 bin := buildTestBinary (t )
149149
150- t .Run ("LLMModel" , func (t * testing.T ) {
151- cmd := exec .CommandContext (
152- t .Context (),
153- bin ,
154- "config" ,
155- "chat.llm.model" ,
156- )
150+ t .Run ("GetScalar" , func (t * testing.T ) {
151+ cmd := exec .CommandContext (t .Context (), bin , "config" , "get" , ".chat.llm.model" )
157152 out , err := cmd .CombinedOutput ()
158- require .NoError (t , err , "config chat.llm.model failed: %s" , out )
153+ require .NoError (t , err , "config get . chat.llm.model failed: %s" , out )
159154 got := strings .TrimSpace (string (out ))
160155 assert .NotEmpty (t , got )
156+ assert .NotContains (t , got , `"` , "scalar should not be JSON-quoted" )
161157 })
162158
163- t .Run ("UnknownKey" , func (t * testing.T ) {
164- cmd := exec .CommandContext (
165- t .Context (),
166- bin ,
167- "config" ,
168- "bogus.key" ,
169- )
159+ t .Run ("GetSection" , func (t * testing.T ) {
160+ cmd := exec .CommandContext (t .Context (), bin , "config" , "get" , ".chat.llm" )
170161 out , err := cmd .CombinedOutput ()
171- require .Error (t , err )
172- assert .Contains (t , string (out ), "unknown config key" )
162+ require .NoError (t , err , "config get .chat.llm failed: %s" , out )
163+ s := string (out )
164+ assert .Contains (t , s , "model =" )
165+ assert .Contains (t , s , "provider =" )
166+ assert .NotContains (t , s , "api_key" )
167+ })
168+
169+ t .Run ("GetNull" , func (t * testing.T ) {
170+ cmd := exec .CommandContext (t .Context (), bin , "config" , "get" , ".bogus" )
171+ out , err := cmd .CombinedOutput ()
172+ require .NoError (t , err , "config get .bogus failed: %s" , out )
173+ assert .Equal (t , "null\n " , string (out ))
174+ })
175+
176+ t .Run ("GetKeys" , func (t * testing.T ) {
177+ cmd := exec .CommandContext (t .Context (), bin , "config" , "get" , ".chat.llm | keys" )
178+ out , err := cmd .CombinedOutput ()
179+ require .NoError (t , err , "config get '.chat.llm | keys' failed: %s" , out )
180+ assert .Contains (t , string (out ), `"model"` )
181+ })
182+
183+ t .Run ("GetDefaultShowConfig" , func (t * testing.T ) {
184+ cmd := exec .CommandContext (t .Context (), bin , "config" , "get" )
185+ out , err := cmd .CombinedOutput ()
186+ require .NoError (t , err , "config get (no filter) failed: %s" , out )
187+ assert .Contains (t , string (out ), "[chat.llm]" )
188+ assert .Contains (t , string (out ), "model =" )
173189 })
174190
175- t .Run ("MissingKey " , func (t * testing.T ) {
191+ t .Run ("GetDefaultViaConfig " , func (t * testing.T ) {
176192 cmd := exec .CommandContext (t .Context (), bin , "config" )
177193 out , err := cmd .CombinedOutput ()
178- require .Error (t , err )
179- assert .Contains (t , string (out ), "provide a config key or use --dump" )
194+ require .NoError (t , err , "config (no args) failed: %s" , out )
195+ assert .Contains (t , string (out ), "[chat.llm]" )
196+ assert .Contains (t , string (out ), "model =" )
197+ })
198+
199+ t .Run ("EditCreatesConfig" , func (t * testing.T ) {
200+ tmpDir := t .TempDir ()
201+ cmd := exec .CommandContext (t .Context (), bin , "config" , "edit" )
202+ cmd .Env = envWithEditor (tmpDir , noopEditor ())
203+ out , err := cmd .CombinedOutput ()
204+ require .NoError (t , err , "config edit failed: %s" , out )
205+
206+ configPath := filepath .Join (tmpDir , "micasa" , "config.toml" )
207+ info , statErr := os .Stat (configPath )
208+ require .NoError (t , statErr , "config file should have been created" )
209+ assert .Positive (t , info .Size (), "config file should not be empty" )
210+ })
211+
212+ t .Run ("EditExistingConfig" , func (t * testing.T ) {
213+ tmpDir := t .TempDir ()
214+ dir := filepath .Join (tmpDir , "micasa" )
215+ require .NoError (t , os .MkdirAll (dir , 0o750 ))
216+ configPath := filepath .Join (dir , "config.toml" )
217+ original := "[locale]\n currency = \" EUR\" \n "
218+ require .NoError (t , os .WriteFile (configPath , []byte (original ), 0o600 ))
219+
220+ cmd := exec .CommandContext (t .Context (), bin , "config" , "edit" )
221+ cmd .Env = envWithEditor (tmpDir , noopEditor ())
222+ out , err := cmd .CombinedOutput ()
223+ require .NoError (t , err , "config edit failed: %s" , out )
224+
225+ content , readErr := os .ReadFile (configPath ) //nolint:gosec // test reads its own temp file
226+ require .NoError (t , readErr )
227+ assert .Equal (t , original , string (content ), "existing config should be untouched" )
180228 })
181229}
182230
@@ -350,3 +398,34 @@ func TestBackupCmd(t *testing.T) {
350398 assert .Contains (t , string (out ), "not a micasa database" )
351399 })
352400}
401+
402+ // noopEditor returns an editor command that exits 0 without modifying
403+ // any files. On Windows this uses "cmd /c echo" (ignores extra args
404+ // safely); on Unix it uses "true".
405+ func noopEditor () string {
406+ if runtime .GOOS == "windows" {
407+ return "cmd /c echo"
408+ }
409+ return "true"
410+ }
411+
412+ // envWithEditor returns a copy of os.Environ() with EDITOR and VISUAL
413+ // replaced, and XDG_CONFIG_HOME set to configHome. This avoids the
414+ // first-occurrence-wins semantics that would let the parent's EDITOR
415+ // shadow the test's override.
416+ func envWithEditor (configHome , editor string ) []string {
417+ var env []string
418+ for _ , e := range os .Environ () {
419+ if strings .HasPrefix (e , "EDITOR=" ) ||
420+ strings .HasPrefix (e , "VISUAL=" ) ||
421+ strings .HasPrefix (e , "XDG_CONFIG_HOME=" ) {
422+ continue
423+ }
424+ env = append (env , e )
425+ }
426+ return append (env ,
427+ "XDG_CONFIG_HOME=" + configHome ,
428+ "EDITOR=" + editor ,
429+ "VISUAL=" + editor ,
430+ )
431+ }
0 commit comments