Skip to content

Commit

Permalink
Merge branch 'main' into edgarrmondragon/refactor/limit-usage-of-pend…
Browse files Browse the repository at this point in the history
…ulum
  • Loading branch information
edgarrmondragon authored Feb 9, 2024
2 parents 25f80c6 + 89d6afc commit 664076b
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

.. autoclass:: APIAuthenticatorBase
:members:
:special-members: __init__, __call__
:special-members: __init__, __call__
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

# Show typehints in the description, along with parameter descriptions
autodoc_typehints = "signature"
autodoc_typehints = "description"
autodoc_class_signature = "separated"
autodoc_member_order = "groupwise"

Expand Down
23 changes: 19 additions & 4 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The Meltano SDK test framework consists of 4 main components:
1. A runner class (`TapTestRunner` and `TargetTestRunner`), responsible for executing Taps/Targets and capturing their output.
1. A suite dataclass, containing a list of tests.
1. A test template classes (`TapTestTemplate`, `StreamTestTemplate`, `AttributeTestTemplate` and `TargetTestTemplate`), with methods to `.setup()`, `.test()`, `.validate()` and `.teardown()` (called in that order using `.run()`).
1. `get_tap_test_class` and `get_target_test_class` factory methods. These wrap a `get_test_class` factory method, which takes a runner and a list of suites and return a `pytest` test class.
1. {func}`get_tap_test_class <singer_sdk.testing.get_tap_test_class>` and {func}`get_target_test_class <singer_sdk.testing.get_target_test_class>` factory methods. These wrap a `get_test_class` factory method, which takes a runner and a list of suites and return a `pytest` test class.

## Example Usage

Expand Down Expand Up @@ -76,7 +76,7 @@ class TestTargetExample(StandardTargetTests):

## Configuring Tests

Test suite behaviors can be configured by passing a `SuiteConfig` instance to the `get_test_class` functions:
Test suite behaviors can be configured by passing a {func}`SuiteConfig <singer_sdk.testing.SuiteConfig>` instance to the `get_test_class` functions:

```python
from singer_sdk.testing import SuiteConfig, get_tap_test_class
Expand All @@ -101,7 +101,7 @@ TestTapStackExchange = get_tap_test_class(
)
```

Check out [`singer_sdk/testing/config.py`](https://github.com/meltano/sdk/tree/main/singer_sdk/testing/config.py) for available config options.
Check out [the reference](#reference) for more information on the available configuration options.

## Writing New Tests

Expand All @@ -127,6 +127,21 @@ my_custom_tap_tests = TestSuite(
)
```

This suite can now be passed to `get_tap_test_class` or `get_target_test_class` in a list of `custom_suites` along with any other suites, to generate your custom test class.
This suite can now be passed to {func}`get_tap_test_class <singer_sdk.testing.get_tap_test_class>` or {func}`get_target_test_class <singer_sdk.testing.get_target_test_class>` in a list of `custom_suites` along with any other suites, to generate your custom test class.

If your new test covers a common or general case, consider contributing to the standard test library via a pull request to [meltano/sdk](https://github.com/meltano/sdk).

## Reference

```{eval-rst}
.. autofunction:: singer_sdk.testing.get_tap_test_class
```

```{eval-rst}
.. autofunction:: singer_sdk.testing.get_target_test_class
```

```{eval-rst}
.. autoclass:: singer_sdk.testing.SuiteConfig
:members:
```
16 changes: 8 additions & 8 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,15 @@ pytest = {version=">=7.2.1", optional = true}
pytest-durations = {version = ">=1.2.0", optional = true}

# installed as optional 'faker' extra
faker = {version = "~=22.5", optional = true}
faker = {version = ">=22.5,<24.0", optional = true}

[tool.poetry.extras]
docs = [
"sphinx",
"furo",
"sphinx-copybutton",
"myst-parser",
"pytest",
"sphinx",
"sphinx-copybutton",
"sphinx-autobuild",
"sphinx-inline-tabs",
"sphinx-notfound-page",
Expand Down
3 changes: 2 additions & 1 deletion singer_sdk/connectors/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,8 @@ def discover_catalog_entry(
th.Property(
name=column_name,
wrapped=th.CustomType(jsonschema_type),
required=not is_nullable,
nullable=is_nullable,
required=column_name in key_properties if key_properties else False,
),
)
schema = table_schema.to_dict()
Expand Down
8 changes: 6 additions & 2 deletions singer_sdk/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ class Property(JSONTypeHelper[T], t.Generic[T]):
"""Generic Property. Should be nested within a `PropertiesList`."""

# TODO: Make some of these arguments keyword-only. This is a breaking change.
def __init__(
def __init__( # noqa: PLR0913
self,
name: str,
wrapped: JSONTypeHelper[T] | type[JSONTypeHelper[T]],
Expand All @@ -527,6 +527,8 @@ def __init__(
secret: bool | None = False, # noqa: FBT002
allowed_values: list[T] | None = None,
examples: list[T] | None = None,
*,
nullable: bool | None = None,
) -> None:
"""Initialize Property object.
Expand All @@ -547,6 +549,7 @@ def __init__(
are permitted. This will define the type as an 'enum'.
examples: Optional. A list of one or more sample values. These may be
displayed to the user as hints of the expected format of inputs.
nullable: If True, the property may be null.
"""
self.name = name
self.wrapped = wrapped
Expand All @@ -556,6 +559,7 @@ def __init__(
self.secret = secret
self.allowed_values = allowed_values or None
self.examples = examples or None
self.nullable = nullable

@property
def type_dict(self) -> dict: # type: ignore[override]
Expand Down Expand Up @@ -585,7 +589,7 @@ def to_dict(self) -> dict:
A JSON Schema dictionary describing the object.
"""
type_dict = self.type_dict
if self.optional:
if self.nullable or self.optional:
type_dict = append_type(type_dict, "null")
if self.default is not None:
type_dict.update({"default": self.default})
Expand Down
9 changes: 8 additions & 1 deletion tests/samples/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ def _sqlite_sample_db(sqlite_connector):
for t in range(3):
conn.execute(sa.text(f"DROP TABLE IF EXISTS t{t}"))
conn.execute(
sa.text(f"CREATE TABLE t{t} (c1 int PRIMARY KEY, c2 varchar(10))"),
sa.text(
f"""
CREATE TABLE t{t} (
c1 int PRIMARY KEY NOT NULL,
c2 varchar(10) NOT NULL
)
"""
),
)
for x in range(100):
conn.execute(
Expand Down
4 changes: 3 additions & 1 deletion tests/samples/test_tap_sqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ def test_sqlite_discovery(sqlite_sample_tap: SQLTap):

assert stream.metadata.root.table_key_properties == ["c1"]
assert stream.primary_keys == ["c1"]
assert stream.schema["properties"]["c1"] == {"type": ["integer"]}
assert stream.schema["required"] == ["c1"]


def test_sqlite_input_catalog(sqlite_sample_tap: SQLTap):
Expand All @@ -90,7 +92,7 @@ def test_sqlite_input_catalog(sqlite_sample_tap: SQLTap):

for schema in [stream.schema, stream.stream_maps[0].transformed_schema]:
assert len(schema["properties"]) == 2
assert schema["properties"]["c1"] == {"type": ["integer", "null"]}
assert schema["properties"]["c1"] == {"type": ["integer"]}
assert schema["properties"]["c2"] == {"type": ["string", "null"]}
assert stream.name == stream.tap_stream_id == "main-t1"

Expand Down

0 comments on commit 664076b

Please # to comment.