Skip to content

Commit 927b602

Browse files
committed
fix: properly handle errors in upgrade auto-approve path
1 parent 93d07c7 commit 927b602

2 files changed

Lines changed: 140 additions & 0 deletions

File tree

cmd/upgrade/upgrade_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,38 @@ func TestUpgradeCommand(t *testing.T) {
7272
}
7373
updatePkgMock.AssertCalled(t, "CheckForUpdates", mock.Anything, mock.Anything, true)
7474
}
75+
76+
func TestUpgradeCommandWithAutoApproveError(t *testing.T) {
77+
// Create a mock of UpdateNotification that returns an error on InstallUpdatesWithoutPrompt
78+
originalCheckForUpdates := checkForUpdatesFunc
79+
defer func() {
80+
checkForUpdatesFunc = originalCheckForUpdates
81+
}()
82+
83+
// Create mocks
84+
ctx := slackcontext.MockContext(t.Context())
85+
clientsMock := shared.NewClientsMock()
86+
87+
// Create clients that is mocked for testing
88+
clients := shared.NewClientFactory(clientsMock.MockClientFactory())
89+
90+
// Mock the checkForUpdates function to simulate an error during auto-approve updates
91+
checkForUpdatesFunc = func(clients *shared.ClientFactory, cmd *cobra.Command, autoApprove bool) error {
92+
if autoApprove {
93+
return assert.AnError // Simulate error when auto-approve is true
94+
}
95+
return nil
96+
}
97+
98+
// Create the command with auto-approve flag
99+
cmd := NewCommand(clients)
100+
testutil.MockCmdIO(clients.IO, cmd)
101+
cmd.SetArgs([]string{"--auto-approve"})
102+
103+
// Execute the command and verify it returns the error
104+
err := cmd.ExecuteContext(ctx)
105+
106+
// Verify the error was properly propagated
107+
assert.Error(t, err)
108+
assert.Equal(t, assert.AnError, err)
109+
}

internal/update/update_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/slackapi/slack-cli/internal/shared"
2424
"github.com/slackapi/slack-cli/internal/slackdeps"
2525
"github.com/spf13/cobra"
26+
"github.com/stretchr/testify/assert"
2627
"github.com/stretchr/testify/mock"
2728
"github.com/stretchr/testify/require"
2829
)
@@ -109,3 +110,107 @@ func Test_Update_HasUpdate(t *testing.T) {
109110
})
110111
}
111112
}
113+
114+
func Test_Update_InstallUpdatesWithoutPrompt(t *testing.T) {
115+
for name, tt := range map[string]struct {
116+
dependencyHasUpdate []bool
117+
installUpdateErrors []error
118+
expectedErrorContains string
119+
}{
120+
"No dependencies have updates": {
121+
dependencyHasUpdate: []bool{false, false},
122+
installUpdateErrors: []error{nil, nil},
123+
expectedErrorContains: "",
124+
},
125+
"Dependencies have updates, all install successfully": {
126+
dependencyHasUpdate: []bool{true, true},
127+
installUpdateErrors: []error{nil, nil},
128+
expectedErrorContains: "",
129+
},
130+
"Dependencies have updates, first fails to install": {
131+
dependencyHasUpdate: []bool{true, false},
132+
installUpdateErrors: []error{assert.AnError, nil},
133+
expectedErrorContains: "general error for testing",
134+
},
135+
"Dependencies have updates, second fails to install": {
136+
dependencyHasUpdate: []bool{true, true},
137+
installUpdateErrors: []error{nil, assert.AnError},
138+
expectedErrorContains: "general error for testing",
139+
},
140+
} {
141+
t.Run(name, func(t *testing.T) {
142+
// Create clients
143+
clients := shared.ClientFactory{
144+
Config: config.NewConfig(slackdeps.NewFsMock(), slackdeps.NewOsMock()),
145+
SDKConfig: hooks.NewSDKConfigMock(),
146+
}
147+
148+
// Create the mocks
149+
var dependencies []Dependency
150+
151+
// Special handling for "first fails to install" case
152+
if name == "Dependencies have updates, first fails to install" {
153+
// Only create and add the first dependency since execution will stop after it fails
154+
mockDep1 := new(mockDependency)
155+
mockDep1.On("HasUpdate").Return(true, nil)
156+
mockDep1.On("PrintUpdateNotification", mock.Anything).Return(false, nil)
157+
mockDep1.On("InstallUpdate", mock.Anything).Return(assert.AnError)
158+
dependencies = append(dependencies, mockDep1)
159+
160+
// Second dependency is never called due to early return
161+
mockDep2 := new(mockDependency)
162+
dependencies = append(dependencies, mockDep2)
163+
} else {
164+
// Handle all other test cases normally
165+
for i, hasUpdate := range tt.dependencyHasUpdate {
166+
mockDep := new(mockDependency)
167+
mockDep.On("HasUpdate").Return(hasUpdate, nil)
168+
169+
// Only set expectations for PrintUpdateNotification and InstallUpdate
170+
// if the dependency hasUpdate = true
171+
if hasUpdate {
172+
mockDep.On("PrintUpdateNotification", mock.Anything).Return(false, nil)
173+
mockDep.On("InstallUpdate", mock.Anything).Return(tt.installUpdateErrors[i])
174+
}
175+
176+
dependencies = append(dependencies, mockDep)
177+
}
178+
}
179+
180+
// Create updateNotification
181+
updateNotification = &UpdateNotification{
182+
clients: &clients,
183+
enabled: true,
184+
envDisabled: "SLACK_SKIP_UPDATE",
185+
hoursToWait: defaultHoursToWait,
186+
dependencies: dependencies,
187+
}
188+
189+
// Create test cmd
190+
cmd := &cobra.Command{}
191+
192+
// Test
193+
err := updateNotification.InstallUpdatesWithoutPrompt(cmd)
194+
195+
if tt.expectedErrorContains != "" {
196+
require.Error(t, err)
197+
require.Contains(t, err.Error(), tt.expectedErrorContains)
198+
} else {
199+
require.NoError(t, err)
200+
}
201+
202+
// Only verify expectations on the first dependency for the "first fails" case
203+
// since the second dependency should never be called
204+
if name == "Dependencies have updates, first fails to install" {
205+
mockDep1 := dependencies[0].(*mockDependency)
206+
mockDep1.AssertExpectations(t)
207+
} else {
208+
// Verify all expectations were met for other cases
209+
for _, dep := range dependencies {
210+
mockDep := dep.(*mockDependency)
211+
mockDep.AssertExpectations(t)
212+
}
213+
}
214+
})
215+
}
216+
}

0 commit comments

Comments
 (0)