@@ -1,465 +0,0 @@
9− "github.com/kujtimiihoxha/termai/internal/llm/models"
10− "github.com/spf13/viper"
11− "github.com/stretchr/testify/assert"
12− "github.com/stretchr/testify/require"
15−func TestLoad(t *testing.T) {
18− t.Run("loads configuration successfully", func(t *testing.T) {
19− homeDir := t.TempDir()
20− t.Setenv("HOME", homeDir)
21− configPath := filepath.Join(homeDir, ".termai.json")
25− "directory": "custom-dir"
32− "command": "test-command",
33− "env": ["TEST_ENV=value"],
34− "args": ["--arg1", "--arg2"],
44− "url": "https://api.example.com/events",
46− "Authorization": "Bearer token123",
47− "Content-Type": "application/json"
53− "apiKey": "test-api-key",
58− "coder": "claude-3-haiku",
59− "task": "claude-3-haiku"
62− err := os.WriteFile(configPath, []byte(configContent), 0o644)
63− require.NoError(t, err)
69− require.NoError(t, err)
72− assert.NotNil(t, config)
73− assert.Equal(t, "custom-dir", config.Data.Directory)
74− assert.Equal(t, "debug", config.Log.Level)
76− assert.Contains(t, config.MCPServers, "test-server")
77− stdioServer := config.MCPServers["test-server"]
78− assert.Equal(t, "test-command", stdioServer.Command)
79− assert.Equal(t, []string{"TEST_ENV=value"}, stdioServer.Env)
80− assert.Equal(t, []string{"--arg1", "--arg2"}, stdioServer.Args)
81− assert.Equal(t, MCPStdio, stdioServer.Type)
82− assert.Equal(t, "", stdioServer.URL)
83− assert.Empty(t, stdioServer.Headers)
85− assert.Contains(t, config.MCPServers, "sse-server")
86− sseServer := config.MCPServers["sse-server"]
87− assert.Equal(t, "", sseServer.Command)
88− assert.Empty(t, sseServer.Env)
89− assert.Empty(t, sseServer.Args)
90− assert.Equal(t, MCPSse, sseServer.Type)
91− assert.Equal(t, "https://api.example.com/events", sseServer.URL)
92− assert.Equal(t, map[string]string{
93− "authorization": "Bearer token123",
94− "content-type": "application/json",
95− }, sseServer.Headers)
97− assert.Contains(t, config.Providers, models.ModelProvider("anthropic"))
98− provider := config.Providers[models.ModelProvider("anthropic")]
99− assert.Equal(t, "test-api-key", provider.APIKey)
100− assert.True(t, provider.Enabled)
102− assert.NotNil(t, config.Model)
103− assert.Equal(t, models.Claude3Haiku, config.Model.Coder)
104− assert.Equal(t, models.Claude3Haiku, config.Model.Task)
105− assert.Equal(t, defaultMaxTokens, config.Model.CoderMaxTokens)
108− t.Run("loads configuration with environment variables", func(t *testing.T) {
109− homeDir := t.TempDir()
110− t.Setenv("HOME", homeDir)
111− configPath := filepath.Join(homeDir, ".termai.json")
112− err := os.WriteFile(configPath, []byte("{}"), 0o644)
113− require.NoError(t, err)
115− t.Setenv("ANTHROPIC_API_KEY", "env-anthropic-key")
116− t.Setenv("OPENAI_API_KEY", "env-openai-key")
117− t.Setenv("GEMINI_API_KEY", "env-gemini-key")
123− require.NoError(t, err)
126− assert.NotNil(t, config)
128− assert.Equal(t, defaultDataDirectory, config.Data.Directory)
129− assert.Equal(t, defaultLogLevel, config.Log.Level)
131− assert.Contains(t, config.Providers, models.ModelProvider("anthropic"))
132− assert.Equal(t, "env-anthropic-key", config.Providers[models.ModelProvider("anthropic")].APIKey)
133− assert.True(t, config.Providers[models.ModelProvider("anthropic")].Enabled)
135− assert.Contains(t, config.Providers, models.ModelProvider("openai"))
136− assert.Equal(t, "env-openai-key", config.Providers[models.ModelProvider("openai")].APIKey)
137− assert.True(t, config.Providers[models.ModelProvider("openai")].Enabled)
139− assert.Contains(t, config.Providers, models.ModelProvider("gemini"))
140− assert.Equal(t, "env-gemini-key", config.Providers[models.ModelProvider("gemini")].APIKey)
141− assert.True(t, config.Providers[models.ModelProvider("gemini")].Enabled)
143− assert.Equal(t, models.Claude37Sonnet, config.Model.Coder)
146− t.Run("local config overrides global config", func(t *testing.T) {
147− homeDir := t.TempDir()
148− t.Setenv("HOME", homeDir)
149− globalConfigPath := filepath.Join(homeDir, ".termai.json")
152− "directory": "global-dir"
158− err := os.WriteFile(globalConfigPath, []byte(globalConfig), 0o644)
159− require.NoError(t, err)
161− workDir := t.TempDir()
162− origDir, err := os.Getwd()
163− require.NoError(t, err)
164− defer os.Chdir(origDir)
165− err = os.Chdir(workDir)
166− require.NoError(t, err)
168− localConfigPath := filepath.Join(workDir, ".termai.json")
171− "directory": "local-dir"
177− err = os.WriteFile(localConfigPath, []byte(localConfig), 0o644)
178− require.NoError(t, err)
184− require.NoError(t, err)
187− assert.NotNil(t, config)
189− assert.Equal(t, "local-dir", config.Data.Directory)
190− assert.Equal(t, "debug", config.Log.Level)
193− t.Run("missing config file should not return error", func(t *testing.T) {
194− emptyDir := t.TempDir()
195− t.Setenv("HOME", emptyDir)
201− assert.NoError(t, err)
204− t.Run("model priority and fallbacks", func(t *testing.T) {
205− testCases := []struct {
210− expectedModel models.ModelID
211− explicitModel models.ModelID
212− useExplicitModel bool
215− name: "anthropic has priority",
216− anthropicKey: "test-key",
217− openaiKey: "test-key",
218− geminiKey: "test-key",
219− expectedModel: models.Claude37Sonnet,
222− name: "fallback to openai when no anthropic",
224− openaiKey: "test-key",
225− geminiKey: "test-key",
226− expectedModel: models.GPT41,
229− name: "fallback to gemini when no others",
232− geminiKey: "test-key",
233− expectedModel: models.GRMINI20Flash,
236− name: "explicit model overrides defaults",
237− anthropicKey: "test-key",
238− openaiKey: "test-key",
239− geminiKey: "test-key",
240− explicitModel: models.GPT41,
241− useExplicitModel: true,
242− expectedModel: models.GPT41,
246− for _, tc := range testCases {
247− t.Run(tc.name, func(t *testing.T) {
248− homeDir := t.TempDir()
249− t.Setenv("HOME", homeDir)
250− configPath := filepath.Join(homeDir, ".termai.json")
252− configContent := "{}"
253− if tc.useExplicitModel {
254− configContent = fmt.Sprintf(`{"model":{"coder":"%s"}}`, tc.explicitModel)
257− err := os.WriteFile(configPath, []byte(configContent), 0o644)
258− require.NoError(t, err)
260− if tc.anthropicKey != "" {
261− t.Setenv("ANTHROPIC_API_KEY", tc.anthropicKey)
263− t.Setenv("ANTHROPIC_API_KEY", "")
266− if tc.openaiKey != "" {
267− t.Setenv("OPENAI_API_KEY", tc.openaiKey)
269− t.Setenv("OPENAI_API_KEY", "")
272− if tc.geminiKey != "" {
273− t.Setenv("GEMINI_API_KEY", tc.geminiKey)
275− t.Setenv("GEMINI_API_KEY", "")
282− require.NoError(t, err)
285− assert.NotNil(t, config)
286− assert.Equal(t, tc.expectedModel, config.Model.Coder)
292−func TestGet(t *testing.T) {
293− t.Run("get returns same config instance", func(t *testing.T) {
295− homeDir := t.TempDir()
296− t.Setenv("HOME", homeDir)
297− configPath := filepath.Join(homeDir, ".termai.json")
298− err := os.WriteFile(configPath, []byte("{}"), 0o644)
299− require.NoError(t, err)
305− require.NotNil(t, config1)
308− require.NotNil(t, config2)
310− assert.Same(t, config1, config2)
313− t.Run("get loads config if not loaded", func(t *testing.T) {
315− homeDir := t.TempDir()
316− t.Setenv("HOME", homeDir)
317− configPath := filepath.Join(homeDir, ".termai.json")
318− configContent := `{"data":{"directory":"test-dir"}}`
319− err := os.WriteFile(configPath, []byte(configContent), 0o644)
320− require.NoError(t, err)
326− require.NotNil(t, config)
327− assert.Equal(t, "test-dir", config.Data.Directory)
331−func TestWorkingDirectory(t *testing.T) {
332− t.Run("returns current working directory", func(t *testing.T) {
334− homeDir := t.TempDir()
335− t.Setenv("HOME", homeDir)
336− configPath := filepath.Join(homeDir, ".termai.json")
337− err := os.WriteFile(configPath, []byte("{}"), 0o644)
338− require.NoError(t, err)
344− require.NoError(t, err)
346− wd := WorkingDirectory()
347− expectedWd, err := os.Getwd()
348− require.NoError(t, err)
349− assert.Equal(t, expectedWd, wd)
353−func TestWrite(t *testing.T) {
354− t.Run("writes config to file", func(t *testing.T) {
356− homeDir := t.TempDir()
357− t.Setenv("HOME", homeDir)
358− configPath := filepath.Join(homeDir, ".termai.json")
359− err := os.WriteFile(configPath, []byte("{}"), 0o644)
360− require.NoError(t, err)
366− require.NoError(t, err)
368− viper.Set("data.directory", "modified-dir")
371− require.NoError(t, err)
373− content, err := os.ReadFile(configPath)
374− require.NoError(t, err)
375− assert.Contains(t, string(content), "modified-dir")
379−func TestMCPType(t *testing.T) {
380− t.Run("MCPType constants", func(t *testing.T) {
381− assert.Equal(t, MCPType("stdio"), MCPStdio)
382− assert.Equal(t, MCPType("sse"), MCPSse)
385− t.Run("MCPType JSON unmarshaling", func(t *testing.T) {
386− homeDir := t.TempDir()
387− t.Setenv("HOME", homeDir)
388− configPath := filepath.Join(homeDir, ".termai.json")
403− err := os.WriteFile(configPath, []byte(configContent), 0o644)
404− require.NoError(t, err)
410− require.NoError(t, err)
413− assert.NotNil(t, config)
415− assert.Equal(t, MCPStdio, config.MCPServers["stdio-server"].Type)
416− assert.Equal(t, MCPSse, config.MCPServers["sse-server"].Type)
417− assert.Equal(t, MCPType("invalid"), config.MCPServers["invalid-server"].Type)
420− t.Run("default MCPType", func(t *testing.T) {
421− homeDir := t.TempDir()
422− t.Setenv("HOME", homeDir)
423− configPath := filepath.Join(homeDir, ".termai.json")
428− "command": "test-command"
432− err := os.WriteFile(configPath, []byte(configContent), 0o644)
433− require.NoError(t, err)
439− require.NoError(t, err)
442− assert.NotNil(t, config)
444− assert.Equal(t, MCPType(""), config.MCPServers["test-server"].Type)
448−func setupTest(t *testing.T) {
449− origHome := os.Getenv("HOME")
450− origXdgConfigHome := os.Getenv("XDG_CONFIG_HOME")
451− origAnthropicKey := os.Getenv("ANTHROPIC_API_KEY")
452− origOpenAIKey := os.Getenv("OPENAI_API_KEY")
453− origGeminiKey := os.Getenv("GEMINI_API_KEY")
456− t.Setenv("HOME", origHome)
457− t.Setenv("XDG_CONFIG_HOME", origXdgConfigHome)
458− t.Setenv("ANTHROPIC_API_KEY", origAnthropicKey)
459− t.Setenv("OPENAI_API_KEY", origOpenAIKey)
460− t.Setenv("GEMINI_API_KEY", origGeminiKey)