[OCaml] High Intensity Training Online
1
(** Unit tests for {!Recovery}. *)
2
3
let secs d = Recovery.duration_to_seconds d
4
let at s = Recovery.timestamp_of_unix_seconds s
5
6
let time_tests =
7
[
8
( "hours and days convert to seconds",
9
`Quick,
10
fun () ->
11
Alcotest.(check int) "48h" 172_800 (secs (Recovery.hours 48));
12
Alcotest.(check int) "2d" 172_800 (secs (Recovery.days 2)) );
13
( "timestamps round-trip",
14
`Quick,
15
fun () ->
16
Alcotest.(check int)
17
"unix seconds" 1_700_000_000
18
(Recovery.timestamp_to_unix_seconds (at 1_700_000_000)) );
19
( "elapsed measures the gap between workouts",
20
`Quick,
21
fun () ->
22
Alcotest.(check int)
23
"48h apart" 172_800
24
(secs (Recovery.elapsed ~since:(at 0) ~now:(at 172_800))) );
25
( "elapsed never goes negative",
26
`Quick,
27
fun () ->
28
Alcotest.(check int)
29
"clamped" 0
30
(secs (Recovery.elapsed ~since:(at 172_800) ~now:(at 0))) );
31
]
32
33
let readiness_tests =
34
[
35
( "the recommended window having passed means ready",
36
`Quick,
37
fun () ->
38
let r =
39
Recovery.evaluate_readiness ~elapsed:(Recovery.hours 48)
40
~recommended:(Recovery.hours 48)
41
in
42
Alcotest.(check bool) "ready" true (Recovery.is_ready r) );
43
( "short of the window means still recovering",
44
`Quick,
45
fun () ->
46
match
47
Recovery.evaluate_readiness ~elapsed:(Recovery.hours 24)
48
~recommended:(Recovery.hours 48)
49
with
50
| Recovery.Ready -> Alcotest.fail "expected Recovering"
51
| Recovery.Recovering { rested; recommended } ->
52
Alcotest.(check int) "rested" 86_400 (secs rested);
53
Alcotest.(check int) "recommended" 172_800 (secs recommended) );
54
]
55
56
let clearance_tests =
57
[
58
( "recovery earns a clearance",
59
`Quick,
60
fun () ->
61
match Recovery.clear Recovery.Ready with
62
| Some c -> (
63
match Recovery.basis c with
64
| Recovery.Recovered -> ()
65
| Recovery.Overridden _ -> Alcotest.fail "expected Recovered")
66
| None -> Alcotest.fail "expected a clearance" );
67
( "an incomplete recovery earns none",
68
`Quick,
69
fun () ->
70
let recovering =
71
Recovery.evaluate_readiness ~elapsed:(Recovery.hours 12)
72
~recommended:(Recovery.hours 48)
73
in
74
Alcotest.(check bool)
75
"refused" true
76
(Option.is_none (Recovery.clear recovering)) );
77
( "an override is granted and records the recovery shortfall",
78
`Quick,
79
fun () ->
80
let recovering =
81
Recovery.evaluate_readiness ~elapsed:(Recovery.hours 12)
82
~recommended:(Recovery.hours 48)
83
in
84
let c = Recovery.override recovering in
85
match Recovery.basis c with
86
| Recovery.Overridden { rested; recommended } ->
87
Alcotest.(check int) "rested" 43_200 (secs rested);
88
Alcotest.(check int) "recommended" 172_800 (secs recommended)
89
| Recovery.Recovered -> Alcotest.fail "expected Overridden" );
90
( "overriding when already recovered is simply recovered",
91
`Quick,
92
fun () ->
93
match Recovery.basis (Recovery.override Recovery.Ready) with
94
| Recovery.Recovered -> ()
95
| Recovery.Overridden _ ->
96
Alcotest.fail "no recovery was outstanding to override" );
97
]
98
99
let suite =
100
[
101
("recovery.time", time_tests);
102
("recovery.readiness", readiness_tests);
103
("recovery.clearance", clearance_tests);
104
]
105