overtime-live-trading-utils 2.1.33 → 2.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +26 -19
- package/main.js +1 -1
- package/package.json +1 -1
- package/resolution_live_markets.md +93 -88
- package/src/tests/mock/MockOpticOddsEvents.ts +144 -0
- package/src/tests/unit/resolution.test.ts +471 -71
- package/src/types/resolution.ts +25 -25
- package/src/utils/resolution.ts +43 -22
- package/src/utils/sportPeriodMapping.ts +36 -0
- package/tsconfig.json +3 -7
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
MockMLBLiveSixthInning,
|
|
16
16
|
MockSoccerLiveFirstHalfInProgress,
|
|
17
17
|
MockNFLCompletedWithOvertime,
|
|
18
|
+
MockNBACompletedEvent,
|
|
19
|
+
MockNBALiveAtHalftime,
|
|
18
20
|
} from '../mock/MockOpticOddsEvents';
|
|
19
21
|
|
|
20
22
|
describe('Resolution Utils', () => {
|
|
@@ -95,6 +97,29 @@ describe('Resolution Utils', () => {
|
|
|
95
97
|
expect(result?.currentPeriod).toBe(6);
|
|
96
98
|
});
|
|
97
99
|
|
|
100
|
+
it('Should detect completed periods for real completed NBA game (Warriors vs Suns)', () => {
|
|
101
|
+
const result = detectCompletedPeriods(MockNBACompletedEvent);
|
|
102
|
+
|
|
103
|
+
expect(result).not.toBeNull();
|
|
104
|
+
expect(result?.completedPeriods).toEqual([1, 2, 3, 4]);
|
|
105
|
+
expect(result?.readyForResolution).toBe(true);
|
|
106
|
+
expect(result?.periodScores['period1']).toEqual({ home: 33.0, away: 19.0 });
|
|
107
|
+
expect(result?.periodScores['period2']).toEqual({ home: 35.0, away: 30.0 });
|
|
108
|
+
expect(result?.periodScores['period3']).toEqual({ home: 24.0, away: 34.0 });
|
|
109
|
+
expect(result?.periodScores['period4']).toEqual({ home: 26.0, away: 24.0 });
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('Should detect completed quarters at halftime for real NBA game (Warriors vs Suns)', () => {
|
|
113
|
+
const result = detectCompletedPeriods(MockNBALiveAtHalftime);
|
|
114
|
+
|
|
115
|
+
expect(result).not.toBeNull();
|
|
116
|
+
expect(result?.completedPeriods).toEqual([1, 2]); // Both quarters complete at halftime
|
|
117
|
+
expect(result?.readyForResolution).toBe(true);
|
|
118
|
+
expect(result?.periodScores['period1']).toEqual({ home: 33.0, away: 19.0 });
|
|
119
|
+
expect(result?.periodScores['period2']).toEqual({ home: 35.0, away: 30.0 });
|
|
120
|
+
expect(result?.currentPeriod).toBe(2); // Highest period with data at halftime
|
|
121
|
+
});
|
|
122
|
+
|
|
98
123
|
it('Should return null for real live soccer game with non-numeric period indicator (1H)', () => {
|
|
99
124
|
const result = detectCompletedPeriods(MockSoccerLiveFirstHalfInProgress);
|
|
100
125
|
|
|
@@ -626,8 +651,291 @@ describe('Resolution Utils', () => {
|
|
|
626
651
|
expect(result?.currentPeriod).toBe(4);
|
|
627
652
|
// Period 4 is currently in_play, so NOT complete yet
|
|
628
653
|
});
|
|
629
|
-
});
|
|
630
654
|
|
|
655
|
+
it('Basketball at halftime should mark periods 1 AND 2 as complete (quarters-based)', () => {
|
|
656
|
+
const event = {
|
|
657
|
+
sport: {
|
|
658
|
+
id: 'basketball',
|
|
659
|
+
name: 'Basketball',
|
|
660
|
+
},
|
|
661
|
+
fixture: {
|
|
662
|
+
id: 'nba-halftime-123',
|
|
663
|
+
status: 'half',
|
|
664
|
+
is_live: true,
|
|
665
|
+
},
|
|
666
|
+
scores: {
|
|
667
|
+
home: {
|
|
668
|
+
total: 52.0,
|
|
669
|
+
periods: {
|
|
670
|
+
period_1: 25.0,
|
|
671
|
+
period_2: 27.0,
|
|
672
|
+
},
|
|
673
|
+
},
|
|
674
|
+
away: {
|
|
675
|
+
total: 48.0,
|
|
676
|
+
periods: {
|
|
677
|
+
period_1: 22.0,
|
|
678
|
+
period_2: 26.0,
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
in_play: {
|
|
683
|
+
period: 'half',
|
|
684
|
+
},
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
const result = detectCompletedPeriods(event);
|
|
688
|
+
|
|
689
|
+
expect(result).not.toBeNull();
|
|
690
|
+
expect(result?.completedPeriods).toEqual([1, 2]); // Both Q1 and Q2 complete at halftime
|
|
691
|
+
expect(result?.readyForResolution).toBe(true);
|
|
692
|
+
expect(result?.periodScores['period1']).toEqual({ home: 25.0, away: 22.0 });
|
|
693
|
+
expect(result?.periodScores['period2']).toEqual({ home: 27.0, away: 26.0 });
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it('Basketball at halftime with status "halftime" should mark periods 1 AND 2 as complete', () => {
|
|
697
|
+
const event = {
|
|
698
|
+
sport: {
|
|
699
|
+
id: 'basketball',
|
|
700
|
+
name: 'Basketball',
|
|
701
|
+
},
|
|
702
|
+
fixture: {
|
|
703
|
+
id: 'nba-halftime-456',
|
|
704
|
+
status: 'halftime',
|
|
705
|
+
is_live: true,
|
|
706
|
+
},
|
|
707
|
+
scores: {
|
|
708
|
+
home: {
|
|
709
|
+
total: 55.0,
|
|
710
|
+
periods: {
|
|
711
|
+
period_1: 28.0,
|
|
712
|
+
period_2: 27.0,
|
|
713
|
+
},
|
|
714
|
+
},
|
|
715
|
+
away: {
|
|
716
|
+
total: 50.0,
|
|
717
|
+
periods: {
|
|
718
|
+
period_1: 24.0,
|
|
719
|
+
period_2: 26.0,
|
|
720
|
+
},
|
|
721
|
+
},
|
|
722
|
+
},
|
|
723
|
+
in_play: {
|
|
724
|
+
period: '2',
|
|
725
|
+
},
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
const result = detectCompletedPeriods(event);
|
|
729
|
+
|
|
730
|
+
expect(result).not.toBeNull();
|
|
731
|
+
expect(result?.completedPeriods).toEqual([1, 2]); // Both Q1 and Q2 complete at halftime
|
|
732
|
+
expect(result?.readyForResolution).toBe(true);
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
it('Football at halftime should mark periods 1 AND 2 as complete (quarters-based)', () => {
|
|
736
|
+
const event = {
|
|
737
|
+
sport: {
|
|
738
|
+
id: 'football',
|
|
739
|
+
name: 'Football',
|
|
740
|
+
},
|
|
741
|
+
fixture: {
|
|
742
|
+
id: 'nfl-halftime-789',
|
|
743
|
+
status: 'half',
|
|
744
|
+
is_live: true,
|
|
745
|
+
},
|
|
746
|
+
scores: {
|
|
747
|
+
home: {
|
|
748
|
+
total: 21.0,
|
|
749
|
+
periods: {
|
|
750
|
+
period_1: 7.0,
|
|
751
|
+
period_2: 14.0,
|
|
752
|
+
},
|
|
753
|
+
},
|
|
754
|
+
away: {
|
|
755
|
+
total: 10.0,
|
|
756
|
+
periods: {
|
|
757
|
+
period_1: 3.0,
|
|
758
|
+
period_2: 7.0,
|
|
759
|
+
},
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
in_play: {
|
|
763
|
+
period: 'half',
|
|
764
|
+
},
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
const result = detectCompletedPeriods(event);
|
|
768
|
+
|
|
769
|
+
expect(result).not.toBeNull();
|
|
770
|
+
expect(result?.completedPeriods).toEqual([1, 2]); // Both Q1 and Q2 complete at halftime
|
|
771
|
+
expect(result?.readyForResolution).toBe(true);
|
|
772
|
+
expect(result?.periodScores['period1']).toEqual({ home: 7.0, away: 3.0 });
|
|
773
|
+
expect(result?.periodScores['period2']).toEqual({ home: 14.0, away: 7.0 });
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
it('Football at halftime with status "halftime" should mark periods 1 AND 2 as complete', () => {
|
|
777
|
+
const event = {
|
|
778
|
+
sport: {
|
|
779
|
+
id: 'football',
|
|
780
|
+
name: 'American Football',
|
|
781
|
+
},
|
|
782
|
+
fixture: {
|
|
783
|
+
id: 'nfl-halftime-890',
|
|
784
|
+
status: 'halftime',
|
|
785
|
+
is_live: true,
|
|
786
|
+
},
|
|
787
|
+
scores: {
|
|
788
|
+
home: {
|
|
789
|
+
total: 17.0,
|
|
790
|
+
periods: {
|
|
791
|
+
period_1: 10.0,
|
|
792
|
+
period_2: 7.0,
|
|
793
|
+
},
|
|
794
|
+
},
|
|
795
|
+
away: {
|
|
796
|
+
total: 14.0,
|
|
797
|
+
periods: {
|
|
798
|
+
period_1: 7.0,
|
|
799
|
+
period_2: 7.0,
|
|
800
|
+
},
|
|
801
|
+
},
|
|
802
|
+
},
|
|
803
|
+
in_play: {
|
|
804
|
+
period: '2',
|
|
805
|
+
},
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
const result = detectCompletedPeriods(event);
|
|
809
|
+
|
|
810
|
+
expect(result).not.toBeNull();
|
|
811
|
+
expect(result?.completedPeriods).toEqual([1, 2]); // Both Q1 and Q2 complete at halftime
|
|
812
|
+
expect(result?.readyForResolution).toBe(true);
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
it('Baseball at halftime should mark periods 1-5 as complete (innings-based)', () => {
|
|
816
|
+
const event = {
|
|
817
|
+
sport: {
|
|
818
|
+
id: 'baseball',
|
|
819
|
+
name: 'Baseball',
|
|
820
|
+
},
|
|
821
|
+
fixture: {
|
|
822
|
+
id: 'mlb-halftime-789',
|
|
823
|
+
status: 'half',
|
|
824
|
+
is_live: true,
|
|
825
|
+
},
|
|
826
|
+
scores: {
|
|
827
|
+
home: {
|
|
828
|
+
total: 3.0,
|
|
829
|
+
periods: {
|
|
830
|
+
period_1: 0.0,
|
|
831
|
+
period_2: 1.0,
|
|
832
|
+
period_3: 1.0,
|
|
833
|
+
period_4: 0.0,
|
|
834
|
+
period_5: 1.0,
|
|
835
|
+
},
|
|
836
|
+
},
|
|
837
|
+
away: {
|
|
838
|
+
total: 2.0,
|
|
839
|
+
periods: {
|
|
840
|
+
period_1: 1.0,
|
|
841
|
+
period_2: 0.0,
|
|
842
|
+
period_3: 0.0,
|
|
843
|
+
period_4: 1.0,
|
|
844
|
+
period_5: 0.0,
|
|
845
|
+
},
|
|
846
|
+
},
|
|
847
|
+
},
|
|
848
|
+
in_play: {
|
|
849
|
+
period: 'half',
|
|
850
|
+
},
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
const result = detectCompletedPeriods(event);
|
|
854
|
+
|
|
855
|
+
expect(result).not.toBeNull();
|
|
856
|
+
expect(result?.completedPeriods).toEqual([1, 2, 3, 4, 5]); // First 5 innings complete at halftime
|
|
857
|
+
expect(result?.readyForResolution).toBe(true);
|
|
858
|
+
expect(result?.periodScores['period1']).toEqual({ home: 0.0, away: 1.0 });
|
|
859
|
+
expect(result?.periodScores['period5']).toEqual({ home: 1.0, away: 0.0 });
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
it('Soccer at halftime should mark only period 1 as complete (halves-based)', () => {
|
|
863
|
+
const event = {
|
|
864
|
+
sport: {
|
|
865
|
+
id: 'soccer',
|
|
866
|
+
name: 'Soccer',
|
|
867
|
+
},
|
|
868
|
+
fixture: {
|
|
869
|
+
id: 'soccer-halftime-101',
|
|
870
|
+
status: 'halftime',
|
|
871
|
+
is_live: true,
|
|
872
|
+
},
|
|
873
|
+
scores: {
|
|
874
|
+
home: {
|
|
875
|
+
total: 2.0,
|
|
876
|
+
periods: {
|
|
877
|
+
period_1: 2.0,
|
|
878
|
+
},
|
|
879
|
+
},
|
|
880
|
+
away: {
|
|
881
|
+
total: 1.0,
|
|
882
|
+
periods: {
|
|
883
|
+
period_1: 1.0,
|
|
884
|
+
},
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
in_play: {
|
|
888
|
+
period: 'half',
|
|
889
|
+
},
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
const result = detectCompletedPeriods(event);
|
|
893
|
+
|
|
894
|
+
expect(result).not.toBeNull();
|
|
895
|
+
expect(result?.completedPeriods).toEqual([1]); // Only first half complete at halftime
|
|
896
|
+
expect(result?.readyForResolution).toBe(true);
|
|
897
|
+
expect(result?.periodScores['period1']).toEqual({ home: 2.0, away: 1.0 });
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
it('Hockey at halftime should NOT mark any periods as complete (period-based, no halftime concept)', () => {
|
|
901
|
+
const event = {
|
|
902
|
+
sport: {
|
|
903
|
+
id: 'hockey',
|
|
904
|
+
name: 'Hockey',
|
|
905
|
+
},
|
|
906
|
+
fixture: {
|
|
907
|
+
id: 'nhl-halftime-202',
|
|
908
|
+
status: 'half',
|
|
909
|
+
is_live: true,
|
|
910
|
+
},
|
|
911
|
+
scores: {
|
|
912
|
+
home: {
|
|
913
|
+
total: 2.0,
|
|
914
|
+
periods: {
|
|
915
|
+
period_1: 1.0,
|
|
916
|
+
period_2: 1.0,
|
|
917
|
+
},
|
|
918
|
+
},
|
|
919
|
+
away: {
|
|
920
|
+
total: 1.0,
|
|
921
|
+
periods: {
|
|
922
|
+
period_1: 0.0,
|
|
923
|
+
period_2: 1.0,
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
},
|
|
927
|
+
in_play: {
|
|
928
|
+
period: 'half',
|
|
929
|
+
},
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
const result = detectCompletedPeriods(event);
|
|
933
|
+
|
|
934
|
+
// Hockey doesn't have traditional halftime, so halftime status shouldn't mark periods complete
|
|
935
|
+
// Periods should only be marked complete based on in_play.period progression
|
|
936
|
+
expect(result).toBeNull();
|
|
937
|
+
});
|
|
938
|
+
});
|
|
631
939
|
|
|
632
940
|
describe('canResolveMarketsForEvent', () => {
|
|
633
941
|
describe('Single typeId checks', () => {
|
|
@@ -652,25 +960,45 @@ describe('Resolution Utils', () => {
|
|
|
652
960
|
});
|
|
653
961
|
|
|
654
962
|
it('Should return true for 1st quarter typeId when quarter 1 complete (NFL)', () => {
|
|
655
|
-
const result = canResolveMarketsForEvent(
|
|
963
|
+
const result = canResolveMarketsForEvent(
|
|
964
|
+
MockNFLLiveThirdQuarter,
|
|
965
|
+
10021,
|
|
966
|
+
SportPeriodType.QUARTERS_BASED
|
|
967
|
+
);
|
|
656
968
|
expect(result).toBe(true);
|
|
657
969
|
});
|
|
658
970
|
|
|
659
971
|
it('Should return true for 2nd quarter typeId when quarters 1-2 complete (NFL)', () => {
|
|
660
|
-
const result = canResolveMarketsForEvent(
|
|
972
|
+
const result = canResolveMarketsForEvent(
|
|
973
|
+
MockNFLLiveThirdQuarter,
|
|
974
|
+
10022,
|
|
975
|
+
SportPeriodType.QUARTERS_BASED
|
|
976
|
+
);
|
|
661
977
|
expect(result).toBe(true);
|
|
662
978
|
});
|
|
663
979
|
|
|
664
980
|
it('Should return false for 3rd quarter typeId during 3rd quarter (NFL)', () => {
|
|
665
|
-
const result = canResolveMarketsForEvent(
|
|
981
|
+
const result = canResolveMarketsForEvent(
|
|
982
|
+
MockNFLLiveThirdQuarter,
|
|
983
|
+
10023,
|
|
984
|
+
SportPeriodType.QUARTERS_BASED
|
|
985
|
+
);
|
|
666
986
|
expect(result).toBe(false);
|
|
667
987
|
});
|
|
668
988
|
|
|
669
989
|
it('Should return true for all quarter typeIds when game is completed (NFL)', () => {
|
|
670
|
-
expect(canResolveMarketsForEvent(MockNFLCompletedEvent, 10021, SportPeriodType.QUARTERS_BASED)).toBe(
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
expect(canResolveMarketsForEvent(MockNFLCompletedEvent,
|
|
990
|
+
expect(canResolveMarketsForEvent(MockNFLCompletedEvent, 10021, SportPeriodType.QUARTERS_BASED)).toBe(
|
|
991
|
+
true
|
|
992
|
+
);
|
|
993
|
+
expect(canResolveMarketsForEvent(MockNFLCompletedEvent, 10022, SportPeriodType.QUARTERS_BASED)).toBe(
|
|
994
|
+
true
|
|
995
|
+
);
|
|
996
|
+
expect(canResolveMarketsForEvent(MockNFLCompletedEvent, 10023, SportPeriodType.QUARTERS_BASED)).toBe(
|
|
997
|
+
true
|
|
998
|
+
);
|
|
999
|
+
expect(canResolveMarketsForEvent(MockNFLCompletedEvent, 10024, SportPeriodType.QUARTERS_BASED)).toBe(
|
|
1000
|
+
true
|
|
1001
|
+
);
|
|
674
1002
|
});
|
|
675
1003
|
|
|
676
1004
|
it('Should return false when no periods are complete', () => {
|
|
@@ -687,35 +1015,55 @@ describe('Resolution Utils', () => {
|
|
|
687
1015
|
describe('Batch typeIds checks', () => {
|
|
688
1016
|
it('Should return only resolvable typeIds for live soccer in 2nd half', () => {
|
|
689
1017
|
const typeIds = [10021, 10022, 10031, 10001];
|
|
690
|
-
const result = filterMarketsThatCanBeResolved(
|
|
1018
|
+
const result = filterMarketsThatCanBeResolved(
|
|
1019
|
+
MockSoccerLiveSecondHalf,
|
|
1020
|
+
typeIds,
|
|
1021
|
+
SportPeriodType.HALVES_BASED
|
|
1022
|
+
);
|
|
691
1023
|
|
|
692
1024
|
expect(result).toEqual([10021, 10031]); // Only period 1 typeIds
|
|
693
1025
|
});
|
|
694
1026
|
|
|
695
1027
|
it('Should exclude full game typeIds during live game', () => {
|
|
696
1028
|
const typeIds = [10021, 10001, 10002, 10003];
|
|
697
|
-
const result = filterMarketsThatCanBeResolved(
|
|
1029
|
+
const result = filterMarketsThatCanBeResolved(
|
|
1030
|
+
MockNFLLiveThirdQuarter,
|
|
1031
|
+
typeIds,
|
|
1032
|
+
SportPeriodType.QUARTERS_BASED
|
|
1033
|
+
);
|
|
698
1034
|
|
|
699
1035
|
expect(result).toEqual([10021]); // Full game typeIds excluded
|
|
700
1036
|
});
|
|
701
1037
|
|
|
702
1038
|
it('Should include full game typeIds when game is completed', () => {
|
|
703
1039
|
const typeIds = [10021, 10022, 10001, 10002];
|
|
704
|
-
const result = filterMarketsThatCanBeResolved(
|
|
1040
|
+
const result = filterMarketsThatCanBeResolved(
|
|
1041
|
+
MockSoccerCompletedEvent,
|
|
1042
|
+
typeIds,
|
|
1043
|
+
SportPeriodType.HALVES_BASED
|
|
1044
|
+
);
|
|
705
1045
|
|
|
706
1046
|
expect(result).toEqual([10021, 10022, 10001, 10002]);
|
|
707
1047
|
});
|
|
708
1048
|
|
|
709
1049
|
it('Should return empty array when no typeIds are resolvable', () => {
|
|
710
1050
|
const typeIds = [10022, 10023, 10024];
|
|
711
|
-
const result = filterMarketsThatCanBeResolved(
|
|
1051
|
+
const result = filterMarketsThatCanBeResolved(
|
|
1052
|
+
MockSoccerLiveSecondHalf,
|
|
1053
|
+
typeIds,
|
|
1054
|
+
SportPeriodType.HALVES_BASED
|
|
1055
|
+
);
|
|
712
1056
|
|
|
713
1057
|
expect(result).toEqual([]);
|
|
714
1058
|
});
|
|
715
1059
|
|
|
716
1060
|
it('Should return multiple period typeIds for NFL game in 3rd quarter', () => {
|
|
717
1061
|
const typeIds = [10021, 10022, 10023, 10024, 10031, 10032, 10051];
|
|
718
|
-
const result = filterMarketsThatCanBeResolved(
|
|
1062
|
+
const result = filterMarketsThatCanBeResolved(
|
|
1063
|
+
MockNFLLiveThirdQuarter,
|
|
1064
|
+
typeIds,
|
|
1065
|
+
SportPeriodType.QUARTERS_BASED
|
|
1066
|
+
);
|
|
719
1067
|
|
|
720
1068
|
// Periods 1 and 2 are complete (period 2 also completes 1st half = 10051)
|
|
721
1069
|
expect(result).toEqual([10021, 10022, 10031, 10032, 10051]);
|
|
@@ -723,14 +1071,22 @@ describe('Resolution Utils', () => {
|
|
|
723
1071
|
|
|
724
1072
|
it('Should handle all 9 periods for completed MLB game', () => {
|
|
725
1073
|
const typeIds = [10021, 10022, 10023, 10024, 10025, 10026, 10027, 10028, 10029];
|
|
726
|
-
const result = filterMarketsThatCanBeResolved(
|
|
1074
|
+
const result = filterMarketsThatCanBeResolved(
|
|
1075
|
+
MockMLBCompletedEvent,
|
|
1076
|
+
typeIds,
|
|
1077
|
+
SportPeriodType.INNINGS_BASED
|
|
1078
|
+
);
|
|
727
1079
|
|
|
728
1080
|
expect(result).toEqual(typeIds); // All 9 innings complete
|
|
729
1081
|
});
|
|
730
1082
|
|
|
731
1083
|
it('Should return empty array when no periods complete', () => {
|
|
732
1084
|
const typeIds = [10021, 10022, 10031];
|
|
733
|
-
const result = filterMarketsThatCanBeResolved(
|
|
1085
|
+
const result = filterMarketsThatCanBeResolved(
|
|
1086
|
+
MockSoccerLiveFirstHalf,
|
|
1087
|
+
typeIds,
|
|
1088
|
+
SportPeriodType.HALVES_BASED
|
|
1089
|
+
);
|
|
734
1090
|
|
|
735
1091
|
expect(result).toEqual([]);
|
|
736
1092
|
});
|
|
@@ -739,35 +1095,55 @@ describe('Resolution Utils', () => {
|
|
|
739
1095
|
describe('Multiple typeIds boolean array checks', () => {
|
|
740
1096
|
it('Should return boolean array for live soccer in 2nd half', () => {
|
|
741
1097
|
const typeIds = [10021, 10022, 10031, 10001];
|
|
742
|
-
const result = canResolveMultipleTypeIdsForEvent(
|
|
1098
|
+
const result = canResolveMultipleTypeIdsForEvent(
|
|
1099
|
+
MockSoccerLiveSecondHalf,
|
|
1100
|
+
typeIds,
|
|
1101
|
+
SportPeriodType.HALVES_BASED
|
|
1102
|
+
);
|
|
743
1103
|
|
|
744
1104
|
expect(result).toEqual([true, false, true, false]); // Period 1 typeIds are true, period 2 and full game are false
|
|
745
1105
|
});
|
|
746
1106
|
|
|
747
1107
|
it('Should return false for full game typeIds during live game', () => {
|
|
748
1108
|
const typeIds = [10021, 10001, 10002, 10003];
|
|
749
|
-
const result = canResolveMultipleTypeIdsForEvent(
|
|
1109
|
+
const result = canResolveMultipleTypeIdsForEvent(
|
|
1110
|
+
MockNFLLiveThirdQuarter,
|
|
1111
|
+
typeIds,
|
|
1112
|
+
SportPeriodType.QUARTERS_BASED
|
|
1113
|
+
);
|
|
750
1114
|
|
|
751
1115
|
expect(result).toEqual([true, false, false, false]); // Only period 1 is true
|
|
752
1116
|
});
|
|
753
1117
|
|
|
754
1118
|
it('Should return all true for completed game', () => {
|
|
755
1119
|
const typeIds = [10021, 10022, 10001, 10002];
|
|
756
|
-
const result = canResolveMultipleTypeIdsForEvent(
|
|
1120
|
+
const result = canResolveMultipleTypeIdsForEvent(
|
|
1121
|
+
MockSoccerCompletedEvent,
|
|
1122
|
+
typeIds,
|
|
1123
|
+
SportPeriodType.HALVES_BASED
|
|
1124
|
+
);
|
|
757
1125
|
|
|
758
1126
|
expect(result).toEqual([true, true, true, true]); // All complete
|
|
759
1127
|
});
|
|
760
1128
|
|
|
761
1129
|
it('Should return all false when no typeIds are resolvable', () => {
|
|
762
1130
|
const typeIds = [10022, 10023, 10024];
|
|
763
|
-
const result = canResolveMultipleTypeIdsForEvent(
|
|
1131
|
+
const result = canResolveMultipleTypeIdsForEvent(
|
|
1132
|
+
MockSoccerLiveSecondHalf,
|
|
1133
|
+
typeIds,
|
|
1134
|
+
SportPeriodType.HALVES_BASED
|
|
1135
|
+
);
|
|
764
1136
|
|
|
765
1137
|
expect(result).toEqual([false, false, false]);
|
|
766
1138
|
});
|
|
767
1139
|
|
|
768
1140
|
it('Should return mixed booleans for NFL game in 3rd quarter', () => {
|
|
769
1141
|
const typeIds = [10021, 10022, 10023, 10024, 10031, 10032, 10051];
|
|
770
|
-
const result = canResolveMultipleTypeIdsForEvent(
|
|
1142
|
+
const result = canResolveMultipleTypeIdsForEvent(
|
|
1143
|
+
MockNFLLiveThirdQuarter,
|
|
1144
|
+
typeIds,
|
|
1145
|
+
SportPeriodType.QUARTERS_BASED
|
|
1146
|
+
);
|
|
771
1147
|
|
|
772
1148
|
// Periods 1 and 2 are complete (period 2 also completes 1st half = 10051)
|
|
773
1149
|
expect(result).toEqual([true, true, false, false, true, true, true]);
|
|
@@ -775,14 +1151,22 @@ describe('Resolution Utils', () => {
|
|
|
775
1151
|
|
|
776
1152
|
it('Should handle all 9 periods for completed MLB game', () => {
|
|
777
1153
|
const typeIds = [10021, 10022, 10023, 10024, 10025, 10026, 10027, 10028, 10029];
|
|
778
|
-
const result = canResolveMultipleTypeIdsForEvent(
|
|
1154
|
+
const result = canResolveMultipleTypeIdsForEvent(
|
|
1155
|
+
MockMLBCompletedEvent,
|
|
1156
|
+
typeIds,
|
|
1157
|
+
SportPeriodType.INNINGS_BASED
|
|
1158
|
+
);
|
|
779
1159
|
|
|
780
1160
|
expect(result).toEqual([true, true, true, true, true, true, true, true, true]); // All 9 innings complete
|
|
781
1161
|
});
|
|
782
1162
|
|
|
783
1163
|
it('Should return all false when no periods complete', () => {
|
|
784
1164
|
const typeIds = [10021, 10022, 10031];
|
|
785
|
-
const result = canResolveMultipleTypeIdsForEvent(
|
|
1165
|
+
const result = canResolveMultipleTypeIdsForEvent(
|
|
1166
|
+
MockSoccerLiveFirstHalf,
|
|
1167
|
+
typeIds,
|
|
1168
|
+
SportPeriodType.HALVES_BASED
|
|
1169
|
+
);
|
|
786
1170
|
|
|
787
1171
|
expect(result).toEqual([false, false, false]);
|
|
788
1172
|
});
|
|
@@ -797,7 +1181,11 @@ describe('Resolution Utils', () => {
|
|
|
797
1181
|
|
|
798
1182
|
describe('Edge cases', () => {
|
|
799
1183
|
it('Should handle event with no completed periods', () => {
|
|
800
|
-
const result = canResolveMarketsForEvent(
|
|
1184
|
+
const result = canResolveMarketsForEvent(
|
|
1185
|
+
MockSoccerLiveFirstHalfInProgress,
|
|
1186
|
+
10021,
|
|
1187
|
+
SportPeriodType.HALVES_BASED
|
|
1188
|
+
);
|
|
801
1189
|
expect(result).toBe(false);
|
|
802
1190
|
});
|
|
803
1191
|
|
|
@@ -806,7 +1194,11 @@ describe('Resolution Utils', () => {
|
|
|
806
1194
|
const fullGameTypeIds = [0, 10001, 10002, 10003, 10004, 10010, 10011, 10012];
|
|
807
1195
|
|
|
808
1196
|
fullGameTypeIds.forEach((typeId) => {
|
|
809
|
-
const result = canResolveMarketsForEvent(
|
|
1197
|
+
const result = canResolveMarketsForEvent(
|
|
1198
|
+
MockNFLLiveThirdQuarter,
|
|
1199
|
+
typeId,
|
|
1200
|
+
SportPeriodType.QUARTERS_BASED
|
|
1201
|
+
);
|
|
810
1202
|
expect(result).toBe(false);
|
|
811
1203
|
});
|
|
812
1204
|
});
|
|
@@ -817,7 +1209,11 @@ describe('Resolution Utils', () => {
|
|
|
817
1209
|
});
|
|
818
1210
|
|
|
819
1211
|
it('Should work with sport type parameter for batch typeIds', () => {
|
|
820
|
-
const result = filterMarketsThatCanBeResolved(
|
|
1212
|
+
const result = filterMarketsThatCanBeResolved(
|
|
1213
|
+
MockSoccerLiveSecondHalf,
|
|
1214
|
+
[10021, 10022],
|
|
1215
|
+
SportPeriodType.HALVES_BASED
|
|
1216
|
+
);
|
|
821
1217
|
expect(result).toEqual([10021]);
|
|
822
1218
|
});
|
|
823
1219
|
});
|
|
@@ -833,42 +1229,74 @@ describe('Resolution Utils', () => {
|
|
|
833
1229
|
});
|
|
834
1230
|
|
|
835
1231
|
it('Should resolve all quarter typeIds (10021-10024) for completed overtime game', () => {
|
|
836
|
-
expect(
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
expect(
|
|
1232
|
+
expect(
|
|
1233
|
+
canResolveMarketsForEvent(MockNFLCompletedWithOvertime, 10021, SportPeriodType.QUARTERS_BASED)
|
|
1234
|
+
).toBe(true);
|
|
1235
|
+
expect(
|
|
1236
|
+
canResolveMarketsForEvent(MockNFLCompletedWithOvertime, 10022, SportPeriodType.QUARTERS_BASED)
|
|
1237
|
+
).toBe(true);
|
|
1238
|
+
expect(
|
|
1239
|
+
canResolveMarketsForEvent(MockNFLCompletedWithOvertime, 10023, SportPeriodType.QUARTERS_BASED)
|
|
1240
|
+
).toBe(true);
|
|
1241
|
+
expect(
|
|
1242
|
+
canResolveMarketsForEvent(MockNFLCompletedWithOvertime, 10024, SportPeriodType.QUARTERS_BASED)
|
|
1243
|
+
).toBe(true);
|
|
840
1244
|
});
|
|
841
1245
|
|
|
842
1246
|
it('Should resolve overtime period typeId (10025)', () => {
|
|
843
|
-
const result = canResolveMarketsForEvent(
|
|
1247
|
+
const result = canResolveMarketsForEvent(
|
|
1248
|
+
MockNFLCompletedWithOvertime,
|
|
1249
|
+
10025,
|
|
1250
|
+
SportPeriodType.QUARTERS_BASED
|
|
1251
|
+
);
|
|
844
1252
|
expect(result).toBe(true);
|
|
845
1253
|
});
|
|
846
1254
|
|
|
847
1255
|
it('Should resolve full game typeIds for completed overtime game', () => {
|
|
848
|
-
expect(
|
|
849
|
-
|
|
1256
|
+
expect(
|
|
1257
|
+
canResolveMarketsForEvent(MockNFLCompletedWithOvertime, 10001, SportPeriodType.QUARTERS_BASED)
|
|
1258
|
+
).toBe(true);
|
|
1259
|
+
expect(
|
|
1260
|
+
canResolveMarketsForEvent(MockNFLCompletedWithOvertime, 10002, SportPeriodType.QUARTERS_BASED)
|
|
1261
|
+
).toBe(true);
|
|
850
1262
|
});
|
|
851
1263
|
|
|
852
1264
|
it('Should return all resolvable typeIds including overtime in batch check', () => {
|
|
853
1265
|
const typeIds = [10021, 10022, 10023, 10024, 10025, 10001];
|
|
854
|
-
const result = filterMarketsThatCanBeResolved(
|
|
1266
|
+
const result = filterMarketsThatCanBeResolved(
|
|
1267
|
+
MockNFLCompletedWithOvertime,
|
|
1268
|
+
typeIds,
|
|
1269
|
+
SportPeriodType.QUARTERS_BASED
|
|
1270
|
+
);
|
|
855
1271
|
|
|
856
1272
|
expect(result).toEqual(typeIds); // All should be resolvable (game completed with overtime)
|
|
857
1273
|
});
|
|
858
1274
|
|
|
859
1275
|
it('Should return false for 8th period typeId (period did not occur)', () => {
|
|
860
|
-
const result = canResolveMarketsForEvent(
|
|
1276
|
+
const result = canResolveMarketsForEvent(
|
|
1277
|
+
MockNFLCompletedWithOvertime,
|
|
1278
|
+
10028,
|
|
1279
|
+
SportPeriodType.QUARTERS_BASED
|
|
1280
|
+
);
|
|
861
1281
|
expect(result).toBe(false);
|
|
862
1282
|
});
|
|
863
1283
|
|
|
864
1284
|
it('Should return false for 9th period typeId (period did not occur)', () => {
|
|
865
|
-
const result = canResolveMarketsForEvent(
|
|
1285
|
+
const result = canResolveMarketsForEvent(
|
|
1286
|
+
MockNFLCompletedWithOvertime,
|
|
1287
|
+
10029,
|
|
1288
|
+
SportPeriodType.QUARTERS_BASED
|
|
1289
|
+
);
|
|
866
1290
|
expect(result).toBe(false);
|
|
867
1291
|
});
|
|
868
1292
|
|
|
869
1293
|
it('Should not include non-existent periods in batch check', () => {
|
|
870
1294
|
const typeIds = [10021, 10022, 10025, 10028, 10029];
|
|
871
|
-
const result = filterMarketsThatCanBeResolved(
|
|
1295
|
+
const result = filterMarketsThatCanBeResolved(
|
|
1296
|
+
MockNFLCompletedWithOvertime,
|
|
1297
|
+
typeIds,
|
|
1298
|
+
SportPeriodType.QUARTERS_BASED
|
|
1299
|
+
);
|
|
872
1300
|
|
|
873
1301
|
// Only periods 1, 2, and 5 occurred, so only their typeIds should be returned
|
|
874
1302
|
expect(result).toEqual([10021, 10022, 10025]);
|
|
@@ -878,11 +1306,7 @@ describe('Resolution Utils', () => {
|
|
|
878
1306
|
describe('Sport-type-specific resolution for typeId 10051 (1st half)', () => {
|
|
879
1307
|
it('Soccer (HALVES_BASED): Should resolve typeId 10051 after period 1', () => {
|
|
880
1308
|
// Soccer: Period 1 = 1st half
|
|
881
|
-
const result = canResolveMarketsForEvent(
|
|
882
|
-
MockSoccerLiveSecondHalf,
|
|
883
|
-
10051,
|
|
884
|
-
SportPeriodType.HALVES_BASED
|
|
885
|
-
);
|
|
1309
|
+
const result = canResolveMarketsForEvent(MockSoccerLiveSecondHalf, 10051, SportPeriodType.HALVES_BASED);
|
|
886
1310
|
expect(result).toBe(true);
|
|
887
1311
|
});
|
|
888
1312
|
|
|
@@ -907,21 +1331,13 @@ describe('Resolution Utils', () => {
|
|
|
907
1331
|
in_play: { period: '2', clock: '5:00' },
|
|
908
1332
|
};
|
|
909
1333
|
|
|
910
|
-
const result = canResolveMarketsForEvent(
|
|
911
|
-
mockNFLFirstQuarter,
|
|
912
|
-
10051,
|
|
913
|
-
SportPeriodType.QUARTERS_BASED
|
|
914
|
-
);
|
|
1334
|
+
const result = canResolveMarketsForEvent(mockNFLFirstQuarter, 10051, SportPeriodType.QUARTERS_BASED);
|
|
915
1335
|
expect(result).toBe(false);
|
|
916
1336
|
});
|
|
917
1337
|
|
|
918
1338
|
it('MLB (INNINGS_BASED): Should resolve typeId 10051 after period 5', () => {
|
|
919
1339
|
// MLB: Period 5 completes 1st half (innings 1-5)
|
|
920
|
-
const result = canResolveMarketsForEvent(
|
|
921
|
-
MockMLBLiveSixthInning,
|
|
922
|
-
10051,
|
|
923
|
-
SportPeriodType.INNINGS_BASED
|
|
924
|
-
);
|
|
1340
|
+
const result = canResolveMarketsForEvent(MockMLBLiveSixthInning, 10051, SportPeriodType.INNINGS_BASED);
|
|
925
1341
|
expect(result).toBe(true);
|
|
926
1342
|
});
|
|
927
1343
|
|
|
@@ -952,40 +1368,24 @@ describe('Resolution Utils', () => {
|
|
|
952
1368
|
in_play: { period: '5', clock: null },
|
|
953
1369
|
};
|
|
954
1370
|
|
|
955
|
-
const result = canResolveMarketsForEvent(
|
|
956
|
-
mockMLBFourthInning,
|
|
957
|
-
10051,
|
|
958
|
-
SportPeriodType.INNINGS_BASED
|
|
959
|
-
);
|
|
1371
|
+
const result = canResolveMarketsForEvent(mockMLBFourthInning, 10051, SportPeriodType.INNINGS_BASED);
|
|
960
1372
|
expect(result).toBe(false);
|
|
961
1373
|
});
|
|
962
1374
|
});
|
|
963
1375
|
|
|
964
1376
|
describe('Sport-type-specific resolution for typeId 10052 (2nd half)', () => {
|
|
965
1377
|
it('Soccer (HALVES_BASED): Should resolve typeId 10052 after period 2', () => {
|
|
966
|
-
const result = canResolveMarketsForEvent(
|
|
967
|
-
MockSoccerCompletedEvent,
|
|
968
|
-
10052,
|
|
969
|
-
SportPeriodType.HALVES_BASED
|
|
970
|
-
);
|
|
1378
|
+
const result = canResolveMarketsForEvent(MockSoccerCompletedEvent, 10052, SportPeriodType.HALVES_BASED);
|
|
971
1379
|
expect(result).toBe(true);
|
|
972
1380
|
});
|
|
973
1381
|
|
|
974
1382
|
it('NFL (QUARTERS_BASED): Should resolve typeId 10052 after period 4', () => {
|
|
975
|
-
const result = canResolveMarketsForEvent(
|
|
976
|
-
MockNFLCompletedEvent,
|
|
977
|
-
10052,
|
|
978
|
-
SportPeriodType.QUARTERS_BASED
|
|
979
|
-
);
|
|
1383
|
+
const result = canResolveMarketsForEvent(MockNFLCompletedEvent, 10052, SportPeriodType.QUARTERS_BASED);
|
|
980
1384
|
expect(result).toBe(true);
|
|
981
1385
|
});
|
|
982
1386
|
|
|
983
1387
|
it('MLB (INNINGS_BASED): Should resolve typeId 10052 after period 9', () => {
|
|
984
|
-
const result = canResolveMarketsForEvent(
|
|
985
|
-
MockMLBCompletedEvent,
|
|
986
|
-
10052,
|
|
987
|
-
SportPeriodType.INNINGS_BASED
|
|
988
|
-
);
|
|
1388
|
+
const result = canResolveMarketsForEvent(MockMLBCompletedEvent, 10052, SportPeriodType.INNINGS_BASED);
|
|
989
1389
|
expect(result).toBe(true);
|
|
990
1390
|
});
|
|
991
1391
|
});
|