@@ -375,4 +375,221 @@ public async Task GetOrderStatusHistory_FlagsCurrentStatusAsStuck_WhenExceedsThr
375375 }
376376
377377 #endregion
378+
379+ #region GetStuckOrdersSummary Tests
380+
381+ [ Fact ]
382+ public async Task GetStuckOrdersSummary_ReturnsOkResult_WithSummary ( )
383+ {
384+ // Arrange
385+ var expectedSummary = new StuckOrdersSummary
386+ {
387+ TotalStuckOrders = 150 ,
388+ ByThreshold = new Dictionary < string , int >
389+ {
390+ [ "PrepStatuses (6h)" ] = 100 ,
391+ [ "FacilityStatuses (48h)" ] = 50
392+ } ,
393+ ByStatusCategory = new Dictionary < string , int >
394+ {
395+ [ "Preparation" ] = 80 ,
396+ [ "PrintBoxAlert" ] = 20 ,
397+ [ "Facility" ] = 40 ,
398+ [ "Shipping" ] = 10
399+ } ,
400+ TopStatuses = new List < StatusCount >
401+ {
402+ new ( ) { StatusId = 3060 , Status = "PreparationDone" , Count = 45 } ,
403+ new ( ) { StatusId = 4800 , Status = "ErrorInFacility" , Count = 30 } ,
404+ new ( ) { StatusId = 3720 , Status = "PrintBoxAlert_RenderStatusFailure" , Count = 15 }
405+ } ,
406+ GeneratedAt = DateTime . UtcNow
407+ } ;
408+
409+ _stuckOrderServiceMock
410+ . Setup ( s => s . GetStuckOrdersSummaryAsync ( It . IsAny < CancellationToken > ( ) ) )
411+ . ReturnsAsync ( expectedSummary ) ;
412+
413+ // Act
414+ var result = await _controller . GetStuckOrdersSummary ( ) ;
415+
416+ // Assert
417+ var okResult = result . Result . Should ( ) . BeOfType < OkObjectResult > ( ) . Subject ;
418+ var summary = okResult . Value . Should ( ) . BeOfType < StuckOrdersSummary > ( ) . Subject ;
419+ summary . TotalStuckOrders . Should ( ) . Be ( 150 ) ;
420+ }
421+
422+ [ Fact ]
423+ public async Task GetStuckOrdersSummary_ReturnsByThresholdCounts ( )
424+ {
425+ // Arrange
426+ var expectedSummary = new StuckOrdersSummary
427+ {
428+ TotalStuckOrders = 100 ,
429+ ByThreshold = new Dictionary < string , int >
430+ {
431+ [ "PrepStatuses (6h)" ] = 60 ,
432+ [ "FacilityStatuses (48h)" ] = 40
433+ } ,
434+ ByStatusCategory = new Dictionary < string , int > ( ) ,
435+ TopStatuses = [ ] ,
436+ GeneratedAt = DateTime . UtcNow
437+ } ;
438+
439+ _stuckOrderServiceMock
440+ . Setup ( s => s . GetStuckOrdersSummaryAsync ( It . IsAny < CancellationToken > ( ) ) )
441+ . ReturnsAsync ( expectedSummary ) ;
442+
443+ // Act
444+ var result = await _controller . GetStuckOrdersSummary ( ) ;
445+
446+ // Assert
447+ var okResult = result . Result . Should ( ) . BeOfType < OkObjectResult > ( ) . Subject ;
448+ var summary = okResult . Value . Should ( ) . BeOfType < StuckOrdersSummary > ( ) . Subject ;
449+ summary . ByThreshold . Should ( ) . ContainKey ( "PrepStatuses (6h)" ) ;
450+ summary . ByThreshold . Should ( ) . ContainKey ( "FacilityStatuses (48h)" ) ;
451+ summary . ByThreshold [ "PrepStatuses (6h)" ] . Should ( ) . Be ( 60 ) ;
452+ summary . ByThreshold [ "FacilityStatuses (48h)" ] . Should ( ) . Be ( 40 ) ;
453+ }
454+
455+ [ Fact ]
456+ public async Task GetStuckOrdersSummary_ReturnsByStatusCategoryCounts ( )
457+ {
458+ // Arrange
459+ var expectedSummary = new StuckOrdersSummary
460+ {
461+ TotalStuckOrders = 100 ,
462+ ByThreshold = new Dictionary < string , int > ( ) ,
463+ ByStatusCategory = new Dictionary < string , int >
464+ {
465+ [ "Preparation" ] = 50 ,
466+ [ "PrintBoxAlert" ] = 20 ,
467+ [ "Facility" ] = 30
468+ } ,
469+ TopStatuses = [ ] ,
470+ GeneratedAt = DateTime . UtcNow
471+ } ;
472+
473+ _stuckOrderServiceMock
474+ . Setup ( s => s . GetStuckOrdersSummaryAsync ( It . IsAny < CancellationToken > ( ) ) )
475+ . ReturnsAsync ( expectedSummary ) ;
476+
477+ // Act
478+ var result = await _controller . GetStuckOrdersSummary ( ) ;
479+
480+ // Assert
481+ var okResult = result . Result . Should ( ) . BeOfType < OkObjectResult > ( ) . Subject ;
482+ var summary = okResult . Value . Should ( ) . BeOfType < StuckOrdersSummary > ( ) . Subject ;
483+ summary . ByStatusCategory . Should ( ) . HaveCount ( 3 ) ;
484+ summary . ByStatusCategory [ "Preparation" ] . Should ( ) . Be ( 50 ) ;
485+ }
486+
487+ [ Fact ]
488+ public async Task GetStuckOrdersSummary_ReturnsTopStatuses ( )
489+ {
490+ // Arrange
491+ var expectedSummary = new StuckOrdersSummary
492+ {
493+ TotalStuckOrders = 100 ,
494+ ByThreshold = new Dictionary < string , int > ( ) ,
495+ ByStatusCategory = new Dictionary < string , int > ( ) ,
496+ TopStatuses = new List < StatusCount >
497+ {
498+ new ( ) { StatusId = 3060 , Status = "PreparationDone" , Count = 45 } ,
499+ new ( ) { StatusId = 4800 , Status = "ErrorInFacility" , Count = 30 } ,
500+ new ( ) { StatusId = 3720 , Status = "PrintBoxAlert_RenderStatusFailure" , Count = 15 }
501+ } ,
502+ GeneratedAt = DateTime . UtcNow
503+ } ;
504+
505+ _stuckOrderServiceMock
506+ . Setup ( s => s . GetStuckOrdersSummaryAsync ( It . IsAny < CancellationToken > ( ) ) )
507+ . ReturnsAsync ( expectedSummary ) ;
508+
509+ // Act
510+ var result = await _controller . GetStuckOrdersSummary ( ) ;
511+
512+ // Assert
513+ var okResult = result . Result . Should ( ) . BeOfType < OkObjectResult > ( ) . Subject ;
514+ var summary = okResult . Value . Should ( ) . BeOfType < StuckOrdersSummary > ( ) . Subject ;
515+ summary . TopStatuses . Should ( ) . HaveCount ( 3 ) ;
516+ summary . TopStatuses . First ( ) . Status . Should ( ) . Be ( "PreparationDone" ) ;
517+ summary . TopStatuses . First ( ) . Count . Should ( ) . Be ( 45 ) ;
518+ }
519+
520+ [ Fact ]
521+ public async Task GetStuckOrdersSummary_WhenNoStuckOrders_ReturnsZeroCounts ( )
522+ {
523+ // Arrange
524+ var expectedSummary = new StuckOrdersSummary
525+ {
526+ TotalStuckOrders = 0 ,
527+ ByThreshold = new Dictionary < string , int >
528+ {
529+ [ "PrepStatuses (6h)" ] = 0 ,
530+ [ "FacilityStatuses (48h)" ] = 0
531+ } ,
532+ ByStatusCategory = new Dictionary < string , int > ( ) ,
533+ TopStatuses = [ ] ,
534+ GeneratedAt = DateTime . UtcNow
535+ } ;
536+
537+ _stuckOrderServiceMock
538+ . Setup ( s => s . GetStuckOrdersSummaryAsync ( It . IsAny < CancellationToken > ( ) ) )
539+ . ReturnsAsync ( expectedSummary ) ;
540+
541+ // Act
542+ var result = await _controller . GetStuckOrdersSummary ( ) ;
543+
544+ // Assert
545+ var okResult = result . Result . Should ( ) . BeOfType < OkObjectResult > ( ) . Subject ;
546+ var summary = okResult . Value . Should ( ) . BeOfType < StuckOrdersSummary > ( ) . Subject ;
547+ summary . TotalStuckOrders . Should ( ) . Be ( 0 ) ;
548+ summary . TopStatuses . Should ( ) . BeEmpty ( ) ;
549+ }
550+
551+ [ Fact ]
552+ public async Task GetStuckOrdersSummary_WhenServiceThrows_Returns500 ( )
553+ {
554+ // Arrange
555+ _stuckOrderServiceMock
556+ . Setup ( s => s . GetStuckOrdersSummaryAsync ( It . IsAny < CancellationToken > ( ) ) )
557+ . ThrowsAsync ( new Exception ( "Database error" ) ) ;
558+
559+ // Act
560+ var result = await _controller . GetStuckOrdersSummary ( ) ;
561+
562+ // Assert
563+ result . Result . Should ( ) . BeOfType < ObjectResult > ( )
564+ . Which . StatusCode . Should ( ) . Be ( 500 ) ;
565+ }
566+
567+ [ Fact ]
568+ public async Task GetStuckOrdersSummary_IncludesGeneratedAtTimestamp ( )
569+ {
570+ // Arrange
571+ var generatedAt = DateTime . UtcNow ;
572+ var expectedSummary = new StuckOrdersSummary
573+ {
574+ TotalStuckOrders = 10 ,
575+ ByThreshold = new Dictionary < string , int > ( ) ,
576+ ByStatusCategory = new Dictionary < string , int > ( ) ,
577+ TopStatuses = [ ] ,
578+ GeneratedAt = generatedAt
579+ } ;
580+
581+ _stuckOrderServiceMock
582+ . Setup ( s => s . GetStuckOrdersSummaryAsync ( It . IsAny < CancellationToken > ( ) ) )
583+ . ReturnsAsync ( expectedSummary ) ;
584+
585+ // Act
586+ var result = await _controller . GetStuckOrdersSummary ( ) ;
587+
588+ // Assert
589+ var okResult = result . Result . Should ( ) . BeOfType < OkObjectResult > ( ) . Subject ;
590+ var summary = okResult . Value . Should ( ) . BeOfType < StuckOrdersSummary > ( ) . Subject ;
591+ summary . GeneratedAt . Should ( ) . BeCloseTo ( generatedAt , TimeSpan . FromSeconds ( 1 ) ) ;
592+ }
593+
594+ #endregion
378595}
0 commit comments