hasura-tests
Wraps Hasura GraphQL Engine testing patterns: docker-compose test instance, the metadata API for declarative schema/permission setup, x-hasura-role and x-hasura-user-id session headers for role-based permission tests, the v1/graphql endpoint via curl/HTTPie/native HTTP clients, and the role-by-table-by-operation permission-matrix pattern. Use for a metadata-driven Hasura engine where row-level permissions dominate; for a code-first server runtime harness use graphql-yoga-tests (Mercurius in its references) or apollo-server-tests instead, not this skill.
Install with skills.sh (any agent)
npx skills add testland/qa --skill hasura-testshasura-tests
Overview
Per hasura.io/docs/2.0/auth/authorization/quickstart/ (opens in new window), Hasura permissions are configured per table, role, and operation (select / insert / update / delete), with row- filter expressions and column-level scopes.
The testable concerns are different from Apollo / Yoga:
When to use
How to use
Authoring
Test instance setup
# docker-compose.test.yml
services:
postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: postgrespassword
hasura:
image: hasura/graphql-engine:v2.42.0
ports: ["8080:8080"]
depends_on: [postgres]
environment:
HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
HASURA_GRAPHQL_ADMIN_SECRET: test-secret
HASURA_GRAPHQL_DISABLE_INTROSPECTION_PUBLIC_API: "true" # per graphql-complexity-limit-tester references/introspection.md
HASURA_GRAPHQL_ENABLE_CONSOLE: "false"docker compose -f docker-compose.test.yml up -dApply metadata for test
Per Hasura docs, the metadata API at /v1/metadata:
curl -X POST http://localhost:8080/v1/metadata \
-H "x-hasura-admin-secret: test-secret" \
-H "Content-Type: application/json" \
-d @hasura/metadata-fixture.jsonmetadata-fixture.json contains the declarative permission rules being tested.
Or via hasura CLI:
hasura migrate apply --endpoint http://localhost:8080 --admin-secret test-secret
hasura metadata apply --endpoint http://localhost:8080 --admin-secret test-secretRole-based permission tests
Tests act as any role via admin secret + x-hasura-role override, and a full per-role x per-table x per-operation audit is generated from a checked-in fixture. Both patterns - the httpx by-role queries and the parametrized matrix - are in references/permission-matrix-tests.md.
Worked example
Scenario: a new user role must see only its own rows in the user table.
Result: one pair of requests proves the row-filter rule for the restricted role and confirms it does not leak into the admin role.
Running
docker compose -f docker-compose.test.yml up -d
hasura metadata apply --endpoint http://localhost:8080 --admin-secret test-secret
pytest tests/hasura/
docker compose -f docker-compose.test.yml down -vParsing results
Hasura returns standard GraphQL response format:
{
"data": { "user": [{"id": 3, "name": "alice"}] },
"errors": [{"message": "user.id: permission has failed", "extensions": {"code": "permission-error"}}]
}Permission failures use extensions.code = "permission-error". Assertion patterns:
def test_user_cannot_update_other_users_row():
resp = httpx.post(ENDPOINT, headers={
"x-hasura-admin-secret": "test-secret",
"x-hasura-role": "user",
"x-hasura-user-id": "3",
}, json={
"query": "mutation { update_user_by_pk(pk_columns: {id: 4}, _set: {name: \"hacked\"}) { id } }"
})
body = resp.json()
# Either errors out OR returns affected_rows: 0 depending on permission setup
if "errors" in body:
assert any(
"permission" in e["extensions"].get("code", "")
for e in body["errors"]
)
else:
assert body["data"]["update_user_by_pk"] is None # row was filtered outCI integration
jobs:
hasura-permission-matrix:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env: { POSTGRES_PASSWORD: postgres }
ports: [5432]
hasura:
image: hasura/graphql-engine:v2.42.0
env:
HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres
HASURA_GRAPHQL_ADMIN_SECRET: ci-secret
HASURA_GRAPHQL_DISABLE_INTROSPECTION_PUBLIC_API: "true"
ports: [8080]
steps:
- uses: actions/checkout@v5
- run: |
npm install -g hasura-cli
hasura migrate apply --endpoint http://localhost:8080 --admin-secret ci-secret
hasura metadata apply --endpoint http://localhost:8080 --admin-secret ci-secret
- run: pytest tests/hasura/ --tb=shortAnti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Tests run against shared Hasura instance | Permission changes leak between tests | Per-test or per-suite ephemeral DB + metadata reset |
Skipping HASURA_GRAPHQL_DISABLE_INTROSPECTION_PUBLIC_API in CI | Production-config drift; introspection assertions don't hold | Set in test docker-compose |
Permission tests using admin secret without x-hasura-role override | Tests run as admin -> bypass all permissions | Always add x-hasura-role |
| Hardcoded user IDs across tests | One test mutates user 3 -> next test stale | Per-test user seeding |
| Skipping insert / update / delete tests | Permission rules differ per operation | Cover the full matrix |
| Not testing JWT path | Admin-secret-override bypasses production auth flow | One smoke test using real JWT against HASURA_GRAPHQL_JWT_SECRET config |
| Permission matrix in code only | PR reviewers can't see what changes | Matrix as a checked-in YAML / JSON fixture |
| Tests don't reset metadata between suites | Migration drift between test runs | hasura metadata clear + reapply in setup |
Limitations
References
Role-based permission tests
View source (opens in new window)Role-based permission tests
The pattern: admin secret + x-hasura-role override lets tests act as any role without going through the production auth service (Auth0, Cognito, custom JWT).
Test queries by role
Per hasura.io/docs/2.0/auth/authorization/quickstart/ (opens in new window):
import httpx
ENDPOINT = "http://localhost:8080/v1/graphql"
def test_user_sees_only_their_rows():
resp = httpx.post(
ENDPOINT,
headers={
"x-hasura-admin-secret": "test-secret", # admin secret for role-override
"x-hasura-role": "user",
"x-hasura-user-id": "3",
},
json={"query": "{ user { id name } }"},
)
assert resp.status_code == 200
rows = resp.json()["data"]["user"]
assert all(r["id"] == 3 for r in rows)
def test_admin_sees_all_rows():
resp = httpx.post(
ENDPOINT,
headers={
"x-hasura-admin-secret": "test-secret",
"x-hasura-role": "admin",
},
json={"query": "{ user { id name } }"},
)
rows = resp.json()["data"]["user"]
assert len(rows) > 1Per-role × per-table × per-operation matrix
For a thorough audit, generate the matrix:
ROLES = ["anonymous", "user", "premium_user", "admin"]
TABLES = ["users", "documents", "audit_log"]
OPERATIONS = ["select", "insert", "update", "delete"]
@pytest.mark.parametrize("role", ROLES)
@pytest.mark.parametrize("table", TABLES)
@pytest.mark.parametrize("op", OPERATIONS)
def test_permission_matrix(role, table, op):
expected = load_expected_matrix()[(role, table, op)]
actual = try_operation(role, table, op)
assert actual == expected, f"Role {role} {op} on {table}: expected {expected}, got {actual}"Permission matrix should be checked in as a fixture, reviewed on PR.
Related skills
apollo-server-tests
Wraps Apollo Server testing patterns: `server.executeOperation()` (in-process, no HTTP), `supertest` against an ephemeral-port HTTP server (port 0), context injection via the `contextValue` second-argument, and assertion patterns for response shape + errors. Includes the production-config gates testable through this skill - introspection-disabled, persisted-query mode, hideSchemaDetailsFromClientErrors. Use when writing tests for an Apollo Server v4+ GraphQL service.
graphql-complexity-limit-tester
The GraphQL attack-surface / hardening skill: crafts over-limit depth and complexity queries then asserts rejection before execution, verifying that graphql-depth-limit, graphql-cost-analysis, and graphql-armor (max-depth / cost-limit / max-tokens plugins) are actually enforced and not just configured; carries the introspection attack-surface catalog (what __schema exposes, per-framework disable controls, hideSchemaDetailsFromClientErrors) in references/introspection.md and the persisted-query allowlisting strategies (Apollo APQ protocol, auto-register vs strict-allowlist vs hybrid modes) in references/persisted-queries.md. Use when auditing a GraphQL service for DoS or schema-disclosure exposure, hardening a production deployment, or adding tests that prove the limits in CI.
graphql-n-plus-one-remediation
Detects and fixes the GraphQL N+1 pattern: scans a repo, PR diff, or schema type for list-returning resolvers (grep-driven detection workflow), traces the resolver tree to locate the fan-out (one parent query returns N rows, then a child field resolver fires once per row), classifies every child field resolver as safe or N+1 risk, and applies one of three fixes: per-request DataLoader batching, eager projection in the parent resolver, or selection-set-aware prefetch. Use when reviewing a PR that adds or changes a list-returning resolver, when a connection-pool exhaustion or slow-query alert traces back to GraphQL traffic, or when a resolver trace shows a child field resolved once per parent row.
graphql-subscription-test-author
Authors GraphQL subscription resolver test suites over graphql-ws (WebSocket) and graphql-sse (Server-Sent Events) transports: subscribe to event streams via the async-iterator API, assert emitted data shape and sequence, verify connection lifecycle and protocol close codes, and validate auth-on-connect (connectionParams / authenticate callback) plus resolver-level pubsub trigger logic. Use for real-time subscription operations; not for queries or mutations - for those use apollo-server-tests or graphql-yoga-tests (Mercurius in its references).
graphql-yoga-tests
Tests a GraphQL Yoga server (the-guild.dev runtime) with `yoga.fetch()` for in-process, no-network request simulation of queries and mutations, `@graphql-tools/executor-http` for subscription and incremental-delivery (streaming) tests, auth-header pass-through, and production-config gates for disabled introspection and persisted operations; also carries the Mercurius (Fastify GraphQL plugin) in-process `app.inject()` testing patterns in references/mercurius.md. Use to test a GraphQL Yoga or Mercurius server, write query, mutation, or subscription tests, or check production plugin config; for other runtimes use apollo-server-tests or hasura-tests instead, not this skill.