| 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(): |
| |
| formula = [ |
| {"cas": "5989-27-5", "weight_fraction": 0.10}, |
| {"cas": "78-70-6", "weight_fraction": 0.20}, |
| {"cas": "64-17-5", "weight_fraction": 0.70} |
| ] |
|
|
| verifier = FragrancePipelineVerifier() |
| trajectory = verifier.run_sim(formula, duration_seconds=3600) |
|
|
| assert "trajectory" in trajectory |
| assert len(trajectory["trajectory"]) > 0 |
| |
| assert trajectory["depletion_rates"]["64-17-5"] > trajectory["depletion_rates"]["78-70-6"] |
|
|
|
|
| def test_out_of_applicability_domain_graceful(): |
| |
| 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(): |
| |
| 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(): |
| |
| 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}, |
| {"cas": "1222-05-5", "weight_fraction": 0.04}, |
| {"cas": "6790-58-5", "weight_fraction": 0.02}, |
| ] |
| verifier = FragrancePipelineVerifier() |
| result = verifier.run_sim(formula, duration_seconds=28800, interval_seconds=600) |
| assert result["status"] == "passed" |
| assert len(result["trajectory"]) == 49 |
| |
| 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"]) |
|
|