graphql-engine/server/tests-py
Philip Lykke Carlsen 369d1ab2f1 Only overwrite expectations if the flag is actually set
## Description

In https://hasurahq.slack.com/archives/C01RZPEPF0W/p1646672587226349 @rakeshkky discovered a flaw in the SQL explain tests golden tests that would overwrite expectation files even when not instructed to do so.

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/3912
GitOrigin-RevId: 0c5acc6b96c7b619915d26ec8f78547959d5f78d
2022-03-07 22:17:52 +00:00
..
pgdump server: add tests ensuring the correct functioning of all endpoints based on user roles 2021-07-16 16:09:25 +00:00
queries server/bigquery: apply 'order by' to 'array_agg' projection inside an array relationship join sub-query 2022-03-07 10:13:02 +00:00
remote_schemas/nodejs server/postgres: Support scalar computed fields in remote joins 2021-07-12 16:04:37 +00:00
test_tests Test result ordering, add --accept test mode to automatically accept changed test cases 2019-11-05 15:15:25 -06:00
webhook/insecure [server] coalesce multiple run_sql calls in tests (#270) 2021-01-06 16:07:22 +00:00
.gitignore server: support EdDSA keys for JWT 2021-08-12 01:54:06 +00:00
auth_webhook_server.py server: forward auth webhook set-cookies header on response 2021-11-09 12:01:31 +00:00
conftest.py server: remove hdb_lib and add tests for read-only source 2022-02-21 10:00:00 +00:00
context.py server: Support returning scalar types in actions 2022-02-15 14:40:34 +00:00
graphql_server.py server/remote-schema: Include OperationName in the request sent to remote schemas 2022-02-16 07:55:19 +00:00
jwk_server.py server: Refresh JWKs maximum once per second 2022-01-28 00:18:56 +00:00
pytest.ini run default tests in test_server_upgrade (#3718) 2020-02-13 14:44:02 +05:30
README.md docs/server: Minor formatting and fix broken links 2022-03-01 10:53:33 +00:00
remote_server.py actions: fix behaviour when using remote relationship in mutation action's relationship (#4982) 2020-06-05 17:33:18 +05:30
requirements-top-level.txt server: bump pytest version 2022-01-13 10:30:55 +00:00
requirements.txt bump python test suite dependencies 2022-01-17 07:40:56 +00:00
super_classes.py fix validation of null values, closes #1981 (#2057) 2019-04-24 13:19:39 +05:30
test_actions.py Nested action joins 2022-03-03 03:44:20 +00:00
test_allowlist_queries.py server, pro: add support for per-role allowlists 2022-02-08 16:54:49 +00:00
test_apis_disabled.py disable explain end-points when metadata API is disabled, fix #3717 (#3751) 2020-01-22 16:20:49 +05:30
test_auth_webhook_cookie.py server: forward auth webhook set-cookies header on response 2021-11-09 12:01:31 +00:00
test_compat.py remove usage of deprecated 'pytest.config' (#3434) 2019-11-29 10:44:26 +05:30
test_compression.py bump python test suite dependencies 2022-01-17 07:40:56 +00:00
test_config_api.py Adding multiple jwt secrets (incorporating provenance requirements) 2022-02-13 23:34:39 +00:00
test_cors.py remove usage of deprecated 'pytest.config' (#3434) 2019-11-29 10:44:26 +05:30
test_dev_endpoints.py server: add tests ensuring the correct functioning of all endpoints based on user roles 2021-07-16 16:09:25 +00:00
test_endpoints.py server: validate REST endpoint queries after metadata change 2022-02-08 04:47:52 +00:00
test_events.py Feature/removable request transform body and modified request transform API 2022-02-17 04:37:18 +00:00
test_graphql_introspection.py server: Customize root field GraphQL schema descriptions 2022-02-28 07:50:12 +00:00
test_graphql_mutations.py server/mssql: improve database exception handling and better API errors 2022-02-07 14:12:55 +00:00
test_graphql_queries.py Only overwrite expectations if the flag is actually set 2022-03-07 22:17:52 +00:00
test_graphql_read_only_source.py server: remove hdb_lib and add tests for read-only source 2022-02-21 10:00:00 +00:00
test_heterogeneous.py server: heterogeneous execution of GraphQL queries (#5869) 2020-10-07 10:23:17 +00:00
test_horizontal_scale.py server, pro: add support for per-role allowlists 2022-02-08 16:54:49 +00:00
test_inconsistent_meta.py server: add inconsistency information in reload_metadata API call 2022-03-03 13:34:44 +00:00
test_jwk.py Try to make JWK refresh integration tests more reliable in CI 2022-02-07 09:13:09 +00:00
test_jwt_claims_map.py bump python test suite dependencies 2022-01-17 07:40:56 +00:00
test_jwt.py bump python test suite dependencies 2022-01-17 07:40:56 +00:00
test_logging.py server: add parameterized query hash for websocket logs 2021-09-06 12:27:48 +00:00
test_metadata.py Adds x-www-form-urlencoded body transformation 2022-03-02 19:43:22 +00:00
test_openapi.py Fixed GQL aliases not being respected in the OpenAPI schema 2022-01-27 05:55:55 +00:00
test_pg_dump.py bump python test suite dependencies 2022-01-17 07:40:56 +00:00
test_query_cache.py server: disable caching for actions with forward client headers enabled 2021-04-13 07:01:34 +00:00
test_remote_relationships.py simplified schema cache representation of remote relationships 2021-12-21 23:15:50 +00:00
test_remote_schema_permissions.py Fix remote relationship invalid type name issue (fix hasura/graphql-engine#8002) 2022-01-27 14:34:01 +00:00
test_roles_inheritance.py Always enable inherited roles tests 2021-09-16 06:24:54 +00:00
test_scheduled_triggers.py server: add a new metadata API to return all the cron triggers 2022-01-27 06:44:37 +00:00
test_schema_duplication.py add tests 2020-09-01 17:13:42 +05:30
test_schema_stitching.py server: add inconsistency information in reload_metadata API call 2022-03-03 13:34:44 +00:00
test_startup_db_calls.py server: fix multiple calls to database on startup 2022-02-09 05:14:29 +00:00
test_subscriptions.py bump python test suite dependencies 2022-01-17 07:40:56 +00:00
test_tests.py server: hasura on PG v13 (#125) 2020-12-01 12:22:42 +00:00
test_v1_queries.py server/mssql: move mssql_run_sql test cases from v1/run_sql to v2/mssql/run_sql 2022-01-18 02:21:04 +00:00
test_v1alpha1_endpoint.py run default tests in test_server_upgrade (#3718) 2020-02-13 14:44:02 +05:30
test_v2_queries.py server/mssql: improve database exception handling and better API errors 2022-02-07 14:12:55 +00:00
test_validation.py server: fix validation of query variables (fixes hasura/graphql-engine#6867) 2021-05-04 17:58:43 +00:00
test_version.py server: add logs for version and healthz endpoints 2021-06-08 14:28:52 +00:00
test_webhook_insecure.py run default tests in test_server_upgrade (#3718) 2020-02-13 14:44:02 +05:30
test_webhook_request_context.py server: call auth webhook even if the request fails to parse 2021-10-28 18:43:47 +00:00
test_webhook.py server: forward auth webhook set-cookies header on response 2021-11-09 12:01:31 +00:00
test_websocket_init_cookie.py server: forward auth webhook set-cookies header on response 2021-11-09 12:01:31 +00:00
utils.py server: restore proper batching behavior in event trigger processing (#1237) 2021-04-29 04:02:05 +00:00
validate.py server, pro: add support for per-role allowlists 2022-02-08 16:54:49 +00:00
webhook.py pass gql requests into auth webhook POST body (#149) 2021-02-03 07:11:39 +00:00
webserver.py server: forward auth webhook set-cookies header on response 2021-11-09 12:01:31 +00:00

Python Integration Test Suite

This document describes the Python integration test suite. Please consult the server/CONTRIBUTING document for general information on the overall test setup and other testing suites.

This document describes running and writing tests, as well as some information on how to update test dependencies.

Running tests

Tests can be run using the dev.sh script or directly using pytest.

Please note that running the BigQuery tests requires a few manual steps.

Running tests via dev.sh

The easiest way to run the test suite is to do:

scripts/dev.sh test --integration

NOTE: this only runs the tests for Postgres. If you want to run tests for a different backend, use:

scripts/dev.sh test --integration --backend mssql

Available options are documented in scripts/parse-pytest-backend:

  • postgres (default)
  • bigquery (see section below)
  • citus
  • mssql
  • mysql

Filtering tests

You can filter tests by using -k <name>. Note that <name> is case- insensitive.

scripts/dev.sh test --integration --backend mssql -k MSSQL

Note that you can also use expressions here, for example:

scripts/dev.sh test --integration --backend mssql -k "MSSQL and not Permission"

See pytest docs for more details.

Failures

If you want to stop after the first test failure you can pass -x:

scripts/dev.sh test --integration --backend mssql -k MSSQL -x

Verbosity

You can increase or decrease the log verbosity by adding -v or -q to the command.

Running tests directly

WARNING: running tests manually will force skipping of some tests. dev.sh deals with setting up some environment variables which decide how and if some of the tests are executed.

  1. To run the Python tests, youll need to install the necessary Python dependencies first. It is recommended that you do this in a self-contained Python venv, which is supported by Python 3.3+ out of the box. To create one, run:

    python3 -m venv .python-venv
    

    (The second argument names a directory where the venv sandbox will be created; it can be anything you like, but .python-venv is .gitignored.)

    With the venv created, you can enter into it in your current shell session by running:

    source .python-venv/bin/activate
    

    (Source .python-venv/bin/activate.fish instead if you are using fish as your shell.)

  2. Install the necessary Python dependencies into the sandbox:

    pip3 install -r tests-py/requirements.txt
    
  3. Install the dependencies for the Node server used by the remote schema tests:

    (cd tests-py/remote_schemas/nodejs && npm ci)
    
  4. Start an instance of graphql-engine for the test suite to use:

    env EVENT_WEBHOOK_HEADER=MyEnvValue \
        WEBHOOK_FROM_ENV=http://localhost:5592/ \
        SCHEDULED_TRIGGERS_WEBHOOK_DOMAIN=http://127.0.0.1:5594 \
      cabal new-run -- exe:graphql-engine \
        --database-url='postgres://<user>:<password>@<host>:<port>/<dbname>' \
        serve --stringify-numeric-types
    

    Optionally, replace the --database-url parameter with --metadata-database-url to enable testing against multiple sources.

    The environment variables are needed for a couple of tests, and the --stringify-numeric-types option is used to avoid the need to do floating-point comparisons.

  5. Optionally, add more sources to test against:

    If the tests include more sources (e.g., by using -k MSSQL or MySQL), then you can use the following commands to add sources to your running graphql instance:

    # Add a Postgres source
    curl "$METADATA_URL" \
    --data-raw '{"type":"pg_add_source","args":{"name":"default","configuration":{"connection_info":{"database_url":"'"$POSTGRES_DB_URL"'","pool_settings":{}}}}}'
    
    # Add a SQL Server source
    curl "$METADATA_URL" \
    --data-raw '{"type":"mssql_add_source","args":{"name":"mssql","configuration":{"connection_info":{"connection_string":"'"$MSSQL_DB_URL"'","pool_settings":{}}}}}'
    
    # Optionally verify sources have been added
    curl "$METADATA_URL" --data-raw '{"type":"export_metadata","args":{}}'
    
  6. With the server running, run the test suite:

    cd tests-py
    pytest --hge-urls http://localhost:8080 \
           --pg-urls 'postgres://<user>:<password>@<host>:<port>/<dbname>'
    

This will run all the tests, which can take a couple minutes (especially since some of the tests are slow). You can configure pytest to run only a subset of the tests; see the pytest documentation for more details.

Some other useful points of note:

  • It is recommended to use a separate Postgres database for testing, since the tests will drop and recreate the hdb_catalog schema, and they may fail if certain tables already exist. (Its also useful to be able to just drop and recreate the entire test database if it somehow gets into a bad state.)

  • You can pass the -v or -vv options to pytest to enable more verbose output while running the tests and in test failures. You can also pass the -l option to display the current values of Python local variables in test failures.

  • Tests can be run against a specific backend (defaulting to Postgres) with the backend flag, for example:

      pytest --hge-urls http://localhost:8080 \
             --pg-urls 'postgres://<user>:<password>@<host>:<port>/<dbname>'
             --backend mssql -k TestGraphQLQueryBasicCommon
    

For more details, please consult pytest --help.

Running BigQuery tests

Running integration tests against a BigQuery data source is a little more involved due to the necessary service account requirements:

HASURA_BIGQUERY_PROJECT_ID=# the project ID of the service account
HASURA_BIGQUERY_SERVICE_ACCOUNT_EMAIL=# eg. "<<SERVICE_ACCOUNT_NAME>>@<<PROJECT_NAME>>.iam.gserviceaccount.com"
HASURA_BIGQUERY_SERVICE_ACCOUNT_FILE=# the filepath to the downloaded service account key

Before running the test suite either manually or via dev.sh:

  1. Ensure you have access to a Google Cloud Console service account. Store the project ID and account email in HASURA_BIGQUERY_PROJECT_ID and (optional) HASURA_BIGQUERY_SERVICE_ACCOUNT_EMAIL variables.
  2. Create and download a new service account key. Store the filepath in a HASURA_BIGQUERY_SERVICE_ACCOUNT_FILE variable.
  3. Login and activate the service account, if it is not already activated.
  4. Verify the service account is accessible via the BigQuery API:
    1. Run source scripts/verify-bigquery-creds.sh $HASURA_BIGQUERY_PROJECT_ID $HASURA_BIGQUERY_SERVICE_ACCOUNT_FILE $HASURA_BIGQUERY_SERVICE_ACCOUNT_EMAIL. If the query succeeds, the service account is setup correctly to run tests against BigQuery locally.
  5. Finally, run the BigQuery test suite with HASURA_BIGQUERY_SERVICE_ACCOUNT_FILE and HASURA_BIGQUERY_PROJECT_ID environment variables set. For example:
scripts/dev.sh test --integration --backend bigquery -k TestGraphQLQueryBasicBigquery

Tests structure

  • Tests are grouped as test classes in test modules (names starting with test_)

  • The configuration files (if needed) for the tests in a class are usually kept in one folder.

    • The folder name is usually either the dir variable or the dir() function
  • Some tests (like in test_graphql_queries.py) requires a setup and teardown per class.

    • Here we are extending the DefaultTestSelectQueries class.
    • This class defines a fixture which will run the configurations in setup.yaml and teardown.yaml once per class
    • Extending test class should define a function name dir(), which returns the configuration folder
  • For mutation tests (like in test_graphql_mutations.py)

    • We need a schema_setup and schema_teardown per class
    • And values_setup and values_teardown per test
    • Doing schema setup and teardown per test is expensive.
    • We are extending the DefaultTestMutations class for this.
    • This class defines a fixture which will run the configuration in setup.yaml and teardown.yaml once per class.
    • Another fixture defined in this class runs the configuration in values_setup.yaml and values_teardown.yaml once per class.

Writing python tests

  1. Check whether the test you intend to write already exists in the test suite, so that there will be no duplicate tests or the existing test will just need to be modified.

  2. All the tests use setup and teardown, the setup step is used to initialize the graphql-engine and the database in a certain state after which the tests should be run. After the tests are run, the state needs to be cleared, which should be done in the teardown step. The setup and teardown is localised for every python test class.

    See TestCreateAndDelete in test_events.py for reference.

  3. The setup and teardown can be configured to run before and after every test in a test class or run before and after running all the tests in a class. Depending on the use case, there are different fixtures like per_class_tests_db_state,per_method_tests_db_state defined in the conftest.py file.

  4. Sometimes, it's required to run the graphql-engine with in a different configuration only for a particular set of tests. In this case, these tests should be run only when the graphql-engine is run with the said configuration and should be skipped in other graphql-engine configurations. This can be done by accepting a new command-line flag from the pytest command and depending on the value or presence of the flag, the tests should be run accordingly. After adding this kind of a test, a new section needs to be added in the test-server.sh. This new section's name should also be added in the server-test-names.txt file, otherwise the test will not be run in the CI.

    For example,

    The tests in the test_remote_schema_permissions.py are only to be run when the remote schema permissions are enabled in the graphql-engine and when it's not set, these tests should be skipped. Now, to run these tests we parse a command line option from pytest called (--enable-remote-schema-permissions) and the presence of this flag means that we need to run these tests. When the tests are run with this command line option, it's assumed that the server has enabled remote schema permissions.

Adding test support for a new backend

The current workflow for supporting a new backend in integration tests is as follows:

  1. Add functions to launch and cleanup a server for the new backend. Example.
  2. Connect to the database you've just launched. Example.
  3. Add setup and teardown files:
    1. setup_<backend>: for v1/query or metadata queries such as <backend>_track_table. Example.
    2. schema_setup_<backend>: for v2/query queries such as <backend>_run_sql. Example.
    3. teardown_<backend> and cleardb_<backend>
    4. Important: filename suffixes should be the same as the value thats being passed to —backend; that's how the files are looked up.
  4. Specify a backend parameter for the per_backend_tests fixture, parameterised by backend. Example.

Note: When teardown is not disabled (via skip_teardown(*) , in which case, this phase is skipped entirely), teardown.yaml always runs before schema_teardown.yaml, even if the tests fail. See setup_and_teardown in server/tests-py/conftest.py for the full source code/logic.

(*): See setup_and_teardown_v1q and setup_and_teardown_v2q in conftest.py for more details.

This means, for example, that if teardown.yaml untracks a table, and schema_teardown.yaml runs raw SQL to drop the table, both would succeed (assuming the table is tracked/exists).

Test suite naming convention The current convention is to indicate the backend(s) tests can be run against in the class name. For example:

  • TestGraphQLQueryBasicMySQL for tests that can only be run on MySQL
  • TestGraphQLQueryBasicCommon for tests that can be run against more than one backend
  • If a test class doesn't have a suffix specifying the backend, nor does its name end in Common, then it is likely a test written pre-v2.0 that can only be run on Postgres

This naming convention enables easier test filtering with pytest command line flags.

The backend-specific and common test suites are disjoint; for example, run pytest --integration -k "Common or MySQL" --backend mysql to run all MySQL tests.

Note that --backend does not interact with the selection of tests. You will generally have to combine --backend with -k.

Updating Python requirements

The packages/requirements are documented in two files:

  • server/tests-py/requirements-top-level.txt
  • server/tests-py/requirements.txt

The server/tests-py/requirements-top-level.txt file is the main file. It contains the direct dependencies along with version requirements we know we should be careful about.

The server/tests-py/requirements.txt file is the lock file. It holds version numbers for all direct and transitive dependencies. This file can be re-generated by:

  1. alter server/tests-py/requirements-top-level.txt
  2. remove server/tests-py/requirements.txt
  3. run dev.sh test --integration
  4. update DEVSH_VERSION in scripts/dev.sh to force reinstall these dependencies

Steps 3 can be done manually:

pip3 install -r requirements-top-level.txt
pip3 freeze > requirements.txt