"""Real pytest suite for qn_check.py -- the quantum-number consistency engine. TESTS MANDATORY + REAL: these run the ACTUAL engine functions on real rows and assert on real output values. The headline guard is `test_broken_charge_row_ fails_closed`, which feeds a row whose stated charge != sum of constituent charges and asserts the engine FAILS that row (non-trivial, fail-closed). Run: python -m pytest tests/ -q (from .) """ import json import subprocess import sys from fractions import Fraction from pathlib import Path import pytest HERE = Path(__file__).resolve().parent ROOT = HERE.parent sys.path.insert(0, str(ROOT)) import qn_check as q # noqa: E402 # --------------------------------------------------------------------------- # Row factory # --------------------------------------------------------------------------- def mkrow(**ov): base = dict.fromkeys(q.SCHEMA, "") base.update({ "pdg_name": "p", "quark_content": "uud", "charge_Q": "1", "J": "1/2", "P": "+", "isospin_I": "1/2", "I3": "1/2", "baryon_B": "1", "strangeness_S": "0", "charm_C": "0", "bottom_Bprime": "0", "mass_MeV": "938.272", "mass_unc_MeV": "0.0001", "status_stars": "4", "sector": "test", "chunk": "T", "pdg_source": "unit-test", }) base.update(ov) return base # --------------------------------------------------------------------------- # Constituent parsing + derivation (real flavour algebra) # --------------------------------------------------------------------------- def test_proton_charge_baryon_derivation(): c = q.parse_quark_content("uud") d = q.derive_quantum_numbers(c, Fraction(1)) assert d["charge_Q"] == Fraction(1) assert d["baryon_B"] == Fraction(1) assert d["strangeness_S"] == 0 assert d["color_singlet"] is True def test_lambda_strangeness(): c = q.parse_quark_content("uds") d = q.derive_quantum_numbers(c, Fraction(0)) assert d["charge_Q"] == Fraction(0) assert d["strangeness_S"] == -1 assert d["baryon_B"] == Fraction(1) def test_kplus_charge_from_u_sbar(): # K+ = u sbar : +2/3 (u) + 1/3 (sbar) = +1 ; S = +1 c = q.parse_quark_content("u sbar") d = q.derive_quantum_numbers(c, Fraction(1)) assert d["charge_Q"] == Fraction(1) assert d["strangeness_S"] == 1 def test_dmeson_charm(): # D0 = c ubar : +2/3 - 2/3 = 0 ; C = +1 c = q.parse_quark_content("c ubar") d = q.derive_quantum_numbers(c, Fraction(0)) assert d["charge_Q"] == Fraction(0) assert d["charm_C"] == 1 def test_bmeson_bottomness_sign(): # u bbar : B' = -(n_b - n_bbar) = -(-1) = +1 c = q.parse_quark_content("u bbar") d = q.derive_quantum_numbers(c, Fraction(1)) assert d["bottom_Bprime"] == 1 def test_omega_minus_sss(): c = q.parse_quark_content("sss") d = q.derive_quantum_numbers(c, Fraction(-1)) assert d["charge_Q"] == Fraction(-1) assert d["strangeness_S"] == -3 assert d["baryon_B"] == Fraction(1) def test_isovector_light_charge_set(): # 'ubar d etc.' is the I=1 (u,d) triplet -> charge member in {+1,0,-1} c = q.parse_quark_content("ubar d etc. 2 3S1") d = q.derive_quantum_numbers(c, None) assert d["charge_set"] == {Fraction(1), Fraction(0), Fraction(-1)} assert d["baryon_B"] == Fraction(0) assert d["color_singlet"] is True def test_qqbar_mix_is_neutral_isoscalar(): c = q.parse_quark_content("c1(ubar u+dbar d)+c2(sbar s) octet-dominant") d = q.derive_quantum_numbers(c, Fraction(0)) assert d["charge_Q"] == Fraction(0) assert d["strangeness_S"] == 0 assert d["color_singlet"] is True def test_nuclear_cluster_deuteron(): c = q.parse_quark_content("pn") d = q.derive_quantum_numbers(c, Fraction(1)) assert d["charge_Q"] == Fraction(1) # p(+1)+n(0) assert d["baryon_B"] == Fraction(2) assert d["color_singlet"] is True def test_hypertriton_cluster_strangeness(): c = q.parse_quark_content("Lambda p n") d = q.derive_quantum_numbers(c, Fraction(1)) assert d["charge_Q"] == Fraction(1) # 0 + 1 + 0 assert d["baryon_B"] == Fraction(3) assert d["strangeness_S"] == -1 # one Lambda def test_slash_multiplet_shares_flavour_numbers(): # uuu/uud/udd/ddd : all members B=1,S=0; charges span {+2,+1,0,-1} c = q.parse_quark_content("uuu/uud/udd/ddd") d = q.derive_quantum_numbers(c, Fraction(1)) assert d["baryon_B"] == Fraction(1) assert d["charge_set"] == {Fraction(2), Fraction(1), Fraction(0), Fraction(-1)} # --------------------------------------------------------------------------- # FAIL-CLOSED guards (the load-bearing tests) # --------------------------------------------------------------------------- def test_broken_charge_row_fails_closed(): """A row whose stated charge (2) != sum of uud constituent charges (1) MUST fail closed. This is the deliberately-broken row.""" r = q.verify_row(mkrow(pdg_name="DELIBERATELY_BROKEN", quark_content="uud", charge_Q="2")) assert r.passed is False fields = [f["field"] for f in r.failures] assert "charge_Q" in fields detail = " ".join(f["detail"] for f in r.failures) assert "charge" in detail.lower() def test_good_proton_row_passes(): assert q.verify_row(mkrow()).passed is True def test_wrong_strangeness_fails_closed(): r = q.verify_row(mkrow(pdg_name="Lam_bad", quark_content="uds", charge_Q="0", strangeness_S="0")) # should be -1 assert r.passed is False assert "strangeness_S" in [f["field"] for f in r.failures] def test_colored_diquark_fails_closed(): # 'ud' is a colour-3 diquark, NOT a colour singlet -> must fail. r = q.verify_row(mkrow(pdg_name="ColorBad", quark_content="ud", charge_Q="1/3", baryon_B="2/3")) assert r.passed is False assert "color_singlet" in [f["field"] for f in r.failures] def test_wrong_baryon_number_fails_closed(): r = q.verify_row(mkrow(pdg_name="Bbad", quark_content="uud", charge_Q="1", baryon_B="2")) # should be 1 assert r.passed is False assert "baryon_B" in [f["field"] for f in r.failures] def test_missing_required_column_fails_closed(): row = mkrow() del row["pdg_source"] # drop a required column r = q.verify_row(row) assert r.passed is False assert any(f["field"] == "pdg_source" for f in r.failures) def test_unparseable_quark_content_fails_closed(): r = q.verify_row(mkrow(pdg_name="Garbage", quark_content="zzz999")) assert r.passed is False assert any(f["field"] == "quark_content" for f in r.failures) # --------------------------------------------------------------------------- # Elementary particles # --------------------------------------------------------------------------- def test_electron_elementary_charge(): r = q.verify_row(mkrow(pdg_name="e-", quark_content="elementary", charge_Q="-1", J="0.5", P="+", isospin_I="", I3="", baryon_B="0", mass_MeV="0.51099895069", mass_unc_MeV="0.00000000016")) assert r.passed is True def test_bare_strange_quark_carries_strangeness(): # a bare s quark listed elementary: Q=-1/3, B=+1/3, S=-1 r = q.verify_row(mkrow(pdg_name="s", quark_content="elementary", charge_Q="-0.3333333333", baryon_B="0.3333333333", strangeness_S="-1", J="0.5", isospin_I="", I3="", mass_MeV="93.5")) assert r.passed is True def test_electron_wrong_charge_fails_closed(): r = q.verify_row(mkrow(pdg_name="e-", quark_content="elementary", charge_Q="+1", J="0.5", isospin_I="", I3="", baryon_B="0")) assert r.passed is False assert "charge_Q" in [f["field"] for f in r.failures] # --------------------------------------------------------------------------- # Decimal-charge reconciliation # --------------------------------------------------------------------------- def test_decimal_two_thirds_accepted(): assert q.fraction_eq(Fraction("0.6666666667"), Fraction(2, 3)) is True def test_decimal_clearly_wrong_rejected(): assert q.fraction_eq(Fraction("0.5"), Fraction(2, 3)) is False # --------------------------------------------------------------------------- # RELATION engine vs PDG-2024 (real config + real aux CSVs) # --------------------------------------------------------------------------- def test_relations_pass_on_real_pdg_data(): config = json.loads((ROOT / "relations_config.json").read_text(encoding="utf-8")) results = q.check_relations(ROOT / "data", config) assert results, "no relations evaluated" for r in results: assert r.passed, f"relation FAILED: {r.name} -> {r.detail}" def test_gmo_octet_residual_is_sub_percent(): config = json.loads((ROOT / "relations_config.json").read_text(encoding="utf-8")) results = q.check_relations(ROOT / "data", config) gmo = [r for r in results if "GMO" in r.name][0] # PDG-2024: GMO holds to ~0.6% assert gmo.value < 0.01 assert gmo.value > 0.0 def test_relation_fails_closed_when_input_missing(tmp_path): # Point the relation engine at an empty data dir -> every relation that # needs an aux CSV must FAIL closed (not silently pass). config = json.loads((ROOT / "relations_config.json").read_text(encoding="utf-8")) results = q.check_relations(tmp_path, config) assert results assert any(not r.passed for r in results) assert any("FAIL-CLOSED" in r.detail for r in results) def test_regge_negative_slope_would_fail(): # Synthesise a non-physical trajectory (M^2 decreasing in J) and confirm # the linear-fit slope test rejects it. xs = [0, 1, 2, 3] ys = [4_000_000, 3_000_000, 2_000_000, 1_000_000] # M^2 DECREASING r2, slope = q._linfit_r2(xs, ys) assert slope < 0 # non-positive slope => Regge ansatz violated # --------------------------------------------------------------------------- # End-to-end: run the engine as a process, assert exit code + report bytes # --------------------------------------------------------------------------- def test_end_to_end_run_passes_and_exits_zero(tmp_path): out = tmp_path / "out" proc = subprocess.run( [sys.executable, str(ROOT / "qn_check.py"), "--data", str(ROOT / "data"), "--out", str(out)], capture_output=True, text=True) assert proc.returncode == 0, proc.stdout + proc.stderr report = json.loads((out / "qn_report.json").read_text(encoding="utf-8")) assert report["overall_pass"] is True assert report["particles_failed"] == 0 assert report["relations_failed"] == 0 assert report["total_particles"] > 400 # scope statement must be present verbatim (safe-wording discipline) assert "does NOT compute or claim absolute hadron masses" in report["scope_statement"] md = (out / "qn_report.md").read_text(encoding="utf-8") assert "Quantum-Number Consistency Report" in md assert "PASS" in md def test_end_to_end_fails_closed_with_a_broken_shard(tmp_path): """Copy the real data, inject ONE row whose charge != constituent sum, and assert the whole suite exits non-zero and names the broken particle.""" import shutil data = tmp_path / "data" shutil.copytree(ROOT / "data", data) # also need the aux relation CSVs already copied by copytree bad = data / "dataset_INJECTED_BROKEN.csv" header = ",".join(q.SCHEMA) # uds has charge 0; we lie and claim +5 => must fail closed row = ("INJECTED_BAD,uds,5,1/2,+,,0,0,1,-1,0,0,1115.0,0.1,4," "test_injected,X,unit-test-broken") bad.write_text(header + "\n" + row + "\n", encoding="utf-8") out = tmp_path / "out" proc = subprocess.run( [sys.executable, str(ROOT / "qn_check.py"), "--data", str(data), "--out", str(out)], capture_output=True, text=True) assert proc.returncode != 0, "engine should fail closed on a broken row" report = json.loads((out / "qn_report.json").read_text(encoding="utf-8")) assert report["overall_pass"] is False names = {f["pdg_name"] for f in report["quantum_number_failures"]} assert "INJECTED_BAD" in names if __name__ == "__main__": sys.exit(pytest.main([__file__, "-q"]))