Skip to content

Commit a34267f

Browse files
authored
Create RuntimeConfig fixture (#10242)
* Create `runtime_config` fixture and necessary upstream fixtures * Check for better scoped `ProjectContractError` in test_runtime tests Previously in `test_unsupported_version_extra_config` and `test_archive_not_allowed` we were checking for `DbtProjectError`. This worked because `ProjectContractError` is a subclass of `DbtProjectError`. However, if we check for `DbtProjectError` in these tests than, some tangential failure which raises a `DbtProejctError` type error would go undetected. As we plan on modifying these tests to be pytest in the coming commits, we want to ensure that the tests are succeeding for the right reason. * Convert `test_str` of `TestRuntimeConfig` to a pytest test using fixtures * Convert `test_from_parts` of `TestRuntimeConfig` to a pytest test using fixtures While converting `test_from_parts` I noticed the comment > TODO(jeb): Adapters must assert that quoting is populated? This led me to beleive that `quoting` shouldn't be "fully" realized in our project fixture unless we're saying that it's gone through adapter instantiation. Thus I update the `quoting` on our project fixture to be an empty dict. This change affected `test__str__` in `test_project.py` which we thus needed to update accordingly. * Convert runtime version specifier tests to pytest tests and move to test_project We've done two things in this commit, which arguably _should_ have been done in two commits. First we moved the version specifier tests from `test_runtime.py::TestRuntimeConfig` to `test_project.py::TestGetRequiredVersion` this is because what is really being tested is the `_get_required_version` method. Doing it via `RuntimeConfig.from_parts` method made actually testing it a lot harder as it requires setting up more of the world and running with a _full_ project config dict. The second thing we did was convert it from the old unittest implementation to a pytest implementation. This saves us from having to create most of the world as we were doing previously in these tests. Of note, I did not move the test `test_unsupported_version_range_bad_config`. This test is a bit different from the rest of the version specifier tests. It was introduced in [1eb5857](1eb5857) of [#2726](#2726) to resolve [#2638](#2638). The focus of #2726 was to ensure the version specifier checks were run _before_ the validation of the `dbt_project.yml`. Thus what this test is actually testing for is order of operations at parse time. As such, this is really more a _functional_ test than a unit test. In the next commit we'll get this test moved (and renamed) * Create a better test for checking that version checks come before project schema validation * Convert `test_get_metadata` to pytest test * Refactor `test_archive_not_allowed` to functional test We do already have tests that ensure "extra" keys aren't allowed in the dbt_project.yaml. This test is different because it's checking that a specific key, `archive`, isn't allowed. We do this because at one point in time `archive` _was_ an allowed key. Specifically, we stopped allowing `archive` in dbt-core 0.15.0 via commit [f26948d](f26948d). Given that it's been 5 years and a major version, we could probably remove this test, but let's keep it around unless we start supporting `archive` again. * Convert `warn_for_unused_resource_config_paths` tests to use pytest
1 parent 1554828 commit a34267f

File tree

6 files changed

+229
-204
lines changed

6 files changed

+229
-204
lines changed

tests/functional/basic/test_project.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import pytest
55
import yaml
66

7-
from dbt.exceptions import ProjectContractError
7+
from dbt.cli.main import dbtRunner
8+
from dbt.exceptions import DbtProjectError, ProjectContractError
89
from dbt.tests.util import run_dbt, update_config_file, write_config_file
910

1011
simple_model_sql = """
@@ -118,3 +119,51 @@ def test_dbt_cloud_invalid(self, project):
118119
with pytest.raises(ProjectContractError) as excinfo:
119120
run_dbt()
120121
assert expected_err in str(excinfo.value)
122+
123+
124+
class TestVersionSpecifierChecksComeBeforeYamlValidation:
125+
def test_version_specifier_checks_before_yaml_validation(self, project) -> None:
126+
runner = dbtRunner()
127+
128+
# if no version specifier error, we should get a yaml validation error
129+
config_update = {"this-is-not-a-valid-key": "my-value-for-invalid-key"}
130+
update_config_file(config_update, "dbt_project.yml")
131+
result = runner.invoke(["parse"])
132+
assert result.exception is not None
133+
assert isinstance(result.exception, ProjectContractError)
134+
assert "Additional properties are not allowed" in str(result.exception)
135+
136+
# add bad version specifier, and assert we get the error for that
137+
update_config_file({"require-dbt-version": [">0.0.0", "<=0.0.1"]}, "dbt_project.yml")
138+
result = runner.invoke(["parse"])
139+
assert result.exception is not None
140+
assert isinstance(result.exception, DbtProjectError)
141+
assert "This version of dbt is not supported"
142+
143+
144+
class TestArchiveNotAllowed:
145+
"""At one point in time we supported an 'archive' key in projects, but no longer"""
146+
147+
def test_archive_not_allowed(self, project):
148+
runner = dbtRunner()
149+
150+
config_update = {
151+
"archive": {
152+
"source_schema": "a",
153+
"target_schema": "b",
154+
"tables": [
155+
{
156+
"source_table": "seed",
157+
"target_table": "archive_actual",
158+
"updated_at": "updated_at",
159+
"unique_key": """id || '-' || first_name""",
160+
},
161+
],
162+
}
163+
}
164+
update_config_file(config_update, "dbt_project.yml")
165+
166+
result = runner.invoke(["parse"])
167+
assert result.exception is not None
168+
assert isinstance(result.exception, ProjectContractError)
169+
assert "Additional properties are not allowed" in str(result.exception)

tests/unit/config/test_project.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import unittest
44
from copy import deepcopy
5+
from typing import Any, Dict
56
from unittest import mock
67

78
import pytest
@@ -10,7 +11,7 @@
1011
import dbt.exceptions
1112
from dbt.adapters.contracts.connection import DEFAULT_QUERY_COMMENT, QueryComment
1213
from dbt.adapters.factory import load_plugin
13-
from dbt.config.project import Project
14+
from dbt.config.project import Project, _get_required_version
1415
from dbt.constants import DEPENDENCIES_FILE_NAME
1516
from dbt.contracts.project import GitPackage, LocalPackage, PackageConfig
1617
from dbt.flags import set_from_args
@@ -42,7 +43,7 @@ def test_fixture_paths(self, project: Project):
4243
def test__str__(self, project: Project):
4344
assert (
4445
str(project)
45-
== "{'name': 'test_project', 'version': 1.0, 'project-root': 'doesnt/actually/exist', 'profile': 'test_profile', 'model-paths': ['models'], 'macro-paths': ['macros'], 'seed-paths': ['seeds'], 'test-paths': ['tests'], 'analysis-paths': ['analyses'], 'docs-paths': ['docs'], 'asset-paths': ['assets'], 'target-path': 'target', 'snapshot-paths': ['snapshots'], 'clean-targets': ['target'], 'log-path': 'path/to/project/logs', 'quoting': {'database': True, 'schema': True, 'identifier': True}, 'models': {}, 'on-run-start': [], 'on-run-end': [], 'dispatch': [{'macro_namespace': 'dbt_utils', 'search_order': ['test_project', 'dbt_utils']}], 'seeds': {}, 'snapshots': {}, 'sources': {}, 'data_tests': {}, 'unit_tests': {}, 'metrics': {}, 'semantic-models': {}, 'saved-queries': {}, 'exposures': {}, 'vars': {}, 'require-dbt-version': ['=0.0.0'], 'restrict-access': False, 'dbt-cloud': {}, 'query-comment': {'comment': \"\\n{%- set comment_dict = {} -%}\\n{%- do comment_dict.update(\\n app='dbt',\\n dbt_version=dbt_version,\\n profile_name=target.get('profile_name'),\\n target_name=target.get('target_name'),\\n) -%}\\n{%- if node is not none -%}\\n {%- do comment_dict.update(\\n node_id=node.unique_id,\\n ) -%}\\n{% else %}\\n {# in the node context, the connection name is the node_id #}\\n {%- do comment_dict.update(connection_name=connection_name) -%}\\n{%- endif -%}\\n{{ return(tojson(comment_dict)) }}\\n\", 'append': False, 'job-label': False}, 'packages': []}"
46+
== "{'name': 'test_project', 'version': 1.0, 'project-root': 'doesnt/actually/exist', 'profile': 'test_profile', 'model-paths': ['models'], 'macro-paths': ['macros'], 'seed-paths': ['seeds'], 'test-paths': ['tests'], 'analysis-paths': ['analyses'], 'docs-paths': ['docs'], 'asset-paths': ['assets'], 'target-path': 'target', 'snapshot-paths': ['snapshots'], 'clean-targets': ['target'], 'log-path': 'path/to/project/logs', 'quoting': {}, 'models': {}, 'on-run-start': [], 'on-run-end': [], 'dispatch': [{'macro_namespace': 'dbt_utils', 'search_order': ['test_project', 'dbt_utils']}], 'seeds': {}, 'snapshots': {}, 'sources': {}, 'data_tests': {}, 'unit_tests': {}, 'metrics': {}, 'semantic-models': {}, 'saved-queries': {}, 'exposures': {}, 'vars': {}, 'require-dbt-version': ['=0.0.0'], 'restrict-access': False, 'dbt-cloud': {}, 'query-comment': {'comment': \"\\n{%- set comment_dict = {} -%}\\n{%- do comment_dict.update(\\n app='dbt',\\n dbt_version=dbt_version,\\n profile_name=target.get('profile_name'),\\n target_name=target.get('target_name'),\\n) -%}\\n{%- if node is not none -%}\\n {%- do comment_dict.update(\\n node_id=node.unique_id,\\n ) -%}\\n{% else %}\\n {# in the node context, the connection name is the node_id #}\\n {%- do comment_dict.update(connection_name=connection_name) -%}\\n{%- endif -%}\\n{{ return(tojson(comment_dict)) }}\\n\", 'append': False, 'job-label': False}, 'packages': []}"
4647
)
4748

4849
def test_get_selector(self, project: Project):
@@ -534,3 +535,53 @@ def setUp(self):
534535
def test_setting_multiple_flags(self):
535536
with pytest.raises(dbt.exceptions.DbtProjectError):
536537
set_from_args(self.args, None)
538+
539+
540+
class TestGetRequiredVersion:
541+
@pytest.fixture
542+
def project_dict(self) -> Dict[str, Any]:
543+
return {
544+
"name": "test_project",
545+
"require-dbt-version": ">0.0.0",
546+
}
547+
548+
def test_supported_version(self, project_dict: Dict[str, Any]) -> None:
549+
specifiers = _get_required_version(project_dict=project_dict, verify_version=True)
550+
assert set(x.to_version_string() for x in specifiers) == {">0.0.0"}
551+
552+
def test_unsupported_version(self, project_dict: Dict[str, Any]) -> None:
553+
project_dict["require-dbt-version"] = ">99999.0.0"
554+
with pytest.raises(
555+
dbt.exceptions.DbtProjectError, match="This version of dbt is not supported"
556+
):
557+
_get_required_version(project_dict=project_dict, verify_version=True)
558+
559+
def test_unsupported_version_no_check(self, project_dict: Dict[str, Any]) -> None:
560+
project_dict["require-dbt-version"] = ">99999.0.0"
561+
specifiers = _get_required_version(project_dict=project_dict, verify_version=False)
562+
assert set(x.to_version_string() for x in specifiers) == {">99999.0.0"}
563+
564+
def test_supported_version_range(self, project_dict: Dict[str, Any]) -> None:
565+
project_dict["require-dbt-version"] = [">0.0.0", "<=99999.0.0"]
566+
specifiers = _get_required_version(project_dict=project_dict, verify_version=True)
567+
assert set(x.to_version_string() for x in specifiers) == {">0.0.0", "<=99999.0.0"}
568+
569+
def test_unsupported_version_range(self, project_dict: Dict[str, Any]) -> None:
570+
project_dict["require-dbt-version"] = [">0.0.0", "<=0.0.1"]
571+
with pytest.raises(
572+
dbt.exceptions.DbtProjectError, match="This version of dbt is not supported"
573+
):
574+
_get_required_version(project_dict=project_dict, verify_version=True)
575+
576+
def test_unsupported_version_range_no_check(self, project_dict: Dict[str, Any]) -> None:
577+
project_dict["require-dbt-version"] = [">0.0.0", "<=0.0.1"]
578+
specifiers = _get_required_version(project_dict=project_dict, verify_version=False)
579+
assert set(x.to_version_string() for x in specifiers) == {">0.0.0", "<=0.0.1"}
580+
581+
def test_impossible_version_range(self, project_dict: Dict[str, Any]) -> None:
582+
project_dict["require-dbt-version"] = [">99999.0.0", "<=0.0.1"]
583+
with pytest.raises(
584+
dbt.exceptions.DbtProjectError,
585+
match="The package version requirement can never be satisfied",
586+
):
587+
_get_required_version(project_dict=project_dict, verify_version=True)

0 commit comments

Comments
 (0)