Skip to content

Commit

Permalink
Merge pull request #31 from godatadriven/add-first-test
Browse files Browse the repository at this point in the history
Adding first test to dbt-bouncer
  • Loading branch information
pgoslatara authored Jul 17, 2024
2 parents 3a1ba46 + 9633564 commit e41f97e
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 14 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ repos:
- id: debug-statements
- id: detect-private-key
- id: end-of-file-fixer
exclude: dbt_project/target/manifest.json
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
Expand Down
14 changes: 11 additions & 3 deletions dbt_bouncer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from dbt_artifacts_parser.parser import parse_manifest

from dbt_bouncer.logger import logger
from dbt_bouncer.runner import runner
from dbt_bouncer.version import version


Expand Down Expand Up @@ -35,15 +36,22 @@ def cli(dbt_artifacts_dir):
for _, v in manifest_obj.nodes.items():
if v.package_name == manifest_obj.metadata.project_name:
if v.resource_type == "model":
project_models.append(v)
project_models.append(v.model_dump())
elif v.resource_type == "test":
project_tests.append(v)
project_tests.append(v.model_dump())

project_sources = []
for _, v in manifest_obj.sources.items():
if v.package_name == manifest_obj.metadata.project_name:
project_sources.append(v)
project_sources.append(v.model_dump())

logger.info(
f"Parsed `{manifest_obj.metadata.project_name}` project, found {len(project_models)} nodes, {len(project_sources)} sources and {len(project_tests)} tests."
)

logger.info("Running tests...")
runner(
models=project_models,
sources=project_sources,
tests=project_tests,
)
40 changes: 40 additions & 0 deletions dbt_bouncer/runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from typing import Dict, List

import pytest


class FixturePlugin(object):
def __init__(self):
self.models_ = None
self.sources_ = None
self.tests_ = None

@pytest.fixture(scope="session")
def models(self):
return self.models_

@pytest.fixture(scope="session")
def sources(self):
return self.sources_

@pytest.fixture(scope="session")
def tests(self):
return self.tests_


def runner(
models: List[Dict[str, str]],
sources: List[Dict[str, str]],
tests: List[Dict[str, str]],
) -> None:
"""
Run pytest using fixtures from artifacts.
"""

# Create a fixture plugin that can be used to inject the manifest into the tests
fixtures = FixturePlugin()
for att in ["models", "sources", "tests"]:
setattr(fixtures, att + "_", locals()[att])

# Run the tests, if one fails then pytest will raise an exception
pytest.main(["dbt_bouncer/tests"], plugins=[fixtures])
13 changes: 13 additions & 0 deletions dbt_bouncer/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
def populated_model_description(models):
"""
Models must have a populated description.
"""

for model in models:
assert (
len(model["description"].strip()) > 4
), f"{model['unique_id']} does not have a populated description."


def test_populated_model_description(models):
populated_model_description(models=models)
8 changes: 8 additions & 0 deletions dbt_project/models/_schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ version: 2

models:
- name: model_1
description: Super basic model that has a description and a test
columns:
- name: id
tests:
- not_null

- name: model_2
description: Super basic model that has a description and a test
columns:
- name: id
tests:
- unique
1 change: 1 addition & 0 deletions dbt_project/models/model_2.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
select 1 as id
Binary file modified dist/dbt-bouncer.pex
Binary file not shown.
20 changes: 10 additions & 10 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ version = "0.0.0"
python = ">=3.8,<3.13"
click = "*"
dbt-artifacts-parser = "^0"
pytest = "*"

[tool.poetry.group.dev.dependencies]
dbt-core="*"
dbt-duckdb="*"
pex = "^2"
pre-commit = "^3"
pytest = "*"
pytest-cov = "*"
pytest-xdist = "*"

Expand Down
82 changes: 82 additions & 0 deletions tests/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from contextlib import nullcontext as does_not_raise

import pytest

from dbt_bouncer.tests.test_models import populated_model_description


@pytest.mark.parametrize(
"models,expectation",
[
(
[
{
"description": "Description that is more than 4 characters.",
"unique_id": "model.package_name.model_1",
}
],
does_not_raise(),
),
(
[
{
"description": """A
multiline
description
""",
"unique_id": "model.package_name.model_2",
}
],
does_not_raise(),
),
(
[
{
"description": "",
"unique_id": "model.package_name.model_3",
}
],
pytest.raises(AssertionError),
),
(
[
{
"description": " ",
"unique_id": "model.package_name.model_4",
}
],
pytest.raises(AssertionError),
),
(
[
{
"description": """
""",
"unique_id": "model.package_name.model_5",
}
],
pytest.raises(AssertionError),
),
(
[
{
"description": "-",
"unique_id": "model.package_name.model_6",
}
],
pytest.raises(AssertionError),
),
(
[
{
"description": "null",
"unique_id": "model.package_name.model_7",
}
],
pytest.raises(AssertionError),
),
],
)
def test_populated_model_description(models, expectation):
with expectation:
populated_model_description(models=models)

0 comments on commit e41f97e

Please sign in to comment.