Skip to content

Commit cc85f17

Browse files
authored
chore(v3): fix state management race condition (#3203)
* chore(v3): fix state management * f * f * f * f * reporting * f * f * f * f * f * cursor feedback * cursor feedback
1 parent 424b7dd commit cc85f17

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1955
-2222
lines changed

api/controllers/app/apppreflight.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ type RunAppPreflightOptions struct {
2323
}
2424

2525
func (c *AppController) RunAppPreflights(ctx context.Context, opts RunAppPreflightOptions) (finalErr error) {
26+
logger := c.logger.WithField("operation", "run-app-preflights")
27+
2628
lock, err := c.stateMachine.AcquireLock()
2729
if err != nil {
2830
return types.NewConflictError(err)
@@ -41,6 +43,11 @@ func (c *AppController) RunAppPreflights(ctx context.Context, opts RunAppPreflig
4143
return types.NewConflictError(err)
4244
}
4345

46+
// Clear any previous preflight results immediately to prevent serving stale data
47+
if err := c.appPreflightManager.ClearAppPreflightResults(ctx); err != nil {
48+
return fmt.Errorf("clear previous preflight results: %w", err)
49+
}
50+
4451
// Get the app config values
4552
configValues, err := c.GetAppConfigValues(ctx)
4653
if err != nil {
@@ -57,7 +64,7 @@ func (c *AppController) RunAppPreflights(ctx context.Context, opts RunAppPreflig
5764
return fmt.Errorf("no app preflight spec found")
5865
}
5966

60-
err = c.stateMachine.Transition(lock, states.StateAppPreflightsRunning)
67+
err = c.stateMachine.Transition(lock, states.StateAppPreflightsRunning, nil)
6168
if err != nil {
6269
return fmt.Errorf("transition states: %w", err)
6370
}
@@ -78,14 +85,14 @@ func (c *AppController) RunAppPreflights(ctx context.Context, opts RunAppPreflig
7885
finalErr = fmt.Errorf("panic: %v: %s", r, string(debug.Stack()))
7986
}
8087
if finalErr != nil {
81-
c.logger.Error(finalErr)
88+
logger.Error(finalErr)
8289

83-
if err := c.stateMachine.Transition(lock, states.StateAppPreflightsExecutionFailed); err != nil {
84-
c.logger.WithError(err).Error("failed to transition states")
90+
if err := c.stateMachine.Transition(lock, states.StateAppPreflightsExecutionFailed, finalErr); err != nil {
91+
logger.WithError(err).Error("failed to transition states")
8592
}
8693

8794
if err := c.setAppPreflightStatus(types.StateFailed, finalErr.Error()); err != nil {
88-
c.logger.WithError(err).Error("failed to set status to failed")
95+
logger.WithError(err).Error("failed to set status to failed")
8996
}
9097
}
9198
}()
@@ -110,15 +117,15 @@ func (c *AppController) RunAppPreflights(ctx context.Context, opts RunAppPreflig
110117
}
111118

112119
if output.HasFail() {
113-
if err := c.stateMachine.Transition(lock, states.StateAppPreflightsFailed); err != nil {
120+
if err := c.stateMachine.Transition(lock, states.StateAppPreflightsFailed, output); err != nil {
114121
return fmt.Errorf("transition states: %w", err)
115122
}
116123

117124
if err := c.setAppPreflightStatus(types.StateFailed, "App preflights failed"); err != nil {
118125
return fmt.Errorf("set status to failed: %w", err)
119126
}
120127
} else {
121-
if err := c.stateMachine.Transition(lock, states.StateAppPreflightsSucceeded); err != nil {
128+
if err := c.stateMachine.Transition(lock, states.StateAppPreflightsSucceeded, output); err != nil {
122129
return fmt.Errorf("transition states: %w", err)
123130
}
124131

api/controllers/app/config.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func (c *AppController) PatchAppConfigValues(ctx context.Context, values types.A
2121
return types.NewConflictError(err)
2222
}
2323

24-
err = c.stateMachine.Transition(lock, states.StateApplicationConfiguring)
24+
err = c.stateMachine.Transition(lock, states.StateApplicationConfiguring, nil)
2525
if err != nil {
2626
return fmt.Errorf("failed to transition states: %w", err)
2727
}
@@ -32,8 +32,8 @@ func (c *AppController) PatchAppConfigValues(ctx context.Context, values types.A
3232
}
3333

3434
if finalErr != nil {
35-
if err := c.stateMachine.Transition(lock, states.StateApplicationConfigurationFailed); err != nil {
36-
c.logger.Errorf("failed to transition states: %w", err)
35+
if err := c.stateMachine.Transition(lock, states.StateApplicationConfigurationFailed, finalErr); err != nil {
36+
c.logger.WithError(err).Error("failed to transition states")
3737
}
3838
}
3939
}()
@@ -48,7 +48,7 @@ func (c *AppController) PatchAppConfigValues(ctx context.Context, values types.A
4848
return fmt.Errorf("patch app config values: %w", err)
4949
}
5050

51-
err = c.stateMachine.Transition(lock, states.StateApplicationConfigured)
51+
err = c.stateMachine.Transition(lock, states.StateApplicationConfigured, nil)
5252
if err != nil {
5353
return fmt.Errorf("failed to transition states: %w", err)
5454
}

api/controllers/app/install.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ var (
1717

1818
// InstallApp triggers app installation with proper state transitions and panic handling
1919
func (c *AppController) InstallApp(ctx context.Context, ignoreAppPreflights bool) (finalErr error) {
20+
logger := c.logger.WithField("operation", "install-app")
21+
2022
lock, err := c.stateMachine.AcquireLock()
2123
if err != nil {
2224
return types.NewConflictError(err)
@@ -47,7 +49,7 @@ func (c *AppController) InstallApp(ctx context.Context, ignoreAppPreflights bool
4749
return types.NewBadRequestError(ErrAppPreflightChecksFailed)
4850
}
4951

50-
err = c.stateMachine.Transition(lock, states.StateAppPreflightsFailedBypassed)
52+
err = c.stateMachine.Transition(lock, states.StateAppPreflightsFailedBypassed, preflightOutput)
5153
if err != nil {
5254
return fmt.Errorf("failed to transition states: %w", err)
5355
}
@@ -63,7 +65,7 @@ func (c *AppController) InstallApp(ctx context.Context, ignoreAppPreflights bool
6365
return fmt.Errorf("get kotsadm config values for app install: %w", err)
6466
}
6567

66-
err = c.stateMachine.Transition(lock, states.StateAppInstalling)
68+
err = c.stateMachine.Transition(lock, states.StateAppInstalling, nil)
6769
if err != nil {
6870
return fmt.Errorf("transition states: %w", err)
6971
}
@@ -79,14 +81,14 @@ func (c *AppController) InstallApp(ctx context.Context, ignoreAppPreflights bool
7981
finalErr = fmt.Errorf("panic: %v: %s", r, string(debug.Stack()))
8082
}
8183
if finalErr != nil {
82-
c.logger.Error(finalErr)
84+
logger.Error(finalErr)
8385

84-
if err := c.stateMachine.Transition(lock, states.StateAppInstallFailed); err != nil {
85-
c.logger.WithError(err).Error("failed to transition states")
86+
if err := c.stateMachine.Transition(lock, states.StateAppInstallFailed, finalErr); err != nil {
87+
logger.WithError(err).Error("failed to transition states")
8688
}
8789

8890
if err := c.setAppInstallStatus(types.StateFailed, finalErr.Error()); err != nil {
89-
c.logger.WithError(err).Error("failed to set status to failed")
91+
logger.WithError(err).Error("failed to set status to failed")
9092
}
9193
}
9294
}()
@@ -101,7 +103,7 @@ func (c *AppController) InstallApp(ctx context.Context, ignoreAppPreflights bool
101103
return fmt.Errorf("install app: %w", err)
102104
}
103105

104-
if err := c.stateMachine.Transition(lock, states.StateSucceeded); err != nil {
106+
if err := c.stateMachine.Transition(lock, states.StateSucceeded, nil); err != nil {
105107
return fmt.Errorf("transition states: %w", err)
106108
}
107109

api/controllers/app/tests/test_suite.go

Lines changed: 30 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ func (s *AppControllerTestSuite) TestRunAppPreflights() {
221221
expectedState: states.StateAppPreflightsSucceeded,
222222
setupMocks: func(apm *apppreflightmanager.MockAppPreflightManager, arm *appreleasemanager.MockAppReleaseManager, acm *appconfig.MockAppConfigManager, store *store.MockStore) {
223223
mock.InOrder(
224+
apm.On("ClearAppPreflightResults", mock.Anything).Return(nil),
224225
acm.On("GetConfigValues").Return(types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, nil),
225226

226227
arm.On("ExtractAppPreflightSpec", mock.Anything, types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, mock.Anything, mock.Anything).Return(expectedAPF, nil),
@@ -256,6 +257,7 @@ func (s *AppControllerTestSuite) TestRunAppPreflights() {
256257
expectedState: states.StateAppPreflightsSucceeded,
257258
setupMocks: func(apm *apppreflightmanager.MockAppPreflightManager, arm *appreleasemanager.MockAppReleaseManager, acm *appconfig.MockAppConfigManager, store *store.MockStore) {
258259
mock.InOrder(
260+
apm.On("ClearAppPreflightResults", mock.Anything).Return(nil),
259261
acm.On("GetConfigValues").Return(types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, nil),
260262

261263
arm.On("ExtractAppPreflightSpec", mock.Anything, types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, mock.Anything, mock.Anything).Return(expectedAPF, nil),
@@ -291,6 +293,7 @@ func (s *AppControllerTestSuite) TestRunAppPreflights() {
291293
expectedState: states.StateAppPreflightsSucceeded,
292294
setupMocks: func(apm *apppreflightmanager.MockAppPreflightManager, arm *appreleasemanager.MockAppReleaseManager, acm *appconfig.MockAppConfigManager, store *store.MockStore) {
293295
mock.InOrder(
296+
apm.On("ClearAppPreflightResults", mock.Anything).Return(nil),
294297
acm.On("GetConfigValues").Return(types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, nil),
295298

296299
arm.On("ExtractAppPreflightSpec", mock.Anything, types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, mock.Anything, mock.Anything).Return(expectedAPF, nil),
@@ -326,6 +329,7 @@ func (s *AppControllerTestSuite) TestRunAppPreflights() {
326329
expectedState: states.StateAppPreflightsFailed,
327330
setupMocks: func(apm *apppreflightmanager.MockAppPreflightManager, arm *appreleasemanager.MockAppReleaseManager, acm *appconfig.MockAppConfigManager, store *store.MockStore) {
328331
mock.InOrder(
332+
apm.On("ClearAppPreflightResults", mock.Anything).Return(nil),
329333
acm.On("GetConfigValues").Return(types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, nil),
330334

331335
arm.On("ExtractAppPreflightSpec", mock.Anything, types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, mock.Anything, mock.Anything).Return(expectedAPF, nil),
@@ -361,6 +365,7 @@ func (s *AppControllerTestSuite) TestRunAppPreflights() {
361365
expectedState: states.StateInfrastructureInstalled,
362366
setupMocks: func(apm *apppreflightmanager.MockAppPreflightManager, arm *appreleasemanager.MockAppReleaseManager, acm *appconfig.MockAppConfigManager, store *store.MockStore) {
363367
mock.InOrder(
368+
apm.On("ClearAppPreflightResults", mock.Anything).Return(nil),
364369
acm.On("GetConfigValues").Return(types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, nil),
365370

366371
arm.On("ExtractAppPreflightSpec", mock.Anything, types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, mock.Anything, mock.Anything).Return(nil, errors.New("extraction error")),
@@ -377,6 +382,7 @@ func (s *AppControllerTestSuite) TestRunAppPreflights() {
377382
expectedState: states.StateAppPreflightsExecutionFailed,
378383
setupMocks: func(apm *apppreflightmanager.MockAppPreflightManager, arm *appreleasemanager.MockAppReleaseManager, acm *appconfig.MockAppConfigManager, store *store.MockStore) {
379384
mock.InOrder(
385+
apm.On("ClearAppPreflightResults", mock.Anything).Return(nil),
380386
acm.On("GetConfigValues").Return(types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, nil),
381387

382388
arm.On("ExtractAppPreflightSpec", mock.Anything, types.AppConfigValues{"test-item": types.AppConfigValue{Value: "test-value"}}, mock.Anything, mock.Anything).Return(expectedAPF, nil),
@@ -721,14 +727,14 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
721727
ignoreAppPreflights bool
722728
currentState statemachine.State
723729
expectedState statemachine.State
724-
setupMocks func(*appconfig.MockAppConfigManager, *appupgrademanager.MockAppUpgradeManager)
730+
setupMocks func(*appconfig.MockAppConfigManager, *appupgrademanager.MockAppUpgradeManager, *store.MockStore)
725731
expectedErr bool
726732
}{
727733
{
728734
name: "invalid state transition from succeeded state",
729735
currentState: states.StateSucceeded,
730736
expectedState: states.StateSucceeded,
731-
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager) {
737+
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager, st *store.MockStore) {
732738
// No mocks needed for invalid state transition
733739
},
734740
expectedErr: true,
@@ -737,7 +743,7 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
737743
name: "invalid state transition from app upgrading state",
738744
currentState: states.StateAppUpgrading,
739745
expectedState: states.StateAppUpgrading,
740-
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager) {
746+
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager, st *store.MockStore) {
741747
// No mocks needed for invalid state transition
742748
},
743749
expectedErr: true,
@@ -746,7 +752,7 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
746752
name: "invalid state transition from app upgrade failed state",
747753
currentState: states.StateAppUpgradeFailed,
748754
expectedState: states.StateAppUpgradeFailed,
749-
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager) {
755+
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager, st *store.MockStore) {
750756
// No mocks needed for invalid state transition
751757
},
752758
expectedErr: true,
@@ -755,7 +761,7 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
755761
name: "invalid state transition from app configured state",
756762
currentState: states.StateApplicationConfigured,
757763
expectedState: states.StateApplicationConfigured,
758-
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager) {
764+
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager, st *store.MockStore) {
759765
// No mocks needed for invalid state transition
760766
},
761767
expectedErr: true,
@@ -764,7 +770,7 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
764770
name: "successful app upgrade from app preflights succeeded state",
765771
currentState: states.StateAppPreflightsSucceeded,
766772
expectedState: states.StateSucceeded,
767-
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager) {
773+
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager, st *store.MockStore) {
768774
mock.InOrder(
769775
acm.On("GetKotsadmConfigValues").Return(kotsv1beta1.ConfigValues{
770776
Spec: kotsv1beta1.ConfigValuesSpec{
@@ -773,9 +779,15 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
773779
},
774780
},
775781
}, nil),
782+
st.AppUpgradeMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool {
783+
return status.State == types.StateRunning
784+
})).Return(nil),
776785
aum.On("Upgrade", mock.Anything, mock.MatchedBy(func(cv kotsv1beta1.ConfigValues) bool {
777786
return cv.Spec.Values["test-key"].Value == "test-value"
778787
})).Return(nil),
788+
st.AppUpgradeMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool {
789+
return status.State == types.StateSucceeded
790+
})).Return(nil),
779791
)
780792
},
781793
expectedErr: false,
@@ -784,7 +796,7 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
784796
name: "get config values error",
785797
currentState: states.StateAppPreflightsSucceeded,
786798
expectedState: states.StateAppPreflightsSucceeded,
787-
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager) {
799+
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager, st *store.MockStore) {
788800
acm.On("GetKotsadmConfigValues").Return(kotsv1beta1.ConfigValues{}, errors.New("config values error"))
789801
},
790802
expectedErr: true,
@@ -793,7 +805,7 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
793805
name: "app upgrade manager error",
794806
currentState: states.StateAppPreflightsSucceeded,
795807
expectedState: states.StateAppUpgradeFailed,
796-
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager) {
808+
setupMocks: func(acm *appconfig.MockAppConfigManager, aum *appupgrademanager.MockAppUpgradeManager, st *store.MockStore) {
797809
mock.InOrder(
798810
acm.On("GetKotsadmConfigValues").Return(kotsv1beta1.ConfigValues{
799811
Spec: kotsv1beta1.ConfigValuesSpec{
@@ -802,9 +814,15 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
802814
},
803815
},
804816
}, nil),
817+
st.AppUpgradeMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool {
818+
return status.State == types.StateRunning
819+
})).Return(nil),
805820
aum.On("Upgrade", mock.Anything, mock.MatchedBy(func(cv kotsv1beta1.ConfigValues) bool {
806821
return cv.Spec.Values["test-key"].Value == "test-value"
807822
})).Return(errors.New("upgrade error")),
823+
st.AppUpgradeMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool {
824+
return status.State == types.StateFailed && status.Description == "upgrade app: upgrade error"
825+
})).Return(nil),
808826
)
809827
},
810828
expectedErr: false,
@@ -822,19 +840,21 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
822840
appConfigManager := &appconfig.MockAppConfigManager{}
823841
appConfigManager.On("TemplateConfig", types.AppConfigValues{}, false, false).Return(types.AppConfig{}, nil)
824842

843+
mockStore := &store.MockStore{}
844+
825845
controller, err := appcontroller.NewAppController(
826846
appcontroller.WithStateMachine(sm),
827847
appcontroller.WithAppConfigManager(appConfigManager),
828848
appcontroller.WithAppPreflightManager(appPreflightManager),
829849
appcontroller.WithAppReleaseManager(appReleaseManager),
830850
appcontroller.WithAppInstallManager(appInstallManager),
831851
appcontroller.WithAppUpgradeManager(appUpgradeManager),
832-
appcontroller.WithStore(&store.MockStore{}),
852+
appcontroller.WithStore(mockStore),
833853
appcontroller.WithReleaseData(&release.ReleaseData{}),
834854
)
835855
require.NoError(t, err, "failed to create app controller")
836856

837-
tt.setupMocks(appConfigManager, appUpgradeManager)
857+
tt.setupMocks(appConfigManager, appUpgradeManager, mockStore)
838858
err = controller.UpgradeApp(t.Context(), tt.ignoreAppPreflights)
839859

840860
if tt.expectedErr {
@@ -857,73 +877,3 @@ func (s *AppControllerTestSuite) TestUpgradeApp() {
857877
})
858878
}
859879
}
860-
861-
func (s *AppControllerTestSuite) TestGetAppUpgradeStatus() {
862-
expectedAppUpgrade := types.AppUpgrade{
863-
Status: types.Status{
864-
State: types.StateRunning,
865-
Description: "Upgrading application",
866-
LastUpdated: time.Now(),
867-
},
868-
Logs: "Upgrade logs\n",
869-
}
870-
871-
tests := []struct {
872-
name string
873-
setupMocks func(*appupgrademanager.MockAppUpgradeManager)
874-
expectedErr bool
875-
}{
876-
{
877-
name: "successful status retrieval",
878-
setupMocks: func(aum *appupgrademanager.MockAppUpgradeManager) {
879-
aum.On("GetStatus").Return(expectedAppUpgrade, nil)
880-
},
881-
expectedErr: false,
882-
},
883-
{
884-
name: "manager returns error",
885-
setupMocks: func(aum *appupgrademanager.MockAppUpgradeManager) {
886-
aum.On("GetStatus").Return(types.AppUpgrade{}, errors.New("status error"))
887-
},
888-
expectedErr: true,
889-
},
890-
}
891-
892-
for _, tt := range tests {
893-
s.T().Run(tt.name, func(t *testing.T) {
894-
appPreflightManager := &apppreflightmanager.MockAppPreflightManager{}
895-
appReleaseManager := &appreleasemanager.MockAppReleaseManager{}
896-
appInstallManager := &appinstallmanager.MockAppInstallManager{}
897-
appUpgradeManager := &appupgrademanager.MockAppUpgradeManager{}
898-
sm := s.CreateUpgradeStateMachine(states.StateNew)
899-
900-
appConfigManager := &appconfig.MockAppConfigManager{}
901-
appConfigManager.On("TemplateConfig", types.AppConfigValues{}, false, false).Return(types.AppConfig{}, nil)
902-
903-
controller, err := appcontroller.NewAppController(
904-
appcontroller.WithStateMachine(sm),
905-
appcontroller.WithAppConfigManager(appConfigManager),
906-
appcontroller.WithAppPreflightManager(appPreflightManager),
907-
appcontroller.WithAppReleaseManager(appReleaseManager),
908-
appcontroller.WithAppInstallManager(appInstallManager),
909-
appcontroller.WithAppUpgradeManager(appUpgradeManager),
910-
appcontroller.WithStore(&store.MockStore{}),
911-
appcontroller.WithReleaseData(&release.ReleaseData{}),
912-
)
913-
require.NoError(t, err, "failed to create app controller")
914-
915-
tt.setupMocks(appUpgradeManager)
916-
result, err := controller.GetAppUpgradeStatus(t.Context())
917-
918-
if tt.expectedErr {
919-
assert.Error(t, err)
920-
assert.Equal(t, types.AppUpgrade{}, result)
921-
} else {
922-
assert.NoError(t, err)
923-
assert.Equal(t, expectedAppUpgrade, result)
924-
}
925-
926-
appUpgradeManager.AssertExpectations(t)
927-
})
928-
}
929-
}

0 commit comments

Comments
 (0)