File size: 3,869 Bytes
9ae68e3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | # test_semantics.py
import copy
import unittest
from semantics import validate_dataset, ptm_parity
def fixture():
# Explicitly synthetic: excluded from scientific conclusions.
return {
"dataset_id": "synthetic-validator-fixture",
"synthetic": True,
"observations": [{
"observation_id": "fixture-o1",
"sample_id": "fixture-s1",
"gene_id": "fixture-g1",
"condition": "control",
"timepoint": None,
"replicate": None,
"batch": None,
"cell_type": None,
"count": "0",
"missing_reason": None,
"exposure": 1.0,
"normalized_expression": None,
"normalization": None,
"measurement_quality": None,
}],
}
class SemanticsTests(unittest.TestCase):
conditions = ["control", "comparison"]
def test_zero_and_missing_remain_distinct_without_mutation(self):
data = fixture()
missing = copy.deepcopy(data["observations"][0])
missing.update(
observation_id="fixture-o2",
count=None,
missing_reason="not_measured",
)
data["observations"].append(missing)
before = copy.deepcopy(data)
validate_dataset(data, self.conditions)
self.assertEqual(data, before)
self.assertEqual(data["observations"][0]["count"], "0")
self.assertIsNone(data["observations"][1]["count"])
def test_bad_counts_are_rejected(self):
for count in [0, True, "-1", "1.5", "1e3", "01", " 1", "2147483648"]:
with self.subTest(count=count):
data = fixture()
data["observations"][0]["count"] = count
with self.assertRaises(ValueError):
validate_dataset(data, self.conditions)
def test_invalid_states_are_rejected(self):
patches = [
{"count": None}, # no missing reason
{"missing_reason": "not_measured"}, # count still present
{"exposure": 0},
{"exposure": float("inf")},
{"exposure": True},
{"condition": "undeclared"},
{"normalized_expression": 1.2}, # no provenance
{"measurement_quality": float("nan")},
]
for patch in patches:
with self.subTest(patch=patch):
data = fixture()
data["observations"][0].update(patch)
with self.assertRaises(ValueError):
validate_dataset(data, self.conditions)
def test_duplicate_identity_and_condition_config(self):
data = fixture()
data["observations"].append(copy.deepcopy(data["observations"][0]))
with self.assertRaises(ValueError):
validate_dataset(data, self.conditions)
with self.assertRaises(ValueError):
validate_dataset(fixture(), ["control", "control"])
def test_normalized_expression_requires_declared_sources(self):
data = fixture()
data["observations"][0].update(
normalized_expression=2.5,
normalization={
"method": "synthetic-test-transform",
"source_ids": ["synthetic-source-artifact"],
},
)
validate_dataset(data, self.conditions)
def test_ptm_outputs_and_resource_bound(self):
for bits, output in [("", "0"), ("0", "0"), ("1", "1"), ("1011", "1")]:
with self.subTest(bits=bits):
result = ptm_parity(bits)
self.assertEqual(result["output"], output)
self.assertEqual(result["transitions"], len(bits) + 1)
self.assertEqual(result["total_tape_cells"], len(bits) + 1)
self.assertEqual(ptm_parity("102")["status"], "rejected")
if __name__ == "__main__":
unittest.main()
|