mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
* docs: Add config merging design spec for #114 Defines the merge-during-setup-install approach for keeping user configs up to date with new template keys while preserving all existing values, comments, and formatting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: Add implementation plan for config merging TDD-based plan with 6 tasks: dependency setup, failing tests, core merge implementation, integration test, WriteTemplateConfig integration, and full verification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: Merge template config into existing user config during setup install Instead of skipping when a config file exists, WriteTemplateConfig() now merges missing keys from the embedded template into the user's config using YAML AST manipulation. Preserves all user values, comments, and formatting. Only adds keys present in the template but absent in the user's config. Closes #114 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: Graceful error handling for config loading and setup commands Replace panics in loadViperConfig with error returns so the app falls back to defaults instead of crashing on malformed config files. Add SilenceUsage to setup install/remove commands so runtime errors don't dump the full usage text. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * add test cmd in readme * update copy text * refactor: Address review feedback on config merging - Rename existing/template to dest/source for generic util naming - Remove unnecessary code comments (Rule N references, obvious comments) - Add AGENTS.md with dev guide and code style rules, symlink CLAUDE.md - Add BenchmarkMergeYAML (~46μs/op on M4 Pro) - Remove stale design spec Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * update `MergeYAML` to use from dry/utils * update AGENTS.md --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
98 lines
2.5 KiB
Go
98 lines
2.5 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestConfigIsNeverNil(t *testing.T) {
|
|
config := Get()
|
|
assert.NotNil(t, config)
|
|
}
|
|
|
|
func TestConfigHasDefaultValues(t *testing.T) {
|
|
t.Run("with non-existent config directory", func(t *testing.T) {
|
|
t.Setenv("PMG_CONFIG_DIR", "/tmp/pmg-test/random-does-not-exist")
|
|
initConfig()
|
|
|
|
config := Get()
|
|
assert.Equal(t, true, config.Config.Transitive)
|
|
assert.Equal(t, 5, config.Config.TransitiveDepth)
|
|
assert.Equal(t, false, config.Config.IncludeDevDependencies)
|
|
assert.Equal(t, false, config.Config.Paranoid)
|
|
assert.Equal(t, []TrustedPackage{}, config.Config.TrustedPackages)
|
|
assert.Equal(t, "/tmp/pmg-test/random-does-not-exist", config.configDir)
|
|
assert.Equal(t, "/tmp/pmg-test/random-does-not-exist/config.yml", config.configFilePath)
|
|
})
|
|
|
|
t.Run("when no config directory is set", func(t *testing.T) {
|
|
t.Setenv("PMG_CONFIG_DIR", "")
|
|
initConfig()
|
|
|
|
config := Get()
|
|
|
|
userConfigDir, err := os.UserConfigDir()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, filepath.Join(userConfigDir, "safedep/pmg"), config.configDir)
|
|
assert.Equal(t, filepath.Join(userConfigDir, "safedep/pmg/config.yml"), config.configFilePath)
|
|
})
|
|
}
|
|
|
|
func TestWriteTemplateConfigMergesExistingConfig(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
t.Setenv("PMG_CONFIG_DIR", tmpDir)
|
|
|
|
configPath := filepath.Join(tmpDir, "config.yml")
|
|
|
|
// Write a partial user config
|
|
userConfig := []byte("transitive: false\ntransitive_depth: 10\n")
|
|
err := os.WriteFile(configPath, userConfig, 0o644)
|
|
require.NoError(t, err)
|
|
|
|
// Re-init so paths point to tmpDir
|
|
initConfig()
|
|
|
|
// Run WriteTemplateConfig — should merge, not skip
|
|
err = WriteTemplateConfig()
|
|
require.NoError(t, err)
|
|
|
|
// Read back
|
|
result, err := os.ReadFile(configPath)
|
|
require.NoError(t, err)
|
|
|
|
raw := string(result)
|
|
|
|
// User values preserved
|
|
assert.Contains(t, raw, "transitive: false")
|
|
assert.Contains(t, raw, "transitive_depth: 10")
|
|
|
|
// New keys from template added
|
|
assert.Contains(t, raw, "proxy_mode:")
|
|
assert.Contains(t, raw, "sandbox:")
|
|
assert.Contains(t, raw, "verbosity:")
|
|
}
|
|
|
|
func TestWriteTemplateConfigCreatesNewFile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
t.Setenv("PMG_CONFIG_DIR", tmpDir)
|
|
|
|
initConfig()
|
|
|
|
err := WriteTemplateConfig()
|
|
require.NoError(t, err)
|
|
|
|
configPath := filepath.Join(tmpDir, "config.yml")
|
|
result, err := os.ReadFile(configPath)
|
|
require.NoError(t, err)
|
|
|
|
// Should be the full template
|
|
assert.Equal(t, templateConfig, string(result))
|
|
}
|