1
+
Added:
(** Unit tests for {!Entry}. *)
2
+
Added:
3
+
Added:
let ok = function Ok v -> v | Error _ -> Alcotest.fail "expected Ok"
4
+
Added:
5
+
Added:
let get id =
6
+
Added:
match Exercise.find id with
7
+
Added:
| Some e -> e
8
+
Added:
| None -> Alcotest.failf "catalog is missing %S" id
9
+
Added:
10
+
Added:
let kg n = ok (Units.Weight.of_kg n)
11
+
Added:
let reps n = ok (Units.Reps.of_int n)
12
+
Added:
let at s = Recovery.timestamp_of_unix_seconds s
13
+
Added:
14
+
Added:
let move ?(outcome = Stimulus.Positive_failure) id load r =
15
+
Added:
Stimulus.Movement.make ~exercise:(get id) ~load:(kg load) ~reps:(reps r)
16
+
Added:
~outcome
17
+
Added:
18
+
Added:
let single id load r = ok (Stimulus.make (Stimulus.Single (move id load r)))
19
+
Added:
20
+
Added:
let pre_exhaust iso_m comp_m =
21
+
Added:
ok
22
+
Added:
(Stimulus.make
23
+
Added:
(Stimulus.Pre_exhaust { isolation = iso_m; compound = comp_m }))
24
+
Added:
25
+
Added:
let day_one = List.hd (Routine.workouts Routine.ideal_routine)
26
+
Added:
let cleared = Option.get (Recovery.clear Recovery.Ready)
27
+
Added:
let fresh () = Entry.start day_one ~clearance:cleared ~started_at:(at 0)
28
+
Added:
29
+
Added:
(* HD1's Day 1, in the order it lists. *)
30
+
Added:
let day_one_stimuli =
31
+
Added:
[
32
+
Added:
pre_exhaust (move "dumbbell-flyes" 20. 9) (move "incline-press" 60. 7);
33
+
Added:
single "laterals" 12. 8;
34
+
Added:
single "bent-over-laterals" 10. 9;
35
+
Added:
pre_exhaust (move "lying-french-press" 30. 8) (move "dips" 0. 6);
36
+
Added:
]
37
+
Added:
38
+
Added:
let lifecycle_tests =
39
+
Added:
[
40
+
Added:
( "a fresh entry has performed nothing and answers its prescription",
41
+
Added:
`Quick,
42
+
Added:
fun () ->
43
+
Added:
let e = fresh () in
44
+
Added:
Alcotest.(check string)
45
+
Added:
"prescription" "Day 1"
46
+
Added:
(Workout_prescription.name (Entry.prescription e));
47
+
Added:
Alcotest.(check int) "no stimuli" 0 (List.length (Entry.stimuli e));
48
+
Added:
Alcotest.(check int)
49
+
Added:
"four slots outstanding" 4
50
+
Added:
(List.length (Entry.unperformed e));
51
+
Added:
Alcotest.(check bool) "unfinished" false (Entry.is_finished e);
52
+
Added:
Alcotest.(check bool)
53
+
Added:
"no duration" true
54
+
Added:
(Option.is_none (Entry.duration e)) );
55
+
Added:
( "HD1's Day 1 can be logged end to end",
56
+
Added:
`Quick,
57
+
Added:
fun () ->
58
+
Added:
let e =
59
+
Added:
List.fold_left
60
+
Added:
(fun e s -> ok (Entry.add_stimulus e s))
61
+
Added:
(fresh ()) day_one_stimuli
62
+
Added:
in
63
+
Added:
Alcotest.(check int) "four stimuli" 4 (List.length (Entry.stimuli e));
64
+
Added:
Alcotest.(check int)
65
+
Added:
"nothing outstanding" 0
66
+
Added:
(List.length (Entry.unperformed e));
67
+
Added:
let finished = ok (Entry.finish e ~ended_at:(at 2400)) in
68
+
Added:
Alcotest.(check bool) "finished" true (Entry.is_finished finished);
69
+
Added:
Alcotest.(check (option int))
70
+
Added:
"40 minutes" (Some 2400)
71
+
Added:
(Option.map Recovery.duration_to_seconds (Entry.duration finished)) );
72
+
Added:
( "stimuli come back in the order performed",
73
+
Added:
`Quick,
74
+
Added:
fun () ->
75
+
Added:
let e =
76
+
Added:
List.fold_left
77
+
Added:
(fun e s -> ok (Entry.add_stimulus e s))
78
+
Added:
(fresh ())
79
+
Added:
[ single "laterals" 12. 8; single "bent-over-laterals" 10. 9 ]
80
+
Added:
in
81
+
Added:
Alcotest.(check (list string))
82
+
Added:
"as performed"
83
+
Added:
[ "Laterals"; "Bent-over Dumbbell Laterals" ]
84
+
Added:
(List.map
85
+
Added:
(fun s -> Exercise.name (List.hd (Stimulus.exercises s)))
86
+
Added:
(Entry.stimuli e)) );
87
+
Added:
( "a finished entry accepts nothing further",
88
+
Added:
`Quick,
89
+
Added:
fun () ->
90
+
Added:
let e = ok (Entry.finish (fresh ()) ~ended_at:(at 60)) in
91
+
Added:
(match Entry.add_stimulus e (single "laterals" 12. 8) with
92
+
Added:
| Error Entry.Already_finished -> ()
93
+
Added:
| _ -> Alcotest.fail "expected Already_finished");
94
+
Added:
match Entry.finish e ~ended_at:(at 120) with
95
+
Added:
| Error Entry.Already_finished -> ()
96
+
Added:
| _ -> Alcotest.fail "expected Already_finished" );
97
+
Added:
]
98
+
Added:
99
+
Added:
let conformance_tests =
100
+
Added:
[
101
+
Added:
( "an unprescribed movement is refused",
102
+
Added:
`Quick,
103
+
Added:
fun () ->
104
+
Added:
match Entry.add_stimulus (fresh ()) (single "shrugs" 80. 10) with
105
+
Added:
| Error (Entry.Not_prescribed id) ->
106
+
Added:
Alcotest.(check string) "shrugs" "shrugs" (id :> string)
107
+
Added:
| _ -> Alcotest.fail "expected Not_prescribed" );
108
+
Added:
( "a lone set where a pre-exhaust was prescribed is refused",
109
+
Added:
`Quick,
110
+
Added:
fun () ->
111
+
Added:
match Entry.add_stimulus (fresh ()) (single "dumbbell-flyes" 20. 9) with
112
+
Added:
| Error
113
+
Added:
(Entry.Delivery_mismatch
114
+
Added:
{
115
+
Added:
prescribed = Entry.As_pre_exhaust;
116
+
Added:
logged = Entry.As_single;
117
+
Added:
_;
118
+
Added:
}) ->
119
+
Added:
()
120
+
Added:
| _ -> Alcotest.fail "expected Delivery_mismatch" );
121
+
Added:
( "the prescribed pair, delivered as prescribed, is accepted",
122
+
Added:
`Quick,
123
+
Added:
fun () ->
124
+
Added:
Alcotest.(check bool)
125
+
Added:
"pec pair conforms" true
126
+
Added:
(Result.is_ok
127
+
Added:
(Entry.add_stimulus (fresh ())
128
+
Added:
(pre_exhaust
129
+
Added:
(move "dumbbell-flyes" 20. 9)
130
+
Added:
(move "incline-press" 60. 7)))) );
131
+
Added:
( "an allowed substitute is accepted in its own role",
132
+
Added:
`Quick,
133
+
Added:
fun () ->
134
+
Added:
let e =
135
+
Added:
ok
136
+
Added:
(Entry.add_stimulus (fresh ())
137
+
Added:
(pre_exhaust (move "pec-deck" 45. 9)
138
+
Added:
(move "incline-press" 60. 7)))
139
+
Added:
in
140
+
Added:
Alcotest.(check int) "recorded" 1 (List.length (Entry.stimuli e));
141
+
Added:
Alcotest.(check int) "slot filled" 3 (List.length (Entry.unperformed e))
142
+
Added:
);
143
+
Added:
( "a movement off the prescription's substitute list is refused",
144
+
Added:
`Quick,
145
+
Added:
fun () ->
146
+
Added:
(* Cable crossovers substitute for flyes in the catalog, but Day 1
147
+
Added:
permits only crossovers and pec deck — squats never. *)
148
+
Added:
match
149
+
Added:
Entry.add_stimulus (fresh ())
150
+
Added:
(pre_exhaust (move "dumbbell-flyes" 20. 9) (move "dips" 0. 7))
151
+
Added:
with
152
+
Added:
| Error _ -> ()
153
+
Added:
| Ok _ -> Alcotest.fail "dips is not the prescribed pec compound" );
154
+
Added:
]
155
+
Added:
156
+
Added:
let volume_tests =
157
+
Added:
[
158
+
Added:
( "repeated work is recorded, and shows as more stimuli than slots",
159
+
Added:
`Quick,
160
+
Added:
fun () ->
161
+
Added:
(* HD1 forbids extra volume, but the log must still say what happened;
162
+
Added:
diagnosing it is Progression's job. *)
163
+
Added:
let e =
164
+
Added:
List.fold_left
165
+
Added:
(fun e s -> ok (Entry.add_stimulus e s))
166
+
Added:
(fresh ())
167
+
Added:
[ single "laterals" 12. 8; single "laterals" 12. 6 ]
168
+
Added:
in
169
+
Added:
Alcotest.(check int) "two stimuli" 2 (List.length (Entry.stimuli e));
170
+
Added:
Alcotest.(check int)
171
+
Added:
"still three slots outstanding" 3
172
+
Added:
(List.length (Entry.unperformed e)) );
173
+
Added:
( "unperformed shrinks as slots are answered",
174
+
Added:
`Quick,
175
+
Added:
fun () ->
176
+
Added:
let e = ok (Entry.add_stimulus (fresh ()) (single "laterals" 12. 8)) in
177
+
Added:
Alcotest.(check int) "three left" 3 (List.length (Entry.unperformed e));
178
+
Added:
let e = ok (Entry.add_stimulus e (single "bent-over-laterals" 10. 9)) in
179
+
Added:
Alcotest.(check int) "two left" 2 (List.length (Entry.unperformed e)) );
180
+
Added:
]
181
+
Added:
182
+
Added:
let clearance_tests =
183
+
Added:
[
184
+
Added:
( "an entry keeps the basis on which it was begun",
185
+
Added:
`Quick,
186
+
Added:
fun () ->
187
+
Added:
let recovering =
188
+
Added:
Recovery.evaluate_readiness ~elapsed:(Recovery.hours 12)
189
+
Added:
~recommended:Routine.training_interval
190
+
Added:
in
191
+
Added:
let e =
192
+
Added:
Entry.start day_one
193
+
Added:
~clearance:(Recovery.override recovering ~reason:"away next week")
194
+
Added:
~started_at:(at 0)
195
+
Added:
in
196
+
Added:
match Recovery.basis (Entry.clearance e) with
197
+
Added:
| Recovery.Overridden { reason; _ } ->
198
+
Added:
Alcotest.(check string) "reason" "away next week" reason
199
+
Added:
| Recovery.Recovered -> Alcotest.fail "expected Overridden" );
200
+
Added:
]
201
+
Added:
202
+
Added:
let suite =
203
+
Added:
[
204
+
Added:
("entry.lifecycle", lifecycle_tests);
205
+
Added:
("entry.conformance", conformance_tests);
206
+
Added:
("entry.volume", volume_tests);
207
+
Added:
("entry.clearance", clearance_tests);
208
+
Added:
]