Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standardize event catching during repository unit tests #10153

2 changes: 1 addition & 1 deletion tests/functional/configs/test_warn_error_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from dbt.events.types import DeprecatedModel
from dbt.tests.util import update_config_file
from dbt_common.events.base_types import EventLevel
from tests.functional.utils import EventCatcher
from tests.utils import EventCatcher

ModelsDictSpec = Dict[str, Union[str, "ModelsDictSpec"]]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
)
from dbt.tests.util import update_config_file
from dbt_common.events.base_types import EventLevel
from tests.functional.utils import EventCatcher
from tests.utils import EventCatcher


class TestSpacesInModelNamesHappyPath:
Expand Down
1 change: 1 addition & 0 deletions tests/unit/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

# All manifest related fixtures.
from tests.unit.utils.adapter import * # noqa
from tests.unit.utils.event_manager import * # noqa
from tests.unit.utils.manifest import * # noqa
from tests.unit.utils.project import * # noqa

Expand Down
42 changes: 42 additions & 0 deletions tests/unit/events/test_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from argparse import Namespace
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverting this commit fixes our broken tests/unit/graph/test_selector.py::test_run_specs[a,b--] test problem. I don't understand why.

from copy import deepcopy

from pytest_mock import MockerFixture

from dbt.events.logging import setup_event_logger
from dbt.flags import get_flags, set_from_args
from dbt_common.events.base_types import BaseEvent
from dbt_common.events.event_manager_client import get_event_manager
from dbt_common.events.logger import LoggerConfig
from tests.utils import EventCatcher


class TestSetupEventLogger:
def test_clears_preexisting_event_manager_state(self, mock_global_event_manager) -> None:
manager = get_event_manager()
manager.add_logger(LoggerConfig(name="test_logger"))
manager.callbacks.append(EventCatcher(BaseEvent).catch)
assert len(manager.loggers) == 1
assert len(manager.callbacks) == 1

flags = deepcopy(get_flags())
# setting both of these to none guarantees that no logger will be added
object.__setattr__(flags, "LOG_LEVEL", "none")
object.__setattr__(flags, "LOG_LEVEL_FILE", "none")

setup_event_logger(flags=flags)
assert len(manager.loggers) == 0
assert len(manager.callbacks) == 0

def test_specify_max_bytes(
self,
mocker: MockerFixture,
mock_global_event_manager,
) -> None:
patched_file_handler = mocker.patch("dbt_common.events.logger.RotatingFileHandler")
args = Namespace(log_file_max_bytes=1234567)
set_from_args(args, {})
setup_event_logger(get_flags())
Comment on lines +37 to +38
Copy link
Contributor Author

@QMalcolm QMalcolm May 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One theory I had was that although this commit primarily just moves the test, I also made one small change which is highlighted here. Specifically, in this test previously, these methods were called via the module instead of directly. That is these two lines were

flags.set_from_args(args, {})
setup_event_logger(flags.get_flags())

However changing these lines back to what they were and the the associated import did not solve the broken test issue we're seeing 🤔

patched_file_handler.assert_called_once_with(
filename="logs/dbt.log", encoding="utf8", maxBytes=1234567, backupCount=5
)
14 changes: 0 additions & 14 deletions tests/unit/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

import dbt.flags as flags
from dbt.adapters.events.types import AdapterDeprecationWarning
from dbt.events.logging import setup_event_logger
from dbt.events.types import NoNodesForSelectionCriteria
from dbt_common.events.event_manager_client import cleanup_event_logger
from dbt_common.events.functions import msg_to_dict, warn_or_error
from dbt_common.events.types import InfoLevel, RetryExternalCall
from dbt_common.exceptions import EventCompilationError
Expand Down Expand Up @@ -83,15 +81,3 @@ def __init__(self):
assert (
False
), f"We expect `msg_to_dict` to gracefully handle exceptions, but it raised {exc}"


def test_setup_event_logger_specify_max_bytes(mocker):
patched_file_handler = mocker.patch("dbt_common.events.logger.RotatingFileHandler")
args = Namespace(log_file_max_bytes=1234567)
flags.set_from_args(args, {})
setup_event_logger(flags.get_flags())
patched_file_handler.assert_called_once_with(
filename="logs/dbt.log", encoding="utf8", maxBytes=1234567, backupCount=5
)
# XXX if we do not clean up event logger here we are going to affect other tests.
cleanup_event_logger()
10 changes: 10 additions & 0 deletions tests/unit/utils/event_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import pytest
from pytest_mock import MockerFixture

from dbt_common.events.event_manager import EventManager


@pytest.fixture
def mock_global_event_manager(mocker: MockerFixture) -> None:
"""Mocks the global _EVENT_MANAGER so that unit tests can safely modify it without worry about other tests."""
mocker.patch("dbt_common.events.event_manager_client._EVENT_MANAGER", EventManager())
17 changes: 17 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from dataclasses import dataclass, field
from typing import List

from dbt_common.events.base_types import BaseEvent, EventMsg


@dataclass
class EventCatcher:
event_to_catch: BaseEvent
caught_events: List[EventMsg] = field(default_factory=list)

def catch(self, event: EventMsg):
if event.info.name == self.event_to_catch.__name__:
self.caught_events.append(event)

def flush(self) -> None:
self.caught_events = []
Loading