Skip to content

Commit

Permalink
Add some more test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
tdg5 committed Apr 20, 2024
1 parent 7d3ed4d commit aa4286a
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def get_field_value(
field_name: str,
) -> Tuple[Any, str, bool]:
# We're not required to do anything here. Implement return to make mypy happy.
return None, "", False
return None, "", False # pragma: no cover

def __call__(self) -> Dict[str, Any]:
return self._yaml_config
31 changes: 30 additions & 1 deletion service_oriented_test/application/generic_main_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import os
from typing import Optional, cast
from typing import Optional, Type, cast

import pytest

from service_oriented import DeploymentEnvironment
from service_oriented.application.base_application import BaseApplication
Expand Down Expand Up @@ -37,6 +39,18 @@ class Main(GenericMain[Config, Application]):
pass


class UndefinedApplicationMain(GenericMain):
@classmethod
def config_class(cls) -> Type[Config]:
return Config


class UndefinedConfigMain(GenericMain):
@classmethod
def application_class(cls) -> Type[Application]:
return Application


def test_init_can_take_a_config_instance() -> None:
config = make_config()
subject = Main(config=config)
Expand Down Expand Up @@ -103,3 +117,18 @@ def test_run_runs_the_application() -> None:
subject = MainWithDummyApp(config=config)
subject.run()
assert cast(DummyApplication, subject.application).run_called


def test_an_exception_is_raised_if_the_application_class_cannot_be_determined() -> None:
config = make_config()
with pytest.raises(RuntimeError) as exinfo:
UndefinedApplicationMain(config=config)
assert RuntimeError == exinfo.type
assert "Unable to determine application class" in str(exinfo)


def test_an_exception_is_raised_if_the_config_class_cannot_be_determined() -> None:
with pytest.raises(RuntimeError) as exinfo:
UndefinedConfigMain()
assert RuntimeError == exinfo.type
assert "Unable to determine config class" in str(exinfo)
64 changes: 62 additions & 2 deletions service_oriented_test/logging/normalized_json_formatter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import time
from io import StringIO
from logging import Logger, StreamHandler
from typing import Dict
from uuid import uuid4

import pytest
Expand All @@ -23,16 +24,75 @@ def initialize_logger(self, formatter: NormalizedJsonFormatter) -> None:
self.handler.setFormatter(formatter)
self.logger.addHandler(self.handler)

def test_basic_normalization(self, mocker: MockerFixture) -> None:
def test_directly_logging_a_dict_of_data(self) -> None:
formatter = NormalizedJsonFormatter()
self.initialize_logger(formatter=formatter)
message = "Hello, world!"
other = "other stuff"
time_before_log = time.time()
self.logger.info({"message": message, "other": other})
time_after_log = time.time()
log_record = json.loads(self.stream.getvalue())
assert message == log_record["message"]
assert other == log_record["other"]
assert "INFO" == log_record["level"]
assert time_before_log <= log_record["timestamp"] <= time_after_log

def test_basic_normalization(self) -> None:
formatter = NormalizedJsonFormatter()
self.initialize_logger(formatter=formatter)
message = "Hello, world!"
time_before_log = time.time()
self.logger.info(message)
time_after_log = time.time()
log_record = json.loads(self.stream.getvalue())
assert message == log_record["message"]
assert "INFO" == log_record["level"]
assert time_before_log <= log_record["timestamp"]
assert time_before_log <= log_record["timestamp"] <= time_after_log

def test_explicit_level_is_normalized(self) -> None:
formatter = NormalizedJsonFormatter()
self.initialize_logger(formatter=formatter)
expected_level = "DEBUG"
self.logger.info({"level": expected_level.lower()})
log_record = json.loads(self.stream.getvalue())
assert expected_level == log_record["level"]

def test_explicit_timestamp_is_honored(self) -> None:
formatter = NormalizedJsonFormatter()
self.initialize_logger(formatter=formatter)
expected_timestamp = time.time()
self.logger.info("message", extra={"timestamp": expected_timestamp})
log_record = json.loads(self.stream.getvalue())
assert expected_timestamp == log_record["timestamp"]

@pytest.mark.usefixtures("mocker")
def test_timestamp_is_generated_when_absent_and_missing_from_log_record(
self,
mocker: MockerFixture,
) -> None:
formatter = NormalizedJsonFormatter()
record = logging.makeLogRecord(
{
"created": None,
"levelno": logging.INFO,
"levelname": "INFO",
"lineno": 56,
"msg": "message",
"name": __name__,
"pathname": __file__,
}
)
log_record: Dict = {}
message_dict: Dict = {}
expected_timestamp = time.time()
mocker.patch.object(time, "time", return_value=expected_timestamp)
formatter.add_fields(
log_record=log_record,
message_dict=message_dict,
record=record,
)
assert expected_timestamp == log_record["timestamp"]

@pytest.mark.usefixtures("mocker")
def test_generate_timestamp_is_not_typically_invoked(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ def test_logging_should_be_configured_by_valid_yaml(
temp_file.file.write("version: 1")
temp_file.seek(0)
spy = mocker.spy(logging_config, "dictConfig")
LoggerServiceWithYamlLoggingConfig(yaml_path=temp_file.name)
logger_service = LoggerServiceWithYamlLoggingConfig(yaml_path=temp_file.name)
assert 1 == spy.call_count
spy.assert_called_once_with({"version": 1})
logger = logger_service.get_logger(__name__)
assert __name__ == logger.name


def test_raises_if_config_file_is_invalid(
Expand Down

0 comments on commit aa4286a

Please sign in to comment.