@@ -553,3 +553,223 @@ func TestGetEditionPlayStats(t *testing.T) {
553553 })
554554 }
555555}
556+
557+ func TestGetGamePlayStats (t * testing.T ) {
558+ t .Parallel ()
559+ ctrl := gomock .NewController (t )
560+
561+ gameID := values .NewGameID ()
562+ gameVersionID := values .NewGameVersionID ()
563+ now := time .Now ()
564+ defaultStart := now .Add (- 24 * time .Hour )
565+ customStart := now .Add (- 48 * time .Hour )
566+ customEnd := now .Add (- 24 * time .Hour )
567+
568+ mockHourlyStats := []* domain.HourlyPlayStats {
569+ domain .NewHourlyPlayStats (now .Truncate (time .Hour ), 5 , 120 * time .Second ),
570+ }
571+ mockStats := domain .NewGamePlayStats (
572+ gameID ,
573+ 10 ,
574+ 300 * time .Second ,
575+ mockHourlyStats ,
576+ )
577+
578+ expectedHourlyStats := []openapi.HourlyPlayStats {
579+ {
580+ StartTime : now .Truncate (time .Hour ),
581+ PlayCount : 5 ,
582+ PlayTime : 120 ,
583+ },
584+ }
585+ expectedGamePlayStats := openapi.GamePlayStats {
586+ GameID : uuid .UUID (gameID ),
587+ TotalPlayCount : 10 ,
588+ TotalPlaySeconds : 300 ,
589+ HourlyStats : expectedHourlyStats ,
590+ }
591+
592+ testCases := map [string ]struct {
593+ gameID values.GameID
594+ queryParams map [string ]string
595+ executeGetGamePlayStats bool
596+ expectedGameVersionID * values.GameVersionID
597+ expectedStart time.Time
598+ expectedEnd time.Time
599+ getGamePlayStatsResult * domain.GamePlayStats
600+ getGamePlayStatsErr error
601+ expectedResponse openapi.GamePlayStats
602+ isError bool
603+ statusCode int
604+ }{
605+ "クエリパラメータなし" : {
606+ gameID : gameID ,
607+ queryParams : map [string ]string {},
608+ executeGetGamePlayStats : true ,
609+ expectedGameVersionID : nil ,
610+ expectedStart : defaultStart ,
611+ expectedEnd : now ,
612+ getGamePlayStatsResult : mockStats ,
613+ expectedResponse : expectedGamePlayStats ,
614+ statusCode : http .StatusOK ,
615+ },
616+ "正常系: game_version_idあり" : {
617+ gameID : gameID ,
618+ queryParams : map [string ]string {
619+ "game_version_id" : uuid .UUID (gameVersionID ).String (),
620+ },
621+ executeGetGamePlayStats : true ,
622+ expectedGameVersionID : & gameVersionID ,
623+ expectedStart : defaultStart ,
624+ expectedEnd : now ,
625+ getGamePlayStatsResult : mockStats ,
626+ expectedResponse : expectedGamePlayStats ,
627+ statusCode : http .StatusOK ,
628+ },
629+ "正常系: start, end指定" : {
630+ gameID : gameID ,
631+ queryParams : map [string ]string {
632+ "start" : customStart .Format (time .RFC3339 ),
633+ "end" : customEnd .Format (time .RFC3339 ),
634+ },
635+ executeGetGamePlayStats : true ,
636+ expectedGameVersionID : nil ,
637+ expectedStart : customStart ,
638+ expectedEnd : customEnd ,
639+ getGamePlayStatsResult : mockStats ,
640+ expectedResponse : expectedGamePlayStats ,
641+ statusCode : http .StatusOK ,
642+ },
643+ "異常系:404 serviceでErrInvalidGame" : {
644+ gameID : gameID ,
645+ queryParams : map [string ]string {},
646+ executeGetGamePlayStats : true ,
647+ expectedStart : defaultStart ,
648+ expectedEnd : now ,
649+ getGamePlayStatsErr : service .ErrInvalidGame ,
650+ isError : true ,
651+ statusCode : http .StatusNotFound ,
652+ },
653+ "異常系: 400 serviceでErrInvalidTimeRange" : {
654+ gameID : gameID ,
655+ queryParams : map [string ]string {},
656+ executeGetGamePlayStats : true ,
657+ expectedStart : defaultStart ,
658+ expectedEnd : now ,
659+ getGamePlayStatsErr : service .ErrInvalidTimeRange ,
660+ isError : true ,
661+ statusCode : http .StatusBadRequest ,
662+ },
663+ "異常系:400 serviceでErrTimePeriodTooLong" : {
664+ gameID : gameID ,
665+ queryParams : map [string ]string {},
666+ executeGetGamePlayStats : true ,
667+ expectedStart : defaultStart ,
668+ expectedEnd : now ,
669+ getGamePlayStatsErr : service .ErrTimePeriodTooLong ,
670+ isError : true ,
671+ statusCode : http .StatusBadRequest ,
672+ },
673+ "異常系:500 serviceでその他のエラー" : {
674+ gameID : gameID ,
675+ queryParams : map [string ]string {},
676+ executeGetGamePlayStats : true ,
677+ expectedStart : defaultStart ,
678+ expectedEnd : now ,
679+ getGamePlayStatsErr : assert .AnError ,
680+ isError : true ,
681+ statusCode : http .StatusInternalServerError ,
682+ },
683+ }
684+
685+ for name , tt := range testCases {
686+ t .Run (name , func (t * testing.T ) {
687+ t .Parallel ()
688+
689+ serviceMock := mock .NewMockGamePlayLogV2 (ctrl )
690+ h := NewGamePlayLog (serviceMock )
691+
692+ if tt .executeGetGamePlayStats {
693+ serviceMock .
694+ EXPECT ().
695+ GetGamePlayStats (
696+ gomock .Any (),
697+ tt .gameID ,
698+ tt .expectedGameVersionID ,
699+ gomock .Cond (func (start time.Time ) bool {
700+ return start .Sub (tt .expectedStart ).Abs () < time .Second
701+ }),
702+ gomock .Cond (func (end time.Time ) bool {
703+ return end .Sub (tt .expectedEnd ).Abs () < time .Second
704+ }),
705+ ).
706+ Return (tt .getGamePlayStatsResult , tt .getGamePlayStatsErr )
707+ }
708+
709+ url := fmt .Sprintf ("/games/%s/play-stats" , uuid .UUID (tt .gameID ).String ())
710+ if len (tt .queryParams ) > 0 {
711+ url += "?"
712+ first := true
713+ for key , value := range tt .queryParams {
714+ if ! first {
715+ url += "&"
716+ }
717+ url += fmt .Sprintf ("%s=%s" , key , value )
718+ first = false
719+ }
720+ }
721+
722+ c , _ , rec := setupTestRequest (t , http .MethodGet , url , nil )
723+
724+ var params openapi.GetGamePlayStatsParams
725+ if v , ok := tt .queryParams ["game_version_id" ]; ok {
726+ parsed , err := uuid .Parse (v )
727+ if err == nil {
728+ params .GameVersionID = & parsed
729+ }
730+ }
731+ if v , ok := tt .queryParams ["start" ]; ok {
732+ parsed , err := time .Parse (time .RFC3339 , v )
733+ if err == nil {
734+ params .Start = & parsed
735+ }
736+ }
737+ if v , ok := tt .queryParams ["end" ]; ok {
738+ parsed , err := time .Parse (time .RFC3339 , v )
739+ if err == nil {
740+ params .End = & parsed
741+ }
742+ }
743+
744+ err := h .GetGamePlayStats (c , openapi .GameIDInPath (tt .gameID ), params )
745+
746+ if tt .isError {
747+ var httpError * echo.HTTPError
748+ if assert .ErrorAs (t , err , & httpError ) {
749+ assert .Equal (t , tt .statusCode , httpError .Code )
750+ }
751+ return
752+ }
753+
754+ assert .NoError (t , err )
755+ assert .Equal (t , tt .statusCode , rec .Code )
756+
757+ var resBody openapi.GamePlayStats
758+ err = json .NewDecoder (rec .Body ).Decode (& resBody )
759+ assert .NoError (t , err )
760+
761+ // GameID, TotalPlayCount, TotalPlaySecondsのチェック
762+ assert .Equal (t , expectedGamePlayStats .GameID , resBody .GameID )
763+ assert .Equal (t , expectedGamePlayStats .TotalPlayCount , resBody .TotalPlayCount )
764+ assert .Equal (t , expectedGamePlayStats .TotalPlaySeconds , resBody .TotalPlaySeconds )
765+
766+ assert .Len (t , resBody .HourlyStats , len (expectedGamePlayStats .HourlyStats ))
767+ for i , expectedHourly := range expectedGamePlayStats .HourlyStats {
768+ assert .WithinDuration (t , expectedHourly .StartTime , resBody .HourlyStats [i ].StartTime , time .Second )
769+ assert .Equal (t , expectedHourly .PlayCount , resBody .HourlyStats [i ].PlayCount )
770+ assert .Equal (t , expectedHourly .PlayTime , resBody .HourlyStats [i ].PlayTime )
771+ }
772+
773+ })
774+ }
775+ }
0 commit comments