# PIF Compiler - Test Suite

## Overview

Comprehensive test suite for the PIF Compiler project using `pytest`.

## Structure

```
tests/
├── __init__.py              # Test package marker
├── conftest.py              # Shared fixtures and configuration
├── test_cosing_service.py   # COSING service tests
├── test_models.py           # (TODO) Pydantic model tests
├── test_echa_service.py     # (TODO) ECHA service tests
└── README.md                # This file
```

## Installation

```bash
# Install test dependencies
uv add --dev pytest pytest-cov pytest-mock

# Or manually install
uv pip install pytest pytest-cov pytest-mock
```

## Running Tests

### Run All Tests (Unit only)
```bash
uv run pytest
```

### Run Specific Test File
```bash
uv run pytest tests/test_cosing_service.py
```

### Run Specific Test Class
```bash
uv run pytest tests/test_cosing_service.py::TestParseCasNumbers
```

### Run Specific Test
```bash
uv run pytest tests/test_cosing_service.py::TestParseCasNumbers::test_single_cas_number
```

### Run with Verbose Output
```bash
uv run pytest -v
```

### Run with Coverage Report
```bash
uv run pytest --cov=src/pif_compiler --cov-report=html
# Open htmlcov/index.html in browser
```

## Test Categories

### Unit Tests (Default)
Fast tests with no external dependencies. Run by default.

```bash
uv run pytest -m unit
```

### Integration Tests
Tests that hit real APIs or databases. Skipped by default.

```bash
uv run pytest -m integration
```

### Slow Tests
Tests that take longer to run. Skipped by default.

```bash
uv run pytest -m slow
```

### Database Tests
Tests requiring MongoDB. Ensure Docker is running.

```bash
cd utils
docker-compose up -d
uv run pytest -m database
```

## Test Organization

### `test_cosing_service.py`

**Coverage:**
- ✅ `parse_cas_numbers()` - CAS parsing logic
  - Single/multiple CAS
  - Different separators (/, ;, ,, --)
  - Parentheses removal
  - Whitespace handling
  - Invalid dash removal

- ✅ `cosing_search()` - API search
  - Search by name
  - Search by CAS
  - Search by EC number
  - Search by ID
  - No results handling
  - Invalid mode error

- ✅ `clean_cosing()` - JSON cleaning
  - Basic field cleaning
  - Empty tag removal
  - CAS parsing
  - URL creation
  - Field renaming

- ✅ Integration tests (marked as `@pytest.mark.integration`)
  - Real API calls (requires internet)

## Writing New Tests

### Example Unit Test

```python
class TestMyFunction:
    """Test my_function."""

    def test_basic_case(self):
        """Test basic functionality."""
        result = my_function("input")
        assert result == "expected"

    def test_edge_case(self):
        """Test edge case handling."""
        with pytest.raises(ValueError):
            my_function("invalid")
```

### Example Mock Test

```python
from unittest.mock import Mock, patch

@patch('module.external_api_call')
def test_with_mock(mock_api):
    """Test with mocked external call."""
    mock_api.return_value = {"data": "mocked"}
    result = my_function()
    assert result == "expected"
    mock_api.assert_called_once()
```

### Example Fixture Usage

```python
def test_with_fixture(sample_cosing_response):
    """Test using a fixture from conftest.py."""
    result = clean_cosing(sample_cosing_response)
    assert "cosingUrl" in result
```

## Best Practices

1. **Naming**: Test files/classes/functions start with `test_`
2. **Arrange-Act-Assert**: Structure tests clearly
3. **One assertion focus**: Each test should test one thing
4. **Use fixtures**: Reuse test data via `conftest.py`
5. **Mock external calls**: Don't hit real APIs in unit tests
6. **Mark appropriately**: Use `@pytest.mark.integration` for slow tests
7. **Descriptive names**: Test names should describe what they test

## Common Commands

```bash
# Run fast tests only (skip integration/slow)
uv run pytest -m "not integration and not slow"

# Run only integration tests
uv run pytest -m integration

# Run with detailed output
uv run pytest -vv

# Stop at first failure
uv run pytest -x

# Run last failed tests
uv run pytest --lf

# Run tests matching pattern
uv run pytest -k "test_parse"

# Generate coverage report
uv run pytest --cov=src/pif_compiler --cov-report=term-missing
```

## CI/CD Integration

For GitHub Actions (example):

```yaml
- name: Run tests
  run: |
    uv run pytest -m "not integration" --cov --cov-report=xml
```

## TODO

- [ ] Add tests for `models.py` (Pydantic validation)
- [ ] Add tests for `echa_service.py`
- [ ] Add tests for `echa_parser.py`
- [ ] Add tests for `echa_extractor.py`
- [ ] Add tests for `database_service.py`
- [ ] Add tests for `pubchem_service.py`
- [ ] Add integration tests with test database
- [ ] Set up GitHub Actions CI
