View raw

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