Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions internal/config/envfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,96 @@ func TestEncodeEnvValue(t *testing.T) {
}
}

func TestEnvFilePathFallbackToDefaultBaseDir(t *testing.T) {
got := EnvFilePath(" ")
if got != filepath.Join(defaultBaseDir(), envFileName) {
t.Fatalf("EnvFilePath(blank) = %q", got)
}
}

func TestPersistEnvVarErrorPaths(t *testing.T) {
t.Run("base dir is file", func(t *testing.T) {
tempRoot := t.TempDir()
fileBase := filepath.Join(tempRoot, "not-a-dir")
if err := os.WriteFile(fileBase, []byte("x"), 0o600); err != nil {
t.Fatalf("WriteFile() error = %v", err)
}
if err := PersistEnvVar(fileBase, "KEY", "value"); err == nil {
t.Fatal("expected mkdir error")
}
})

t.Run("read env file failed", func(t *testing.T) {
baseDir := t.TempDir()
envPath := EnvFilePath(baseDir)
if err := os.MkdirAll(envPath, 0o755); err != nil {
t.Fatalf("MkdirAll() error = %v", err)
}
if err := PersistEnvVar(baseDir, "KEY", "value"); err == nil {
t.Fatal("expected read error when env path is a directory")
}
})

t.Run("write env file failed", func(t *testing.T) {
baseDir := t.TempDir()
envPath := EnvFilePath(baseDir)
if err := os.WriteFile(envPath, []byte("KEY=old\n"), 0o400); err != nil {
t.Fatalf("WriteFile() error = %v", err)
}
if err := PersistEnvVar(baseDir, "KEY", "new"); err == nil {
t.Fatal("expected write error")
}
})
}

func TestLoadPersistedEnvErrorPaths(t *testing.T) {
t.Run("read env file failed", func(t *testing.T) {
baseDir := t.TempDir()
envPath := EnvFilePath(baseDir)
if err := os.MkdirAll(envPath, 0o755); err != nil {
t.Fatalf("MkdirAll() error = %v", err)
}
if err := LoadPersistedEnv(baseDir); err == nil {
t.Fatal("expected read error when env path is a directory")
}
})

t.Run("setenv failed on invalid key", func(t *testing.T) {
baseDir := t.TempDir()
envPath := EnvFilePath(baseDir)
if err := os.WriteFile(envPath, []byte("BAD\x00KEY=value\n"), 0o600); err != nil {
t.Fatalf("WriteFile() error = %v", err)
}
if err := LoadPersistedEnv(baseDir); err == nil {
t.Fatal("expected setenv error")
}
})
}

func TestRemovePersistedEnvVarErrorPaths(t *testing.T) {
t.Run("read env file failed", func(t *testing.T) {
baseDir := t.TempDir()
envPath := EnvFilePath(baseDir)
if err := os.MkdirAll(envPath, 0o755); err != nil {
t.Fatalf("MkdirAll() error = %v", err)
}
if err := RemovePersistedEnvVar(baseDir, "KEY"); err == nil {
t.Fatal("expected read error")
}
})

t.Run("write env file failed", func(t *testing.T) {
baseDir := t.TempDir()
envPath := EnvFilePath(baseDir)
if err := os.WriteFile(envPath, []byte("KEY=value\n"), 0o400); err != nil {
t.Fatalf("WriteFile() error = %v", err)
}
if err := RemovePersistedEnvVar(baseDir, "KEY"); err == nil {
t.Fatal("expected write error")
}
})
}

func captureEnv(t *testing.T, key string) func() {
t.Helper()
value, exists := os.LookupEnv(key)
Expand Down
96 changes: 96 additions & 0 deletions internal/config/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,102 @@ func TestDeleteCustomProviderRemovesProviderDir(t *testing.T) {
}
}

func TestLoadCustomProvidersReadDirAndStatErrors(t *testing.T) {
t.Run("providers dir read error", func(t *testing.T) {
baseDir := t.TempDir()
providersPath := filepath.Join(baseDir, providersDirName)
if err := os.WriteFile(providersPath, []byte("file"), 0o600); err != nil {
t.Fatalf("WriteFile() error = %v", err)
}

if _, err := loadCustomProviders(baseDir); err == nil {
t.Fatal("expected read providers dir error")
}
})

t.Run("provider yaml stat error", func(t *testing.T) {
baseDir := t.TempDir()
providerDir := filepath.Join(baseDir, providersDirName, "blocked")
if err := os.MkdirAll(providerDir, 0o755); err != nil {
t.Fatalf("MkdirAll() error = %v", err)
}
if err := os.Chmod(providerDir, 0o000); err != nil {
t.Fatalf("Chmod() error = %v", err)
}
defer func() { _ = os.Chmod(providerDir, 0o755) }()

if _, err := loadCustomProviders(baseDir); err == nil {
t.Fatal("expected stat error")
}
})
}

func TestLoadCustomProviderReadErrors(t *testing.T) {
t.Run("missing provider yaml", func(t *testing.T) {
providerDir := t.TempDir()
if _, err := loadCustomProvider(providerDir); err == nil {
t.Fatal("expected missing provider yaml error")
}
})

t.Run("provider yaml read error", func(t *testing.T) {
providerDir := t.TempDir()
providerPath := filepath.Join(providerDir, customProviderConfigName)
if err := os.MkdirAll(providerPath, 0o755); err != nil {
t.Fatalf("MkdirAll() error = %v", err)
}
if _, err := loadCustomProvider(providerDir); err == nil {
t.Fatal("expected provider yaml read error")
}
})
}

