API contract and authorisation testing on a federated graph
62 API test cases, a persistence read-back module called from 44 sites, and 22 silent skips turned into hard failures.

- API test cases
- 62
- call sites for the persistence read-back module
- 44
- silent skips turned into hard failures
- 22
An identity and membership domain moved off a REST backend-for-frontend onto GraphQL subgraphs behind a federated router. That domain covers users, user groups, organisations, role grants, store access and third-party application installs. Its API-level coverage had four gaps, and each of them meant tests that could pass while the system was broken.
Mutation tests asserted only on the mutation's own response payload, so a service returning a well-formed success without persisting anything would still pass. When test setup failed, the test called skip, so broken setup appeared as "skipped" and never as "failed". Typed error-union members were never selected in the test documents, so they could not be asserted at all. And some pagination tests did not send what their titles claimed.
What we built
Sixty-two API test cases: seventeen for application installs, sixteen for organisations, fifteen for users, fourteen for user groups.
A persistence read-back module, sixteen helpers in a single file, called from forty-four sites across twenty-two spec files. Every mutation's effect is now re-queried from the graph rather than trusted from its own response.
Typed error-union coverage, including an archived-users error arm added to three membership mutations, with the precedence rule pinned: when a request mixes missing and archived users, "not found" wins and lists only the missing identifier.
Twenty-two setup-failure skips converted into hard failures.
The decision that mattered
Assert the error's type and its payload, not merely that an error occurred. Every negative test checks the returned type name plus the error's contents, such as which identifiers it lists. A test that only asserts "this failed" passes when the backend returns the wrong failure, which is exactly the bug you most want to catch.
What it produced
Sixty-two cases on the integration branch in a fifty-two file suite. Twenty-two tests that previously could not fail now can. Seven tests are quarantined with linked tickets rather than deleted, and one of those quarantines records a real backend defect: a group-hierarchy mutation accepted a cycle, after which the tree query returned a self-repeating path.
