# Tests Overview

ETLPlus organizes tests by **scope** and labels cross-cutting test **intent**
with pytest markers.

- [Tests Overview](#tests-overview)
  - [Scope Markers / Directory Layout](#scope-markers--directory-layout)
  - [Intent Markers](#intent-markers)
  - [Integration File Smoke Pattern](#integration-file-smoke-pattern)
  - [Discovery and Selection](#discovery-and-selection)
  - [Dependency Prerequisites](#dependency-prerequisites)
  - [Cloud CLI Integration](#cloud-cli-integration)
  - [Common Commands](#common-commands)
  - [Post-Move Validation Checklist](#post-move-validation-checklist)

## Scope Markers / Directory Layout

| Marker | Path | Scope | Meaning |
| --- | --- | --- | --- |
| `meta` | `tests/meta/` | Repository guardrails and compatibility policy | Fast checks for layout, docs, markers, and stable public surface |
| `unit` | `tests/unit/` | Isolated function/class behavior | Fast, deterministic, minimal external I/O |
| `integration` | `tests/integration/` | Cross-module behavior and boundary wiring | May use temp files and fakes/mocks |
| `e2e` | `tests/e2e/` | Full workflow/system-boundary behavior | Slowest, broadest confidence checks |

File-format integration smoke conventions are documented in
[`tests/integration/file/README.md`](integration/file/README.md).

## Intent Markers

Intent markers are orthogonal to scope. A test can be both `integration` and
`smoke`, for example.

| Marker | Meaning |
| --- | --- |
| `smoke` | Go/no-go viability checks (broad and shallow) |
| `contract` | Compatibility checks for stable interfaces and metadata |
| `acceptance` | User-facing behavior checks |
| `data_quality` | Output semantic invariants |
| `resilience` | Failure/retry/recovery behavior |
| `perf` | Throughput/latency performance checks |

See `pytest.ini` for the complete marker registry.

## Integration File Smoke Pattern

Integration file smoke tests default to
`SmokeRoundtripModuleContract` with convention-based paths:

- `data.<format>`

Structural exceptions are intentionally limited to:

- `test_i_file_gz.py` (`file_name = "data.json.gz"`)
- `test_i_file_zip.py` (`file_name = "data.json.zip"`)
- `test_i_file_xls.py` (`expect_write_error` / `error_match` for read-only write)

## Discovery and Selection

Default test discovery is controlled by `pytest.ini`:

- `testpaths = tests/meta tests/e2e tests/integration tests/unit`

## Dependency Prerequisites

The default install already includes the dependencies used by the built-in file handlers covered by
the default test matrix.

For full coverage of the remaining optional scientific and specialty formats (local CI parity), install:

```bash
pip install -e ".[dev,file]"
```

For lighter development without the remaining optional format coverage:

```bash
pip install -e ".[dev]"
```

## Cloud CLI Integration

The CLI integration suite includes two layers of remote-storage coverage:

- default in-memory remote harness for deterministic local smoke coverage
- env-gated real cloud tests for stricter S3/Azure Blob realism

The real cloud layer is skipped unless these environment variables are set:

- `ETLPLUS_TEST_S3_URI`
- `ETLPLUS_TEST_AZURE_BLOB_URI`

Safe example placeholder values:

```bash
export ETLPLUS_TEST_S3_URI="s3://my-etlplus-integration-bucket/cli"
export ETLPLUS_TEST_AZURE_BLOB_URI="azure-blob://etlplus-integration/cli"
```

These values may expose internal bucket/container names, so do not commit real values into the
repository. Prefer setting them in one of these places:

- Local shell startup files or one-off terminal exports
- `.envrc` or another developer-local environment loader
- Local IDE (e.g., VS Code) test/debug environment settings
- CI secret stores and workflow environment configuration

The variables should contain only base URIs. Authentication should come from the normal cloud SDK
credential chain already configured in the environment.

Useful commands:

```bash
# Full CLI integration suite (real cloud tests auto-skip if env vars are unset)
pytest tests/integration/cli

# Focus on the real-cloud read/write CLI paths
pytest tests/integration/cli/test_i_cli_extract.py \
  tests/integration/cli/test_i_cli_load.py \
  tests/integration/cli/test_i_cli_run.py \
  tests/integration/cli/test_i_cli_transform.py \
  tests/integration/cli/test_i_cli_validate.py
```

## Common Commands

```bash
# Full dependency install + default non-perf test run (recommended for CI parity)
make test-full

# Default discovery from pytest.ini (unit + integration + e2e, excluding perf)
make test

# Scope folders
pytest tests/meta
pytest tests/unit
pytest tests/integration
pytest tests/e2e

# Marker-based selection by scope (within configured testpaths)
pytest -m meta
pytest -m unit
pytest -m integration
pytest -m e2e

# Marker-based selection by intent
pytest -m smoke
pytest -m contract

# Perf smoke coverage (kept out of the default CI-parity suite)
make test-perf
pytest -m perf
```

## Post-Move Validation Checklist

Use these checks after moving/renaming test modules or changing test scope
directories.

```bash
# 1) Fast import/collection sanity checks
python -m pytest --collect-only -q tests/meta tests/unit tests/integration tests/e2e

# 2) Scope-layout guardrails (legacy paths, filename patterns, marker coverage)
python -m pytest -q \
  tests/meta/test_m_test_layout.py \
  tests/meta/test_m_test_filenames.py \
  tests/meta/test_m_marker_coverage.py \
  tests/meta/test_m_integration_file_conventions.py \
  tests/meta/test_m_contract_readme.py

# 3) Scope-focused smoke run (catches broken imports quickly)
python -m pytest -q tests/meta tests/unit tests/integration

# 4) Conftest scope-marker grep checks
rg -n "pytest\\.mark\\.meta" tests/meta/conftest.py
rg -n "pytest\\.mark\\.unit" tests/unit/**/conftest.py tests/unit/conftest.py
rg -n "pytest\\.mark\\.integration" tests/integration/**/conftest.py tests/integration/conftest.py
rg -n "pytest\\.mark\\.smoke" tests/integration/file/conftest.py

# 5) Filename hygiene checks (spaces / duplicate-copy suffixes)
find tests -type f -name '*.py' | awk '/ / {print}'
rg --files tests | rg " 2\\.py$| 3\\.py$"
```
