From 5bbf34c027f0605746704c8275b9120239ca7732 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Wed, 26 Aug 2026 21:03:11 +0200 Subject: [PATCH] fix: stop client.branch.validate() requesting removed BranchValidate.messages field (#1279) * fix: stop client.branch.validate() requesting removed BranchValidate.messages field The BranchValidate mutation stopped exposing the messages field in Infrahub 1.1.0, but both validate() variants still requested it (plus an unused object selection), so every call raised a GraphQLError against any supported server. Request only ok, which is the single field the method returns. Adds a unit regression test asserting the rendered mutation no longer contains messages, and integration coverage for both client variants on the existing branch test classes. * test: validate sync-branch01 in sync branch.validate integration test The sync test class's base_dataset creates sync-branch01, not branch01; the test referenced the async class's branch name and failed with BRANCH_NOT_FOUND. --- changelog/1263.fixed.md | 1 + infrahub_sdk/branch.py | 18 ++----------- tests/integration/test_infrahub_client.py | 3 +++ .../integration/test_infrahub_client_sync.py | 3 +++ tests/unit/sdk/test_branch.py | 27 +++++++++++++++++++ 5 files changed, 36 insertions(+), 16 deletions(-) create mode 100644 changelog/1263.fixed.md diff --git a/changelog/1263.fixed.md b/changelog/1263.fixed.md new file mode 100644 index 000000000..f2a23fd7e --- /dev/null +++ b/changelog/1263.fixed.md @@ -0,0 +1 @@ +Fixed `client.branch.validate()` and `infrahubctl branch validate`, which raised a `GraphQLError` against every supported Infrahub server because the mutation requested a `messages` field that was removed from `BranchValidate` in Infrahub 1.1.0. diff --git a/infrahub_sdk/branch.py b/infrahub_sdk/branch.py index b310522e4..4d760573d 100644 --- a/infrahub_sdk/branch.py +++ b/infrahub_sdk/branch.py @@ -133,14 +133,7 @@ async def validate(self, branch_name: str) -> BranchData: } } - query_data = { - "ok": None, - "messages": None, - "object": { - "id": None, - "name": None, - }, - } + query_data = {"ok": None} query = Mutation(mutation="BranchValidate", input_data=input_data, query=query_data) response = await self.client.execute_graphql(query=query.render(), tracker="mutation-branch-validate") @@ -288,14 +281,7 @@ def validate(self, branch_name: str) -> BranchData: } } - query_data = { - "ok": None, - "messages": None, - "object": { - "id": None, - "name": None, - }, - } + query_data = {"ok": None} query = Mutation(mutation="BranchValidate", input_data=input_data, query=query_data) response = self.client.execute_graphql(query=query.render(), tracker="mutation-branch-validate") diff --git a/tests/integration/test_infrahub_client.py b/tests/integration/test_infrahub_client.py index 54be643ac..bc39c2320 100644 --- a/tests/integration/test_infrahub_client.py +++ b/tests/integration/test_infrahub_client.py @@ -63,6 +63,9 @@ async def test_branch_delete(self, client: InfrahubClient, base_dataset: None) - assert async_branch in pre_delete assert async_branch not in post_delete + async def test_branch_validate(self, client: InfrahubClient, base_dataset: None) -> None: + assert await client.branch.validate(branch_name="branch01") is True + async def test_get_all(self, client: InfrahubClient, base_dataset: None) -> None: nodes = await client.all(kind=TESTING_CAT) assert len(nodes) == 2 diff --git a/tests/integration/test_infrahub_client_sync.py b/tests/integration/test_infrahub_client_sync.py index 34c91a446..57d7c842e 100644 --- a/tests/integration/test_infrahub_client_sync.py +++ b/tests/integration/test_infrahub_client_sync.py @@ -59,6 +59,9 @@ def test_branch_delete(self, client_sync: InfrahubClientSync, base_dataset: None assert sync_branch in pre_delete assert sync_branch not in post_delete + def test_branch_validate(self, client_sync: InfrahubClientSync, base_dataset: None) -> None: + assert client_sync.branch.validate(branch_name="sync-branch01") is True + def test_get_all(self, client_sync: InfrahubClientSync, base_dataset: None) -> None: nodes = client_sync.all(kind=TESTING_CAT) assert len(nodes) == 2 diff --git a/tests/unit/sdk/test_branch.py b/tests/unit/sdk/test_branch.py index a86182e9e..87eeadd66 100644 --- a/tests/unit/sdk/test_branch.py +++ b/tests/unit/sdk/test_branch.py @@ -49,6 +49,33 @@ async def test_get_branches(clients: BothClients, mock_branches_list_query: HTTP assert isinstance(branches["main"], BranchData) +@pytest.mark.parametrize("client_type", client_types) +async def test_branch_validate_query_excludes_removed_fields( + httpx_mock: HTTPXMock, clients: BothClients, client_type: str +) -> None: + """validate() must only request fields the BranchValidate mutation still exposes. + + The server removed the `messages` field from BranchValidate, so the rendered + mutation must not request it, otherwise the server rejects the whole query. + """ + httpx_mock.add_response( + method="POST", + json={"data": {"BranchValidate": {"ok": True}}}, + match_headers={"X-Infrahub-Tracker": "mutation-branch-validate"}, + ) + + if client_type == "standard": + result = await clients.standard.branch.validate(branch_name="branch01") + else: + result = clients.sync.branch.validate(branch_name="branch01") + + assert result is True + + post_requests = [r for r in httpx_mock.get_requests() if r.method == "POST"] + assert len(post_requests) == 1 + assert b"messages" not in post_requests[0].content + + @pytest.mark.parametrize("client_type", client_types) async def test_branch_merge_enforces_minimum_timeout( httpx_mock: HTTPXMock, clients: BothClients, client_type: str