cosmoguard-bd/tests/conftest.py

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)