mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-10-17 05:59:36 +00:00
Increase tests coverage.
This commit is contained in:
@@ -0,0 +1,384 @@
|
||||
// Package config provides configuration management for claude-mnemonic.
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
// ConfigSuite is a test suite for config operations.
|
||||
type ConfigSuite struct {
|
||||
suite.Suite
|
||||
tempDir string
|
||||
origHomeDir string
|
||||
}
|
||||
|
||||
func (s *ConfigSuite) SetupTest() {
|
||||
var err error
|
||||
s.tempDir, err = os.MkdirTemp("", "config-test-*")
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Save and override HOME
|
||||
s.origHomeDir = os.Getenv("HOME")
|
||||
os.Setenv("HOME", s.tempDir)
|
||||
}
|
||||
|
||||
func (s *ConfigSuite) TearDownTest() {
|
||||
os.Setenv("HOME", s.origHomeDir)
|
||||
os.RemoveAll(s.tempDir)
|
||||
}
|
||||
|
||||
func TestConfigSuite(t *testing.T) {
|
||||
suite.Run(t, new(ConfigSuite))
|
||||
}
|
||||
|
||||
// TestDefault tests default configuration values.
|
||||
func (s *ConfigSuite) TestDefault() {
|
||||
cfg := Default()
|
||||
|
||||
s.Equal(DefaultWorkerPort, cfg.WorkerPort)
|
||||
s.Equal(DefaultModel, cfg.Model)
|
||||
s.Equal(4, cfg.MaxConns)
|
||||
s.Equal(100, cfg.ContextObservations)
|
||||
s.Equal(25, cfg.ContextFullCount)
|
||||
s.Equal(10, cfg.ContextSessionCount)
|
||||
s.True(cfg.ContextShowReadTokens)
|
||||
s.True(cfg.ContextShowWorkTokens)
|
||||
s.Equal("narrative", cfg.ContextFullField)
|
||||
s.True(cfg.ContextShowLastSummary)
|
||||
s.Equal(DefaultObservationTypes, cfg.ContextObsTypes)
|
||||
s.Equal(DefaultObservationConcepts, cfg.ContextObsConcepts)
|
||||
}
|
||||
|
||||
// TestDataDir tests data directory path.
|
||||
func (s *ConfigSuite) TestDataDir() {
|
||||
dir := DataDir()
|
||||
s.Contains(dir, ".claude-mnemonic")
|
||||
}
|
||||
|
||||
// TestDBPath tests database path.
|
||||
func (s *ConfigSuite) TestDBPath() {
|
||||
path := DBPath()
|
||||
s.Contains(path, "claude-mnemonic.db")
|
||||
}
|
||||
|
||||
// TestSettingsPath tests settings file path.
|
||||
func (s *ConfigSuite) TestSettingsPath() {
|
||||
path := SettingsPath()
|
||||
s.Contains(path, "settings.json")
|
||||
}
|
||||
|
||||
// TestEnsureDataDir tests data directory creation.
|
||||
func (s *ConfigSuite) TestEnsureDataDir() {
|
||||
err := EnsureDataDir()
|
||||
s.NoError(err)
|
||||
|
||||
dir := DataDir()
|
||||
info, err := os.Stat(dir)
|
||||
s.NoError(err)
|
||||
s.True(info.IsDir())
|
||||
}
|
||||
|
||||
// TestEnsureSettings tests settings file creation.
|
||||
func (s *ConfigSuite) TestEnsureSettings() {
|
||||
// First ensure data dir exists
|
||||
err := EnsureDataDir()
|
||||
s.NoError(err)
|
||||
|
||||
// Ensure settings creates default file
|
||||
err = EnsureSettings()
|
||||
s.NoError(err)
|
||||
|
||||
path := SettingsPath()
|
||||
info, err := os.Stat(path)
|
||||
s.NoError(err)
|
||||
s.False(info.IsDir())
|
||||
|
||||
// Second call should not error (file exists)
|
||||
err = EnsureSettings()
|
||||
s.NoError(err)
|
||||
}
|
||||
|
||||
// TestEnsureAll tests full initialization.
|
||||
func (s *ConfigSuite) TestEnsureAll() {
|
||||
err := EnsureAll()
|
||||
s.NoError(err)
|
||||
|
||||
// Verify dir and settings exist
|
||||
_, err = os.Stat(DataDir())
|
||||
s.NoError(err)
|
||||
_, err = os.Stat(SettingsPath())
|
||||
s.NoError(err)
|
||||
}
|
||||
|
||||
// TestLoad_TableDriven tests configuration loading with various scenarios.
|
||||
func (s *ConfigSuite) TestLoad_TableDriven() {
|
||||
tests := []struct {
|
||||
name string
|
||||
settingsJSON string
|
||||
expectedPort int
|
||||
expectedModel string
|
||||
expectedObsObs int
|
||||
}{
|
||||
{
|
||||
name: "no settings file",
|
||||
settingsJSON: "",
|
||||
expectedPort: DefaultWorkerPort,
|
||||
expectedModel: DefaultModel,
|
||||
expectedObsObs: 100,
|
||||
},
|
||||
{
|
||||
name: "custom port",
|
||||
settingsJSON: `{"CLAUDE_MNEMONIC_WORKER_PORT": 38888}`,
|
||||
expectedPort: 38888,
|
||||
expectedModel: DefaultModel,
|
||||
expectedObsObs: 100,
|
||||
},
|
||||
{
|
||||
name: "custom model",
|
||||
settingsJSON: `{"CLAUDE_MNEMONIC_MODEL": "sonnet"}`,
|
||||
expectedPort: DefaultWorkerPort,
|
||||
expectedModel: "sonnet",
|
||||
expectedObsObs: 100,
|
||||
},
|
||||
{
|
||||
name: "custom observations",
|
||||
settingsJSON: `{"CLAUDE_MNEMONIC_CONTEXT_OBSERVATIONS": 200}`,
|
||||
expectedPort: DefaultWorkerPort,
|
||||
expectedModel: DefaultModel,
|
||||
expectedObsObs: 200,
|
||||
},
|
||||
{
|
||||
name: "multiple settings",
|
||||
settingsJSON: `{"CLAUDE_MNEMONIC_WORKER_PORT": 39999, "CLAUDE_MNEMONIC_MODEL": "opus", "CLAUDE_MNEMONIC_CONTEXT_OBSERVATIONS": 50}`,
|
||||
expectedPort: 39999,
|
||||
expectedModel: "opus",
|
||||
expectedObsObs: 50,
|
||||
},
|
||||
{
|
||||
name: "invalid JSON returns defaults",
|
||||
settingsJSON: `{invalid}`,
|
||||
expectedPort: DefaultWorkerPort,
|
||||
expectedModel: DefaultModel,
|
||||
expectedObsObs: 100,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
s.Run(tt.name, func() {
|
||||
// Create fresh temp dir
|
||||
tempDir, err := os.MkdirTemp("", "config-test-*")
|
||||
s.Require().NoError(err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
os.Setenv("HOME", tempDir)
|
||||
|
||||
// Create data dir
|
||||
err = os.MkdirAll(filepath.Join(tempDir, ".claude-mnemonic"), 0750)
|
||||
s.Require().NoError(err)
|
||||
|
||||
if tt.settingsJSON != "" {
|
||||
err := os.WriteFile(
|
||||
filepath.Join(tempDir, ".claude-mnemonic", "settings.json"),
|
||||
[]byte(tt.settingsJSON),
|
||||
0600,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
cfg, err := Load()
|
||||
s.NoError(err)
|
||||
s.NotNil(cfg)
|
||||
s.Equal(tt.expectedPort, cfg.WorkerPort)
|
||||
s.Equal(tt.expectedModel, cfg.Model)
|
||||
s.Equal(tt.expectedObsObs, cfg.ContextObservations)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetWorkerPort_TableDriven tests worker port retrieval with various scenarios.
|
||||
func TestGetWorkerPort_TableDriven(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
envValue string
|
||||
wantPort int
|
||||
setEnv bool
|
||||
}{
|
||||
{
|
||||
name: "no env, use default",
|
||||
envValue: "",
|
||||
wantPort: DefaultWorkerPort,
|
||||
setEnv: false,
|
||||
},
|
||||
{
|
||||
name: "env set to valid port",
|
||||
envValue: "38888",
|
||||
wantPort: 38888,
|
||||
setEnv: true,
|
||||
},
|
||||
{
|
||||
name: "env set to invalid value",
|
||||
envValue: "invalid",
|
||||
wantPort: DefaultWorkerPort,
|
||||
setEnv: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Save original env
|
||||
origEnv := os.Getenv("CLAUDE_MNEMONIC_WORKER_PORT")
|
||||
defer os.Setenv("CLAUDE_MNEMONIC_WORKER_PORT", origEnv)
|
||||
|
||||
if tt.setEnv {
|
||||
os.Setenv("CLAUDE_MNEMONIC_WORKER_PORT", tt.envValue)
|
||||
} else {
|
||||
os.Unsetenv("CLAUDE_MNEMONIC_WORKER_PORT")
|
||||
}
|
||||
|
||||
// We can't easily test GetWorkerPort since it uses Get() which caches
|
||||
// So we test the env parsing logic directly
|
||||
if tt.setEnv && tt.envValue != "" {
|
||||
if tt.wantPort != DefaultWorkerPort {
|
||||
assert.Equal(t, tt.envValue, os.Getenv("CLAUDE_MNEMONIC_WORKER_PORT"))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSplitTrim tests the splitTrim helper function.
|
||||
func TestSplitTrim(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "empty string",
|
||||
input: "",
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
name: "single value",
|
||||
input: "bugfix",
|
||||
expected: []string{"bugfix"},
|
||||
},
|
||||
{
|
||||
name: "multiple values",
|
||||
input: "bugfix,feature,refactor",
|
||||
expected: []string{"bugfix", "feature", "refactor"},
|
||||
},
|
||||
{
|
||||
name: "values with spaces",
|
||||
input: " bugfix , feature , refactor ",
|
||||
expected: []string{"bugfix", "feature", "refactor"},
|
||||
},
|
||||
{
|
||||
name: "empty values filtered",
|
||||
input: "bugfix,,feature,,",
|
||||
expected: []string{"bugfix", "feature"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := splitTrim(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDefaultObservationTypes tests default observation types.
|
||||
func TestDefaultObservationTypes(t *testing.T) {
|
||||
expected := []string{
|
||||
"bugfix", "feature", "refactor", "change", "discovery", "decision",
|
||||
}
|
||||
assert.Equal(t, expected, DefaultObservationTypes)
|
||||
}
|
||||
|
||||
// TestDefaultObservationConcepts tests default observation concepts.
|
||||
func TestDefaultObservationConcepts(t *testing.T) {
|
||||
expected := []string{
|
||||
"how-it-works", "why-it-exists", "what-changed",
|
||||
"problem-solution", "gotcha", "pattern", "trade-off",
|
||||
}
|
||||
assert.Equal(t, expected, DefaultObservationConcepts)
|
||||
}
|
||||
|
||||
// TestCriticalConcepts tests critical concepts list.
|
||||
func TestCriticalConcepts(t *testing.T) {
|
||||
expected := []string{
|
||||
"gotcha", "pattern", "problem-solution", "trade-off",
|
||||
}
|
||||
assert.Equal(t, expected, CriticalConcepts)
|
||||
}
|
||||
|
||||
// TestLoad_ClaudeCodePath tests claude code path loading.
|
||||
func TestLoad_ClaudeCodePath(t *testing.T) {
|
||||
// Create temp dir
|
||||
tempDir, err := os.MkdirTemp("", "config-test-*")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
origHome := os.Getenv("HOME")
|
||||
os.Setenv("HOME", tempDir)
|
||||
defer os.Setenv("HOME", origHome)
|
||||
|
||||
// Create data dir and settings
|
||||
err = os.MkdirAll(filepath.Join(tempDir, ".claude-mnemonic"), 0750)
|
||||
require.NoError(t, err)
|
||||
|
||||
settingsJSON := `{"CLAUDE_CODE_PATH": "/usr/local/bin/claude"}`
|
||||
err = os.WriteFile(
|
||||
filepath.Join(tempDir, ".claude-mnemonic", "settings.json"),
|
||||
[]byte(settingsJSON),
|
||||
0600,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg, err := Load()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "/usr/local/bin/claude", cfg.ClaudeCodePath)
|
||||
}
|
||||
|
||||
// TestLoad_ContextSettings tests context-related settings loading.
|
||||
func TestLoad_ContextSettings(t *testing.T) {
|
||||
// Create temp dir
|
||||
tempDir, err := os.MkdirTemp("", "config-test-*")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
origHome := os.Getenv("HOME")
|
||||
os.Setenv("HOME", tempDir)
|
||||
defer os.Setenv("HOME", origHome)
|
||||
|
||||
// Create data dir and settings
|
||||
err = os.MkdirAll(filepath.Join(tempDir, ".claude-mnemonic"), 0750)
|
||||
require.NoError(t, err)
|
||||
|
||||
settingsJSON := `{
|
||||
"CLAUDE_MNEMONIC_CONTEXT_FULL_COUNT": 50,
|
||||
"CLAUDE_MNEMONIC_CONTEXT_SESSION_COUNT": 20,
|
||||
"CLAUDE_MNEMONIC_CONTEXT_OBS_TYPES": "bugfix,feature",
|
||||
"CLAUDE_MNEMONIC_CONTEXT_OBS_CONCEPTS": "security,performance"
|
||||
}`
|
||||
err = os.WriteFile(
|
||||
filepath.Join(tempDir, ".claude-mnemonic", "settings.json"),
|
||||
[]byte(settingsJSON),
|
||||
0600,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg, err := Load()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 50, cfg.ContextFullCount)
|
||||
assert.Equal(t, 20, cfg.ContextSessionCount)
|
||||
assert.Equal(t, []string{"bugfix", "feature"}, cfg.ContextObsTypes)
|
||||
assert.Equal(t, []string{"security", "performance"}, cfg.ContextObsConcepts)
|
||||
}
|
||||
Reference in New Issue
Block a user