Skip to content

Commit 79c53de

Browse files
Merge pull request #425 from vladistan/convert-test-ruleutils-to-pytest
Convert test ruleutils to pytest
2 parents 70d66e9 + 00038e9 commit 79c53de

File tree

2 files changed

+114
-37
lines changed

2 files changed

+114
-37
lines changed

tests/test_utils/test_ruleutils.py

Lines changed: 111 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,116 @@
1-
import os
2-
import unittest
1+
from pathlib import Path
2+
3+
import pytest
34

4-
from linkml_runtime.dumpers import yaml_dumper
5-
from linkml_runtime.loaders.yaml_loader import YAMLLoader
65
from linkml_runtime.utils.ruleutils import get_range_as_disjunction, subclass_to_rules
76
from linkml_runtime.utils.schemaview import SchemaView
87
from tests.test_utils import INPUT_DIR
98

10-
SCHEMA = os.path.join(INPUT_DIR, "rules-example.yaml")
11-
12-
yaml_loader = YAMLLoader()
13-
14-
15-
class RuleUtilsTestCase(unittest.TestCase):
16-
def test_disjunction(self):
17-
# no import schema
18-
view = SchemaView(SCHEMA)
19-
analyte = view.induced_slot("analyte", "Sample")
20-
# print(analyte)
21-
# print(analyte.any_of)
22-
disj = get_range_as_disjunction(analyte)
23-
# print(disj)
24-
self.assertCountEqual(disj, {"MissingValueEnum", "AnalyteEnum"})
25-
for s in view.all_slots().values():
26-
disj = get_range_as_disjunction(s)
27-
print(f"{s.name} DISJ: {disj}")
28-
29-
def test_roll_up(self):
30-
# no import schema
31-
view = SchemaView(SCHEMA)
32-
c = view.get_class("ProteinCodingGene")
33-
rules = subclass_to_rules(view, "ProteinCodingGene", "SeqFeature")
34-
rule = rules[0]
35-
print(f"IF: {rule.preconditions}")
36-
print(f"THEN: {rule.postconditions}")
37-
print(yaml_dumper.dumps(rule))
38-
39-
40-
if __name__ == "__main__":
41-
unittest.main()
9+
10+
@pytest.fixture
11+
def rules_schema():
12+
"""SchemaView loaded with rules example schema."""
13+
return SchemaView(Path(INPUT_DIR) / "rules-example.yaml")
14+
15+
16+
def test_disjunction_analyte_slot(rules_schema):
17+
"""Test get_range_as_disjunction for analyte slot with any_of."""
18+
analyte = rules_schema.induced_slot("analyte", "Sample")
19+
disj = get_range_as_disjunction(analyte)
20+
assert disj == {"MissingValueEnum", "AnalyteEnum"}
21+
22+
23+
@pytest.mark.parametrize(
24+
"slot_name,expected_disjunction",
25+
[
26+
("vital_status", {"MissingValueEnum", "VitalStatusEnum"}), # any_of with two enums
27+
("primary_address", {"Address"}), # explicit range to class
28+
("age", {"int"}), # int range
29+
("encodes", {"SeqFeature"}), # class range
30+
("id", None), # no explicit range specified
31+
("name", None), # no explicit range specified
32+
("telephone", None), # no explicit range specified
33+
],
34+
)
35+
def test_disjunction_various_slots(rules_schema, slot_name, expected_disjunction):
36+
"""Test get_range_as_disjunction for various slot types."""
37+
slot = rules_schema.get_slot(slot_name)
38+
disj = get_range_as_disjunction(slot)
39+
assert disj == expected_disjunction
40+
41+
42+
def test_roll_up_protein_coding_gene(rules_schema):
43+
"""Test subclass_to_rules for ProteinCodingGene to SeqFeature."""
44+
rules = subclass_to_rules(rules_schema, "ProteinCodingGene", "SeqFeature")
45+
46+
# Should generate one rule
47+
assert len(rules) == 1
48+
49+
rule = rules[0]
50+
51+
# Check preconditions - uses type designator slot
52+
assert rule.preconditions is not None
53+
assert rule.preconditions.slot_conditions is not None
54+
assert "type" in rule.preconditions.slot_conditions
55+
56+
# Check the type slot condition
57+
type_condition = rule.preconditions.slot_conditions["type"]
58+
assert type_condition.equals_string == "ProteinCodingGene"
59+
60+
# Check postconditions - should have slot assignments
61+
assert rule.postconditions is not None
62+
assert rule.postconditions.slot_conditions is not None
63+
# Should include slots from the class
64+
assert "encodes" in rule.postconditions.slot_conditions
65+
assert "id" in rule.postconditions.slot_conditions
66+
67+
68+
def test_roll_up_noncoding_gene(rules_schema):
69+
"""Test subclass_to_rules for NoncodingGene to SeqFeature."""
70+
rules = subclass_to_rules(rules_schema, "NoncodingGene", "SeqFeature")
71+
72+
# Should generate one rule
73+
assert len(rules) == 1
74+
75+
rule = rules[0]
76+
77+
# Check preconditions - uses type designator slot
78+
assert rule.preconditions is not None
79+
assert rule.preconditions.slot_conditions is not None
80+
assert "type" in rule.preconditions.slot_conditions
81+
82+
# Check the type slot condition
83+
type_condition = rule.preconditions.slot_conditions["type"]
84+
assert type_condition.equals_string == "NoncodingGene"
85+
86+
# Check postconditions - should have slot assignments
87+
assert rule.postconditions is not None
88+
assert rule.postconditions.slot_conditions is not None
89+
assert "encodes" in rule.postconditions.slot_conditions
90+
assert "id" in rule.postconditions.slot_conditions
91+
92+
93+
def test_roll_up_genomic_sample(rules_schema):
94+
"""Test subclass_to_rules for GenomicSample to Sample."""
95+
rules = subclass_to_rules(rules_schema, "GenomicSample", "Sample")
96+
97+
# Should generate one rule
98+
assert len(rules) == 1
99+
100+
rule = rules[0]
101+
102+
# Check preconditions - uses type designator slot
103+
assert rule.preconditions is not None
104+
assert rule.preconditions.slot_conditions is not None
105+
assert "type" in rule.preconditions.slot_conditions
106+
107+
# Check the type slot condition
108+
type_condition = rule.preconditions.slot_conditions["type"]
109+
assert type_condition.equals_string == "GenomicSample"
110+
111+
# Check postconditions - should have slot assignments
112+
assert rule.postconditions is not None
113+
assert rule.postconditions.slot_conditions is not None
114+
# Should include slots from the class
115+
assert "id" in rule.postconditions.slot_conditions
116+
assert "analyte" in rule.postconditions.slot_conditions

tests/test_utils/test_walker_utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
from jsonasobj2 import as_dict
55

66
from linkml_runtime.linkml_model import ClassDefinition, SchemaDefinition
7+
from linkml_runtime.loaders.yaml_loader import YAMLLoader
78
from linkml_runtime.utils.walker_utils import traverse_object_tree
89
from linkml_runtime.utils.yamlutils import YAMLRoot
910
from tests.test_utils import INPUT_DIR
10-
from tests.test_utils.test_ruleutils import yaml_loader
1111

1212
SCHEMA = os.path.join(INPUT_DIR, "kitchen_sink_noimports.yaml")
1313
INSERTED_COMMENT = "INSERTED COMMENT"
1414

15+
yaml_loader = YAMLLoader()
16+
1517

1618
def count_classes(obj: YAMLRoot) -> int:
1719
"""

0 commit comments

Comments
 (0)