File size: 3,905 Bytes
b233cf7 2671b56 b233cf7 2671b56 | 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 | import pytest
from pino.verifier import FragrancePipelineVerifier
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from scripts.ingest_fraterworks_free_formulas import extract_pdf_text, parse_formula_text
def test_classic_accord_evaporation():
# Test formula: 10% Limonene, 20% Linalool, 70% Ethanol base
formula = [
{"cas": "5989-27-5", "weight_fraction": 0.10}, # Limonene
{"cas": "78-70-6", "weight_fraction": 0.20}, # Linalool
{"cas": "64-17-5", "weight_fraction": 0.70} # Ethanol
]
verifier = FragrancePipelineVerifier()
trajectory = verifier.run_sim(formula, duration_seconds=3600)
assert "trajectory" in trajectory
assert len(trajectory["trajectory"]) > 0
# Ensure ethanol depletes significantly faster than linalool
assert trajectory["depletion_rates"]["64-17-5"] > trajectory["depletion_rates"]["78-70-6"]
def test_out_of_applicability_domain_graceful():
# Invalid CAS that PubChem and the registry cannot resolve.
formula = [
{"cas": "0000-00-0", "weight_fraction": 0.50},
{"cas": "64-17-5", "weight_fraction": 0.50},
]
verifier = FragrancePipelineVerifier()
result = verifier.run_sim(formula, duration_seconds=3600)
assert result["status"] == "rejected"
assert result["applicability_errors"]
def test_ifra_rejection():
# Cinnamaldehyde is restricted/prohibited in the embedded IFRA table.
formula = [
{"cas": "104-55-2", "weight_fraction": 0.10},
{"cas": "64-17-5", "weight_fraction": 0.90},
]
verifier = FragrancePipelineVerifier()
result = verifier.run_sim(formula, duration_seconds=3600)
assert result["status"] == "rejected"
assert result["ifra_report"]["passed"] is False
def test_five_component_accord_with_registry():
# Classic 5-component accord in ethanol base via the registry path.
formula = [
{"cas": "64-17-5", "weight_fraction": 0.70},
{"cas": "5989-27-5", "weight_fraction": 0.10},
{"cas": "78-70-6", "weight_fraction": 0.08},
{"cas": "24851-98-7", "weight_fraction": 0.06}, # Hedione via registry
{"cas": "1222-05-5", "weight_fraction": 0.04}, # Galaxolide
{"cas": "6790-58-5", "weight_fraction": 0.02}, # Ambroxan
]
verifier = FragrancePipelineVerifier()
result = verifier.run_sim(formula, duration_seconds=28800, interval_seconds=600)
assert result["status"] == "passed"
assert len(result["trajectory"]) == 49
# Ethanol depletes faster than musks
assert result["depletion_rates"]["64-17-5"] > result["depletion_rates"]["6790-58-5"]
def test_fraterworks_parser_skips_multipage_title_rows():
pdf = Path("data/fraterworks_free_pdfs/F_FABULOUS_242463.pdf")
if not pdf.exists():
pytest.skip("Fraterworks fixture PDF is not present")
parsed = parse_formula_text(extract_pdf_text(pdf))
assert parsed["name"] == "F* Fabulous"
assert parsed["formula_code"] == "242463"
assert parsed["declared_profile"] == "a leather fragrance"
assert parsed["declared_total_parts"] == 1000.0
assert not any(row["name"] == "F* FABULOUS" for row in parsed["raw_formula"])
assert max(row["parts"] for row in parsed["raw_formula"]) == 230.0
def test_fraterworks_parser_handles_title_after_total():
pdf = Path("data/fraterworks_free_pdfs/incense-rori.pdf")
if not pdf.exists():
pytest.skip("Fraterworks fixture PDF is not present")
parsed = parse_formula_text(extract_pdf_text(pdf))
assert parsed["name"] == "Incense Rori"
assert parsed["formula_code"] == "989045"
assert parsed["declared_profile"] == "an oriental woody attar"
assert parsed["declared_total_parts"] == 1000.0
assert parsed["raw_formula"][0]["name"] == "Helvetolide®"
assert not any(row["name"] == "INCENSE RORI" for row in parsed["raw_formula"])
|