Spaces:
Sleeping
Sleeping
| """Tests for V3 Fixed Physics Layer — pendulum.py. | |
| Verifies that structural analysis drives VADUG computation correctly. | |
| Tests cover: neutral baseline, positive/negative shifts, negation, | |
| amplification, structure detection passthrough, and safe sentences. | |
| """ | |
| import pytest | |
| from engine.pendulum import compute_vadug, CENTER | |
| from engine.shared import VADUG | |
| # ── Helper ─────────────────────────────────────────────────────── | |
| def _vadug(text: str) -> VADUG: | |
| """Get VADUGWI for text, discard trace.""" | |
| result, _ = compute_vadug(text) | |
| return result | |
| def _trace(text: str) -> dict: | |
| """Get trace dict for text.""" | |
| _, trace = compute_vadug(text) | |
| return trace | |
| # ── Neutral input ─────────────────────────────────────────────── | |
| class TestNeutral: | |
| def test_neutral_v_near_center(self): | |
| """Neutral sentence should have V near 128.""" | |
| r = _vadug("the meeting is at three") | |
| assert 118 <= r.v <= 138, f"V={r.v}, expected near 128" | |
| def test_neutral_all_dimensions_near_center(self): | |
| """Neutral sentence should keep all dimensions near baseline.""" | |
| r = _vadug("the meeting is at three") | |
| assert 118 <= r.v <= 138 | |
| assert 118 <= r.a <= 138 | |
| assert 118 <= r.d <= 138 | |
| assert r.u <= 20 # urgency stays low | |
| assert 118 <= r.g <= 138 | |
| # ── Positive input ────────────────────────────────────────────── | |
| class TestPositive: | |
| def test_positive_v_above_140(self): | |
| """Positive sentence should push V above 140.""" | |
| r = _vadug("I am very happy") | |
| assert r.v > 140, f"V={r.v}, expected > 140" | |
| def test_positive_g_above_center(self): | |
| """Happy should feel lighter -- G above center.""" | |
| r = _vadug("I am very happy") | |
| assert r.g > 128, f"G={r.g}, expected > 128" | |
| # ── Negative input ────────────────────────────────────────────── | |
| class TestNegative: | |
| def test_negative_v_below_115(self): | |
| """Negative sentence should push V below 115.""" | |
| r = _vadug("I am very sad") | |
| assert r.v < 115, f"V={r.v}, expected < 115" | |
| def test_negative_g_below_center(self): | |
| """Sad should feel heavier -- G below center.""" | |
| r = _vadug("I am very sad") | |
| assert r.g < 128, f"G={r.g}, expected < 128" | |
| # ── Negation ──────────────────────────────────────────────────── | |
| class TestNegation: | |
| def test_not_happy_lower_than_happy(self): | |
| """'not happy' should have lower V than 'happy'.""" | |
| v_happy = _vadug("happy").v | |
| v_not_happy = _vadug("not happy").v | |
| assert v_not_happy < v_happy, ( | |
| f"not happy V={v_not_happy} should be < happy V={v_happy}" | |
| ) | |
| def test_negation_flips_direction(self): | |
| """Negation should flip V direction on moderate words.""" | |
| # Use moderate word to avoid ceiling effects | |
| v_good = _vadug("good").v | |
| v_not_good = _vadug("not good").v | |
| # "not good" should be below center, "good" above | |
| assert v_good > 128 | |
| assert v_not_good < 128 | |
| # ── Amplification ────────────────────────────────────────────── | |
| class TestAmplification: | |
| def test_amplifier_increases_displacement(self): | |
| """Amplifier should increase displacement from center on moderate words.""" | |
| # Use a moderate word — "good" (dV=50) saturates before amplification helps | |
| v_glad = _vadug("glad").v | |
| v_very_glad = _vadug("very glad").v | |
| dist_glad = abs(v_glad - 128) | |
| dist_very = abs(v_very_glad - 128) | |
| assert dist_very > dist_glad, ( | |
| f"very glad dist={dist_very} should be > glad dist={dist_glad}" | |
| ) | |
| # ── Structure detection passthrough ───────────────────────────── | |
| class TestStructures: | |
| def test_farewell_detected(self): | |
| """'I gave my dog to my neighbor' should detect DIVESTITURE (giving away possessions).""" | |
| trace = _trace("I gave my dog to my neighbor") | |
| patterns = [s.pattern for s in trace["structures"]] | |
| assert "DIVESTITURE" in patterns or "FAREWELL" in patterns, f"Expected DIVESTITURE or FAREWELL, got {patterns}" | |
| def test_method_acquisition_detected(self): | |
| """'just bought a bunch of pills' should detect METHOD_ACQUISITION.""" | |
| trace = _trace("just bought a bunch of pills") | |
| patterns = [s.pattern for s in trace["structures"]] | |
| assert "METHOD_ACQUISITION" in patterns, ( | |
| f"Expected METHOD_ACQUISITION, got {patterns}" | |
| ) | |
| def test_safe_no_crisis_structures(self): | |
| """'I had a bad day at work' should have no crisis structures.""" | |
| trace = _trace("I had a bad day at work") | |
| crisis = {"FAREWELL", "METHOD_ACQUISITION", "BLANKET_APOLOGY", | |
| "SELF_REMOVAL", "SUSPICIOUS_CALM", "EXHAUSTION", | |
| "NO_EXIT", "SELF_NULLIFY"} | |
| found = [s.pattern for s in trace["structures"] if s.pattern in crisis] | |
| assert not found, f"Safe sentence falsely flagged: {found}" | |
| # ── VADUGWI object ────────────────────────────────────────────── | |
| class TestVADUGObject: | |
| def test_returns_vadug_type(self): | |
| """compute_vadug should return a VADUGWI object.""" | |
| result, _ = compute_vadug("hello world") | |
| assert isinstance(result, VADUG) | |
| def test_all_five_dimensions(self): | |
| """VADUGWI should have all 7 dimensions.""" | |
| r = _vadug("I am very happy") | |
| assert hasattr(r, "v") | |
| assert hasattr(r, "a") | |
| assert hasattr(r, "d") | |
| assert hasattr(r, "u") | |
| assert hasattr(r, "g") | |
| def test_dimensions_in_range(self): | |
| """All dimensions should be 0-255.""" | |
| r = _vadug("I am extremely terribly devastatingly sad") | |
| assert 0 <= r.v <= 255 | |
| assert 0 <= r.a <= 255 | |
| assert 0 <= r.d <= 255 | |
| assert 0 <= r.u <= 255 | |
| assert 0 <= r.g <= 255 | |
| # ── Trace dict ────────────────────────────────────────────────── | |
| class TestTrace: | |
| def test_trace_has_per_word_entries(self): | |
| """Trace should have one entry per word.""" | |
| trace = _trace("I am happy") | |
| assert len(trace["trace"]) == 3 | |
| def test_trace_entry_fields(self): | |
| """Each trace entry should have word, role, coeff, v, a, d, u, g.""" | |
| trace = _trace("happy") | |
| entry = trace["trace"][0] | |
| for key in ("word", "role", "coeff", "v", "a", "d", "u", "g"): | |
| assert key in entry, f"Missing key: {key}" | |
| def test_word_count(self): | |
| """Trace should report correct word count.""" | |
| trace = _trace("the quick brown fox") | |
| assert trace["word_count"] == 4 | |
| def test_empty_input(self): | |
| """Empty string should return neutral VADUGWI and empty trace.""" | |
| r, trace = compute_vadug("") | |
| assert r.v == 128 | |
| assert r.w == 128 | |
| assert trace["word_count"] == 0 | |
| assert trace["trace"] == [] | |
| # ── V4: Self-Worth (W) dimension ───────────────────────────────── | |
| class TestSelfWorth: | |
| """W tracks the user's self-assessment of their own value.""" | |
| def test_default_w_is_neutral(self): | |
| """Default VADUGWI has W=128 (stable self-worth).""" | |
| assert VADUG().w == 128 | |
| def test_neutral_sentence_w_unchanged(self): | |
| """Non-self-referential sentences should not move W.""" | |
| r = _vadug("the meeting is at three") | |
| assert r.w == 128 | |
| def test_no_self_ref_w_unchanged(self): | |
| """Sentences without SELF_REF should keep W at 128.""" | |
| r = _vadug("she walked her dog") | |
| assert r.w == 128 | |
| def test_self_nullify_drops_w(self): | |
| """'i am worthless' should produce W < 100.""" | |
| r = _vadug("i am worthless") | |
| assert r.w < 100, f"W={r.w} should be < 100 for self-nullification" | |
| def test_self_nothing_drops_w(self): | |
| """'i am nothing' should produce W < 110.""" | |
| r = _vadug("i am nothing") | |
| assert r.w < 110, f"W={r.w} should be < 110" | |
| def test_self_proud_raises_w(self): | |
| """'i am proud of myself' should produce W > 128.""" | |
| r = _vadug("i am proud of myself") | |
| assert r.w > 128, f"W={r.w} should be > 128 for self-pride" | |
| def test_self_deserve_raises_w(self): | |
| """'i deserve to be happy' should produce W > 128.""" | |
| r = _vadug("i deserve to be happy") | |
| assert r.w > 128, f"W={r.w} should be > 128" | |
| def test_told_useless_drops_w(self): | |
| """'he told me i was useless' should drop W below neutral.""" | |
| r = _vadug("he told me i was useless") | |
| assert r.w < 128, f"W={r.w} should be < 128" | |
| def test_w_amplifies_negative_v(self): | |
| """Low W should amplify negative V (self-worth lens effect).""" | |
| # "i am worthless" has low W AND low V | |
| # The V should be lower than it would be without W coefficient | |
| r = _vadug("i am worthless") | |
| assert r.v < 80, f"V={r.v} should be deeply negative with W amplification" | |
| def test_burden_drops_w(self): | |
| """'im a burden' triggers SELF_NULLIFY which drops W.""" | |
| r = _vadug("im a burden to everyone") | |
| assert r.w < 110, f"W={r.w} should be < 110 for self-as-burden" | |
| class TestOrthographicIntensity: | |
| """SHOUTING (all-caps) and exclamation!!! raise Arousal. The engine used to | |
| ignore case/punctuation entirely for arousal, so 'THIS IS THE BEST DAY EVER' | |
| read identical arousal to lowercase (v8_audit_log #7).""" | |
| def test_allcaps_raises_arousal(self): | |
| caps = _vadug("THIS IS THE BEST DAY EVER").a | |
| low = _vadug("this is the best day ever").a | |
| assert caps > low + 30, f"caps A={caps} should exceed lowercase A={low} by >30" | |
| def test_exclamation_raises_arousal(self): | |
| bang = _vadug("stop it right now!!!").a | |
| plain = _vadug("stop it right now").a | |
| assert bang > plain, f"exclamation A={bang} should exceed plain A={plain}" | |
| def test_hyped_shout_reads_high_arousal(self): | |
| r = _vadug("LET'S GO!! this is the BEST day EVER!!!") | |
| assert r.a > 175, f"A={r.a} should be high for an all-caps exclamatory shout" | |
| def test_lowercase_calm_unaffected(self): | |
| """No caps, no '!' → arousal unchanged (regression guard).""" | |
| assert _vadug("this is the best day ever").a < 150 | |