Skip to content

Commit 0ef7a33

Browse files
committed
fix conditions for failure, add "with_()" and "without_()" morph methods
1 parent a760cf7 commit 0ef7a33

File tree

5 files changed

+69
-21
lines changed

5 files changed

+69
-21
lines changed

airbyte_cdk/test/standard_tests/_job_runner.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def run_test_job(
5858
connector: IConnector | type[IConnector] | Callable[[], IConnector],
5959
verb: Literal["spec", "read", "check", "discover"],
6060
*,
61+
connector_root: Path,
6162
test_scenario: ConnectorTestScenario | None = None,
6263
catalog: ConfiguredAirbyteCatalog | dict[str, Any] | None = None,
6364
) -> entrypoint_wrapper.EntrypointOutput:
@@ -84,7 +85,10 @@ def run_test_job(
8485
)
8586

8687
args: list[str] = [verb]
87-
config_dict = test_scenario.get_config_dict(empty_if_missing=True)
88+
config_dict = test_scenario.get_config_dict(
89+
empty_if_missing=True,
90+
connector_root=connector_root,
91+
)
8892
if config_dict and verb != "spec":
8993
# Write the config to a temp json file and pass the path to the file as an argument.
9094
config_path = (

airbyte_cdk/test/standard_tests/connector_base.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ def test_check(
116116
self.create_connector(scenario),
117117
"check",
118118
test_scenario=scenario,
119+
connector_root=self.get_connector_root_dir(),
119120
)
120121
conn_status_messages: list[AirbyteMessage] = [
121122
msg for msg in result._messages if msg.type == Type.CONNECTION_STATUS
@@ -164,12 +165,16 @@ def get_scenarios(
164165
continue
165166

166167
for test in all_tests_config["acceptance_tests"][category]["tests"]:
167-
scenario = ConnectorTestScenario.model_validate(test)
168+
if "config_path" not in test:
169+
# Skip tests without a config_path
170+
continue
168171

169-
if "config_path" in test and "iam_role" in test["config_path"]:
172+
if "iam_role" in test["config_path"]:
170173
# We skip iam_role tests for now, as they are not supported in the test suite.
171174
continue
172175

176+
scenario = ConnectorTestScenario.model_validate(test)
177+
173178
if scenario.expect_exception:
174179
# For now, we skip tests that are expected to fail.
175180
# This is because they create false-positives in the test suite
@@ -182,11 +187,4 @@ def get_scenarios(
182187

183188
test_scenarios.append(scenario)
184189

185-
connector_root = cls.get_connector_root_dir().absolute()
186-
for test in test_scenarios:
187-
if test.config_path:
188-
test.config_path = connector_root / test.config_path
189-
if test.configured_catalog_path:
190-
test.configured_catalog_path = connector_root / test.configured_catalog_path
191-
192190
return test_scenarios

airbyte_cdk/test/standard_tests/declarative_sources.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,12 @@ def create_connector(
7878
config = {
7979
"__injected_manifest": manifest_dict,
8080
}
81-
config.update(scenario.get_config_dict(empty_if_missing=True))
81+
config.update(
82+
scenario.get_config_dict(
83+
empty_if_missing=True,
84+
connector_root=cls.get_connector_root_dir(),
85+
),
86+
)
8287

8388
if cls.components_py_path and cls.components_py_path.exists():
8489
os.environ["AIRBYTE_ENABLE_UNSAFE_CODE"] = "true"

airbyte_cdk/test/standard_tests/models/scenario.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from typing import Any, Literal, cast
1414

1515
import yaml
16-
from pydantic import BaseModel
16+
from pydantic import BaseModel, ConfigDict
1717

1818

1919
class ConnectorTestScenario(BaseModel):
@@ -24,6 +24,8 @@ class ConnectorTestScenario(BaseModel):
2424
acceptance test configuration file.
2525
"""
2626

27+
model_config = ConfigDict(frozen=True)
28+
2729
class AcceptanceTestExpectRecords(BaseModel):
2830
path: Path
2931
exact_order: bool = False
@@ -46,6 +48,7 @@ class AcceptanceTestFileTypes(BaseModel):
4648
def get_config_dict(
4749
self,
4850
*,
51+
connector_root: Path,
4952
empty_if_missing: bool,
5053
) -> dict[str, Any]:
5154
"""Return the config dictionary.
@@ -61,7 +64,15 @@ def get_config_dict(
6164
return self.config_dict
6265

6366
if self.config_path is not None:
64-
return cast(dict[str, Any], yaml.safe_load(self.config_path.read_text()))
67+
config_path = self.config_path
68+
if not config_path.is_absolute():
69+
# We usually receive a relative path here. Let's resolve it.
70+
config_path = (connector_root / self.config_path).resolve().absolute()
71+
72+
return cast(
73+
dict[str, Any],
74+
yaml.safe_load(config_path.read_text()),
75+
)
6576

6677
if empty_if_missing:
6778
return {}
@@ -83,3 +94,29 @@ def __str__(self) -> str:
8394
return f"'{self.config_path.name}' Test Scenario"
8495

8596
return f"'{hash(self)}' Test Scenario"
97+
98+
def without_expecting_failure(self) -> ConnectorTestScenario:
99+
"""Return a copy of the scenario that does not expect failure.
100+
101+
This is useful when you need to run multiple steps and you
102+
want to defer failure expectation for one or more steps.
103+
"""
104+
if self.status != "failed":
105+
return self
106+
107+
return ConnectorTestScenario(
108+
**self.model_dump(exclude={"status"}),
109+
)
110+
111+
def with_expecting_failure(self) -> ConnectorTestScenario:
112+
"""Return a copy of the scenario that expects failure.
113+
114+
This is useful when deriving new scenarios from existing ones.
115+
"""
116+
if self.status == "failed":
117+
return self
118+
119+
return ConnectorTestScenario(
120+
**self.model_dump(exclude={"status"}),
121+
status="failed",
122+
)

airbyte_cdk/test/standard_tests/source_base.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def test_check(
4343
self.create_connector(scenario),
4444
"check",
4545
test_scenario=scenario,
46+
connector_root=self.get_connector_root_dir(),
4647
)
4748
conn_status_messages: list[AirbyteMessage] = [
4849
msg for msg in result._messages if msg.type == Type.CONNECTION_STATUS
@@ -61,6 +62,7 @@ def test_discover(
6162
run_test_job(
6263
self.create_connector(scenario),
6364
"discover",
65+
connector_root=self.get_connector_root_dir(),
6466
test_scenario=scenario,
6567
)
6668

@@ -80,6 +82,7 @@ def test_spec(self) -> None:
8082
verb="spec",
8183
test_scenario=None,
8284
connector=self.create_connector(scenario=None),
85+
connector_root=self.get_connector_root_dir(),
8386
)
8487
# If an error occurs, it will be raised above.
8588

@@ -102,10 +105,11 @@ def test_basic_read(
102105
discover_result = run_test_job(
103106
self.create_connector(scenario),
104107
"discover",
105-
test_scenario=scenario,
108+
connector_root=self.get_connector_root_dir(),
109+
test_scenario=scenario.without_expecting_failure(),
106110
)
107-
if scenario.expect_exception:
108-
assert discover_result.errors, "Expected exception but got none."
111+
if scenario.expect_exception and discover_result.errors:
112+
# Failed as expected; we're done.
109113
return
110114

111115
configured_catalog = ConfiguredAirbyteCatalog(
@@ -122,6 +126,7 @@ def test_basic_read(
122126
self.create_connector(scenario),
123127
"read",
124128
test_scenario=scenario,
129+
connector_root=self.get_connector_root_dir(),
125130
catalog=configured_catalog,
126131
)
127132

@@ -149,15 +154,14 @@ def test_fail_read_with_bad_catalog(
149154
),
150155
sync_mode="INVALID", # type: ignore [reportArgumentType]
151156
destination_sync_mode="INVALID", # type: ignore [reportArgumentType]
152-
)
153-
]
157+
),
158+
],
154159
)
155-
# Set expected status to "failed" to ensure the test fails if the connector.
156-
scenario.status = "failed"
157160
result: entrypoint_wrapper.EntrypointOutput = run_test_job(
158161
self.create_connector(scenario),
159162
"read",
160-
test_scenario=scenario,
163+
connector_root=self.get_connector_root_dir(),
164+
test_scenario=scenario.with_expecting_failure(), # Expect failure due to bad catalog
161165
catalog=asdict(invalid_configured_catalog),
162166
)
163167
assert result.errors, "Expected errors but got none."

0 commit comments

Comments
 (0)