View raw

1 (** Unit tests for {!Exercise}, authored against the exercise.mli contract. *) 2 3 let get id = 4 match Exercise.find_id id with 5 | Some e -> e 6 | None -> Alcotest.failf "catalog is missing %S" id 7 8 let dumbbell_flyes = 9 { 10 Exercise.equipment = Exercise.Dumbbell; 11 movement = Exercise.Fly; 12 variation = Exercise.Standard; 13 } 14 15 let lookup_tests = 16 [ 17 ( "a structured key locates the curated exercise", 18 `Quick, 19 fun () -> 20 match Exercise.find dumbbell_flyes with 21 | Some exercise -> 22 Alcotest.(check string) 23 "name" "Dumbbell Flyes" (Exercise.name exercise); 24 Alcotest.(check bool) 25 "key round-trips" true 26 (Exercise.key exercise = dumbbell_flyes); 27 Alcotest.(check string) 28 "stable id" "dumbbell-flyes" 29 (Exercise.id exercise :> string) 30 | None -> Alcotest.fail "Dumbbell Flyes key is absent" ); 31 ( "an absent structured key is rejected", 32 `Quick, 33 fun () -> 34 Alcotest.(check bool) 35 "no dumbbell press in HD1 catalog" true 36 (Option.is_none 37 (Exercise.find 38 { 39 Exercise.equipment = Exercise.Dumbbell; 40 movement = Exercise.Press; 41 variation = Exercise.Standard; 42 })) ); 43 ( "a stable identifier remains an external lookup boundary", 44 `Quick, 45 fun () -> 46 Alcotest.(check bool) 47 "stable id" true 48 (Option.is_some (Exercise.find_id "dumbbell-flyes")); 49 Alcotest.(check bool) 50 "unknown id" true 51 (Option.is_none (Exercise.find_id "jefferson-curl")) ); 52 ] 53 54 let pre_exhaust_tests = 55 [ 56 ( "accepts HD1's pec pairing", 57 `Quick, 58 fun () -> 59 Alcotest.(check bool) 60 "flyes then incline press" true 61 (Exercise.may_pre_exhaust ~isolation:(get "dumbbell-flyes") 62 ~compound:(get "incline-press")) ); 63 ( "accepts HD1's triceps pairing, where dips serves a second muscle", 64 `Quick, 65 fun () -> 66 Alcotest.(check bool) 67 "french press then dips" true 68 (Exercise.may_pre_exhaust ~isolation:(get "lying-french-press") 69 ~compound:(get "dips")); 70 Alcotest.(check bool) 71 "flyes then dips" true 72 (Exercise.may_pre_exhaust ~isolation:(get "dumbbell-flyes") 73 ~compound:(get "dips")) ); 74 ( "accepts HD1's lat and leg pairings", 75 `Quick, 76 fun () -> 77 Alcotest.(check bool) 78 "pullovers then pulldowns" true 79 (Exercise.may_pre_exhaust ~isolation:(get "pullovers") 80 ~compound:(get "close-grip-pulldowns")); 81 Alcotest.(check bool) 82 "leg extensions then leg presses" true 83 (Exercise.may_pre_exhaust ~isolation:(get "leg-extensions") 84 ~compound:(get "leg-presses")) ); 85 ( "rejects invalid pairings", 86 `Quick, 87 fun () -> 88 Alcotest.(check bool) 89 "compound then compound" false 90 (Exercise.may_pre_exhaust ~isolation:(get "dips") 91 ~compound:(get "incline-press")); 92 Alcotest.(check bool) 93 "isolation then isolation" false 94 (Exercise.may_pre_exhaust ~isolation:(get "dumbbell-flyes") 95 ~compound:(get "pec-deck")); 96 Alcotest.(check bool) 97 "unrelated targets" false 98 (Exercise.may_pre_exhaust ~isolation:(get "dumbbell-flyes") 99 ~compound:(get "squats")) ); 100 ] 101 102 let substitution_tests = 103 [ 104 ( "HD1's alternatives are substitutable", 105 `Quick, 106 fun () -> 107 Alcotest.(check bool) 108 "flyes for pec deck" true 109 (Exercise.may_substitute ~original:(get "dumbbell-flyes") 110 ~candidate:(get "pec-deck")); 111 Alcotest.(check bool) 112 "leg press for squat" true 113 (Exercise.may_substitute ~original:(get "leg-presses") 114 ~candidate:(get "squats")) ); 115 ( "substitution is symmetric but excludes the original and unrelated \ 116 movements", 117 `Quick, 118 fun () -> 119 let flyes = get "dumbbell-flyes" in 120 let pec_deck = get "pec-deck" in 121 Alcotest.(check bool) 122 "pec deck for flyes" true 123 (Exercise.may_substitute ~original:pec_deck ~candidate:flyes); 124 Alcotest.(check bool) 125 "not self" false 126 (Exercise.may_substitute ~original:flyes ~candidate:flyes); 127 Alcotest.(check bool) 128 "curls for squats" false 129 (Exercise.may_substitute ~original:(get "curls") 130 ~candidate:(get "squats")) ); 131 ] 132 133 let suite = 134 [ 135 ("exercise.lookup", lookup_tests); 136 ("exercise.pre_exhaust", pre_exhaust_tests); 137 ("exercise.substitution", substitution_tests); 138 ] 139