-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Better error messages when config validation fails (#768)
- Loading branch information
1 parent
b8ab97e
commit 2a64cf6
Showing
10 changed files
with
387 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
"""Tap, target and stream test fixtures.""" | ||
|
||
from __future__ import annotations | ||
|
||
import typing as t | ||
|
||
import pendulum | ||
import pytest | ||
|
||
from singer_sdk import Stream, Tap | ||
from singer_sdk.typing import ( | ||
DateTimeType, | ||
IntegerType, | ||
PropertiesList, | ||
Property, | ||
StringType, | ||
) | ||
|
||
|
||
class SimpleTestStream(Stream): | ||
"""Test stream class.""" | ||
|
||
name = "test" | ||
schema = PropertiesList( | ||
Property("id", IntegerType, required=True), | ||
Property("value", StringType, required=True), | ||
Property("updatedAt", DateTimeType, required=True), | ||
).to_dict() | ||
replication_key = "updatedAt" | ||
|
||
def __init__(self, tap: Tap): | ||
"""Create a new stream.""" | ||
super().__init__(tap, schema=self.schema, name=self.name) | ||
|
||
def get_records( | ||
self, | ||
context: dict | None, # noqa: ARG002 | ||
) -> t.Iterable[dict[str, t.Any]]: | ||
"""Generate records.""" | ||
yield {"id": 1, "value": "Egypt"} | ||
yield {"id": 2, "value": "Germany"} | ||
yield {"id": 3, "value": "India"} | ||
|
||
|
||
class UnixTimestampIncrementalStream(SimpleTestStream): | ||
name = "unix_ts" | ||
schema = PropertiesList( | ||
Property("id", IntegerType, required=True), | ||
Property("value", StringType, required=True), | ||
Property("updatedAt", IntegerType, required=True), | ||
).to_dict() | ||
replication_key = "updatedAt" | ||
|
||
|
||
class UnixTimestampIncrementalStream2(UnixTimestampIncrementalStream): | ||
name = "unix_ts_override" | ||
|
||
def compare_start_date(self, value: str, start_date_value: str) -> str: | ||
"""Compare a value to a start date value.""" | ||
|
||
start_timestamp = pendulum.parse(start_date_value).format("X") | ||
return max(value, start_timestamp, key=float) | ||
|
||
|
||
class SimpleTestTap(Tap): | ||
"""Test tap class.""" | ||
|
||
name = "test-tap" | ||
config_jsonschema = PropertiesList( | ||
Property("username", StringType, required=True), | ||
Property("password", StringType, required=True), | ||
Property("start_date", DateTimeType), | ||
additional_properties=False, | ||
).to_dict() | ||
|
||
def discover_streams(self) -> list[Stream]: | ||
"""List all streams.""" | ||
return [ | ||
SimpleTestStream(self), | ||
UnixTimestampIncrementalStream(self), | ||
UnixTimestampIncrementalStream2(self), | ||
] | ||
|
||
|
||
@pytest.fixture | ||
def tap_class(): | ||
"""Return the tap class.""" | ||
return SimpleTestTap | ||
|
||
|
||
@pytest.fixture | ||
def tap() -> SimpleTestTap: | ||
"""Tap instance.""" | ||
return SimpleTestTap( | ||
config={ | ||
"username": "utest", | ||
"password": "ptest", | ||
"start_date": "2021-01-01", | ||
}, | ||
parse_env_config=False, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from __future__ import annotations | ||
|
||
import json | ||
from contextlib import nullcontext | ||
|
||
import pytest | ||
from click.testing import CliRunner | ||
|
||
from samples.sample_mapper.mapper import StreamTransform | ||
from singer_sdk.exceptions import ConfigValidationError | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"config_dict,expectation,errors", | ||
[ | ||
pytest.param( | ||
{}, | ||
pytest.raises(ConfigValidationError, match="Config validation failed"), | ||
["'stream_maps' is a required property"], | ||
id="missing_stream_maps", | ||
), | ||
pytest.param( | ||
{"stream_maps": {}}, | ||
nullcontext(), | ||
[], | ||
id="valid_config", | ||
), | ||
], | ||
) | ||
def test_config_errors(config_dict: dict, expectation, errors: list[str]): | ||
with expectation as exc: | ||
StreamTransform(config=config_dict, validate_config=True) | ||
|
||
if isinstance(exc, pytest.ExceptionInfo): | ||
assert exc.value.errors == errors | ||
|
||
|
||
def test_cli_help(): | ||
"""Test the CLI help message.""" | ||
runner = CliRunner(mix_stderr=False) | ||
result = runner.invoke(StreamTransform.cli, ["--help"]) | ||
assert result.exit_code == 0 | ||
assert "Show this message and exit." in result.output | ||
|
||
|
||
def test_cli_config_validation(tmp_path): | ||
"""Test the CLI config validation.""" | ||
runner = CliRunner(mix_stderr=False) | ||
config_path = tmp_path / "config.json" | ||
config_path.write_text(json.dumps({})) | ||
result = runner.invoke(StreamTransform.cli, ["--config", str(config_path)]) | ||
assert result.exit_code == 1 | ||
assert not result.stdout | ||
assert "'stream_maps' is a required property" in result.stderr |
Oops, something went wrong.