@@ -14,6 +14,7 @@ import (
1414 "github.com/replicatedhq/embedded-cluster/api/internal/store"
1515 "github.com/replicatedhq/embedded-cluster/api/types"
1616 ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1"
17+ "github.com/replicatedhq/embedded-cluster/pkg/metrics"
1718 "github.com/replicatedhq/embedded-cluster/pkg/release"
1819 "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
1920 kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
@@ -271,6 +272,171 @@ func TestGetInfra(t *testing.T) {
271272 }
272273}
273274
275+ func TestReportingHandlers (t * testing.T ) {
276+ tests := []struct {
277+ name string
278+ currentState statemachine.State
279+ targetState statemachine.State
280+ requiresInfraUpgrade bool
281+ targetVersion string
282+ initialVersion string
283+ setupMocks func (* metrics.MockReporter , * store.MockStore )
284+ }{
285+ {
286+ name : "report upgrade succeeded" ,
287+ currentState : states .StateAppUpgrading ,
288+ targetState : states .StateSucceeded ,
289+ requiresInfraUpgrade : false ,
290+ targetVersion : "1.0.0" ,
291+ initialVersion : "0.9.0" ,
292+ setupMocks : func (mr * metrics.MockReporter , st * store.MockStore ) {
293+ mr .On ("ReportUpgradeSucceeded" , mock .Anything , "1.0.0" , "0.9.0" )
294+ },
295+ },
296+ {
297+ name : "report infrastructure upgrade failed" ,
298+ currentState : states .StateInfrastructureUpgrading ,
299+ targetState : states .StateInfrastructureUpgradeFailed ,
300+ requiresInfraUpgrade : true ,
301+ targetVersion : "1.0.0" ,
302+ initialVersion : "0.9.0" ,
303+ setupMocks : func (mr * metrics.MockReporter , st * store.MockStore ) {
304+ st .LinuxInfraMockStore .On ("GetStatus" ).Return (types.Status {
305+ State : types .StateFailed ,
306+ Description : "infrastructure upgrade failed" ,
307+ }, nil )
308+ mr .On ("ReportUpgradeFailed" , mock .Anything , mock .MatchedBy (func (err error ) bool {
309+ return err .Error () == "infrastructure upgrade failed"
310+ }), "1.0.0" , "0.9.0" )
311+ },
312+ },
313+ {
314+ name : "report app upgrade failed" ,
315+ currentState : states .StateAppUpgrading ,
316+ targetState : states .StateAppUpgradeFailed ,
317+ requiresInfraUpgrade : false ,
318+ targetVersion : "1.0.0" ,
319+ initialVersion : "0.9.0" ,
320+ setupMocks : func (mr * metrics.MockReporter , st * store.MockStore ) {
321+ st .AppUpgradeMockStore .On ("GetStatus" ).Return (types.Status {
322+ State : types .StateFailed ,
323+ Description : "app upgrade failed" ,
324+ }, nil )
325+ mr .On ("ReportUpgradeFailed" , mock .Anything , mock .MatchedBy (func (err error ) bool {
326+ return err .Error () == "app upgrade failed"
327+ }), "1.0.0" , "0.9.0" )
328+ },
329+ },
330+ {
331+ name : "report app preflights succeeded" ,
332+ currentState : states .StateAppPreflightsRunning ,
333+ targetState : states .StateAppPreflightsSucceeded ,
334+ requiresInfraUpgrade : false ,
335+ setupMocks : func (mr * metrics.MockReporter , st * store.MockStore ) {
336+ mr .On ("ReportAppPreflightsSucceeded" , mock .Anything )
337+ },
338+ },
339+ {
340+ name : "report app preflights failed" ,
341+ currentState : states .StateAppPreflightsRunning ,
342+ targetState : states .StateAppPreflightsFailed ,
343+ requiresInfraUpgrade : false ,
344+ setupMocks : func (mr * metrics.MockReporter , st * store.MockStore ) {
345+ output := & types.PreflightsOutput {
346+ Fail : []types.PreflightsRecord {
347+ {
348+ Title : "Test Check" ,
349+ Message : "Test check failed" ,
350+ Strict : true ,
351+ },
352+ },
353+ }
354+ st .AppPreflightMockStore .On ("GetOutput" ).Return (output , nil )
355+ mr .On ("ReportAppPreflightsFailed" , mock .Anything , output )
356+ },
357+ },
358+ {
359+ name : "report app preflights bypassed" ,
360+ currentState : states .StateAppPreflightsFailed ,
361+ targetState : states .StateAppPreflightsFailedBypassed ,
362+ requiresInfraUpgrade : false ,
363+ setupMocks : func (mr * metrics.MockReporter , st * store.MockStore ) {
364+ output := & types.PreflightsOutput {
365+ Fail : []types.PreflightsRecord {
366+ {
367+ Title : "Non-strict Check" ,
368+ Message : "Test check failed but can be bypassed" ,
369+ Strict : false ,
370+ },
371+ },
372+ }
373+ st .AppPreflightMockStore .On ("GetOutput" ).Return (output , nil )
374+ mr .On ("ReportAppPreflightsBypassed" , mock .Anything , output )
375+ },
376+ },
377+ }
378+
379+ for _ , tt := range tests {
380+ t .Run (tt .name , func (t * testing.T ) {
381+ mockMetricsReporter := & metrics.MockReporter {}
382+ mockStore := & store.MockStore {}
383+ mockInfraManager := & infra.MockInfraManager {}
384+
385+ tt .setupMocks (mockMetricsReporter , mockStore )
386+
387+ // Mock RequiresUpgrade which is called during controller initialization
388+ mockInfraManager .On ("RequiresUpgrade" , mock .Anything , mock .Anything ).Return (tt .requiresInfraUpgrade , nil )
389+
390+ // Create state machine starting in the current state
391+ sm := NewStateMachine (
392+ WithCurrentState (tt .currentState ),
393+ WithRequiresInfraUpgrade (tt .requiresInfraUpgrade ),
394+ )
395+
396+ // Create app controller (required for upgrade controller)
397+ appController , err := appcontroller .NewAppController (
398+ appcontroller .WithStateMachine (sm ),
399+ appcontroller .WithStore (mockStore ),
400+ appcontroller .WithReleaseData (getTestReleaseData (& kotsv1beta1.Config {})),
401+ )
402+ require .NoError (t , err )
403+
404+ // Create upgrade controller with metrics reporter
405+ controller , err := NewUpgradeController (
406+ WithStateMachine (sm ),
407+ WithAppController (appController ),
408+ WithMetricsReporter (mockMetricsReporter ),
409+ WithStore (mockStore ),
410+ WithInfraManager (mockInfraManager ),
411+ WithTargetVersion (tt .targetVersion ),
412+ WithInitialVersion (tt .initialVersion ),
413+ WithReleaseData (getTestReleaseData (& kotsv1beta1.Config {})),
414+ )
415+ require .NoError (t , err )
416+
417+ // Trigger the state transition
418+ lock , err := sm .AcquireLock ()
419+ require .NoError (t , err )
420+ defer lock .Release ()
421+
422+ err = sm .Transition (lock , tt .targetState )
423+ require .NoError (t , err )
424+
425+ // Wait for the event handler goroutine to complete
426+ time .Sleep (1 * time .Second )
427+
428+ // Verify that the metrics reporter was called as expected
429+ mockMetricsReporter .AssertExpectations (t )
430+ mockStore .LinuxInfraMockStore .AssertExpectations (t )
431+ mockStore .AppUpgradeMockStore .AssertExpectations (t )
432+ mockStore .AppPreflightMockStore .AssertExpectations (t )
433+
434+ // Avoid unused variable error
435+ _ = controller
436+ })
437+ }
438+ }
439+
274440func getTestReleaseData (appConfig * kotsv1beta1.Config ) * release.ReleaseData {
275441 return & release.ReleaseData {
276442 EmbeddedClusterConfig : & ecv1beta1.Config {},
0 commit comments