From 2ab316ee1d8e748fc594919df40d97b40eab2f81 Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 4 Jun 2026 22:05:40 +0200 Subject: [PATCH] test(e2e): make E2E suite robust to non-US / keypair / branched-storage projects Surfaced by running the E2E suite in CI against a GCP / Snowflake-keypair project (the new nightly e2e.yml from #385). All findings are test/environment robustness, NOT product bugs -- no product code changed. - ConfigSecretEncryption (x2): read `created["data"]["id"]`, not `created["id"]`. `config new --push` returns the standard `{status, data}` envelope; the test forgot to unwrap `["data"]` (the sibling `_test_config_new_push` already does). Manifested as `KeyError: 'id'`. - swap-tables rejection test: assert `"requires a branch"` instead of the old `"dev branch"` wording. The wording was corrected in #373 (swap works on any branch, incl. production); the CLI test was updated there but this E2E assertion was missed. - FullE2E workspace password: keypair-auth workspaces (Snowflake person-keypair login) have no password, so the API returns HTTP 400 "not supported for login type ..." -- skip that step cleanly instead of failing the whole run. - metastore_scope_available preflight: also treat an unreachable metastore host (connection error) as "scope unavailable" and skip, so a misconfigured stack URL yields one clean skip instead of a wall of errors across every dependent test. --- tests/helpers.py | 8 +++++++- tests/test_e2e.py | 24 ++++++++++++++++++------ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/tests/helpers.py b/tests/helpers.py index ac06058f..7e07ac17 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -28,7 +28,13 @@ def metastore_scope_available(url: str, token: str) -> bool: mc.list_items(SEMANTIC_TYPES[0]) # ty: ignore[invalid-argument-type] # probe; str vs SemanticType Literal return True except KeboolaApiError as exc: - if exc.status_code == 502 or "scope" in (exc.message or "").lower(): + # Skip cleanly when the scope is genuinely unavailable (502 / "scope") + # OR the metastore host is simply unreachable (network / DNS -- e.g. a + # malformed or accidentally doubled stack URL). Both mean "no usable + # metastore here"; raising would turn one preflight failure into a wall + # of errors across every dependent test. + msg = (exc.message or "").lower() + if exc.status_code == 502 or "scope" in msg or "cannot connect" in msg: return False raise diff --git a/tests/test_e2e.py b/tests/test_e2e.py index e2bf93cb..dca322e8 100644 --- a/tests/test_e2e.py +++ b/tests/test_e2e.py @@ -1895,8 +1895,14 @@ def _test_workspace_detail(self, workspace_id: int) -> None: assert detail["workspace_id"] == workspace_id def _test_workspace_password(self, workspace_id: int) -> None: - """Reset workspace password and verify a new password is returned.""" - data = self._run_ok( + """Reset workspace password and verify a new password is returned. + + Keypair-auth workspaces (Snowflake ``person-keypair`` login) have no + password, so the API rejects the reset with HTTP 400 "Reset password is + not supported for login type ...". That is an environment property of + the project, not a failure -- skip cleanly when it happens. + """ + result = self._run( "workspace", "password", "--project", @@ -1904,6 +1910,10 @@ def _test_workspace_password(self, workspace_id: int) -> None: "--workspace-id", str(workspace_id), ) + if result.exit_code != 0 and "not supported for login type" in result.output: + print(f" {_DIM}skip: keypair-auth workspace has no password to reset{_RESET}") + return + data = _json_ok(result) assert data["data"]["password"] # non-empty password def _test_workspace_load(self, workspace_id: int, table_id: str) -> None: @@ -7393,7 +7403,9 @@ def test_swap_without_branch_is_rejected(self) -> None: assert result.exit_code == 5, result.output payload = json.loads(result.output) assert payload["status"] == "error" - assert "dev branch" in payload["error"]["message"] + # Wording corrected in #373 (swap works on any branch, incl. production); + # match the stable part of the message, not the old "dev branch" phrasing. + assert "requires a branch" in payload["error"]["message"] # --------------------------------------------------------------------------- @@ -11307,7 +11319,7 @@ def test_config_create_and_update_encrypt_secret(self) -> None: "--no-validate", "--configuration", '{"parameters":{"#password":"e2e-canary-create"}}', - ) + )["data"] config_id = str(created["id"]) self._created.append((self.COMPONENT, config_id)) @@ -11353,7 +11365,7 @@ def test_config_update_dry_run_is_not_encrypted(self) -> None: "--no-validate", "--configuration", '{"parameters":{"user":"probe"}}', - ) + )["data"] config_id = str(created["id"]) self._created.append((self.COMPONENT, config_id)) @@ -11370,6 +11382,6 @@ def test_config_update_dry_run_is_not_encrypted(self) -> None: "--configuration", '{"parameters":{"#password":"e2e-canary-dryrun"}}', "--dry-run", - ) + )["data"] new_pw = data["new_configuration"]["parameters"]["#password"] assert new_pw == "e2e-canary-dryrun", f"dry-run encrypted the diff: {new_pw!r}"