from importlib.util import module_from_spec, spec_from_file_location from pathlib import Path from pino.registry import AromaRegistry PATH = Path(__file__).parents[1] / "scripts" / "build_genre_challenge_v2.py" SPEC = spec_from_file_location("build_genre_challenge_v2", PATH) MODULE = module_from_spec(SPEC) assert SPEC.loader SPEC.loader.exec_module(MODULE) def record(genre, components): return {"genre": genre, "formula": components} def component(cas, smiles=None, weight=0.5): if smiles is None: smiles = {"a": "CC", "b": "CCC", "c": "CCCC", "d": "CCCCC", "e": "CCCCCC", "f": "CCCCCCC", "g": "CCCCCCCC"}.get(cas, "CCO") return {"cas": cas, "smiles": smiles, "weight_fraction": weight} def test_characterize_excludes_solvent_and_requires_valid_single_fragment_smiles(): row = record("fougere", [component("64-17-5", weight=0.2), component("a"), component("b")]) result = MODULE.characterize(row, 7) assert result["active_count"] == 2 assert result["structurally_valid"] is True assert result["source_index"] == 7 invalid = MODULE.characterize(record("fougere", [component("a", "not-smiles"), component("b")]), 8) assert invalid["structurally_valid"] is False def test_composition_match_enforces_size_concentration_and_identity_calipers(): base = MODULE.characterize(record("fougere", [component("a"), component("b")]), 0) matched = MODULE.characterize(record("floral_woody", [component("c"), component("d")]), 1) overlap = MODULE.characterize(record("floral_woody", [component("a"), component("d")]), 2) singleton = MODULE.characterize(record("floral_woody", [component("c", weight=1.0)]), 3) assert MODULE.composition_match(base, matched) assert not MODULE.composition_match(base, overlap) assert not MODULE.composition_match(base, singleton) def test_census_keeps_wildcard_out_of_primary_pool(): rows = [ record("fougere", [component("a"), component("b")]), record("floral_woody", [component("c"), component("d")]), record("wildcard", [component("e"), component("f")]), record("wildcard", [component("g", weight=1.0)]), ] result = MODULE.census(rows) assert result["per_substantive_genre"]["fougere"]["composition_matchable"] == 1 assert result["wildcard_open_set_pool"] == { "multi_component_structurally_valid": 1, "singleton_sanity_check": 1, } def test_characterize_resolves_cas_smiles_keys_expands_naturals_and_aggregates(tmp_path): registry = AromaRegistry(tmp_path / "registry.db") registry.add({"cas": "5989-27-5", "name": "limonene", "smiles": "CC1=CCC(CC1)C(=C)C"}) registry.add({"cas": "123-35-3", "name": "myrcene", "smiles": "CC(=CCCC(=C)C=C)C"}) row = record("citrus_cologne", [ component("NATURAL:8008-57-9", smiles="", weight=0.5), component("SMILES:CCO", smiles="", weight=0.25), component("5989-27-5", smiles="", weight=0.25), ]) result = MODULE.characterize(row, 0, registry) registry.close() assert result["structurally_valid"] is True assert result["active_count"] == 3 assert result["unresolved_identifiers"] == [] assert len(result["ingredients"]) == 3 def test_characterize_reports_unresolved_identifier(tmp_path): with AromaRegistry(tmp_path / "registry.db") as registry: result = MODULE.characterize(record("fougere", [component("unknown", smiles="")]), 0, registry) assert result["structurally_valid"] is False assert result["unresolved_identifiers"] == ["unknown"] def test_duplicate_collapse_is_order_independent_and_conservative(): rows = [] for index, weights in enumerate(((.5, .5), (.51, .49), (.8, .2))): result = MODULE.characterize(record("fougere", [ component("a", weight=weights[0]), component("b", weight=weights[1])]), index) result["resolved_components"], _ = MODULE.resolved_active_components(record("fougere", [ component("b", weight=weights[1]), component("a", weight=weights[0])])) rows.append(result) kept, audit = MODULE.collapse_duplicates(rows) assert [row["source_index"] for row in kept] == [0, 2] assert audit["collapsed_records"] == 1 def test_natural_weight_share_uses_pre_expansion_formula_weight(): row = record("citrus_cologne", [ component("NATURAL:8008-57-9", smiles="", weight=.25), component("SMILES:CCO", smiles="", weight=.75), ]) assert MODULE.natural_weight_share(row) == .25