volleyballsimtypes 0.0.462 → 0.0.464
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/dist/cjs/src/api/index.d.ts +14 -1
- package/dist/cjs/src/data/init-models.js +17 -0
- package/dist/cjs/src/data/models/currency-transaction.d.ts +4 -2
- package/dist/cjs/src/data/models/currency-transaction.js +2 -0
- package/dist/cjs/src/data/models/index.d.ts +3 -0
- package/dist/cjs/src/data/models/index.js +3 -0
- package/dist/cjs/src/data/models/match-preset.d.ts +28 -0
- package/dist/cjs/src/data/models/match-preset.js +65 -0
- package/dist/cjs/src/data/models/match-set.d.ts +2 -0
- package/dist/cjs/src/data/models/match-set.js +5 -0
- package/dist/cjs/src/data/models/match.d.ts +5 -2
- package/dist/cjs/src/data/models/match.js +4 -0
- package/dist/cjs/src/data/models/physician.d.ts +25 -0
- package/dist/cjs/src/data/models/physician.js +57 -0
- package/dist/cjs/src/data/models/physiotherapist.d.ts +25 -0
- package/dist/cjs/src/data/models/physiotherapist.js +57 -0
- package/dist/cjs/src/data/models/player.d.ts +8 -0
- package/dist/cjs/src/data/models/player.js +17 -0
- package/dist/cjs/src/data/models/tactics.d.ts +9 -1
- package/dist/cjs/src/data/models/tactics.js +9 -0
- package/dist/cjs/src/data/transformers/division.js +1 -1
- package/dist/cjs/src/data/transformers/lineup-preset.js +7 -1
- package/dist/cjs/src/data/transformers/lineup-preset.test.js +15 -2
- package/dist/cjs/src/data/transformers/match-preset-override.test.d.ts +1 -0
- package/dist/cjs/src/data/transformers/match-preset-override.test.js +275 -0
- package/dist/cjs/src/data/transformers/match-set.js +2 -0
- package/dist/cjs/src/data/transformers/match.d.ts +2 -1
- package/dist/cjs/src/data/transformers/match.js +21 -5
- package/dist/cjs/src/data/transformers/player.js +15 -1
- package/dist/cjs/src/data/transformers/qualifier.js +1 -1
- package/dist/cjs/src/data/transformers/rally-event-injury.test.d.ts +1 -0
- package/dist/cjs/src/data/transformers/rally-event-injury.test.js +33 -0
- package/dist/cjs/src/data/transformers/rally-event.d.ts +1 -0
- package/dist/cjs/src/data/transformers/rally-event.js +4 -1
- package/dist/cjs/src/data/transformers/season.js +1 -1
- package/dist/cjs/src/data/transformers/tactics.d.ts +3 -2
- package/dist/cjs/src/data/transformers/tactics.js +106 -3
- package/dist/cjs/src/data/transformers/team.d.ts +7 -1
- package/dist/cjs/src/data/transformers/team.js +22 -4
- package/dist/cjs/src/data/transformers/tournament.js +1 -1
- package/dist/cjs/src/service/event/schemas/substitution.z.d.ts +1 -0
- package/dist/cjs/src/service/event/schemas/substitution.z.js +2 -1
- package/dist/cjs/src/service/event/substitution.d.ts +1 -0
- package/dist/cjs/src/service/event/substitution.js +4 -2
- package/dist/cjs/src/service/match/match-set-forfeit.test.d.ts +1 -0
- package/dist/cjs/src/service/match/match-set-forfeit.test.js +76 -0
- package/dist/cjs/src/service/match/match-set.d.ts +8 -0
- package/dist/cjs/src/service/match/match-set.js +23 -1
- package/dist/cjs/src/service/match/match.d.ts +2 -0
- package/dist/cjs/src/service/match/match.js +2 -1
- package/dist/cjs/src/service/match/schemas/match-set.z.d.ts +1 -0
- package/dist/cjs/src/service/match/schemas/match-set.z.js +30 -2
- package/dist/cjs/src/service/match/schemas/match.z.d.ts +4 -0
- package/dist/cjs/src/service/match/schemas/match.z.js +4 -1
- package/dist/cjs/src/service/player/index.d.ts +1 -0
- package/dist/cjs/src/service/player/index.js +1 -0
- package/dist/cjs/src/service/player/injury.d.ts +14 -0
- package/dist/cjs/src/service/player/injury.js +39 -0
- package/dist/cjs/src/service/player/injury.test.d.ts +1 -0
- package/dist/cjs/src/service/player/injury.test.js +78 -0
- package/dist/cjs/src/service/player/player-generator.js +3 -1
- package/dist/cjs/src/service/player/player.d.ts +3 -0
- package/dist/cjs/src/service/player/player.js +3 -1
- package/dist/cjs/src/service/player/schemas/player.z.d.ts +6 -0
- package/dist/cjs/src/service/player/schemas/player.z.js +7 -0
- package/dist/cjs/src/service/team/index.d.ts +1 -0
- package/dist/cjs/src/service/team/index.js +1 -0
- package/dist/cjs/src/service/team/reserve-priority.d.ts +6 -0
- package/dist/cjs/src/service/team/reserve-priority.js +2 -0
- package/dist/cjs/src/service/team/schemas/reserve-priority.z.test.d.ts +1 -0
- package/dist/cjs/src/service/team/schemas/reserve-priority.z.test.js +125 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +5 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.js +32 -1
- package/dist/cjs/src/service/team/schemas/team.z.d.ts +5 -0
- package/dist/cjs/src/service/team/tactics.d.ts +5 -0
- package/dist/cjs/src/service/team/tactics.js +3 -1
- package/dist/esm/src/api/index.d.ts +14 -1
- package/dist/esm/src/data/init-models.js +18 -1
- package/dist/esm/src/data/models/currency-transaction.d.ts +4 -2
- package/dist/esm/src/data/models/currency-transaction.js +2 -0
- package/dist/esm/src/data/models/index.d.ts +3 -0
- package/dist/esm/src/data/models/index.js +3 -0
- package/dist/esm/src/data/models/match-preset.d.ts +28 -0
- package/dist/esm/src/data/models/match-preset.js +61 -0
- package/dist/esm/src/data/models/match-set.d.ts +2 -0
- package/dist/esm/src/data/models/match-set.js +5 -0
- package/dist/esm/src/data/models/match.d.ts +5 -2
- package/dist/esm/src/data/models/match.js +4 -0
- package/dist/esm/src/data/models/physician.d.ts +25 -0
- package/dist/esm/src/data/models/physician.js +53 -0
- package/dist/esm/src/data/models/physiotherapist.d.ts +25 -0
- package/dist/esm/src/data/models/physiotherapist.js +53 -0
- package/dist/esm/src/data/models/player.d.ts +8 -0
- package/dist/esm/src/data/models/player.js +17 -0
- package/dist/esm/src/data/models/tactics.d.ts +9 -1
- package/dist/esm/src/data/models/tactics.js +9 -0
- package/dist/esm/src/data/transformers/division.js +1 -1
- package/dist/esm/src/data/transformers/lineup-preset.js +7 -1
- package/dist/esm/src/data/transformers/lineup-preset.test.js +15 -2
- package/dist/esm/src/data/transformers/match-preset-override.test.d.ts +1 -0
- package/dist/esm/src/data/transformers/match-preset-override.test.js +273 -0
- package/dist/esm/src/data/transformers/match-set.js +2 -0
- package/dist/esm/src/data/transformers/match.d.ts +2 -1
- package/dist/esm/src/data/transformers/match.js +21 -5
- package/dist/esm/src/data/transformers/player.js +15 -1
- package/dist/esm/src/data/transformers/qualifier.js +1 -1
- package/dist/esm/src/data/transformers/rally-event-injury.test.d.ts +1 -0
- package/dist/esm/src/data/transformers/rally-event-injury.test.js +31 -0
- package/dist/esm/src/data/transformers/rally-event.d.ts +1 -0
- package/dist/esm/src/data/transformers/rally-event.js +4 -1
- package/dist/esm/src/data/transformers/season.js +1 -1
- package/dist/esm/src/data/transformers/tactics.d.ts +3 -2
- package/dist/esm/src/data/transformers/tactics.js +106 -4
- package/dist/esm/src/data/transformers/team.d.ts +7 -1
- package/dist/esm/src/data/transformers/team.js +20 -5
- package/dist/esm/src/data/transformers/tournament.js +1 -1
- package/dist/esm/src/service/event/schemas/substitution.z.d.ts +1 -0
- package/dist/esm/src/service/event/schemas/substitution.z.js +2 -1
- package/dist/esm/src/service/event/substitution.d.ts +1 -0
- package/dist/esm/src/service/event/substitution.js +4 -2
- package/dist/esm/src/service/match/match-set-forfeit.test.d.ts +1 -0
- package/dist/esm/src/service/match/match-set-forfeit.test.js +74 -0
- package/dist/esm/src/service/match/match-set.d.ts +8 -0
- package/dist/esm/src/service/match/match-set.js +23 -1
- package/dist/esm/src/service/match/match.d.ts +2 -0
- package/dist/esm/src/service/match/match.js +2 -1
- package/dist/esm/src/service/match/schemas/match-set.z.d.ts +1 -0
- package/dist/esm/src/service/match/schemas/match-set.z.js +30 -2
- package/dist/esm/src/service/match/schemas/match.z.d.ts +4 -0
- package/dist/esm/src/service/match/schemas/match.z.js +4 -1
- package/dist/esm/src/service/player/index.d.ts +1 -0
- package/dist/esm/src/service/player/index.js +1 -0
- package/dist/esm/src/service/player/injury.d.ts +14 -0
- package/dist/esm/src/service/player/injury.js +33 -0
- package/dist/esm/src/service/player/injury.test.d.ts +1 -0
- package/dist/esm/src/service/player/injury.test.js +76 -0
- package/dist/esm/src/service/player/player-generator.js +3 -1
- package/dist/esm/src/service/player/player.d.ts +3 -0
- package/dist/esm/src/service/player/player.js +3 -1
- package/dist/esm/src/service/player/schemas/player.z.d.ts +6 -0
- package/dist/esm/src/service/player/schemas/player.z.js +7 -0
- package/dist/esm/src/service/team/index.d.ts +1 -0
- package/dist/esm/src/service/team/index.js +1 -0
- package/dist/esm/src/service/team/reserve-priority.d.ts +6 -0
- package/dist/esm/src/service/team/reserve-priority.js +1 -0
- package/dist/esm/src/service/team/schemas/reserve-priority.z.test.d.ts +1 -0
- package/dist/esm/src/service/team/schemas/reserve-priority.z.test.js +123 -0
- package/dist/esm/src/service/team/schemas/tactics.z.d.ts +5 -0
- package/dist/esm/src/service/team/schemas/tactics.z.js +32 -1
- package/dist/esm/src/service/team/schemas/team.z.d.ts +5 -0
- package/dist/esm/src/service/team/tactics.d.ts +5 -0
- package/dist/esm/src/service/team/tactics.js +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as Sequelize from 'sequelize';
|
|
2
|
+
import { Model } from 'sequelize';
|
|
3
|
+
import { TeamId, TeamModel } from '.';
|
|
4
|
+
export interface PhysiotherapistAttributes {
|
|
5
|
+
physiotherapist_id: string;
|
|
6
|
+
team_id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
rarity: string;
|
|
9
|
+
currency_invested: number;
|
|
10
|
+
}
|
|
11
|
+
export type PhysiotherapistPk = 'physiotherapist_id';
|
|
12
|
+
export type PhysiotherapistId = PhysiotherapistModel[PhysiotherapistPk];
|
|
13
|
+
export type PhysiotherapistCreationAttributes = PhysiotherapistAttributes;
|
|
14
|
+
export declare class PhysiotherapistModel extends Model<PhysiotherapistAttributes, PhysiotherapistCreationAttributes> implements PhysiotherapistAttributes {
|
|
15
|
+
physiotherapist_id: string;
|
|
16
|
+
team_id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
rarity: string;
|
|
19
|
+
currency_invested: number;
|
|
20
|
+
team: TeamModel;
|
|
21
|
+
getTeam: Sequelize.BelongsToGetAssociationMixin<TeamModel>;
|
|
22
|
+
setTeam: Sequelize.BelongsToSetAssociationMixin<TeamModel, TeamId>;
|
|
23
|
+
createTeam: Sequelize.BelongsToCreateAssociationMixin<TeamModel>;
|
|
24
|
+
static initModel(sequelize: Sequelize.Sequelize): typeof PhysiotherapistModel;
|
|
25
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { DataTypes, Model } from 'sequelize';
|
|
2
|
+
export class PhysiotherapistModel extends Model {
|
|
3
|
+
static initModel(sequelize) {
|
|
4
|
+
return PhysiotherapistModel.init({
|
|
5
|
+
physiotherapist_id: {
|
|
6
|
+
type: DataTypes.UUID,
|
|
7
|
+
allowNull: false,
|
|
8
|
+
primaryKey: true
|
|
9
|
+
},
|
|
10
|
+
team_id: {
|
|
11
|
+
type: DataTypes.UUID,
|
|
12
|
+
allowNull: false,
|
|
13
|
+
unique: true,
|
|
14
|
+
references: {
|
|
15
|
+
model: 'Team',
|
|
16
|
+
key: 'team_id'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
name: {
|
|
20
|
+
type: DataTypes.STRING,
|
|
21
|
+
allowNull: false,
|
|
22
|
+
defaultValue: ''
|
|
23
|
+
},
|
|
24
|
+
rarity: {
|
|
25
|
+
type: DataTypes.STRING,
|
|
26
|
+
allowNull: false,
|
|
27
|
+
defaultValue: 'COMMON'
|
|
28
|
+
},
|
|
29
|
+
currency_invested: {
|
|
30
|
+
type: DataTypes.INTEGER,
|
|
31
|
+
allowNull: false,
|
|
32
|
+
defaultValue: 0
|
|
33
|
+
}
|
|
34
|
+
}, {
|
|
35
|
+
sequelize,
|
|
36
|
+
tableName: 'Physiotherapist',
|
|
37
|
+
schema: 'public',
|
|
38
|
+
timestamps: false,
|
|
39
|
+
indexes: [
|
|
40
|
+
{
|
|
41
|
+
name: 'Physiotherapist_pk',
|
|
42
|
+
unique: true,
|
|
43
|
+
fields: [{ name: 'physiotherapist_id' }]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'Physiotherapist_team_id_unique',
|
|
47
|
+
unique: true,
|
|
48
|
+
fields: [{ name: 'team_id' }]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -15,6 +15,10 @@ export interface PlayerAttributes {
|
|
|
15
15
|
birth_iteration: number;
|
|
16
16
|
last_declined_iteration: number | null;
|
|
17
17
|
decline_profile: DeclineProfile;
|
|
18
|
+
injury_prone_profile?: number | string;
|
|
19
|
+
injury_severity?: number | null;
|
|
20
|
+
injured_until?: Date | string | null;
|
|
21
|
+
injury_debuff?: number | string | null;
|
|
18
22
|
PerformanceStat?: PerformanceStatsAttributes;
|
|
19
23
|
BoxScores?: BoxScoreAttributes[];
|
|
20
24
|
}
|
|
@@ -34,6 +38,10 @@ export declare class PlayerModel extends Model<PlayerAttributes, PlayerCreationA
|
|
|
34
38
|
birth_iteration: number;
|
|
35
39
|
last_declined_iteration: number | null;
|
|
36
40
|
decline_profile: DeclineProfile;
|
|
41
|
+
injury_prone_profile: number | string;
|
|
42
|
+
injury_severity: number | null;
|
|
43
|
+
injured_until: Date | string | null;
|
|
44
|
+
injury_debuff: number | string | null;
|
|
37
45
|
country: CountryModel;
|
|
38
46
|
getCountry: Sequelize.BelongsToGetAssociationMixin<CountryModel>;
|
|
39
47
|
setCountry: Sequelize.BelongsToSetAssociationMixin<CountryModel, CountryId>;
|
|
@@ -59,6 +59,23 @@ export class PlayerModel extends Model {
|
|
|
59
59
|
type: DataTypes.ENUM(...Object.values(DeclineProfileEnum)),
|
|
60
60
|
allowNull: false,
|
|
61
61
|
defaultValue: DeclineProfileEnum.STANDARD
|
|
62
|
+
},
|
|
63
|
+
injury_prone_profile: {
|
|
64
|
+
type: DataTypes.DECIMAL(3, 2),
|
|
65
|
+
allowNull: false,
|
|
66
|
+
defaultValue: 0.5
|
|
67
|
+
},
|
|
68
|
+
injury_severity: {
|
|
69
|
+
type: DataTypes.INTEGER,
|
|
70
|
+
allowNull: true
|
|
71
|
+
},
|
|
72
|
+
injured_until: {
|
|
73
|
+
type: DataTypes.DATE,
|
|
74
|
+
allowNull: true
|
|
75
|
+
},
|
|
76
|
+
injury_debuff: {
|
|
77
|
+
type: DataTypes.DECIMAL(3, 2),
|
|
78
|
+
allowNull: true
|
|
62
79
|
}
|
|
63
80
|
}, {
|
|
64
81
|
sequelize,
|
|
@@ -39,6 +39,10 @@ export interface SystemSetAttributes {
|
|
|
39
39
|
rotationSystem: RotationSystemEnum;
|
|
40
40
|
designatedSetters: PlayerId[];
|
|
41
41
|
}
|
|
42
|
+
export interface ReservePriorityAttributes {
|
|
43
|
+
position: CourtPosition;
|
|
44
|
+
reserves: PlayerId[];
|
|
45
|
+
}
|
|
42
46
|
export interface LiberoSetSubConfigAttributes {
|
|
43
47
|
mode: LiberoSubMode;
|
|
44
48
|
fatigueBand?: EnergyBand;
|
|
@@ -61,10 +65,12 @@ export interface TacticsAttributes {
|
|
|
61
65
|
offensive_preferences: OffensivePreferenceAttributes[];
|
|
62
66
|
system_sets?: SystemSetAttributes[];
|
|
63
67
|
offensive_preference_sets?: OffensivePreferenceAttributes[][];
|
|
68
|
+
replace_injured_immediately?: boolean;
|
|
69
|
+
reserve_priorities?: ReservePriorityAttributes[];
|
|
64
70
|
}
|
|
65
71
|
export type TacticsPk = 'team_id';
|
|
66
72
|
export type TacticsId = TacticsModel[TacticsPk];
|
|
67
|
-
export type TacticsOptionalAttributes = 'lineup' | 'libero_replacements' | 'receive_rotation_offset' | 'designated_subs' | 'substitution_band' | 'second_libero' | 'libero_sub' | 'libero_replacement_sets' | 'rotation_system' | 'designated_setters' | 'offensive_preferences' | 'system_sets' | 'offensive_preference_sets';
|
|
73
|
+
export type TacticsOptionalAttributes = 'lineup' | 'libero_replacements' | 'receive_rotation_offset' | 'designated_subs' | 'substitution_band' | 'second_libero' | 'libero_sub' | 'libero_replacement_sets' | 'rotation_system' | 'designated_setters' | 'offensive_preferences' | 'system_sets' | 'offensive_preference_sets' | 'replace_injured_immediately' | 'reserve_priorities';
|
|
68
74
|
export type TacticsCreationAttributes = Optional<TacticsAttributes, TacticsOptionalAttributes>;
|
|
69
75
|
export declare class TacticsModel extends Model<TacticsAttributes, TacticsCreationAttributes> implements TacticsAttributes {
|
|
70
76
|
team_id: string;
|
|
@@ -81,6 +87,8 @@ export declare class TacticsModel extends Model<TacticsAttributes, TacticsCreati
|
|
|
81
87
|
offensive_preferences: OffensivePreferenceAttributes[];
|
|
82
88
|
system_sets?: SystemSetAttributes[];
|
|
83
89
|
offensive_preference_sets?: OffensivePreferenceAttributes[][];
|
|
90
|
+
replace_injured_immediately?: boolean;
|
|
91
|
+
reserve_priorities?: ReservePriorityAttributes[];
|
|
84
92
|
team: TeamModel;
|
|
85
93
|
getTeam: Sequelize.BelongsToGetAssociationMixin<TeamModel>;
|
|
86
94
|
setTeam: Sequelize.BelongsToSetAssociationMixin<TeamModel, TeamId>;
|
|
@@ -71,6 +71,15 @@ export class TacticsModel extends Model {
|
|
|
71
71
|
offensive_preference_sets: {
|
|
72
72
|
type: DataTypes.JSONB,
|
|
73
73
|
allowNull: true
|
|
74
|
+
},
|
|
75
|
+
replace_injured_immediately: {
|
|
76
|
+
type: DataTypes.BOOLEAN,
|
|
77
|
+
allowNull: false,
|
|
78
|
+
defaultValue: true
|
|
79
|
+
},
|
|
80
|
+
reserve_priorities: {
|
|
81
|
+
type: DataTypes.JSONB,
|
|
82
|
+
allowNull: true
|
|
74
83
|
}
|
|
75
84
|
}, {
|
|
76
85
|
sequelize,
|
|
@@ -23,7 +23,7 @@ function transformToObject(model) {
|
|
|
23
23
|
subdivision: model.subdivision,
|
|
24
24
|
leagueId: model.league_id,
|
|
25
25
|
seasons: (model.DivisionSeasons ?? []).map((ds) => transformToSeason(ds.competition)),
|
|
26
|
-
teams: model.Teams != null ? model.Teams.map(transformToTeam) : undefined
|
|
26
|
+
teams: model.Teams != null ? model.Teams.map(team => transformToTeam(team)) : undefined
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
export { transformToObject as transformToDivision, transformToAttributes as transformFromDivision };
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
// `config` is already stored as the API Tactics DTO, so a preset row maps to its API shape with no hydration.
|
|
2
|
+
// Presets saved before the injuries fields exist lack replaceInjuredImmediately: heal it to the effective
|
|
3
|
+
// default (true, matching transformToTactics' hydration) so the DTO's required field is honest and a UI
|
|
4
|
+
// toggle seeded from an old preset cannot silently flip the user's effective setting on re-save.
|
|
2
5
|
function transformToObject(model) {
|
|
3
6
|
return {
|
|
4
7
|
presetId: model.preset_id,
|
|
5
8
|
name: model.name,
|
|
6
9
|
isActive: model.is_active,
|
|
7
10
|
orderIndex: model.order_index,
|
|
8
|
-
config:
|
|
11
|
+
config: {
|
|
12
|
+
...model.config,
|
|
13
|
+
replaceInjuredImmediately: model.config.replaceInjuredImmediately ?? true
|
|
14
|
+
}
|
|
9
15
|
};
|
|
10
16
|
}
|
|
11
17
|
export { transformToObject as transformToLineupPreset };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect } from '@jest/globals';
|
|
2
2
|
import { transformToLineupPreset } from './lineup-preset';
|
|
3
3
|
describe('transformToLineupPreset()', () => {
|
|
4
|
-
it('maps preset columns to the API shape and
|
|
4
|
+
it('maps preset columns to the API shape and heals a pre-injuries config to the effective default', () => {
|
|
5
5
|
const config = { lineup: { bench: [] }, substitutionBand: 'TIRED' };
|
|
6
6
|
const preset = transformToLineupPreset({
|
|
7
7
|
preset_id: 'p1',
|
|
@@ -16,8 +16,21 @@ describe('transformToLineupPreset()', () => {
|
|
|
16
16
|
name: 'Starters',
|
|
17
17
|
isActive: true,
|
|
18
18
|
orderIndex: 2,
|
|
19
|
+
// A config stored before the injuries fields lacks replaceInjuredImmediately; the transform heals it
|
|
20
|
+
// to true (the effective hydration default) so the DTO's required field is honest.
|
|
21
|
+
config: { ...config, replaceInjuredImmediately: true }
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
it('keeps an explicit replaceInjuredImmediately: false', () => {
|
|
25
|
+
const config = { lineup: { bench: [] }, substitutionBand: 'TIRED', replaceInjuredImmediately: false };
|
|
26
|
+
const preset = transformToLineupPreset({
|
|
27
|
+
preset_id: 'p2',
|
|
28
|
+
team_id: 't1',
|
|
29
|
+
name: 'Risky',
|
|
30
|
+
is_active: false,
|
|
31
|
+
order_index: 3,
|
|
19
32
|
config
|
|
20
33
|
});
|
|
21
|
-
expect(preset.config).toBe(
|
|
34
|
+
expect(preset.config.replaceInjuredImmediately).toBe(false);
|
|
22
35
|
});
|
|
23
36
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { describe, it, expect, jest } from '@jest/globals';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { apiTacticsConfigToTacticsAttributes, tacticsColumnsToApiDto, transformToTactics } from './tactics';
|
|
4
|
+
import { transformToTeam } from './team';
|
|
5
|
+
import { transformToMatch } from './match';
|
|
6
|
+
import { CourtPosition, DeclineProfileEnum, RarityEnum, RoleEnum } from '../../service';
|
|
7
|
+
// ─── shared mocks (model-shaped plain objects, cast through unknown) ──────────
|
|
8
|
+
const countryFixture = {
|
|
9
|
+
country_id: uuidv4(),
|
|
10
|
+
name: 'Test Country',
|
|
11
|
+
alpha_2: 'TC',
|
|
12
|
+
alpha_3: 'TCN',
|
|
13
|
+
country_code: '999',
|
|
14
|
+
iso_3166_2: 'TC-TEST',
|
|
15
|
+
region: '',
|
|
16
|
+
region_code: '',
|
|
17
|
+
sub_region: '',
|
|
18
|
+
sub_region_code: '',
|
|
19
|
+
intermediate_region: '',
|
|
20
|
+
intermediate_region_code: ''
|
|
21
|
+
};
|
|
22
|
+
function makePlayerModel(id) {
|
|
23
|
+
const stat = 50;
|
|
24
|
+
return {
|
|
25
|
+
player_id: id,
|
|
26
|
+
first_name: 'Test',
|
|
27
|
+
last_name: `P-${id.slice(0, 4)}`,
|
|
28
|
+
rarity: RarityEnum.COMMON,
|
|
29
|
+
roles: [RoleEnum.OUTSIDE_HITTER],
|
|
30
|
+
traits: [],
|
|
31
|
+
birth_age: 18,
|
|
32
|
+
birth_iteration: 1,
|
|
33
|
+
last_declined_iteration: null,
|
|
34
|
+
decline_profile: DeclineProfileEnum.STANDARD,
|
|
35
|
+
BoxScores: [],
|
|
36
|
+
BoxScoreTotals: null,
|
|
37
|
+
country: countryFixture,
|
|
38
|
+
PerformanceStat: {
|
|
39
|
+
setting: stat,
|
|
40
|
+
serve: stat,
|
|
41
|
+
spike: stat,
|
|
42
|
+
quick: stat,
|
|
43
|
+
power: stat,
|
|
44
|
+
awareness: stat,
|
|
45
|
+
attack: stat,
|
|
46
|
+
back_attack: stat,
|
|
47
|
+
reception: stat,
|
|
48
|
+
overhand: stat,
|
|
49
|
+
bump: stat,
|
|
50
|
+
block: stat,
|
|
51
|
+
read: stat,
|
|
52
|
+
commit: stat,
|
|
53
|
+
focus: stat,
|
|
54
|
+
defense: stat,
|
|
55
|
+
stamina: stat,
|
|
56
|
+
reflex: stat
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Six starters; index order is the row lineup's zone 1..6 order.
|
|
61
|
+
const ids = Array.from({ length: 6 }, () => uuidv4());
|
|
62
|
+
const teamId = uuidv4();
|
|
63
|
+
function rowLineup(order) {
|
|
64
|
+
return {
|
|
65
|
+
[CourtPosition.RIGHT_BACK]: order[0],
|
|
66
|
+
[CourtPosition.RIGHT_FRONT]: order[1],
|
|
67
|
+
[CourtPosition.MIDDLE_FRONT]: order[2],
|
|
68
|
+
[CourtPosition.LEFT_FRONT]: order[3],
|
|
69
|
+
[CourtPosition.LEFT_BACK]: order[4],
|
|
70
|
+
[CourtPosition.MIDDLE_BACK]: order[5],
|
|
71
|
+
bench: []
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function makeTacticsRow(order) {
|
|
75
|
+
return {
|
|
76
|
+
team_id: teamId,
|
|
77
|
+
lineup: rowLineup(order),
|
|
78
|
+
libero_replacements: [],
|
|
79
|
+
receive_rotation_offset: false,
|
|
80
|
+
designated_subs: [],
|
|
81
|
+
substitution_band: 'TIRED',
|
|
82
|
+
rotation_system: '6-0',
|
|
83
|
+
designated_setters: [],
|
|
84
|
+
offensive_preferences: []
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function makeTeamModel(activeOrder) {
|
|
88
|
+
return {
|
|
89
|
+
team_id: teamId,
|
|
90
|
+
name: 'Test Team',
|
|
91
|
+
short_name: 'TST',
|
|
92
|
+
rating: 0,
|
|
93
|
+
division_id: uuidv4(),
|
|
94
|
+
active: true,
|
|
95
|
+
country: countryFixture,
|
|
96
|
+
tactics: makeTacticsRow(activeOrder),
|
|
97
|
+
PlayerTeams: ids.map(id => ({ player: makePlayerModel(id) }))
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// The API-DTO config for a preset whose starters are the REVERSE of the active order, so an applied
|
|
101
|
+
// override is visible through the hydrated lineup.
|
|
102
|
+
function makeConfig(order) {
|
|
103
|
+
return {
|
|
104
|
+
lineup: {
|
|
105
|
+
[CourtPosition.RIGHT_BACK]: order[0],
|
|
106
|
+
[CourtPosition.RIGHT_FRONT]: order[1],
|
|
107
|
+
[CourtPosition.MIDDLE_FRONT]: order[2],
|
|
108
|
+
[CourtPosition.LEFT_FRONT]: order[3],
|
|
109
|
+
[CourtPosition.LEFT_BACK]: order[4],
|
|
110
|
+
[CourtPosition.MIDDLE_BACK]: order[5],
|
|
111
|
+
bench: []
|
|
112
|
+
},
|
|
113
|
+
liberoReplacements: [],
|
|
114
|
+
receiveRotationOffset: false,
|
|
115
|
+
designatedSubs: [],
|
|
116
|
+
substitutionBand: 'TIRED',
|
|
117
|
+
rotationSystem: '6-0',
|
|
118
|
+
designatedSetters: [],
|
|
119
|
+
offensivePreferences: []
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const activeOrder = ids;
|
|
123
|
+
const presetOrder = [...ids].reverse();
|
|
124
|
+
function starterAt(tactics, zone) {
|
|
125
|
+
return tactics.lineup[zone]?.id;
|
|
126
|
+
}
|
|
127
|
+
// ─── apiTacticsConfigToTacticsAttributes ──────────────────────────────────────
|
|
128
|
+
describe('apiTacticsConfigToTacticsAttributes()', () => {
|
|
129
|
+
it('round-trips a fully-loaded Tactics row through tacticsColumnsToApiDto', () => {
|
|
130
|
+
const full = {
|
|
131
|
+
team_id: teamId,
|
|
132
|
+
lineup: { ...rowLineup(activeOrder), [CourtPosition.LIBERO_ZONE]: undefined },
|
|
133
|
+
libero_replacements: [ids[5]],
|
|
134
|
+
receive_rotation_offset: true,
|
|
135
|
+
designated_subs: [{ starterId: ids[0], benchId: undefined, mode: 'FATIGUE', fatigueBand: 'WORN', sets: [{ mode: 'NEVER' }] }],
|
|
136
|
+
substitution_band: 'TIRED',
|
|
137
|
+
second_libero: undefined,
|
|
138
|
+
libero_sub: undefined,
|
|
139
|
+
libero_replacement_sets: [[ids[0]], [ids[1]]],
|
|
140
|
+
rotation_system: '5-1',
|
|
141
|
+
designated_setters: [ids[0]],
|
|
142
|
+
offensive_preferences: [{ rotation: 1, order: [ids[1], ids[2]] }],
|
|
143
|
+
system_sets: [{ rotationSystem: '5-1', designatedSetters: [ids[0]] }],
|
|
144
|
+
offensive_preference_sets: [[{ rotation: 2, order: [ids[3]] }]]
|
|
145
|
+
};
|
|
146
|
+
const dto = tacticsColumnsToApiDto(full);
|
|
147
|
+
const back = apiTacticsConfigToTacticsAttributes(dto, teamId);
|
|
148
|
+
expect(back.lineup).toEqual(full.lineup);
|
|
149
|
+
expect(back.libero_replacements).toEqual(full.libero_replacements);
|
|
150
|
+
expect(back.receive_rotation_offset).toBe(true);
|
|
151
|
+
expect(back.designated_subs).toEqual(full.designated_subs);
|
|
152
|
+
expect(back.libero_replacement_sets).toEqual(full.libero_replacement_sets);
|
|
153
|
+
expect(back.rotation_system).toBe('5-1');
|
|
154
|
+
expect(back.designated_setters).toEqual(full.designated_setters);
|
|
155
|
+
expect(back.offensive_preferences).toEqual(full.offensive_preferences);
|
|
156
|
+
expect(back.system_sets).toEqual(full.system_sets);
|
|
157
|
+
expect(back.offensive_preference_sets).toEqual(full.offensive_preference_sets);
|
|
158
|
+
});
|
|
159
|
+
it('hydrates through transformToTactics identically to the row path', () => {
|
|
160
|
+
const roster = ids.map(id => transformToTeam(makeTeamModel(activeOrder)).roster.find(p => p.id === id));
|
|
161
|
+
expect(roster.every(p => p != null)).toBe(true);
|
|
162
|
+
const teamModel = makeTeamModel(activeOrder);
|
|
163
|
+
const players = transformToTeam(teamModel).roster;
|
|
164
|
+
const fromRow = transformToTactics(makeTacticsRow(activeOrder), players);
|
|
165
|
+
const fromConfig = transformToTactics(apiTacticsConfigToTacticsAttributes(makeConfig(activeOrder), teamId), players);
|
|
166
|
+
for (const zone of [1, 2, 3, 4, 5, 6]) {
|
|
167
|
+
expect(starterAt(fromConfig, zone)).toBe(starterAt(fromRow, zone));
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
it('throws PLAYER_NOT_FOUND when the config references an off-roster player', () => {
|
|
171
|
+
const players = transformToTeam(makeTeamModel(activeOrder)).roster;
|
|
172
|
+
const drifted = makeConfig([uuidv4(), ...ids.slice(1)]);
|
|
173
|
+
expect(() => transformToTactics(apiTacticsConfigToTacticsAttributes(drifted, teamId), players)).toThrow('PLAYER_NOT_FOUND');
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
// ─── transformToTeam override / fallback ──────────────────────────────────────
|
|
177
|
+
describe('transformToTeam() tacticsOverride', () => {
|
|
178
|
+
it('uses the override config when it hydrates', () => {
|
|
179
|
+
const team = transformToTeam(makeTeamModel(activeOrder), undefined, {
|
|
180
|
+
presetId: 'preset-1',
|
|
181
|
+
config: makeConfig(presetOrder)
|
|
182
|
+
});
|
|
183
|
+
expect(starterAt(team.tactics, CourtPosition.RIGHT_BACK)).toBe(presetOrder[0]);
|
|
184
|
+
});
|
|
185
|
+
it('falls back to the active tactics and notifies onFallback when the override fails', () => {
|
|
186
|
+
const onFallback = jest.fn();
|
|
187
|
+
const drifted = makeConfig([uuidv4(), ...ids.slice(1)]);
|
|
188
|
+
const team = transformToTeam(makeTeamModel(activeOrder), undefined, {
|
|
189
|
+
presetId: 'preset-1',
|
|
190
|
+
config: drifted,
|
|
191
|
+
onFallback
|
|
192
|
+
});
|
|
193
|
+
expect(onFallback).toHaveBeenCalledTimes(1);
|
|
194
|
+
expect(onFallback.mock.calls[0][0]).toBe('preset-1');
|
|
195
|
+
expect(starterAt(team.tactics, CourtPosition.RIGHT_BACK)).toBe(activeOrder[0]);
|
|
196
|
+
});
|
|
197
|
+
it('keeps the active tactics when no override is given', () => {
|
|
198
|
+
const team = transformToTeam(makeTeamModel(activeOrder));
|
|
199
|
+
expect(starterAt(team.tactics, CourtPosition.RIGHT_BACK)).toBe(activeOrder[0]);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
// ─── transformToMatch per-team override ───────────────────────────────────────
|
|
203
|
+
describe('transformToMatch() MatchPresets', () => {
|
|
204
|
+
const awayIds = Array.from({ length: 6 }, () => uuidv4());
|
|
205
|
+
const awayTeamId = uuidv4();
|
|
206
|
+
function makeAwayModel() {
|
|
207
|
+
return {
|
|
208
|
+
team_id: awayTeamId,
|
|
209
|
+
name: 'Away Team',
|
|
210
|
+
short_name: 'AWY',
|
|
211
|
+
rating: 0,
|
|
212
|
+
division_id: uuidv4(),
|
|
213
|
+
active: true,
|
|
214
|
+
country: countryFixture,
|
|
215
|
+
tactics: {
|
|
216
|
+
team_id: awayTeamId,
|
|
217
|
+
lineup: {
|
|
218
|
+
[CourtPosition.RIGHT_BACK]: awayIds[0],
|
|
219
|
+
[CourtPosition.RIGHT_FRONT]: awayIds[1],
|
|
220
|
+
[CourtPosition.MIDDLE_FRONT]: awayIds[2],
|
|
221
|
+
[CourtPosition.LEFT_FRONT]: awayIds[3],
|
|
222
|
+
[CourtPosition.LEFT_BACK]: awayIds[4],
|
|
223
|
+
[CourtPosition.MIDDLE_BACK]: awayIds[5],
|
|
224
|
+
bench: []
|
|
225
|
+
},
|
|
226
|
+
libero_replacements: [],
|
|
227
|
+
receive_rotation_offset: false,
|
|
228
|
+
designated_subs: [],
|
|
229
|
+
substitution_band: 'TIRED',
|
|
230
|
+
rotation_system: '6-0',
|
|
231
|
+
designated_setters: [],
|
|
232
|
+
offensive_preferences: []
|
|
233
|
+
},
|
|
234
|
+
PlayerTeams: awayIds.map(id => ({ player: makePlayerModel(id) }))
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function makeMatchModel(withPreset) {
|
|
238
|
+
return {
|
|
239
|
+
match_id: uuidv4(),
|
|
240
|
+
home_team: teamId,
|
|
241
|
+
away_team: awayTeamId,
|
|
242
|
+
scheduled_date: new Date(),
|
|
243
|
+
status: 'PENDING',
|
|
244
|
+
HomeTeam: makeTeamModel(activeOrder),
|
|
245
|
+
AwayTeam: makeAwayModel(),
|
|
246
|
+
MatchSets: [],
|
|
247
|
+
VPERs: [],
|
|
248
|
+
MatchPresets: withPreset
|
|
249
|
+
? [{ match_id: 'm', team_id: teamId, preset_id: 'preset-1', preset: { config: makeConfig(presetOrder) } }]
|
|
250
|
+
: undefined
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
it('applies the home override and leaves the away team on active tactics', () => {
|
|
254
|
+
const match = transformToMatch(makeMatchModel(true));
|
|
255
|
+
expect(starterAt(match.homeTeam.tactics, CourtPosition.RIGHT_BACK)).toBe(presetOrder[0]);
|
|
256
|
+
expect(starterAt(match.awayTeam.tactics, CourtPosition.RIGHT_BACK)).toBe(awayIds[0]);
|
|
257
|
+
});
|
|
258
|
+
it('behaves exactly as before when the association is absent', () => {
|
|
259
|
+
const match = transformToMatch(makeMatchModel(false));
|
|
260
|
+
expect(starterAt(match.homeTeam.tactics, CourtPosition.RIGHT_BACK)).toBe(activeOrder[0]);
|
|
261
|
+
expect(starterAt(match.awayTeam.tactics, CourtPosition.RIGHT_BACK)).toBe(awayIds[0]);
|
|
262
|
+
});
|
|
263
|
+
it('routes the fallback callback with the failing team id', () => {
|
|
264
|
+
const onPresetFallback = jest.fn();
|
|
265
|
+
const model = makeMatchModel(true);
|
|
266
|
+
model.MatchPresets[0].preset.config = makeConfig([uuidv4(), ...ids.slice(1)]);
|
|
267
|
+
const match = transformToMatch(model, undefined, onPresetFallback);
|
|
268
|
+
expect(onPresetFallback).toHaveBeenCalledTimes(1);
|
|
269
|
+
expect(onPresetFallback.mock.calls[0][0]).toBe(teamId);
|
|
270
|
+
expect(onPresetFallback.mock.calls[0][1]).toBe('preset-1');
|
|
271
|
+
expect(starterAt(match.homeTeam.tactics, CourtPosition.RIGHT_BACK)).toBe(activeOrder[0]);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
@@ -23,6 +23,7 @@ function transformToAttributes(set, matchId) {
|
|
|
23
23
|
is_tie_break: set.isTieBreak,
|
|
24
24
|
home_score: set.homeScore,
|
|
25
25
|
away_score: set.awayScore,
|
|
26
|
+
forfeited: set.forfeited,
|
|
26
27
|
player_positions: [...homeSetPositions, ...awaySetPositions],
|
|
27
28
|
BoxScores: (set.boxScores ?? []).map(transformFromBoxScore),
|
|
28
29
|
Rallies: (set.rallies ?? []).map((rally) => transformFromRally(rally, set.id))
|
|
@@ -39,6 +40,7 @@ function transformToObject(model) {
|
|
|
39
40
|
homeScore: model.home_score,
|
|
40
41
|
awayScore: model.away_score,
|
|
41
42
|
isTieBreak: model.is_tie_break,
|
|
43
|
+
forfeited: model.forfeited ?? false,
|
|
42
44
|
boxScores: (model.BoxScores ?? []).map(transformToBoxScore),
|
|
43
45
|
homePlayerPosition,
|
|
44
46
|
awayPlayerPosition,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MatchAttributes, MatchModel } from '../models';
|
|
2
2
|
import { Match } from '../../service';
|
|
3
|
+
export type PresetFallbackHandler = (teamId: string, presetId: string, err: unknown) => void;
|
|
3
4
|
declare function transformToAttributes(match: Match): MatchAttributes;
|
|
4
|
-
declare function transformToObject(model: MatchModel, currentIteration?: number): Match;
|
|
5
|
+
declare function transformToObject(model: MatchModel, currentIteration?: number, onPresetFallback?: PresetFallbackHandler): Match;
|
|
5
6
|
export { transformToObject as transformToMatch, transformToAttributes as transformFromMatch };
|
|
@@ -8,10 +8,25 @@ function transformToAttributes(match) {
|
|
|
8
8
|
away_team: match.awayTeam.id,
|
|
9
9
|
home_team: match.homeTeam.id,
|
|
10
10
|
scheduled_date: match.scheduledDate,
|
|
11
|
-
status: match.status
|
|
11
|
+
status: match.status,
|
|
12
|
+
// Both directions on purpose (the read is in transformToObject below). NOTE for the Sim: updateMatch's
|
|
13
|
+
// completion write uses an explicit column list and must include forfeit_type or the marker is lost.
|
|
14
|
+
forfeit_type: match.forfeitType ?? null
|
|
12
15
|
};
|
|
13
16
|
}
|
|
14
|
-
function transformToObject(model, currentIteration) {
|
|
17
|
+
function transformToObject(model, currentIteration, onPresetFallback) {
|
|
18
|
+
// The (match, team)-chosen preset override for one side, when the loader included MatchPresets with the
|
|
19
|
+
// preset loaded (only the simulator's match query does). Association absent = active tactics, as always.
|
|
20
|
+
const overrideFor = (teamId) => {
|
|
21
|
+
const row = (model.MatchPresets ?? []).find(mp => mp.team_id === teamId);
|
|
22
|
+
if (row?.preset == null)
|
|
23
|
+
return undefined;
|
|
24
|
+
return {
|
|
25
|
+
presetId: row.preset_id,
|
|
26
|
+
config: row.preset.config,
|
|
27
|
+
onFallback: (presetId, err) => onPresetFallback?.(teamId, presetId, err)
|
|
28
|
+
};
|
|
29
|
+
};
|
|
15
30
|
const sets = (model.MatchSets ?? []).map(transformToMatchSet)
|
|
16
31
|
.sort((s1, s2) => s1.order - s2.order);
|
|
17
32
|
let homeScore = 0;
|
|
@@ -43,15 +58,16 @@ function transformToObject(model, currentIteration) {
|
|
|
43
58
|
const VPERs = (model.VPERs ?? []).map(transformToVPER);
|
|
44
59
|
return Match.create({
|
|
45
60
|
id: model.match_id,
|
|
46
|
-
homeTeam: transformToTeam(model.HomeTeam, currentIteration),
|
|
47
|
-
awayTeam: transformToTeam(model.AwayTeam, currentIteration),
|
|
61
|
+
homeTeam: transformToTeam(model.HomeTeam, currentIteration, overrideFor(model.home_team)),
|
|
62
|
+
awayTeam: transformToTeam(model.AwayTeam, currentIteration, overrideFor(model.away_team)),
|
|
48
63
|
scheduledDate: new Date(model.scheduled_date),
|
|
49
64
|
sets,
|
|
50
65
|
status: model.status,
|
|
51
66
|
VPERs,
|
|
52
67
|
homeScore,
|
|
53
68
|
awayScore,
|
|
54
|
-
winner: model.MatchResult?.Winner != null ? transformToTeam(model.MatchResult.Winner, currentIteration) : undefined
|
|
69
|
+
winner: model.MatchResult?.Winner != null ? transformToTeam(model.MatchResult.Winner, currentIteration) : undefined,
|
|
70
|
+
forfeitType: model.forfeit_type ?? undefined
|
|
55
71
|
});
|
|
56
72
|
}
|
|
57
73
|
export { transformToObject as transformToMatch, transformToAttributes as transformFromMatch };
|
|
@@ -21,7 +21,11 @@ function transformToAttributes(player) {
|
|
|
21
21
|
birth_age: player.birthAge,
|
|
22
22
|
birth_iteration: player.birthIteration,
|
|
23
23
|
last_declined_iteration: null,
|
|
24
|
-
decline_profile: player.declineProfile
|
|
24
|
+
decline_profile: player.declineProfile,
|
|
25
|
+
injury_prone_profile: player.injuryProneProfile,
|
|
26
|
+
injury_severity: player.injury?.severity ?? null,
|
|
27
|
+
injured_until: player.injury?.injuredUntil ?? null,
|
|
28
|
+
injury_debuff: player.injury?.debuff ?? null
|
|
25
29
|
};
|
|
26
30
|
}
|
|
27
31
|
function transformToBoxScoreTotals(model) {
|
|
@@ -87,6 +91,16 @@ function transformToObject(model, currentIteration) {
|
|
|
87
91
|
birthAge: model.birth_age,
|
|
88
92
|
birthIteration: model.birth_iteration,
|
|
89
93
|
declineProfile: model.decline_profile,
|
|
94
|
+
// DECIMAL comes back as a string from pg; schema-defaulted when the column is absent (pre-migration rows).
|
|
95
|
+
injuryProneProfile: model.injury_prone_profile != null ? Number(model.injury_prone_profile) : undefined,
|
|
96
|
+
// Severity defends against a partial write: an injured_until without severity reads as MINOR.
|
|
97
|
+
injury: model.injured_until != null
|
|
98
|
+
? {
|
|
99
|
+
severity: model.injury_severity != null ? Number(model.injury_severity) : 1,
|
|
100
|
+
injuredUntil: model.injured_until,
|
|
101
|
+
debuff: model.injury_debuff != null ? Number(model.injury_debuff) : undefined
|
|
102
|
+
}
|
|
103
|
+
: undefined,
|
|
90
104
|
boxScores: (model.BoxScores ?? []).map(transformToBoxScore),
|
|
91
105
|
boxScoreTotals
|
|
92
106
|
});
|
|
@@ -40,7 +40,7 @@ function transformToObject(model) {
|
|
|
40
40
|
id: model.competition_id,
|
|
41
41
|
iteration: transformToIteration(model.Iteration),
|
|
42
42
|
matches: (model.CompetitionMatches ?? []).map(transformToQualifierMatch),
|
|
43
|
-
teams: sortTeamsByCompetitionIndex(model.Teams ?? []).map(transformToTeam),
|
|
43
|
+
teams: sortTeamsByCompetitionIndex(model.Teams ?? []).map(team => transformToTeam(team)),
|
|
44
44
|
status: model.status,
|
|
45
45
|
region: transformToRegion(regionQualifier),
|
|
46
46
|
champion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { EventTypeEnum, Substitution } from '../../service';
|
|
4
|
+
import { transformToCompact, transformToSubstitution } from './rally-event';
|
|
5
|
+
describe('rally-event compact round trip — injury substitution marker', () => {
|
|
6
|
+
const playerIn = uuidv4();
|
|
7
|
+
const playerOut = uuidv4();
|
|
8
|
+
const roster = [playerIn, playerOut];
|
|
9
|
+
const playerIndex = new Map(roster.map((id, i) => [id, i]));
|
|
10
|
+
it('packs the injury flag as j=1 and round-trips it', () => {
|
|
11
|
+
const sub = Substitution.create({ playerId: playerIn, playerOut, injury: true });
|
|
12
|
+
const compact = transformToCompact(sub, playerIndex);
|
|
13
|
+
expect(compact.j).toBe(1);
|
|
14
|
+
const decoded = transformToSubstitution(compact, roster);
|
|
15
|
+
expect(decoded.injury).toBe(true);
|
|
16
|
+
expect(decoded.playerId).toBe(playerIn);
|
|
17
|
+
expect(decoded.playerOut).toBe(playerOut);
|
|
18
|
+
});
|
|
19
|
+
it('omits j entirely for a normal substitution', () => {
|
|
20
|
+
const sub = Substitution.create({ playerId: playerIn, playerOut });
|
|
21
|
+
const compact = transformToCompact(sub, playerIndex);
|
|
22
|
+
expect('j' in compact).toBe(false);
|
|
23
|
+
const decoded = transformToSubstitution(compact, roster);
|
|
24
|
+
expect(decoded.injury).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
it('decodes a legacy compact event (no j key) as a non-injury substitution', () => {
|
|
27
|
+
const legacy = { p: 0, e: EventTypeEnum.SUBSTITUTION, o: 1 };
|
|
28
|
+
const decoded = transformToSubstitution(legacy, roster);
|
|
29
|
+
expect(decoded.injury).toBeUndefined();
|
|
30
|
+
});
|
|
31
|
+
});
|