Unraid deployment fixes and generator improvements
- Add Next.js rewrites to proxy API calls through same origin (fixes login/media on werkout.treytartt.com) - Fix mediaUrl() in DayCard and ExerciseRow to use relative paths in production - Add proxyTimeout for long-running workout generation endpoints - Add CSRF trusted origin for treytartt.com - Split docker-compose into production (Unraid) and dev configs - Show display_name and descriptions on workout type cards - Generator: rules engine improvements, movement enforcement, exercise selector updates - Add new test files for rules drift, workout research generation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -73,7 +73,7 @@ class TestWorkoutTypeRulesCoverage(TestCase):
|
||||
expected_types = [
|
||||
'traditional_strength_training',
|
||||
'hypertrophy',
|
||||
'hiit',
|
||||
'high_intensity_interval_training',
|
||||
'functional_strength_training',
|
||||
'cross_training',
|
||||
'core_training',
|
||||
@@ -116,14 +116,14 @@ class TestDBCalibrationCoverage(TestCase):
|
||||
|
||||
def test_all_8_types_in_calibration(self):
|
||||
expected_names = [
|
||||
'Functional Strength Training',
|
||||
'Traditional Strength Training',
|
||||
'HIIT',
|
||||
'Cross Training',
|
||||
'Core Training',
|
||||
'Flexibility',
|
||||
'Cardio',
|
||||
'Hypertrophy',
|
||||
'functional_strength_training',
|
||||
'traditional_strength_training',
|
||||
'high_intensity_interval_training',
|
||||
'cross_training',
|
||||
'core_training',
|
||||
'flexibility',
|
||||
'cardio',
|
||||
'hypertrophy',
|
||||
]
|
||||
for name in expected_names:
|
||||
self.assertIn(name, DB_CALIBRATION, f"Missing {name} in DB_CALIBRATION")
|
||||
@@ -137,7 +137,11 @@ class TestHelperFunctions(TestCase):
|
||||
_normalize_type_key('Traditional Strength Training'),
|
||||
'traditional_strength_training',
|
||||
)
|
||||
self.assertEqual(_normalize_type_key('HIIT'), 'hiit')
|
||||
self.assertEqual(_normalize_type_key('HIIT'), 'high_intensity_interval_training')
|
||||
self.assertEqual(
|
||||
_normalize_type_key('high intensity interval training'),
|
||||
'high_intensity_interval_training',
|
||||
)
|
||||
self.assertEqual(_normalize_type_key('cardio'), 'cardio')
|
||||
|
||||
def test_classify_rep_weight(self):
|
||||
@@ -500,6 +504,86 @@ class TestValidateWorkout(TestCase):
|
||||
"Expected superset size warning for 8-exercise superset in strength",
|
||||
)
|
||||
|
||||
def test_superset_focus_repetition_error(self):
|
||||
"""Two curl-family exercises in one superset should produce an error."""
|
||||
curl_a = _make_exercise(
|
||||
name='Alternating Bicep Curls',
|
||||
movement_patterns='upper pull',
|
||||
is_compound=False,
|
||||
exercise_tier='accessory',
|
||||
)
|
||||
curl_b = _make_exercise(
|
||||
name='Bicep Curls',
|
||||
movement_patterns='upper pull',
|
||||
is_compound=False,
|
||||
exercise_tier='accessory',
|
||||
)
|
||||
workout_spec = {
|
||||
'supersets': [
|
||||
_make_superset(name='Warm Up', exercises=[
|
||||
_make_entry(exercise=_make_exercise(is_reps=False), duration=30),
|
||||
], rounds=1),
|
||||
_make_superset(
|
||||
name='Working Set 1',
|
||||
exercises=[
|
||||
_make_entry(exercise=curl_a, reps=10, order=1),
|
||||
_make_entry(exercise=curl_b, reps=10, order=2),
|
||||
],
|
||||
rounds=3,
|
||||
),
|
||||
_make_superset(name='Cool Down', exercises=[
|
||||
_make_entry(exercise=_make_exercise(is_reps=False), duration=30),
|
||||
], rounds=1),
|
||||
],
|
||||
}
|
||||
violations = validate_workout(
|
||||
workout_spec, 'functional_strength_training', 'general_fitness',
|
||||
)
|
||||
repetition_errors = [
|
||||
v for v in violations
|
||||
if v.rule_id == 'superset_focus_repetition' and v.severity == 'error'
|
||||
]
|
||||
self.assertTrue(
|
||||
repetition_errors,
|
||||
f"Expected superset focus repetition error, got {[v.rule_id for v in violations]}",
|
||||
)
|
||||
|
||||
def test_adjacent_focus_repetition_info(self):
|
||||
"""Adjacent working supersets with same focus profile should be advisory."""
|
||||
pull_a = _make_exercise(name='Bicep Curl', movement_patterns='upper pull')
|
||||
pull_b = _make_exercise(name='Hammer Curl', movement_patterns='upper pull')
|
||||
workout_spec = {
|
||||
'supersets': [
|
||||
_make_superset(name='Warm Up', exercises=[
|
||||
_make_entry(exercise=_make_exercise(is_reps=False), duration=30),
|
||||
], rounds=1),
|
||||
_make_superset(
|
||||
name='Working Set 1',
|
||||
exercises=[_make_entry(exercise=pull_a, reps=10, order=1)],
|
||||
rounds=3,
|
||||
),
|
||||
_make_superset(
|
||||
name='Working Set 2',
|
||||
exercises=[_make_entry(exercise=pull_b, reps=10, order=1)],
|
||||
rounds=3,
|
||||
),
|
||||
_make_superset(name='Cool Down', exercises=[
|
||||
_make_entry(exercise=_make_exercise(is_reps=False), duration=30),
|
||||
], rounds=1),
|
||||
],
|
||||
}
|
||||
violations = validate_workout(
|
||||
workout_spec, 'functional_strength_training', 'general_fitness',
|
||||
)
|
||||
adjacent_infos = [
|
||||
v for v in violations
|
||||
if v.rule_id == 'adjacent_superset_focus_repetition' and v.severity == 'info'
|
||||
]
|
||||
self.assertTrue(
|
||||
adjacent_infos,
|
||||
"Expected adjacent superset focus repetition advisory info.",
|
||||
)
|
||||
|
||||
def test_compound_before_isolation_info(self):
|
||||
"""Isolation before compound should produce info violation."""
|
||||
isolation = _make_exercise(
|
||||
|
||||
Reference in New Issue
Block a user