func TestSaveCustomProviderFileSystemErrors(t *testing.T) {
t.Run("mkdir provider dir failed", func(t *testing.T) {
root := t.TempDir()
baseDir := filepath.Join(root, "base-file")
if err := os.WriteFile(baseDir, []byte("x"), 0o600); err != nil {
t.Fatalf("WriteFile() error = %v", err)
}

err := SaveCustomProvider(
baseDir,
"team-gateway",
provider.DriverOpenAICompat,
"https://llm.example.com/v1",
"TEAM_GATEWAY_API_KEY",
provider.OpenAICompatibleAPIStyleChatCompletions,
"",
"",
)
if err == nil {
t.Fatal("expected create provider dir error")
}
})

t.Run("write provider yaml failed", func(t *testing.T) {
baseDir := t.TempDir()
providerDir := filepath.Join(baseDir, providersDirName, "team-gateway")
if err := os.MkdirAll(filepath.Join(providerDir, customProviderConfigName), 0o755); err != nil {
t.Fatalf("MkdirAll() error = %v", err)
}

err := SaveCustomProvider(
baseDir,
"team-gateway",
provider.DriverOpenAICompat,
"https://llm.example.com/v1",
"TEAM_GATEWAY_API_KEY",
provider.OpenAICompatibleAPIStyleChatCompletions,
"",
"",
)
if err == nil {
t.Fatal("expected write provider error")
}
})
}

func TestLoaderLoadsUnknownCustomProviderDriverUsingTopLevelBaseURL(t *testing.T) {
t.Parallel()

Expand Down
101 changes: 101 additions & 0 deletions internal/tui/core/app/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2581,6 +2581,107 @@ func TestUpdateLocalAndWorkspaceCommandResultBranches(t *testing.T) {
}
}

func TestUpdateCompactFinishedAndRefreshMessagesError(t *testing.T) {
app, runtime := newTestApp(t)
app.state.ActiveSessionID = "session-error"
runtime.loadSessionErr = errors.New("load session failed")

model, _ := app.Update(compactFinishedMsg{Err: errors.New("compact failed")})
app = model.(App)
if app.state.IsCompacting {
t.Fatalf("expected compacting state to be cleared")
}
if app.state.ExecutionError != "load session failed" {
t.Fatalf("expected refresh message error to win, got %q", app.state.ExecutionError)
}
if len(app.activeMessages) == 0 || app.activeMessages[len(app.activeMessages)-1].Role != roleError {
t.Fatalf("expected inline error message appended")
}
}

func TestUpdateLocalCommandProviderChangedRefreshErrors(t *testing.T) {
app, _ := newTestApp(t)
app.providerSvc = errorProviderService{err: errors.New("refresh providers failed")}

model, _ := app.Update(localCommandResultMsg{
Notice: "ok",
ProviderChanged: true,
})
app = model.(App)
if app.state.ExecutionError != "refresh providers failed" {
t.Fatalf("expected provider refresh error, got %q", app.state.ExecutionError)
}
if len(app.activities) == 0 {
t.Fatalf("expected failure activity")
}
}

func TestUpdateKeyToggleQuitCancelAndPickerClose(t *testing.T) {
app, runtime := newTestApp(t)

model, _ := app.Update(tea.KeyMsg{Type: tea.KeyCtrlQ})
app = model.(App)
if !app.state.ShowHelp {
t.Fatalf("expected help to toggle on")
}

app.state.IsAgentRunning = true
model, _ = app.Update(tea.KeyMsg{Type: tea.KeyCtrlW})
app = model.(App)
if !runtime.cancelInvoked {
t.Fatalf("expected cancel to be invoked")
}
if app.state.StatusText != statusCanceling {
t.Fatalf("expected canceling status, got %q", app.state.StatusText)
}

app.openHelpPicker()
model, cmd := app.Update(tea.KeyMsg{Type: tea.KeyEsc})
app = model.(App)
if cmd != nil {
t.Fatalf("expected nil cmd when closing active picker")
}
if app.state.ActivePicker != pickerNone {
t.Fatalf("expected picker to close on focus input key")
}

model, cmd = app.Update(tea.KeyMsg{Type: tea.KeyCtrlU})
if model == nil || cmd == nil {
t.Fatalf("expected quit command")
}
}

func TestUpdatePickerEnterInvalidSelectionsAndSessionActivationError(t *testing.T) {
app, _ := newTestApp(t)

app.providerPicker.SetItems([]list.Item{sessionItem{Summary: agentsession.Summary{ID: "s1"}}})
app.openPicker(pickerProvider, statusChooseProvider, &app.providerPicker, "")
model, cmd := app.updatePicker(tea.KeyMsg{Type: tea.KeyEnter})
app = model.(App)
if cmd != nil {
t.Fatalf("expected nil cmd when provider picker item type is invalid")
}

app.modelPicker.SetItems([]list.Item{sessionItem{Summary: agentsession.Summary{ID: "s1"}}})
app.openPicker(pickerModel, statusChooseModel, &app.modelPicker, "")
model, cmd = app.updatePicker(tea.KeyMsg{Type: tea.KeyEnter})
app = model.(App)
if cmd != nil {
t.Fatalf("expected nil cmd when model picker item type is invalid")
}

app.sessionPicker.SetItems([]list.Item{sessionItem{Summary: agentsession.Summary{ID: "missing", Title: "missing"}}})
app.openPicker(pickerSession, statusChooseSession, &app.sessionPicker, "")
model, cmd = app.updatePicker(tea.KeyMsg{Type: tea.KeyEnter})
app = model.(App)
if cmd != nil {
t.Fatalf("expected nil cmd for session picker enter")
}
if app.state.ExecutionError == "" {
t.Fatalf("expected session activation error to be recorded")
}
}

func TestUpdateInputPanelSlashAndWorkspaceBranches(t *testing.T) {
app, _ := newTestApp(t)

Expand Down
Loading
Loading