Skip to content

Commit

Permalink
unit tests for --warn-error-options
Browse files Browse the repository at this point in the history
  • Loading branch information
MichelleArk committed Jan 9, 2023
1 parent fdf01c9 commit 25c4f55
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 15 deletions.
10 changes: 1 addition & 9 deletions core/dbt/events/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,7 @@ def warn_or_error(event, node=None):
from dbt.helper_types import WarnErrorOptions

warn_error_options = WarnErrorOptions.from_yaml_string(str(flags.WARN_ERROR_OPTIONS))

if (
flags.WARN_ERROR
or (
warn_error_options.include in warn_error_options.INCLUDE_ALL
and event.info.name not in warn_error_options.exclude
)
or (event.info.name in warn_error_options.include)
):
if flags.WARN_ERROR or warn_error_options.includes(event.info.name):
# TODO: resolve this circular import when at top
from dbt.exceptions import EventCompilationException

Expand Down
1 change: 1 addition & 0 deletions core/dbt/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"INDIRECT_SELECTION",
"TARGET_PATH",
"LOG_PATH",
"WARN_ERROR_OPTIONS",
]

_NON_DBT_ENV_FLAGS = ["DO_NOT_TRACK"]
Expand Down
11 changes: 7 additions & 4 deletions core/dbt/helper_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from dataclasses import dataclass, field
from datetime import timedelta
from importlib import import_module
from pathlib import Path
from typing import Tuple, AbstractSet, Union
from hologram import FieldEncoder, JsonDict
Expand All @@ -18,6 +17,7 @@
StrEnum,
)
from dbt.exceptions import ValidationException
import dbt.events.types as dbt_event_types


class Port(int, SerializableType):
Expand Down Expand Up @@ -114,11 +114,15 @@ def __post_init__(self):
if isinstance(self.exclude, list):
self._validate_items(self.exclude)

def includes(self, item_name: str):
return (
item_name in self.include or self.include in self.INCLUDE_ALL
) and item_name not in self.exclude

def _validate_items(self, items: List[str]):
pass


# TODO: find a better spot for this
class WarnErrorOptions(IncludeExclude):
# TODO: this method can be removed once the click CLI is in use
@classmethod
Expand All @@ -134,9 +138,8 @@ def from_yaml_string(cls, warn_error_options_str: str):
)

def _validate_items(self, items: List[str]):
mod = import_module("dbt.events.types")
valid_exception_names = set(
[name for name, cls in mod.__dict__.items() if isinstance(cls, type)]
[name for name, cls in dbt_event_types.__dict__.items() if isinstance(cls, type)]
)
for item in items:
if item not in valid_exception_names:
Expand Down
75 changes: 73 additions & 2 deletions test/unit/test_flags.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os
from unittest import mock, TestCase
from unittest import TestCase
from argparse import Namespace
import pytest

from .utils import normalize
from dbt import flags
from dbt.contracts.project import UserConfig
from dbt.graph.selector_spec import IndirectSelection
Expand Down Expand Up @@ -63,6 +63,21 @@ def test__flags(self):
flags.WARN_ERROR = False
self.user_config.warn_error = None

# warn_error_options
self.user_config.warn_error_options = '{"include": "all"}'
flags.set_from_args(self.args, self.user_config)
self.assertEqual(flags.WARN_ERROR_OPTIONS, '{"include": "all"}')
os.environ['DBT_WARN_ERROR_OPTIONS'] = '{"include": []}'
flags.set_from_args(self.args, self.user_config)
self.assertEqual(flags.WARN_ERROR_OPTIONS, '{"include": []}')
setattr(self.args, 'warn_error_options', '{"include": "all"}')
flags.set_from_args(self.args, self.user_config)
self.assertEqual(flags.WARN_ERROR_OPTIONS, '{"include": "all"}')
# cleanup
os.environ.pop('DBT_WARN_ERROR_OPTIONS')
delattr(self.args, 'warn_error_options')
self.user_config.warn_error_options = None

# write_json
self.user_config.write_json = True
flags.set_from_args(self.args, self.user_config)
Expand Down Expand Up @@ -261,3 +276,59 @@ def test__flags(self):
# cleanup
os.environ.pop('DBT_LOG_PATH')
delattr(self.args, 'log_path')

def test__flags_are_mutually_exclusive(self):
# options from user config
self.user_config.warn_error = False
self.user_config.warn_error_options = '{"include":"all}'
with pytest.raises(ValueError):
flags.set_from_args(self.args, self.user_config)
#cleanup
self.user_config.warn_error = None
self.user_config.warn_error_options = None

