Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ on:
paths:
- 'src/**'
- 'tests/**'
release:
types: [published]
workflow_dispatch:

jobs:
Expand All @@ -30,5 +32,30 @@ jobs:
- name: Install test dependencies
run: pip install -e '.[dev]'

- name: Run Tests
run: pytest -v tests
- name: Run unit & integration tests (exclude e2e)
run: pytest -v --ignore=tests/e2e tests

run-e2e:
needs: run-tests
timeout-minutes: 30
runs-on: ubuntu-latest
if: >
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main') ||
(github.event_name == 'release' && github.event.action == 'published')

steps:
- name: Check out the repository
uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.9'
cache: "pip"

- name: Install test dependencies
run: pip install -e '.[dev]'

- name: Run e2e tests
run: pytest -q tests/e2e/test_e2e.py
14 changes: 12 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,20 @@ print(info)

For a detailed API reference, see the [API Reference](api.md) section of the documentation.

## Release Workflow

> [!NOTE]
> This section is intended for maintainers of the `datacollective` library.

## Tests

Run the full test suite:
```bash
pytest -v
```

Note that the e2e tests require a valid `MDC_TEST_API_KEY` and a `MDC_TEST_API_URL` key set in your environment. Pytest will skip the live e2e tests automatically if either is missing.


## Release Workflow

Check out the [Release Workflow](release.md) document for details on how to
publish new versions of the library to PyPI using GitHub Actions.
2 changes: 1 addition & 1 deletion src/datacollective/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import Any

import pandas as pd
from fox_progress_bar import ProgressBar

from datacollective.api_utils import (
ENV_DOWNLOAD_PATH,
Expand All @@ -17,7 +18,6 @@
from datacollective.dataset_loading_scripts.registry import (
load_dataset_from_name_as_dataframe,
)
from fox_progress_bar import ProgressBar


def get_dataset_details(dataset_id: str) -> dict[str, Any]:
Expand Down
Empty file.
43 changes: 43 additions & 0 deletions tests/dataset_loading_scripts/test_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from pathlib import Path

import pytest
from _pytest.monkeypatch import MonkeyPatch

from datacollective.dataset_loading_scripts import registry


def test_load_dataset_routes_to_scripted(
monkeypatch: MonkeyPatch, tmp_path: Path
) -> None:
sentinel = object()

def fake_loader(extract_dir: Path) -> object:
assert extract_dir == tmp_path
return sentinel

monkeypatch.setattr(registry, "_load_scripted", fake_loader)
result = registry.load_dataset_from_name_as_dataframe(
"common voice scripted", tmp_path
)
assert result is sentinel


def test_load_dataset_routes_to_spontaneous(
monkeypatch: MonkeyPatch, tmp_path: Path
) -> None:
sentinel = object()

def fake_loader(extract_dir: Path) -> object:
assert extract_dir == tmp_path
return sentinel

monkeypatch.setattr(registry, "_load_spontaneous", fake_loader)
result = registry.load_dataset_from_name_as_dataframe(
"common voice spontaneous", tmp_path
)
assert result is sentinel


def test_load_dataset_invalid_name(tmp_path: Path) -> None:
with pytest.raises(ValueError):
registry.load_dataset_from_name_as_dataframe("unknown dataset", tmp_path)
Empty file added tests/e2e/__init__.py
Empty file.
72 changes: 72 additions & 0 deletions tests/e2e/test_e2e.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import os
from pathlib import Path

import pandas as pd
import pytest
from _pytest.monkeypatch import MonkeyPatch
from requests import HTTPError

from datacollective import get_dataset_details, load_dataset

MDC_TEST_API_KEY = os.getenv("MDC_TEST_API_KEY")
MDC_TEST_API_URL = os.getenv("MDC_TEST_API_URL")

pytestmark = pytest.mark.skipif(
not (MDC_TEST_API_KEY and MDC_TEST_API_URL),
reason="Set MDC_TEST_API_KEY and MDC_TEST_API_URL to run live API tests.",
)


def _skip_if_rate_limited(exc: Exception) -> None:
"""
Since our backend implements strict rate limiting
there is a chance that our e2e tests might hit it,
so we skip the tests when backend returns HTTP 429 (rate limit)."""
if isinstance(exc, HTTPError) and getattr(exc, "response", None):
if getattr(exc.response, "status_code", None) == 429:
pytest.skip("Skipped due to API rate limiting (HTTP 429)")
raise exc


def test_get_dataset_details_live_api(
monkeypatch: MonkeyPatch,
dataset_id: str = "cmiq2s3q5000fo207k9g6g7ou",
) -> None:
"""NOTE: This test calls a live MDC API endpoint (dev)."""
monkeypatch.setenv("MDC_API_KEY", MDC_TEST_API_KEY)
monkeypatch.setenv("MDC_API_URL", MDC_TEST_API_URL)

try:
details = get_dataset_details(dataset_id)
except Exception as exc:
_skip_if_rate_limited(exc)

assert isinstance(details, dict)
assert details.get("id") == dataset_id
assert isinstance(details.get("name"), str) and details["name"].strip()


def test_load_dataset_live_api(
tmp_path: Path,
monkeypatch: MonkeyPatch,
dataset_id: str = "cmiq2s3q5000fo207k9g6g7ou",
) -> None:
"""NOTE: This test calls a live MDC API endpoint (dev)."""

monkeypatch.setenv("MDC_API_KEY", MDC_TEST_API_KEY)
monkeypatch.setenv("MDC_DOWNLOAD_PATH", str(tmp_path))
monkeypatch.setenv("MDC_API_URL", MDC_TEST_API_URL)

try:
df = load_dataset(
dataset_id,
download_directory=str(tmp_path),
show_progress=False,
overwrite_existing=True,
)
except Exception as exc: # noqa: BLE001
_skip_if_rate_limited(exc)

assert isinstance(df, pd.DataFrame)
assert not df.empty
assert len(df.columns) > 0
35 changes: 35 additions & 0 deletions tests/test_datasets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from pathlib import Path

from _pytest.monkeypatch import MonkeyPatch

from datacollective.datasets import _resolve_download_dir, _strip_archive_suffix


def test_strip_archive_suffix_removes_known_extensions(tmp_path: Path) -> None:
tar_path = tmp_path / "sample.tar.gz"
zip_path = tmp_path / "sample.zip"

assert _strip_archive_suffix(tar_path).name == "sample"
assert _strip_archive_suffix(zip_path).name == "sample"


def test_resolve_download_dir_prefers_argument(
tmp_path: Path, monkeypatch: MonkeyPatch
) -> None:
monkeypatch.delenv("MDC_DOWNLOAD_PATH", raising=False)
custom_dir = tmp_path / "custom"
resolved = _resolve_download_dir(str(custom_dir))

assert resolved == custom_dir
assert custom_dir.exists()


def test_resolve_download_dir_uses_env_default(
tmp_path: Path, monkeypatch: MonkeyPatch
) -> None:
env_dir = tmp_path / "env"
monkeypatch.setenv("MDC_DOWNLOAD_PATH", str(env_dir))
resolved = _resolve_download_dir(None)

assert resolved == env_dir
assert env_dir.exists()
Loading