# -*- coding: utf-8 -*- """The handling compression limit, from the window down to the job file. `handling.compression_limit_ratio` multiplies f'c (f'ci at stripping) to give the compression allowable in psi. Unlike the tensile limit next to it, there is no second school of thought to weigh -- the owner closed the question, 2026-09-01: "0.6保持可调" ("0.6 stays adjustable") -- so 0.6 remains the shipped default (ACI 318-19 Table 24.5.3.1 / Sec. 24.5.4.1) and becomes a per-job field the same way `tension_limit_sqrt_fc` already is, in the same Project Config module, following the exact same plumbing: `configmodel.SETTINGS` -> `configform.MODULE_OF` -> `liftexport` writes it onto every stage -> panellift reads it into `Stage`. Nothing here implements the higher 0.70*f'ci ACI permits at member ends -- the owner ruled that out the same night ("先不用实现0.7...别搞这么复杂") -- so there is exactly one ratio, and this file never tests a location split. Mirrors `test_stress_limit_setting.py` structurally; see that file's own docstring for why each half is tested where it is. """ from vibe import configform, configmodel, knowledge, liftexport from test_liftexport import FACE_AND_EDGE_LIFTERS, make_job, make_know KEY = 'handling.compression_limit_ratio' # ------------------------------------------------------- it is in the window def test_the_compression_limit_is_a_field_in_lifting_and_handling(): assert configform.MODULE_OF[KEY] == configform.LIFTING assert KEY not in configform.NOT_SHOWN def test_it_is_a_box_you_type_a_number_into(): field = [f for f in configform.settings_fields() if f['key'] == KEY][0] assert field['kind'] == 'number' ok, value, _why = configform.parse(field, '0.7') assert ok and value == 0.7 def test_the_row_note_names_the_default_and_the_citation(): """A box that says only 'Compression limit' asks a person to trust a number with no way to check it. The note carries what it multiplies, the default, and the ACI table it comes from.""" field = [f for f in configform.settings_fields() if f['key'] == KEY][0] note = field['note'] assert '0.6' in note assert "f'c" in note assert '24.5' in note # the ACI section, either citation def test_the_note_says_the_higher_member_end_value_is_not_implemented(): """The owner's ruling against implementing ACI's 0.70*f'ci at member ends has to be visible at the control, not only in a comment nobody editing the job sees.""" field = [f for f in configform.settings_fields() if f['key'] == KEY][0] assert '0.70' in field['note'] assert 'conservative' in field['note'] or 'flat' in field['note'] def test_the_full_view_says_where_it_is_edited_now(): assert configform.placement()[KEY] == 'Lifting and handling' def test_nothing_fell_through_the_move(): """The guard that makes the not-shown pool honest: every editable setting is in a module or has a written reason for not being. Failing this here would mean the new field landed with no home.""" assert configform.unmapped() == [] # ------------------------------------------------------------ what it refuses def test_a_zero_or_negative_ratio_is_refused(): """The exact mirror of the tensile limit's own refusal: a zero allowable fails every piece in compression at any stress, and a negative one inverts the comparison so every piece passes.""" assert configmodel.validate(KEY, 0) assert configmodel.validate(KEY, -0.6) assert 'greater than zero' in ' '.join(configmodel.validate(KEY, 0)) def test_a_garbage_value_is_refused_not_silently_reverted(): assert configmodel.validate(KEY, 'static_lift') assert configmodel.validate(KEY, None) == [] # blank means "inherit" def test_ordinary_values_are_accepted_without_argument(): """0.6 is the shipped default, not a doctrine -- the validator does not get to refuse a job that wants a different positive ratio.""" assert configmodel.validate(KEY, 0.6) == [] assert configmodel.validate(KEY, 0.5) == [] assert configmodel.validate(KEY, 0.7) == [] # ------------------------------------------------- it reaches the calculation def test_the_default_is_still_point_six(): """Byte-identity when nobody sets it. Every job configured today has no override, and this is the number they have all been running.""" assert knowledge.get(knowledge.DEFAULTS, KEY) == 0.6 def test_the_job_layer_value_lands_on_every_stage_of_the_job_file(): know = make_know() know['handling']['compression_limit_ratio'] = 0.7 stages, _rows, _problems = liftexport.default_stages( make_job(FACE_AND_EDGE_LIFTERS), know) assert stages assert all(s['compression_limit_ratio'] == 0.7 for s in stages) def test_an_unset_ratio_writes_the_point_six_it_always_wrote(): stages, _rows, _problems = liftexport.default_stages( make_job(FACE_AND_EDGE_LIFTERS), make_know()) assert all(s['compression_limit_ratio'] == 0.6 for s in stages) assert all(s['compression_limit_source'] == 'job' for s in stages) def test_a_stage_that_states_its_own_ratio_is_marked_as_the_override(): know = make_know() know['handling']['compression_limit_ratio'] = 0.6 know['handling']['stages'][0]['compression_limit_ratio'] = 0.7 stages, _rows, _problems = liftexport.default_stages( make_job(FACE_AND_EDGE_LIFTERS), know) assert stages[0]['compression_limit_ratio'] == 0.7 assert stages[0]['compression_limit_source'] == 'stage' assert stages[1]['compression_limit_ratio'] == 0.6 assert stages[1]['compression_limit_source'] == 'job' def test_the_provenance_words_are_the_ones_panellift_reads(): """Written on one side, refused on the other if they disagree -- see `panellift.model.COMPRESSION_LIMIT_SOURCES`.""" stages, _rows, _problems = liftexport.default_stages( make_job(FACE_AND_EDGE_LIFTERS), make_know()) assert set(s['compression_limit_source'] for s in stages) <= \ set(['job', 'stage', 'default']) def test_tension_and_compression_are_independent_fields(): """Setting one must never move the other -- they are two separate dotted keys, two separate stage entries, and this is the regression a copy-paste of the tension plumbing could introduce silently.""" know = make_know() know['handling']['tension_limit_sqrt_fc'] = 7.5 know['handling']['compression_limit_ratio'] = 0.7 stages, _rows, _problems = liftexport.default_stages( make_job(FACE_AND_EDGE_LIFTERS), know) assert all(s['tension_limit_sqrt_fc'] == 7.5 for s in stages) assert all(s['compression_limit_ratio'] == 0.7 for s in stages)