247 lines
6.6 KiB
Python
247 lines
6.6 KiB
Python
"""
|
|
Pytest configuration and fixtures for PIF Compiler tests.
|
|
|
|
This file contains shared fixtures and configuration for all tests.
|
|
"""
|
|
|
|
import pytest
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Add src to Python path for imports
|
|
src_path = Path(__file__).parent.parent / "src"
|
|
sys.path.insert(0, str(src_path))
|
|
|
|
|
|
# Sample data fixtures
|
|
@pytest.fixture
|
|
def sample_cas_numbers():
|
|
"""Real CAS numbers for testing common cosmetic ingredients."""
|
|
return {
|
|
"water": "7732-18-5",
|
|
"glycerin": "56-81-5",
|
|
"sodium_hyaluronate": "9067-32-7",
|
|
"niacinamide": "98-92-0",
|
|
"ascorbic_acid": "50-81-7",
|
|
"retinol": "68-26-8",
|
|
"lanolin": "85507-69-3",
|
|
"sodium_chloride": "7647-14-5",
|
|
"propylene_glycol": "57-55-6",
|
|
"butylene_glycol": "107-88-0",
|
|
"salicylic_acid": "69-72-7",
|
|
"tocopherol": "59-02-9",
|
|
"caffeine": "58-08-2",
|
|
"citric_acid": "77-92-9",
|
|
"hyaluronic_acid": "9004-61-9",
|
|
"sodium_hyaluronate_crosspolymer": "63148-62-9",
|
|
"zinc_oxide": "1314-13-2",
|
|
"titanium_dioxide": "13463-67-7",
|
|
"lactic_acid": "50-21-5",
|
|
"lanolin_oil": "8006-54-0",
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_cosing_response():
|
|
"""Sample COSING API response for testing."""
|
|
return {
|
|
"inciName": ["WATER"],
|
|
"casNo": ["7732-18-5"],
|
|
"ecNo": ["231-791-2"],
|
|
"substanceId": ["12345"],
|
|
"itemType": ["Ingredient"],
|
|
"functionName": ["Solvent"],
|
|
"chemicalName": ["Dihydrogen monoxide"],
|
|
"nameOfCommonIngredientsGlossary": ["Water"],
|
|
"sccsOpinion": [],
|
|
"sccsOpinionUrls": [],
|
|
"otherRestrictions": [],
|
|
"identifiedIngredient": [],
|
|
"annexNo": [],
|
|
"otherRegulations": [],
|
|
"refNo": ["REF123"],
|
|
"phEurName": [],
|
|
"innName": []
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_ingredient_data():
|
|
"""Sample ingredient data for Pydantic model testing."""
|
|
return {
|
|
"inci_name": "WATER",
|
|
"cas": "7732-18-5",
|
|
"quantity": 70.0,
|
|
"mol_weight": 18,
|
|
"dap": 0.5,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_pif_data():
|
|
"""Sample PIF data for testing."""
|
|
return {
|
|
"company": "Beauty Corp",
|
|
"product_name": "Face Cream",
|
|
"type": "MOISTURIZER",
|
|
"physical_form": "CREMA",
|
|
"CNCP": 123456,
|
|
"production_company": {
|
|
"prod_company_name": "Manufacturer Inc",
|
|
"prod_vat": 12345678,
|
|
"prod_address": "123 Main St, City, Country"
|
|
},
|
|
"ingredients": [
|
|
{
|
|
"inci_name": "WATER",
|
|
"cas": "7732-18-5",
|
|
"quantity": 70.0,
|
|
"dap": 0.5
|
|
},
|
|
{
|
|
"inci_name": "GLYCERIN",
|
|
"cas": "56-81-5",
|
|
"quantity": 10.0,
|
|
"dap": 0.5
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_echa_substance_response():
|
|
"""Sample ECHA substance search API response for glycerin."""
|
|
return {
|
|
"items": [{
|
|
"substanceIndex": {
|
|
"rmlId": "100.029.181",
|
|
"rmlName": "glycerol",
|
|
"rmlCas": "56-81-5",
|
|
"rmlEc": "200-289-5"
|
|
}
|
|
}]
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_echa_substance_response_water():
|
|
"""Sample ECHA substance search API response for water."""
|
|
return {
|
|
"items": [{
|
|
"substanceIndex": {
|
|
"rmlId": "100.028.902",
|
|
"rmlName": "water",
|
|
"rmlCas": "7732-18-5",
|
|
"rmlEc": "231-791-2"
|
|
}
|
|
}]
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_echa_substance_response_niacinamide():
|
|
"""Sample ECHA substance search API response for niacinamide."""
|
|
return {
|
|
"items": [{
|
|
"substanceIndex": {
|
|
"rmlId": "100.002.530",
|
|
"rmlName": "nicotinamide",
|
|
"rmlCas": "98-92-0",
|
|
"rmlEc": "202-713-4"
|
|
}
|
|
}]
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_echa_dossier_response():
|
|
"""Sample ECHA dossier list API response."""
|
|
return {
|
|
"items": [{
|
|
"assetExternalId": "abc123def456",
|
|
"rootKey": "key123",
|
|
"lastUpdatedDate": "2024-01-15T10:30:00Z"
|
|
}]
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_echa_index_html_full():
|
|
"""Sample ECHA index.html with all toxicology sections."""
|
|
return """
|
|
<html>
|
|
<head><title>ECHA Dossier</title></head>
|
|
<body>
|
|
<div id="id_7_Toxicologicalinformation">
|
|
<a href="tox_summary_001"></a>
|
|
</div>
|
|
<div id="id_72_AcuteToxicity">
|
|
<a href="acute_tox_001"></a>
|
|
</div>
|
|
<div id="id_75_Repeateddosetoxicity">
|
|
<a href="repeated_dose_001"></a>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_echa_index_html_partial():
|
|
"""Sample ECHA index.html with only ToxSummary section."""
|
|
return """
|
|
<html>
|
|
<head><title>ECHA Dossier</title></head>
|
|
<body>
|
|
<div id="id_7_Toxicologicalinformation">
|
|
<a href="tox_summary_001"></a>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_echa_index_html_empty():
|
|
"""Sample ECHA index.html with no toxicology sections."""
|
|
return """
|
|
<html>
|
|
<head><title>ECHA Dossier</title></head>
|
|
<body>
|
|
<p>No toxicology information available</p>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
|
|
# Skip markers
|
|
def pytest_configure(config):
|
|
"""Configure custom markers."""
|
|
config.addinivalue_line(
|
|
"markers", "unit: mark test as a unit test (fast, no external deps)"
|
|
)
|
|
config.addinivalue_line(
|
|
"markers", "integration: mark test as integration test (may use real APIs)"
|
|
)
|
|
config.addinivalue_line(
|
|
"markers", "slow: mark test as slow (skip by default)"
|
|
)
|
|
config.addinivalue_line(
|
|
"markers", "database: mark test as requiring database"
|
|
)
|
|
|
|
|
|
def pytest_collection_modifyitems(config, items):
|
|
"""Modify test collection to skip slow/integration tests by default."""
|
|
skip_slow = pytest.mark.skip(reason="Slow test (use -m slow to run)")
|
|
skip_integration = pytest.mark.skip(reason="Integration test (use -m integration to run)")
|
|
|
|
# Only skip if not explicitly requested
|
|
run_slow = config.getoption("-m") == "slow"
|
|
run_integration = config.getoption("-m") == "integration"
|
|
|
|
for item in items:
|
|
if "slow" in item.keywords and not run_slow:
|
|
item.add_marker(skip_slow)
|
|
if "integration" in item.keywords and not run_integration:
|
|
item.add_marker(skip_integration)
|