Skip to content

Commit

Permalink
Setup Coverage Report & Test config.py (#31)
Browse files Browse the repository at this point in the history
This is based on #29 

Adds tests to reach above 80% coverage and workflow that fails if its
below.
  • Loading branch information
bh2smith authored Oct 26, 2024
1 parent 7075728 commit 7a47e72
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,16 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
python-version: '3.13'
- name: Install Requirements
run: pip install -r requirements/dev.txt
- name: Tests
run: python -m pytest tests/e2e_test.py
- name: Run tests with coverage
run: pytest --cov=src --cov-report=xml --cov-fail-under=80
# Environment variables used by the `pg_client.py`
env:
DB_URL: postgresql://postgres:postgres@localhost:5432/postgres
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage.xml
6 changes: 5 additions & 1 deletion .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
python-version: [ "3.12", "3.13" ]
# Mark Python 3.13 as allowed to fail
include:
- python-version: "3.13"
continue-on-error: true

steps:
- uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
__pycache__/
.pytest_cache/
.mypy_cache
.coverage
1 change: 1 addition & 0 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ pandas-stubs
sqlalchemy-stubs
pre-commit
pytest
pytest-cov
4 changes: 2 additions & 2 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ def load(cls) -> Env:
db_url = os.environ.get("DB_URL")

if dune_api_key is None:
raise EnvironmentError("DUNE_API_KEY environment variable must be set!")
raise RuntimeError("DUNE_API_KEY environment variable must be set!")
if db_url is None:
raise EnvironmentError("DB_URL environment variable must be set!")
raise RuntimeError("DB_URL environment variable must be set!")

return cls(db_url, dune_api_key)

Expand Down
96 changes: 96 additions & 0 deletions tests/unit/config_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import os
import unittest
from unittest.mock import patch, mock_open
from src.config import Env, RuntimeConfig


class TestEnv(unittest.TestCase):
@patch(
"src.config.load_dotenv"
) # Mock load_dotenv to prevent loading the actual .env file
@patch.dict(
os.environ, {"DUNE_API_KEY": "test_key", "DB_URL": "postgres://localhost/test"}
)
def test_load_env_success(self, mock_load_dotenv):
env = Env.load()
self.assertEqual(env.dune_api_key, "test_key")
self.assertEqual(env.db_url, "postgres://localhost/test")

@patch(
"src.config.load_dotenv"
) # Mock load_dotenv to prevent loading the actual .env file
@patch.dict(os.environ, {}, clear=True)
def test_load_env_missing_dune_api_key(self, mock_load_dotenv):
with self.assertRaises(RuntimeError) as context:
Env.load()
self.assertEqual(
str(context.exception), "DUNE_API_KEY environment variable must be set!"
)

@patch(
"src.config.load_dotenv"
) # Mock load_dotenv to prevent loading the actual .env file
@patch.dict(os.environ, {"DUNE_API_KEY": "test_key"}, clear=True)
def test_load_env_missing_db_url(self, mock_load_dotenv):
with self.assertRaises(RuntimeError) as context:
Env.load()
self.assertEqual(
str(context.exception), "DB_URL environment variable must be set!"
)


class TestRuntimeConfig(unittest.TestCase):
@patch(
"builtins.open",
new_callable=mock_open,
read_data=b"""
[[jobs]]
query_id = 123
table_name = "test_table"
poll_frequency = 5
query_engine = "medium"
""",
)
def test_load_from_toml_success(self, mock_file):
config = RuntimeConfig.load_from_toml("config.toml")
self.assertEqual(len(config.jobs), 1)
job = config.jobs[0]
self.assertEqual(job.query_id, 123)
self.assertEqual(job.table_name, "test_table")
self.assertEqual(job.poll_frequency, 5)
self.assertEqual(job.query_engine, "medium")

@patch(
"builtins.open",
new_callable=mock_open,
read_data=b"""
[[jobs]]
query_id = 123
table_name = "test_table"
poll_frequency = 5
query_engine = "invalid"
""",
)
def test_load_from_toml_invalid_query_engine(self, mock_file):
with self.assertRaises(ValueError) as context:
RuntimeConfig.load_from_toml("config.toml")
self.assertEqual(
str(context.exception), "query_engine must be either 'medium' or 'large'."
)

@patch(
"builtins.open",
new_callable=mock_open,
read_data=b"""
[[jobs]]
query_id = 123
""",
)
def test_load_from_toml_missing_values(self, mock_file):
config = RuntimeConfig.load_from_toml("config.toml")
self.assertEqual(len(config.jobs), 1)
job = config.jobs[0]
self.assertEqual(job.query_id, 123)
self.assertEqual(job.table_name, "dune_data_123") # Default table name
self.assertEqual(job.poll_frequency, 1) # Default poll frequency
self.assertEqual(job.query_engine, "medium") # Default query engine

0 comments on commit 7a47e72

Please sign in to comment.