From 3cb9bab9f135bce361c3da5e0acd9ab6c6e6f94a Mon Sep 17 00:00:00 2001 From: Samir Talwar Date: Thu, 29 Sep 2022 19:18:49 +0200 Subject: [PATCH] server/tests-py: Provide the admin secret to the HGE server. When we run the HGE server inside the test harness, it needs to run with an admin secret for some tests to make sense. This tags each test that requires an admin secret with `pytest.mark.admin_secret`, which then generates a UUID and injects that into both the server and the test case (if required). It also simplifies the way the test harness picks up an existing admin secret, allowing it to use the environment variable instead of requiring it via a parameter. PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6120 GitOrigin-RevId: 55c5b9e8c99bdad9c8304098444ddb9516749a2c --- .circleci/test-server.sh | 50 +--- server/tests-py/conftest.py | 55 ++-- server/tests-py/context.py | 9 +- server/tests-py/docker-compose.yml | 8 + server/tests-py/fixtures/hge.py | 4 + server/tests-py/pytest.ini | 1 + server/tests-py/run-new.sh | 2 +- server/tests-py/run.sh | 4 +- server/tests-py/test_apis_disabled.py | 1 + server/tests-py/test_apollo_federation.py | 18 +- server/tests-py/test_auth_webhook_cookie.py | 1 + server/tests-py/test_compat.py | 7 +- server/tests-py/test_dev_endpoints.py | 2 +- server/tests-py/test_graphql_mutations.py | 1 + server/tests-py/test_graphql_queries.py | 11 +- server/tests-py/test_jwt.py | 4 +- .../test_remote_schema_permissions.py | 1 + server/tests-py/test_roles_inheritance.py | 1 + server/tests-py/test_subscriptions.py | 241 +++++++++--------- server/tests-py/test_webhook.py | 26 +- 20 files changed, 222 insertions(+), 225 deletions(-) diff --git a/.circleci/test-server.sh b/.circleci/test-server.sh index 0d369e4b3fb27..bc9b97dda0380 100755 --- a/.circleci/test-server.sh +++ b/.circleci/test-server.sh @@ -74,7 +74,7 @@ init_hge_and_test_jwt() { run_hge_with_args serve wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-jwt-key-file="$key_file" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" \ + --hge-jwt-key-file="$key_file" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" \ "$@" kill_hge_servers } @@ -266,7 +266,7 @@ admin-secret) start_multiple_hge_servers - run_pytest_parallel --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" + run_pytest_parallel kill_hge_servers ;; @@ -282,7 +282,6 @@ admin-secret-unauthorized-role) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-unauthorized-role \ test_graphql_queries.py::TestUnauthorizedRolePermission @@ -302,7 +301,7 @@ jwt-rs512) start_multiple_hge_servers - run_pytest_parallel --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-jwt-key-file="$OUTPUT_FOLDER/ssl/jwt_private.key" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" + run_pytest_parallel --hge-jwt-key-file="$OUTPUT_FOLDER/ssl/jwt_private.key" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" kill_hge_servers @@ -320,7 +319,7 @@ jwt-ed25519) start_multiple_hge_servers - run_pytest_parallel --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-jwt-key-file="$OUTPUT_FOLDER/ssl/ed25519_jwt_private.key" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" + run_pytest_parallel --hge-jwt-key-file="$OUTPUT_FOLDER/ssl/ed25519_jwt_private.key" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" kill_hge_servers @@ -574,7 +573,7 @@ jwt-cookie-unauthorized-role) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-jwt-key-file="$OUTPUT_FOLDER/ssl/jwt_private.key" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" \ + --hge-jwt-key-file="$OUTPUT_FOLDER/ssl/jwt_private.key" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" \ --test-unauthorized-role \ test_graphql_queries.py::TestFallbackUnauthorizedRoleCookie @@ -586,7 +585,7 @@ jwt-cookie-unauthorized-role) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-jwt-key-file="$OUTPUT_FOLDER/ssl/jwt_private.key" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" \ + --hge-jwt-key-file="$OUTPUT_FOLDER/ssl/jwt_private.key" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" \ --test-no-cookie-and-unauth-role \ test_graphql_queries.py::TestMissingUnauthorizedRoleAndCookie @@ -605,7 +604,6 @@ cors-domains) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_cors.py kill_hge_servers @@ -624,7 +622,7 @@ auth-webhook-cookie) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK" \ + --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK" \ --test-auth-webhook-header \ test_auth_webhook_cookie.py @@ -644,7 +642,6 @@ ws-init-cookie-read-cors-enabled) echo "$(time_elapsed): testcase 1: read cookie, cors enabled" pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-ws-init-cookie=read \ test_websocket_init_cookie.py @@ -662,7 +659,6 @@ ws-init-cookie-noread) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-ws-init-cookie=noread \ test_websocket_init_cookie.py @@ -680,7 +676,6 @@ ws-init-cookie-read-cors-disabled) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-ws-init-cookie=read \ test_websocket_init_cookie.py @@ -698,7 +693,6 @@ ws-graphql-api-disabled) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_apis_disabled.py kill_hge_servers @@ -716,7 +710,6 @@ ws-metadata-api-disabled) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_apis_disabled.py kill_hge_servers @@ -731,7 +724,6 @@ remote-schema-permissions) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_remote_schema_permissions.py unset HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS @@ -748,10 +740,8 @@ function-permissions) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_graphql_queries.py::TestGraphQLQueryFunctionPermissions pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_graphql_mutations.py::TestGraphQLMutationFunctions unset HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS @@ -771,7 +761,6 @@ roles-inheritance) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_roles_inheritance.py unset HASURA_GRAPHQL_ADMIN_SECRET @@ -791,7 +780,6 @@ naming-conventions) unset HASURA_GRAPHQL_EXPERIMENTAL_FEATURES pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_naming_conventions.py kill_hge_servers @@ -801,7 +789,6 @@ naming-conventions) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_naming_conventions.py kill_hge_servers @@ -815,7 +802,6 @@ naming-conventions) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_naming_conventions.py unset HASURA_GRAPHQL_ADMIN_SECRET @@ -835,8 +821,6 @@ streaming-subscriptions) # run all the subscriptions tests with streaming subscriptions enabled pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ - --test-streaming-subscriptions \ test_subscriptions.py unset HASURA_GRAPHQL_ADMIN_SECRET @@ -853,7 +837,6 @@ query-caching) run_hge_with_args +RTS -N1 -RTS serve wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_graphql_queries.py::TestGraphQLQueryCaching kill_hge_servers ;; @@ -877,7 +860,6 @@ query-logs) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-logging \ test_logging.py @@ -912,7 +894,6 @@ startup-db-calls) run_hge_with_args serve wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-startup-db-calls \ test_startup_db_calls.py @@ -961,7 +942,6 @@ EOF pytest "${PYTEST_REPORTING_ARGS[@]}" \ --hge-urls "$HGE_URL" \ --pg-urls "$HASURA_GRAPHQL_PG_SOURCE_URL_1" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-read-only-source \ test_graphql_read_only_source.py @@ -1011,7 +991,7 @@ post-webhook) WH_PID=$! wait_for_port 9090 - run_pytest_parallel --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK" + run_pytest_parallel --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK" kill_hge_servers ;; @@ -1028,7 +1008,6 @@ webhook-request-context) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK" \ --test-webhook-request-context \ test_webhook_request_context.py @@ -1051,7 +1030,7 @@ get-webhook) WH_PID=$! wait_for_port 9090 - run_pytest_parallel --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK" + run_pytest_parallel --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK" kill_hge_servers ;; @@ -1077,7 +1056,6 @@ insecure-webhook) wait_for_port 9090 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK" \ --test-webhook-insecure \ test_webhook_insecure.py @@ -1105,7 +1083,6 @@ insecure-webhook-with-admin-secret) wait_for_port 9090 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK" \ --test-webhook-insecure \ test_webhook_insecure.py @@ -1124,7 +1101,6 @@ apollo-federation) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_apollo_federation.py unset HASURA_GRAPHQL_EXPERIMENTAL_FEATURES @@ -1142,7 +1118,6 @@ allowlist-queries) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_allowlist_queries.py kill_hge_servers @@ -1157,7 +1132,6 @@ developer-api-tests) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ test_dev_endpoints.py unset HASURA_GRAPHQL_ENABLED_APIS @@ -1187,7 +1161,6 @@ jwk-url) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-jwk-url \ -k 'test_cache_control_header_max_age' @@ -1200,7 +1173,6 @@ jwk-url) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-jwk-url \ -k 'test_cache_control_header_max_age' @@ -1213,7 +1185,6 @@ jwk-url) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-jwk-url \ -k 'test_cache_control_header_no_caching' @@ -1226,7 +1197,6 @@ jwk-url) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-jwk-url \ -k 'test_cache_control_header_no_caching' @@ -1239,7 +1209,6 @@ jwk-url) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-jwk-url \ -k 'test_cache_control_header_no_caching' @@ -1252,7 +1221,6 @@ jwk-url) wait_for_port 8080 pytest "${PYTEST_COMMON_ARGS[@]}" \ - --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" \ --test-jwk-url \ -k 'test_expires_header' diff --git a/server/tests-py/conftest.py b/server/tests-py/conftest.py index f38609fd87fca..bd30baf72c908 100644 --- a/server/tests-py/conftest.py +++ b/server/tests-py/conftest.py @@ -7,6 +7,7 @@ import threading import time from typing import Optional +import uuid import auth_webhook_server from context import HGECtx, HGECtxGQLServer, ActionsWebhookServer, EvtsWebhookServer, GQLWsClient, PytestConf, GraphQLWSClient @@ -34,9 +35,6 @@ def pytest_addoption(parser): required=False, nargs='+' ) - parser.addoption( - "--hge-key", metavar="HGE_KEY", help="admin secret key for graphql-engine", required=False - ) parser.addoption( "--hge-webhook", metavar="HGE_WEBHOOK", help="url for graphql-engine's access control webhook", required=False ) @@ -85,14 +83,6 @@ def pytest_addoption(parser): help="Run testcases for startup database calls" ) - parser.addoption( - "--test-streaming-subscriptions", - action="store_true", - default=False, - required=False, - help="Run streaming subscription tests" - ) - parser.addoption( "--test-jwk-url", action="store_true", @@ -372,8 +362,23 @@ def hge_fixture_env() -> dict[str, str]: return {} @pytest.fixture(scope='class') -def hge_key(request) -> Optional[str]: - return request.config.getoption('--hge-key') +def hge_key( + request: pytest.FixtureRequest, + hge_bin: Optional[str], +) -> Optional[str]: + marker = request.node.get_closest_marker('admin_secret') + if hge_bin: + # If the test requests an admin secret, generate one. + return str(uuid.uuid4()) if marker else None + else: + # If the environment variable is set, use it. + # This will be used in the event that we start the server outside the test harness. + # We skip the test if it explicitly requires that we have no admin secret. + anti_marker = request.node.get_closest_marker('no_admin_secret') + env_key = os.environ.get('HASURA_GRAPHQL_ADMIN_SECRET') + if anti_marker and env_key: + pytest.skip('This test requires that the admin secret is not set.') + return env_key @pytest.fixture(scope='class') def hge_server( @@ -381,16 +386,28 @@ def hge_server( hge_bin: Optional[str], hge_port: int, hge_url: str, + hge_key: Optional[str], hge_fixture_env: dict[str, str], pg_url: str, ) -> Optional[str]: if not hge_bin: return None - return fixtures.hge.hge_server(request, hge_bin, hge_port, hge_url, hge_fixture_env, pg_url) + return fixtures.hge.hge_server(request, hge_bin, hge_port, hge_url, hge_key, hge_fixture_env, pg_url) + +@pytest.fixture(scope='class') +def enabled_apis(request: pytest.FixtureRequest, hge_bin: Optional[str]) -> Optional[set[str]]: + if hge_bin: + hge_marker_env: dict[str, str] = {marker.args[0]: marker.args[1] for marker in request.node.iter_markers('hge_env') if marker.args[1] is not None} + enabled_apis_str = hge_marker_env.get('HASURA_GRAPHQL_ENABLED_APIS') + else: + enabled_apis_str = os.environ.get('HASURA_GRAPHQL_ENABLED_APIS') + if not enabled_apis_str: + return None + return set(enabled_apis_str.split(',')) @pytest.fixture(scope='class') -def hge_ctx(request, hge_url, pg_url, hge_server): - hge_ctx = HGECtx(hge_url, pg_url, request.config) +def hge_ctx(request, hge_url, pg_url, hge_key, enabled_apis, hge_server): + hge_ctx = HGECtx(hge_url, pg_url, hge_key, enabled_apis, request.config) yield hge_ctx @@ -442,12 +459,6 @@ def auth_hook(hge_fixture_env: dict[str, str]): yield server auth_webhook_server.stop_server(server) -@pytest.fixture(scope='class') -def streaming_subscriptions_fixtures(hge_ctx): - if not hge_ctx.streaming_subscriptions: - pytest.skip('These tests are meant to be run with --test-streaming-subscriptions set with pytest') - return - @pytest.fixture(scope='class') def pro_tests_fixtures(hge_ctx): if not hge_ctx.pro_tests: diff --git a/server/tests-py/context.py b/server/tests-py/context.py index 45cd0608a9749..aff1edc1a9829 100644 --- a/server/tests-py/context.py +++ b/server/tests-py/context.py @@ -790,10 +790,9 @@ def url(self): class HGECtx: - def __init__(self, hge_url, pg_url, config): - + def __init__(self, hge_url, pg_url, hge_key, enabled_apis, config): self.http = requests.Session() - self.hge_key = config.getoption('--hge-key') + self.hge_key = hge_key self.timeout = 120 # BigQuery can take a while self.hge_url = hge_url self.pg_url = pg_url @@ -812,7 +811,6 @@ def __init__(self, hge_url, pg_url, config): self.hge_jwt_algo = "EdDSA" self.webhook_insecure = config.getoption('--test-webhook-insecure') self.may_skip_test_teardown = False - self.streaming_subscriptions = config.getoption('--test-streaming-subscriptions') # This will be GC'd, but we also explicitly dispose() in teardown() self.engine = sqlalchemy.create_engine(self.pg_url) @@ -833,9 +831,6 @@ def __init__(self, hge_url, pg_url, config): self.default_backend = 'postgres' self.is_default_backend = self.backend == self.default_backend - enabled_apis_str = os.environ.get('HASURA_GRAPHQL_ENABLED_APIS') - enabled_apis = set(enabled_apis_str.split(',')) if enabled_apis_str else None - env_version = os.getenv('VERSION') if env_version: self.version = env_version diff --git a/server/tests-py/docker-compose.yml b/server/tests-py/docker-compose.yml index e45ae9af753cd..7e5cd89db612b 100644 --- a/server/tests-py/docker-compose.yml +++ b/server/tests-py/docker-compose.yml @@ -80,6 +80,14 @@ services: volumes: - /var/lib/postgresql/data + citus-healthy: + image: busybox + command: + - 'true' + depends_on: + citus: + condition: service_healthy + mssql: # Uses a different image for arm64. image: ${MSSQL_IMAGE:-mcr.microsoft.com/mssql/server:2019-latest@sha256:a098c9ff6fbb8e1c9608ad7511fa42dba8d22e0d50b48302761717840ccc26af} diff --git a/server/tests-py/fixtures/hge.py b/server/tests-py/fixtures/hge.py index e515936d831a8..23eb630b2f29d 100644 --- a/server/tests-py/fixtures/hge.py +++ b/server/tests-py/fixtures/hge.py @@ -24,6 +24,7 @@ def hge_server( hge_bin: str, hge_port: int, hge_url: str, + hge_key: Optional[str], hge_fixture_env: dict[str, str], pg_url: str, ) -> Optional[str]: @@ -35,6 +36,8 @@ def hge_server( **hge_marker_env, } + hge_key_args = ['--admin-secret', hge_key] if hge_key else [] + print(f'Starting GraphQL Engine on {hge_url}...') hge_process = subprocess.Popen( args = [ @@ -43,6 +46,7 @@ def hge_server( 'serve', '--server-port', str(hge_port), '--stringify-numeric-types', + *hge_key_args, ], env = env, ) diff --git a/server/tests-py/pytest.ini b/server/tests-py/pytest.ini index c4e18e3d6a09d..dfb8edcd002e0 100644 --- a/server/tests-py/pytest.ini +++ b/server/tests-py/pytest.ini @@ -6,6 +6,7 @@ norecursedirs = queries webhook test_upgrade xfail_strict = true markers = backend: The backends supported by the test case + admin_secret: Generate and use an admin secret hge_env: Pass additional environment variables to the GraphQL Engine skip_server_upgrade_test: Tests with this marker should not be run as part of server upgrade test allow_server_upgrade_test: Add tests with this marker to server upgrade test, as far as they don't have the skip_server_upgarde_test market diff --git a/server/tests-py/run-new.sh b/server/tests-py/run-new.sh index 8e974b70654ae..16487fca3d5c2 100755 --- a/server/tests-py/run-new.sh +++ b/server/tests-py/run-new.sh @@ -30,7 +30,7 @@ if [[ "$(uname -m)" == 'arm64' ]]; then fi docker compose rm -svf citus mssql postgres -docker compose up -d citus mssql-healthcheck postgres-healthy +docker compose up -d citus-healthy mssql-healthcheck postgres-healthy HASURA_GRAPHQL_CITUS_SOURCE_URL="postgresql://postgres:hasura@localhost:$(docker compose port citus 5432 | sed -E 's/.*://')/postgres" HASURA_GRAPHQL_MSSQL_SOURCE_URL="DRIVER={ODBC Driver 17 for SQL Server};SERVER=localhost,$(docker compose port mssql 1433 | sed -E 's/.*://');Uid=sa;Pwd=Password!;" diff --git a/server/tests-py/run.sh b/server/tests-py/run.sh index f01de3649c50a..75e88729601d0 100755 --- a/server/tests-py/run.sh +++ b/server/tests-py/run.sh @@ -49,12 +49,12 @@ else fi echo "*** Building HGE ***" -docker compose run hge-build +docker compose run --rm hge-build for SERVER_TEST_TO_RUN in "${SERVER_TESTS_TO_RUN[@]}"; do export SERVER_TEST_TO_RUN echo echo "*** Running test suite: ${SERVER_TEST_TO_RUN} ***" docker compose rm -svf citus mssql postgres # tear down databases beforehand - docker compose run tests-py + docker compose run --rm tests-py done diff --git a/server/tests-py/test_apis_disabled.py b/server/tests-py/test_apis_disabled.py index 627c2c7a326ae..e669482b92f7e 100644 --- a/server/tests-py/test_apis_disabled.py +++ b/server/tests-py/test_apis_disabled.py @@ -6,6 +6,7 @@ pytestmark = [ pytest.mark.usefixtures('auth_hook'), + pytest.mark.admin_secret, pytest.mark.hge_env('HASURA_GRAPHQL_AUTH_HOOK_MODE', 'POST'), ] diff --git a/server/tests-py/test_apollo_federation.py b/server/tests-py/test_apollo_federation.py index 15f67bd7b66bb..02f6dd7d1b8e8 100644 --- a/server/tests-py/test_apollo_federation.py +++ b/server/tests-py/test_apollo_federation.py @@ -12,18 +12,20 @@ def make_request(url, query): resp = requests.post(url, json=payload) return resp -@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', 'apollo_federation') @pytest.mark.usefixtures('per_class_tests_db_state') +@pytest.mark.admin_secret +@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', 'apollo_federation') class TestApolloFederation: @classmethod def dir(cls): return 'queries/apollo_federation' - def test_apollo_federated_server_with_hge_only(self, hge_url: str): + def test_apollo_federated_server_with_hge_only(self, hge_url: str, hge_key: str): # start the node server fed_server = NodeGraphQL(["node", "remote_schemas/nodejs/apollo_federated_server_with_hge_only.js"], env={ 'HGE_URL': hge_url, + 'HASURA_GRAPHQL_ADMIN_SECRET': hge_key, }) fed_server.start() @@ -45,16 +47,16 @@ def test_apollo_federated_server_with_hge_only(self, hge_url: str): assert resp.status_code == 200, resp.text assert 'data' in resp.text - def test_apollo_federated_server_with_hge_and_apollo_graphql_server(self, hge_url: str): + def test_apollo_federated_server_with_hge_and_apollo_graphql_server(self, hge_url: str, hge_key: str): # start the node servers server_1 = NodeGraphQL(["node", "remote_schemas/nodejs/apollo_server_1.js"], env={ 'HGE_URL': hge_url, }) - server_env = { + fed_server = NodeGraphQL(["node", "remote_schemas/nodejs/apollo_federated_server_with_hge_and_server1.js"], env={ 'HGE_URL': hge_url, 'OTHER_URL': server_1.url, - } - fed_server = NodeGraphQL(["node", "remote_schemas/nodejs/apollo_federated_server_with_hge_and_server1.js"], env=server_env) + 'HASURA_GRAPHQL_ADMIN_SECRET': hge_key, + }) server_1.start() fed_server.start() @@ -80,8 +82,8 @@ def test_apollo_federated_server_with_hge_and_apollo_graphql_server(self, hge_ur assert resp.status_code == 200, resp.text assert 'data' in resp.text - def test_apollo_federation_fields(self,hge_ctx): + def test_apollo_federation_fields(self, hge_ctx): check_query_f(hge_ctx, self.dir() + '/root_fields.yaml') - def test_apollo_federation_entities(self,hge_ctx): + def test_apollo_federation_entities(self, hge_ctx): check_query_f(hge_ctx, self.dir() + '/entities.yaml') diff --git a/server/tests-py/test_auth_webhook_cookie.py b/server/tests-py/test_auth_webhook_cookie.py index 2ac364ef137b8..fcf77a8e046f0 100644 --- a/server/tests-py/test_auth_webhook_cookie.py +++ b/server/tests-py/test_auth_webhook_cookie.py @@ -14,6 +14,7 @@ pytest.skip("--test-auth-webhook-header flag is missing, skipping tests", allow_module_level=True) @pytest.mark.usefixtures('auth_hook', 'per_class_tests_db_state') +@pytest.mark.admin_secret class TestWebhookHeaderCookie(object): ''' To run the test, run an instance of the auth_webhook server using `python3 auth_webhook_server.py` diff --git a/server/tests-py/test_compat.py b/server/tests-py/test_compat.py index c652728e84451..bc6324168a127 100644 --- a/server/tests-py/test_compat.py +++ b/server/tests-py/test_compat.py @@ -1,10 +1,6 @@ import pytest -from context import PytestConf -if not PytestConf.config.getoption("--hge-key"): - pytest.skip("--hge-key flag is missing, skipping tests", allow_module_level=True) - -def v1qCompat(hge_ctx, q, headers = {}): +def v1qCompat(hge_ctx, q): h = {'X-Hasura-Access-Key': hge_ctx.hge_key} resp = hge_ctx.http.post( hge_ctx.hge_url + "/v1/query", @@ -13,6 +9,7 @@ def v1qCompat(hge_ctx, q, headers = {}): ) return resp.status_code, resp.json() +@pytest.mark.admin_secret class TestGraphQLCompatAccessKey(): export_metadata = { diff --git a/server/tests-py/test_dev_endpoints.py b/server/tests-py/test_dev_endpoints.py index 5d88bcb33591d..5992f717f7e58 100644 --- a/server/tests-py/test_dev_endpoints.py +++ b/server/tests-py/test_dev_endpoints.py @@ -1,6 +1,5 @@ import requests import pytest -from context import PytestConf """ @@ -25,6 +24,7 @@ def get_headers(hge_ctx, role='admin'): headers['x-hasura-role'] = role return headers +@pytest.mark.admin_secret @pytest.mark.hge_env('HASURA_GRAPHQL_ENABLED_APIS', 'metadata,graphql,developer,config,pgdump') class TestDevEndpoints: diff --git a/server/tests-py/test_graphql_mutations.py b/server/tests-py/test_graphql_mutations.py index ad4cf87191401..00223737fbca7 100644 --- a/server/tests-py/test_graphql_mutations.py +++ b/server/tests-py/test_graphql_mutations.py @@ -798,6 +798,7 @@ def test_delete_where_enum_field(self, hge_ctx, transport): # Tracking VOLATILE SQL functions as mutations, or queries (#1514) @pytest.mark.parametrize('transport', ['http', 'websocket']) @use_mutation_fixtures +@pytest.mark.admin_secret @pytest.mark.hge_env('HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS', 'false') class TestGraphQLMutationFunctions: @classmethod diff --git a/server/tests-py/test_graphql_queries.py b/server/tests-py/test_graphql_queries.py index c909d43362f30..922177617c06d 100644 --- a/server/tests-py/test_graphql_queries.py +++ b/server/tests-py/test_graphql_queries.py @@ -1,12 +1,12 @@ -import pytest -from validate import assert_response_code, check_query_f, check_query, get_conf_f -from context import PytestConf - import json -import textwrap +import pytest import ruamel.yaml as yaml +import textwrap import warnings +from context import PytestConf +from validate import assert_response_code, check_query_f, get_conf_f + # Mark that all tests in this module can be run as server upgrade tests pytestmark = pytest.mark.allow_server_upgrade_test @@ -1303,6 +1303,7 @@ def _test_relay_pagination(hge_ctx, transport, test_file_prefix, no_of_pages): @pytest.mark.parametrize('transport', ['http', 'websocket']) @pytest.mark.usefixtures('per_method_tests_db_state') +@pytest.mark.admin_secret @pytest.mark.hge_env('HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS', 'false') class TestGraphQLQueryFunctionPermissions: @classmethod diff --git a/server/tests-py/test_jwt.py b/server/tests-py/test_jwt.py index 0459df8b5934f..b597e4f4486d7 100644 --- a/server/tests-py/test_jwt.py +++ b/server/tests-py/test_jwt.py @@ -410,7 +410,7 @@ def gen_rsa_key(): class TestSubscriptionJwtExpiry(object): - def test_jwt_expiry(self, hge_ctx, ws_client): + def test_jwt_expiry(self, hge_ctx, hge_key, ws_client): curr_time = datetime.now() self.claims = { 'sub': '1234567890', @@ -433,7 +433,7 @@ def test_jwt_expiry(self, hge_ctx, ws_client): authz_header = mk_authz_header(hge_ctx.hge_jwt_conf, token) payload = dict() payload['headers'] = authz_header - init_ws_conn(hge_ctx, ws_client, payload) + init_ws_conn(hge_key, ws_client, payload) time.sleep(6) assert ws_client.remote_closed == True, ws_client.remote_closed diff --git a/server/tests-py/test_remote_schema_permissions.py b/server/tests-py/test_remote_schema_permissions.py index 8977eab8cc76a..e2edab7032fa6 100644 --- a/server/tests-py/test_remote_schema_permissions.py +++ b/server/tests-py/test_remote_schema_permissions.py @@ -7,6 +7,7 @@ from validate import check_query_f pytestmark = [ + pytest.mark.admin_secret, pytest.mark.hge_env('HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS', 'true'), ] diff --git a/server/tests-py/test_roles_inheritance.py b/server/tests-py/test_roles_inheritance.py index a3c219d0447b1..b1a7a43c382a7 100644 --- a/server/tests-py/test_roles_inheritance.py +++ b/server/tests-py/test_roles_inheritance.py @@ -6,6 +6,7 @@ from validate import check_query_f pytestmark = [ + pytest.mark.admin_secret, pytest.mark.hge_env('HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS', 'true'), ] diff --git a/server/tests-py/test_subscriptions.py b/server/tests-py/test_subscriptions.py index 954f7f4924fa9..549fe38ceb903 100644 --- a/server/tests-py/test_subscriptions.py +++ b/server/tests-py/test_subscriptions.py @@ -1,111 +1,104 @@ -#!/usr/bin/env python3 - -import datetime -import time -import pytest +from collections import OrderedDict import json +import pytest import queue +from ruamel.yaml import YAML +import time +import uuid + from validate import check_query_f -from collections import OrderedDict from utils import insert_many -from ruamel.yaml import YAML -usefixtures = pytest.mark.usefixtures yaml=YAML(typ='safe', pure=True) +usefixtures = pytest.mark.usefixtures + @pytest.fixture(scope='class') -def ws_conn_init(hge_ctx, ws_client): - init_ws_conn(hge_ctx, ws_client) +def ws_conn_init(hge_key, ws_client): + init_ws_conn(hge_key, ws_client) @pytest.fixture(scope='class') -def ws_conn_init_graphql_ws(hge_ctx, ws_client_graphql_ws): - init_graphql_ws_conn(hge_ctx, ws_client_graphql_ws) +def ws_conn_init_graphql_ws(hge_key, ws_client_graphql_ws): + init_graphql_ws_conn(hge_key, ws_client_graphql_ws) ''' Refer: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init ''' -def init_ws_conn(hge_ctx, ws_client, payload = None): - if payload is None: - payload = {} - if hge_ctx.hge_key is not None: - payload = { - 'headers' : { - 'X-Hasura-Admin-Secret': hge_ctx.hge_key - } - } - +# This is used in other test files! Be careful when modifying it. +def init_ws_conn(hge_key, ws_client, payload = None): init_msg = { 'type': 'connection_init', - 'payload': payload, + 'payload': payload or ws_payload(hge_key), } ws_client.send(init_msg) ev = ws_client.get_ws_event(3) assert ev['type'] == 'connection_ack', ev -def init_graphql_ws_conn(hge_ctx, ws_client_graphql_ws, payload = None): - if payload is None: - payload = {} - if hge_ctx.hge_key is not None: - payload = { - 'headers' : { - 'X-Hasura-Admin-Secret': hge_ctx.hge_key - } - } - +def init_graphql_ws_conn(hge_key, ws_client_graphql_ws): init_msg = { 'type': 'connection_init', - 'payload': payload, + 'payload': ws_payload(hge_key), } ws_client_graphql_ws.send(init_msg) ev = ws_client_graphql_ws.get_ws_event(3) assert ev['type'] == 'connection_ack', ev -def get_explain_graphql_query_response(hge_ctx, query, variables, user_headers = {}): - admin_secret = hge_ctx.hge_key +def ws_payload(hge_key): + if hge_key is not None: + return { + 'headers': { + 'X-Hasura-Admin-Secret': hge_key, + } + } + else: + return {} + +def get_explain_graphql_query_response(hge_ctx, hge_key, query, variables, user_headers = {}): headers = {} - if admin_secret is not None: - headers['X-Hasura-Admin-Secret'] = admin_secret + if hge_key is not None: + headers['X-Hasura-Admin-Secret'] = hge_key request = { 'query': { 'query': query, 'variables': variables }, 'user': user_headers } status_code, response, _ = hge_ctx.anyq('/v1/graphql/explain', request, headers) assert status_code == 200, (request, status_code, response) return response -class TestSubscriptionCtrl(object): +@pytest.mark.no_admin_secret +class TestSubscriptionCtrlWithoutSecret(object): + def test_connection(self, ws_client): + ws_client.recreate_conn() + init_ws_conn(None, ws_client) - def test_init_without_payload(self, hge_ctx, ws_client): - if hge_ctx.hge_key is not None: - pytest.skip("Payload is needed when admin secret is set") - init_msg = { - 'type': 'connection_init' + obj = { + 'type': 'connection_terminate' } - ws_client.send(init_msg) - ev = ws_client.get_ws_event(15) - assert ev['type'] == 'connection_ack', ev - + ws_client.send(obj) + with pytest.raises(queue.Empty): + ws_client.get_ws_event(3) +@pytest.mark.admin_secret +class TestSubscriptionCtrl(object): ''' - Refer: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init + References: + https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init + https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_terminate ''' - def test_init(self, hge_ctx, ws_client): - init_ws_conn(hge_ctx, ws_client) + def test_connection(self, hge_key, ws_client): + ws_client.recreate_conn() + init_ws_conn(hge_key, ws_client) - ''' - Refer: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_terminate - ''' - - def test_connection_terminate(self, hge_ctx, ws_client): obj = { 'type': 'connection_terminate' } ws_client.send(obj) with pytest.raises(queue.Empty): - ev = ws_client.get_ws_event(3) + ws_client.get_ws_event(3) @pytest.mark.backend('mssql', 'postgres') @usefixtures('per_class_tests_db_state', 'ws_conn_init') +@pytest.mark.admin_secret class TestSubscriptionBasic: @classmethod def dir(cls): @@ -129,6 +122,7 @@ def test_connection_error(self, ws_client): ''' def test_start(self, ws_client): + id = str(uuid.uuid4()) query = """ subscription { hge_tests_test_t1(order_by: {c1: desc}, limit: 1) { @@ -138,7 +132,7 @@ def test_start(self, ws_client): } """ obj = { - 'id': '1', + 'id': id, 'payload': { 'query': query }, @@ -148,13 +142,13 @@ def test_start(self, ws_client): ''' Refer: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_data ''' - ev = ws_client.get_ws_query_event('1',15) - assert ev['type'] == 'data' and ev['id'] == '1', ev + ev = ws_client.get_ws_query_event(id, 15) + assert ev['type'] == 'data' and ev['id'] == id, ev ''' Refer https://github.com/apollographql/subscriptions-transport-ws/blob/01e0b2b65df07c52f5831cce5c858966ba095993/src/server.ts#L306 ''' - @pytest.mark.skip(reason="refer https://github.com/hasura/graphql-engine/pull/387#issuecomment-421343098") + @pytest.mark.skip(reason="refer to https://github.com/hasura/graphql-engine/pull/387#issuecomment-421343098") def test_start_duplicate(self, ws_client): self.test_start(ws_client) @@ -187,7 +181,7 @@ def test_start_after_stop(self, ws_client): Refer: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_complete ''' - def test_complete(self, hge_ctx, ws_client): + def test_complete(self, ws_client): query = """ query { hge_tests_test_t1(order_by: {c1: desc}, limit: 1) { @@ -215,6 +209,7 @@ def test_complete(self, hge_ctx, ws_client): ## FIXME: There's an issue with the tests being parametrized with both ## postgres and mssql data sources enabled(See issue #2084). @usefixtures('per_method_tests_db_state', 'ws_conn_init_graphql_ws') +@pytest.mark.admin_secret class TestSubscriptionBasicGraphQLWS: @classmethod def dir(cls): @@ -224,10 +219,10 @@ def dir(cls): def test_negative(self, hge_ctx, transport): check_query_f(hge_ctx, self.dir() + '/negative_test.yaml', transport, gqlws=True) - def test_connection_error(self, hge_ctx, ws_client_graphql_ws): + def test_connection_error(self, hge_key, ws_client_graphql_ws): if ws_client_graphql_ws.get_conn_close_state(): ws_client_graphql_ws.create_conn() - if hge_ctx.hge_key == None: + if hge_key == None: ws_client_graphql_ws.init() else: ws_client_graphql_ws.init_as_admin() @@ -236,13 +231,15 @@ def test_connection_error(self, hge_ctx, ws_client_graphql_ws): ev = ws_client_graphql_ws.get_conn_close_state() assert ev == True, ev - def test_start(self, hge_ctx, ws_client_graphql_ws): + def test_start(self, hge_key, ws_client_graphql_ws): if ws_client_graphql_ws.get_conn_close_state(): ws_client_graphql_ws.create_conn() - if hge_ctx.hge_key == None: + if hge_key == None: ws_client_graphql_ws.init() else: ws_client_graphql_ws.init_as_admin() + + id = str(uuid.uuid4()) query = """ subscription { hge_tests_test_t1(order_by: {c1: desc}, limit: 1) { @@ -252,30 +249,30 @@ def test_start(self, hge_ctx, ws_client_graphql_ws): } """ obj = { - 'id': '1', + 'id': id, 'payload': { 'query': query }, 'type': 'subscribe' } ws_client_graphql_ws.send(obj) - ev = ws_client_graphql_ws.get_ws_query_event('1',15) - assert ev['type'] == 'next' and ev['id'] == '1', ev + ev = ws_client_graphql_ws.get_ws_query_event(id, 15) + assert ev['type'] == 'next' and ev['id'] == id, ev - @pytest.mark.skip(reason="refer https://github.com/hasura/graphql-engine/pull/387#issuecomment-421343098") - def test_start_duplicate(self, hge_ctx, ws_client_graphql_ws): + @pytest.mark.skip(reason="refer to https://github.com/hasura/graphql-engine/pull/387#issuecomment-421343098") + def test_start_duplicate(self, hge_key, ws_client_graphql_ws): if ws_client_graphql_ws.get_conn_close_state(): ws_client_graphql_ws.create_conn() - if hge_ctx.hge_key == None: + if hge_key == None: ws_client_graphql_ws.init() else: ws_client_graphql_ws.init_as_admin() - self.test_start(ws_client_graphql_ws) + self.test_start(hge_key, ws_client_graphql_ws) - def test_stop_without_id(self, hge_ctx, ws_client_graphql_ws): + def test_stop_without_id(self, hge_key, ws_client_graphql_ws): if ws_client_graphql_ws.get_conn_close_state(): ws_client_graphql_ws.create_conn() - if hge_ctx.hge_key == None: + if hge_key == None: ws_client_graphql_ws.init() else: ws_client_graphql_ws.init_as_admin() @@ -287,10 +284,10 @@ def test_stop_without_id(self, hge_ctx, ws_client_graphql_ws): ev = ws_client_graphql_ws.get_conn_close_state() assert ev == True, ev - def test_stop(self, hge_ctx, ws_client_graphql_ws): + def test_stop(self, hge_key, ws_client_graphql_ws): if ws_client_graphql_ws.get_conn_close_state(): ws_client_graphql_ws.create_conn() - if hge_ctx.hge_key == None: + if hge_key == None: ws_client_graphql_ws.init() else: ws_client_graphql_ws.init_as_admin() @@ -303,10 +300,10 @@ def test_stop(self, hge_ctx, ws_client_graphql_ws): with pytest.raises(queue.Empty): ev = ws_client_graphql_ws.get_ws_event(3) - def test_start_after_stop(self, hge_ctx, ws_client_graphql_ws): + def test_start_after_stop(self, hge_key, hge_ctx, ws_client_graphql_ws): if ws_client_graphql_ws.get_conn_close_state(): ws_client_graphql_ws.create_conn() - if hge_ctx.hge_key == None: + if hge_key == None: ws_client_graphql_ws.init() else: ws_client_graphql_ws.init_as_admin() @@ -316,10 +313,11 @@ def test_start_after_stop(self, hge_ctx, ws_client_graphql_ws): ws_client_graphql_ws.clear_queue() self.test_stop(hge_ctx, ws_client_graphql_ws) - def test_complete(self, hge_ctx, ws_client_graphql_ws): + def test_complete(self, hge_key, ws_client_graphql_ws): + id = str(uuid.uuid4()) if ws_client_graphql_ws.get_conn_close_state(): ws_client_graphql_ws.create_conn() - if hge_ctx.hge_key == None: + if hge_key == None: ws_client_graphql_ws.init() else: ws_client_graphql_ws.init_as_admin() @@ -332,27 +330,28 @@ def test_complete(self, hge_ctx, ws_client_graphql_ws): } """ obj = { - 'id': '2', + 'id': id, 'payload': { 'query': query }, 'type': 'subscribe' } ws_client_graphql_ws.send(obj) - ev = ws_client_graphql_ws.get_ws_query_event('2',3) - assert ev['type'] == 'next' and ev['id'] == '2', ev + ev = ws_client_graphql_ws.get_ws_query_event(id, 3) + assert ev['type'] == 'next' and ev['id'] == id, ev # Check for complete type - ev = ws_client_graphql_ws.get_ws_query_event('2',3) - assert ev['type'] == 'complete' and ev['id'] == '2', ev + ev = ws_client_graphql_ws.get_ws_query_event(id, 3) + assert ev['type'] == 'complete' and ev['id'] == id, ev @usefixtures('per_method_tests_db_state','ws_conn_init') +@pytest.mark.admin_secret class TestSubscriptionLiveQueries: @classmethod def dir(cls): return 'queries/subscriptions/live_queries' - def test_live_queries(self, hge_ctx, ws_client): + def test_live_queries(self, hge_key, ws_client): ''' Create connection using connection_init ''' @@ -375,8 +374,8 @@ def test_live_queries(self, hge_ctx, ws_client): for i, resultLimit in queries: query = queryTmplt.replace('{0}',str(i)) headers={} - if hge_ctx.hge_key is not None: - headers['X-Hasura-Admin-Secret'] = hge_ctx.hge_key + if hge_key is not None: + headers['X-Hasura-Admin-Secret'] = hge_key subscrPayload = { 'query': query, 'variables': { 'result_limit': resultLimit } } respLive = ws_client.send_query(subscrPayload, query_id='live_'+str(i), headers=headers, timeout=15) liveQs.append(respLive) @@ -427,14 +426,15 @@ def test_live_queries(self, hge_ctx, ws_client): with pytest.raises(queue.Empty): ev = ws_client.get_ws_event(3) -@usefixtures('per_method_tests_db_state','ws_conn_init','streaming_subscriptions_fixtures') +@usefixtures('per_method_tests_db_state', 'ws_conn_init') +@pytest.mark.hge_env('HASURA_GRAPHQL_EXPERIMENTAL_FEATURES', 'streaming_subscriptions') class TestStreamingSubscription: @classmethod def dir(cls): return 'queries/subscriptions/streaming' - def test_basic_streaming_subscription_existing_static_data(self, hge_ctx, ws_client): + def test_basic_streaming_subscription_existing_static_data(self, hge_key, hge_ctx, ws_client): ''' Create connection using connection_init ''' @@ -455,8 +455,8 @@ def test_basic_streaming_subscription_existing_static_data(self, hge_ctx, ws_cli for i in range(10): articles_to_insert.append({"id": i + 1, "title": "Article title {}".format(i + 1)}) insert_many(hge_ctx, {"schema": "hge_tests", "name": "articles"}, articles_to_insert) - if hge_ctx.hge_key is not None: - headers['X-Hasura-Admin-Secret'] = hge_ctx.hge_key + if hge_key is not None: + headers['X-Hasura-Admin-Secret'] = hge_key subscrPayload = { 'query': query, 'variables': { 'batch_size': 2 } } respLive = ws_client.send_query(subscrPayload, query_id='stream_1', headers=headers, timeout=15) liveQs.append(respLive) @@ -478,7 +478,7 @@ def test_basic_streaming_subscription_existing_static_data(self, hge_ctx, ws_cli with pytest.raises(queue.Empty): ev = ws_client.get_ws_event(3) - def test_streaming_subscriptions_with_concurrent_data_inserts(self, hge_ctx, ws_client): + def test_streaming_subscriptions_with_concurrent_data_inserts(self, ws_client): ''' Create connection using connection_init ''' @@ -534,7 +534,7 @@ def test_streaming_subscriptions_with_concurrent_data_inserts(self, hge_ctx, ws_ with pytest.raises(queue.Empty): ev = ws_client.get_ws_event(3) - def test_streaming_subscription_with_custom_name_set_for_cursor(self, hge_ctx, ws_client): + def test_streaming_subscription_with_custom_name_set_for_cursor(self, hge_key, ws_client): ''' Create connection using connection_init ''' @@ -551,8 +551,8 @@ def test_streaming_subscription_with_custom_name_set_for_cursor(self, hge_ctx, w liveQs = [] headers={} - if hge_ctx.hge_key is not None: - headers['X-Hasura-Admin-Secret'] = hge_ctx.hge_key + if hge_key is not None: + headers['X-Hasura-Admin-Secret'] = hge_key subscrPayload = { 'query': query, 'variables': { 'batch_size': 1 } } respLive = ws_client.send_query(subscrPayload, query_id='stream_1', headers=headers, timeout=15) liveQs.append(respLive) @@ -574,16 +574,15 @@ def test_streaming_subscription_with_custom_name_set_for_cursor(self, hge_ctx, w with pytest.raises(queue.Empty): ev = ws_client.get_ws_event(3) - - -@usefixtures('per_method_tests_db_state','ws_conn_init_graphql_ws') +@usefixtures('per_method_tests_db_state', 'ws_conn_init_graphql_ws') +@pytest.mark.admin_secret class TestSubscriptionLiveQueriesForGraphQLWS: @classmethod def dir(cls): return 'queries/subscriptions/live_queries' - def test_live_queries(self, hge_ctx, ws_client_graphql_ws): + def test_live_queries(self, hge_key, ws_client_graphql_ws): ''' Create connection using connection_init ''' @@ -606,8 +605,8 @@ def test_live_queries(self, hge_ctx, ws_client_graphql_ws): for i, resultLimit in queries: query = queryTmplt.replace('{0}',str(i)) headers={} - if hge_ctx.hge_key is not None: - headers['X-Hasura-Admin-Secret'] = hge_ctx.hge_key + if hge_key is not None: + headers['X-Hasura-Admin-Secret'] = hge_key subscrPayload = { 'query': query, 'variables': { 'result_limit': resultLimit } } respLive = ws_client_graphql_ws.send_query(subscrPayload, query_id='live_'+str(i), headers=headers, timeout=15) liveQs.append(respLive) @@ -658,13 +657,14 @@ def test_live_queries(self, hge_ctx, ws_client_graphql_ws): @pytest.mark.backend('mssql', 'postgres') @usefixtures('per_class_tests_db_state') +@pytest.mark.admin_secret class TestSubscriptionMultiplexingPostgresMSSQL: @classmethod def dir(cls): return 'queries/subscriptions/multiplexing' - def test_extraneous_session_variables_are_discarded_from_query(self, hge_ctx): + def test_extraneous_session_variables_are_discarded_from_query(self, hge_key, hge_ctx): with open(self.dir() + '/articles_query.yaml') as c: config = yaml.load(c) @@ -673,7 +673,7 @@ def test_extraneous_session_variables_are_discarded_from_query(self, hge_ctx): "X-Hasura-Role":"public", "X-Hasura-User-Id":"1" # extraneous session variable } - response = get_explain_graphql_query_response(hge_ctx, query, {}, session_variables) + response = get_explain_graphql_query_response(hge_ctx, hge_key, query, {}, session_variables) # The input session variables should be ignored because the only check for the role is # if `is_public` is `true` assert response["variables"]["session"] == {}, response["variables"] @@ -683,35 +683,37 @@ def test_extraneous_session_variables_are_discarded_from_query(self, hge_ctx): "X-Hasura-User-Id":"1", "X-Hasura-Allowed-Ids":"{1,3,4}" # extraneous session variable } - response = get_explain_graphql_query_response(hge_ctx, query, {}, session_variables) + response = get_explain_graphql_query_response(hge_ctx, hge_key, query, {}, session_variables) # The input session variable should not be ignored because the `user` role can only # select those roles where `user_id = X-Hasura-User-Id` assert response["variables"]["session"] == {'x-hasura-user-id':"1"}, response["variables"] # test case for https://github.com/hasura/graphql-engine-mono/issues/3689 @usefixtures('per_class_tests_db_state') +@pytest.mark.admin_secret class TestSubscriptionMultiplexingPostgres: @classmethod def dir(cls): return 'queries/subscriptions/multiplexing' - def test_simple_variables_are_parameterized(self, hge_ctx): + def test_simple_variables_are_parameterized(self, hge_key, hge_ctx): with open(self.dir() + '/articles_query_simple_variable.yaml') as c: config = yaml.load(c) - response = get_explain_graphql_query_response(hge_ctx, config['query'], config['variables'], {}) + response = get_explain_graphql_query_response(hge_ctx, hge_key, config['query'], config['variables'], {}) assert response["variables"]["synthetic"] == ['1'], response["variables"] - def test_array_variables_are_parameterized(self, hge_ctx): + def test_array_variables_are_parameterized(self, hge_key, hge_ctx): with open(self.dir() + '/articles_query_array_variable.yaml') as c: config = yaml.load(c) - response = get_explain_graphql_query_response(hge_ctx, config['query'], config['variables'], {}) + response = get_explain_graphql_query_response(hge_ctx, hge_key, config['query'], config['variables'], {}) assert response["variables"]["synthetic"] == ['{1,2,3}'], response["variables"] @pytest.mark.backend('postgres') @usefixtures('per_class_tests_db_state', 'ws_conn_init') +@pytest.mark.admin_secret class TestSubscriptionUDFWithSessionArg: """ Test a user-defined function which uses the entire session variables as argument @@ -730,11 +732,11 @@ class TestSubscriptionUDFWithSessionArg: def dir(cls): return 'queries/subscriptions/udf_session_args' - def test_user_defined_function_with_session_argument(self, hge_ctx, ws_client): + def test_user_defined_function_with_session_argument(self, hge_key, ws_client): ws_client.init_as_admin() headers = {'x-hasura-role': 'user', 'x-hasura-user-id': '42'} - if hge_ctx.hge_key is not None: - headers['X-Hasura-Admin-Secret'] = hge_ctx.hge_key + if hge_key is not None: + headers['X-Hasura-Admin-Secret'] = hge_key payload = {'query': self.query} resp = ws_client.send_query(payload, headers=headers, timeout=15) ev = next(resp) @@ -743,6 +745,7 @@ def test_user_defined_function_with_session_argument(self, hge_ctx, ws_client): @pytest.mark.backend('mssql', 'postgres') @usefixtures('per_class_tests_db_state', 'ws_conn_init') +@pytest.mark.admin_secret class TestSubscriptionCustomizedSourceMSSQLPostgres: @classmethod def dir(cls): @@ -754,7 +757,8 @@ def dir(cls): Refer: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_complete ''' - def test_complete(self, hge_ctx, ws_client): + def test_complete(self, ws_client): + id = str(uuid.uuid4()) query = """ subscription MySubscription { a: my_source { @@ -766,7 +770,7 @@ def test_complete(self, hge_ctx, ws_client): } """ obj = { - 'id': '1', + 'id': id, 'payload': { 'query': query }, @@ -776,11 +780,11 @@ def test_complete(self, hge_ctx, ws_client): ''' Refer: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_data ''' - ev = ws_client.get_ws_query_event('1',15) - assert ev['type'] == 'data' and ev['id'] == '1', ev + ev = ws_client.get_ws_query_event(id, 15) + assert ev['type'] == 'data' and ev['id'] == id, ev assert ev['payload']['data']['a'] == OrderedDict([('b', [OrderedDict([('id', 1), ('name', 'Author 1')]), OrderedDict([('id', 2), ('name', 'Author 2')])])]), ev - def test_double_alias(self, hge_ctx, ws_client): + def test_double_alias(self, ws_client): ''' This should give an error even though @_multiple_top_level_fields is specified. The two different aliases for `my_source` mean that we would have to wrap different @@ -819,6 +823,7 @@ def test_double_alias(self, hge_ctx, ws_client): @pytest.mark.backend('mssql') @usefixtures('per_class_tests_db_state', 'ws_conn_init') +@pytest.mark.admin_secret class TestSubscriptionMSSQLChunkedResults: @classmethod def dir(cls): diff --git a/server/tests-py/test_webhook.py b/server/tests-py/test_webhook.py index 4bfe6f6f8111c..ab51268b34309 100644 --- a/server/tests-py/test_webhook.py +++ b/server/tests-py/test_webhook.py @@ -19,7 +19,7 @@ def ws_conn_recreate(ws_client): ws_client.recreate_conn() -def connect_with(hge_ctx, ws_client, headers): +def connect_with(hge_key, ws_client, headers): headers['X-Hasura-Role'] = 'user' headers['X-Hasura-User-Id'] = '1234321' headers['X-Hasura-Auth-Mode'] = 'webhook' @@ -27,22 +27,22 @@ def connect_with(hge_ctx, ws_client, headers): token = base64.b64encode(json.dumps(headers).encode('utf-8')).decode('utf-8') headers['Authorization'] = 'Bearer ' + token payload = {'headers': headers} - init_ws_conn(hge_ctx, ws_client, payload) + init_ws_conn(hge_key, ws_client, payload) EXPIRE_TIME_FORMAT = '%a, %d %b %Y %T GMT' @usefixtures('ws_conn_recreate') class TestWebhookSubscriptionExpiry(object): - def test_expiry_with_no_header(self, hge_ctx, ws_client): + def test_expiry_with_no_header(self, hge_key, ws_client): # no expiry time => the connextion will remain alive - connect_with(hge_ctx, ws_client, {}) + connect_with(hge_key, ws_client, {}) time.sleep(5) assert ws_client.remote_closed == False, ws_client.remote_closed - def test_expiry_with_expires_header(self, hge_ctx, ws_client): + def test_expiry_with_expires_header(self, hge_key, ws_client): exp = datetime.utcnow() + timedelta(seconds=6) - connect_with(hge_ctx, ws_client, { + connect_with(hge_key, ws_client, { 'Expires': exp.strftime(EXPIRE_TIME_FORMAT) }) time.sleep(4) @@ -50,8 +50,8 @@ def test_expiry_with_expires_header(self, hge_ctx, ws_client): time.sleep(4) assert ws_client.remote_closed == True, ws_client.remote_closed - def test_expiry_with_cache_control(self, hge_ctx, ws_client): - connect_with(hge_ctx, ws_client, { + def test_expiry_with_cache_control(self, hge_key, ws_client): + connect_with(hge_key, ws_client, { 'Cache-Control': 'max-age=6' }) time.sleep(4) @@ -59,9 +59,9 @@ def test_expiry_with_cache_control(self, hge_ctx, ws_client): time.sleep(4) assert ws_client.remote_closed == True, ws_client.remote_closed - def test_expiry_with_both(self, hge_ctx, ws_client): + def test_expiry_with_both(self, hge_key, ws_client): exp = datetime.utcnow() + timedelta(seconds=6) - connect_with(hge_ctx, ws_client, { + connect_with(hge_key, ws_client, { 'Expires': exp.strftime(EXPIRE_TIME_FORMAT), 'Cache-Control': 'max-age=10', }) @@ -73,12 +73,12 @@ def test_expiry_with_both(self, hge_ctx, ws_client): time.sleep(4) assert ws_client.remote_closed == True, ws_client.remote_closed - def test_expiry_with_parse_error(self, hge_ctx, ws_client): + def test_expiry_with_parse_error(self, hge_key, ws_client): exp = datetime.utcnow() + timedelta(seconds=3) - connect_with(hge_ctx, ws_client, { + connect_with(hge_key, ws_client, { 'Expires': exp.strftime('%a, %d %m %Y %T UTC'), 'Cache-Control': 'maxage=3', }) # neither will parse, the connection will remain alive time.sleep(5) - assert ws_client.remote_closed == False, ws_client.remote_closed \ No newline at end of file + assert ws_client.remote_closed == False, ws_client.remote_closed