# options from args
setattr(self.args, 'warn_error', False)
setattr(self.args, 'warn_error_options', '{"include":"all}')
with pytest.raises(ValueError):
flags.set_from_args(self.args, self.user_config)
# cleanup
delattr(self.args, 'warn_error')
delattr(self.args, 'warn_error_options')

# options from environment
os.environ['DBT_WARN_ERROR'] = 'false'
os.environ['DBT_WARN_ERROR_OPTIONS'] = '{"include": []}'
with pytest.raises(ValueError):
flags.set_from_args(self.args, self.user_config)
#cleanup
os.environ.pop('DBT_WARN_ERROR')
os.environ.pop('DBT_WARN_ERROR_OPTIONS')

# options from user config + args
self.user_config.warn_error = False
setattr(self.args, 'warn_error_options', '{"include":"all}')
with pytest.raises(ValueError):
flags.set_from_args(self.args, self.user_config)
# cleanup
self.user_config.warn_error = None
delattr(self.args, 'warn_error_options')

# options from user config + environ
self.user_config.warn_error = False
os.environ['DBT_WARN_ERROR_OPTIONS'] = '{"include": []}'
with pytest.raises(ValueError):
flags.set_from_args(self.args, self.user_config)
# cleanup
self.user_config.warn_error = None
os.environ.pop('DBT_WARN_ERROR_OPTIONS')

# options from args + environ
setattr(self.args, 'warn_error', False)
os.environ['DBT_WARN_ERROR_OPTIONS'] = '{"include": []}'
with pytest.raises(ValueError):
flags.set_from_args(self.args, self.user_config)
# cleanup
delattr(self.args, 'warn_error')
os.environ.pop('DBT_WARN_ERROR_OPTIONS')

49 changes: 49 additions & 0 deletions tests/unit/test_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from argparse import Namespace
import pytest

import dbt.flags as flags
from dbt.events.functions import warn_or_error
from dbt.events.types import NoNodesForSelectionCriteria
from dbt.exceptions import EventCompilationException


@pytest.mark.parametrize(
"warn_error_options,expect_compilation_exception",
[
('{"include": "all"}', True),
('{"include": [NoNodesForSelectionCriteria]}', True),
('{"include": []}', False),
('{}', False),
('{"include": [MainTrackingUserState]}', False),
('{"include": "all", "exclude": [NoNodesForSelectionCriteria]}', False),
],
)
def test_warn_or_error_warn_error_options(warn_error_options, expect_compilation_exception):
args = Namespace(
warn_error_options=warn_error_options
)
flags.set_from_args(args, {})
if expect_compilation_exception:
with pytest.raises(EventCompilationException):
warn_or_error(NoNodesForSelectionCriteria())
else:
warn_or_error(NoNodesForSelectionCriteria())


@pytest.mark.parametrize(
"warn_error,expect_compilation_exception",
[
(True, True),
(False, False),
],
)
def test_warn_or_error_warn_error(warn_error, expect_compilation_exception):
args = Namespace(
warn_error=warn_error
)
flags.set_from_args(args, {})
if expect_compilation_exception:
with pytest.raises(EventCompilationException):
warn_or_error(NoNodesForSelectionCriteria())
else:
warn_or_error(NoNodesForSelectionCriteria())
46 changes: 46 additions & 0 deletions tests/unit/test_helper_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

import pytest

from dbt.helper_types import IncludeExclude, WarnErrorOptions
from dbt.exceptions import ValidationException


class TestIncludeExclude:
def test_init_invalid(self):
with pytest.raises(ValidationException):
IncludeExclude(include="invalid")

with pytest.raises(ValidationException):
IncludeExclude(include=["ItemA"], exclude=["ItemB"])

@pytest.mark.parametrize(
"include,exclude,expected_includes",
[
("all", [], True),
("*", [], True),
("*", ["ItemA"], False),
(["ItemA"], [], True),
(["ItemA", "ItemB"], [], True),
]
)
def test_includes(self, include, exclude, expected_includes):
include_exclude = IncludeExclude(include=include, exclude=exclude)

assert include_exclude.includes("ItemA") == expected_includes


class TestWarnErrorOptions:
def test_init(self):
with pytest.raises(ValidationException):
WarnErrorOptions(include=["InvalidError"])

with pytest.raises(ValidationException):
WarnErrorOptions(include="*", exclude=["InvalidError"])

warn_error_options = WarnErrorOptions(include=["NoNodesForSelectionCriteria"])
assert warn_error_options.include == ["NoNodesForSelectionCriteria"]
assert warn_error_options.exclude == []

warn_error_options = WarnErrorOptions(include="*", exclude=["NoNodesForSelectionCriteria"])
assert warn_error_options.include == "*"
assert warn_error_options.exclude == ["NoNodesForSelectionCriteria"]

0 comments on commit 25c4f55

Please sign in to comment.