2019-05-10 09:05:11 +03:00
|
|
|
import pytest
|
|
|
|
from validate import check_query
|
2019-05-14 14:20:55 +03:00
|
|
|
import validate
|
2019-05-10 09:05:11 +03:00
|
|
|
from context import GQLWsClient
|
|
|
|
|
2020-02-13 12:14:02 +03:00
|
|
|
usefixtures = pytest.mark.usefixtures
|
2019-05-10 09:05:11 +03:00
|
|
|
|
2020-02-13 12:14:02 +03:00
|
|
|
@usefixtures('per_class_tests_db_state')
|
|
|
|
class TestV1Alpha1GraphQLErrors:
|
2019-05-10 09:05:11 +03:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def dir(cls):
|
|
|
|
return 'queries/graphql_query/v1alpha1/errors'
|
|
|
|
|
2019-05-14 14:20:55 +03:00
|
|
|
def test_v1alpha1_authorization_error(self, hge_ctx):
|
|
|
|
gql_query = """
|
|
|
|
query {
|
|
|
|
author {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
http_conf = {
|
|
|
|
'url': '/v1alpha1/graphql',
|
|
|
|
'status': 200,
|
|
|
|
'query': {'query': gql_query},
|
|
|
|
}
|
|
|
|
|
server/tests-py: Start webhook.py inside the test harness.
We use a helper service to start a webhook-based authentication service for some tests. This moves the initialization of the service out of _test-server.sh_ and into the Python test harness, as a fixture.
In order to do this, I had to make a few changes. The main deviation is that we no longer run _all_ tests against an HGE with this authentication service, just a few (those in _test_webhook.py_). Because this reduced coverage, I have added some more tests there, which actually cover some areas not exacerbated elsewhere (mainly trying to use webhook credentials to talk to an admin-only endpoint).
The webhook service can run both with and without TLS, and decide whether it's necessary to skip one of these based on the arguments passed and how HGE is started, according to the following logic:
* If a TLS CA certificate is passed in, it will run with TLS, otherwise it will skip it.
* If HGE was started externally and a TLS certificate is provided, it will skip running without TLS, as it will assume that HGE was configured to talk to a webhook over HTTPS.
* Some tests should only be run with TLS; this is marked with a `tls_webhook_server` marker.
* Some tests should only be run _without_ TLS; this is marked with a `no_tls_webhook_server` marker.
The actual parameterization of the webhook service configuration is done through test subclasses, because normal pytest parameterization doesn't work with the `hge_fixture_env` hack that we use. Because `hge_fixture_env` is not a sanctioned way of conveying data between fixtures (and, unfortunately, there isn't a sanctioned way of doing this when the fixtures in question may not know about each other directly), parameterizing the `webhook_server` fixture doesn't actually parameterize `hge_server` properly. Subclassing forces this to work correctly.
The certificate generation is moved to a Python fixture, so that we don't have to revoke the CA certificate for _test_webhook_insecure.py_; we can just generate a bogus certificate instead. The CA certificate is still generated in the _test-server.sh_ script, as it needs to be installed into the OS certificate store.
Interestingly, the CA certificate installation wasn't actually working, because the certificates were written to the wrong location. This didn't cause any failures, as we weren't actually testing this behavior. This is now fixed with the other changes.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6363
GitOrigin-RevId: 0f277d374daa64f657257ed2a4c2057c74b911db
2022-10-20 21:58:36 +03:00
|
|
|
if hge_ctx.hge_key is not None and not hge_ctx.webhook and hge_ctx.hge_jwt_key is None:
|
2019-05-14 14:20:55 +03:00
|
|
|
# Test whether it is forbidden when incorrect/no admin_secret is specified
|
|
|
|
validate.test_forbidden_when_admin_secret_reqd(hge_ctx, http_conf)
|
server/tests-py: Start webhook.py inside the test harness.
We use a helper service to start a webhook-based authentication service for some tests. This moves the initialization of the service out of _test-server.sh_ and into the Python test harness, as a fixture.
In order to do this, I had to make a few changes. The main deviation is that we no longer run _all_ tests against an HGE with this authentication service, just a few (those in _test_webhook.py_). Because this reduced coverage, I have added some more tests there, which actually cover some areas not exacerbated elsewhere (mainly trying to use webhook credentials to talk to an admin-only endpoint).
The webhook service can run both with and without TLS, and decide whether it's necessary to skip one of these based on the arguments passed and how HGE is started, according to the following logic:
* If a TLS CA certificate is passed in, it will run with TLS, otherwise it will skip it.
* If HGE was started externally and a TLS certificate is provided, it will skip running without TLS, as it will assume that HGE was configured to talk to a webhook over HTTPS.
* Some tests should only be run with TLS; this is marked with a `tls_webhook_server` marker.
* Some tests should only be run _without_ TLS; this is marked with a `no_tls_webhook_server` marker.
The actual parameterization of the webhook service configuration is done through test subclasses, because normal pytest parameterization doesn't work with the `hge_fixture_env` hack that we use. Because `hge_fixture_env` is not a sanctioned way of conveying data between fixtures (and, unfortunately, there isn't a sanctioned way of doing this when the fixtures in question may not know about each other directly), parameterizing the `webhook_server` fixture doesn't actually parameterize `hge_server` properly. Subclassing forces this to work correctly.
The certificate generation is moved to a Python fixture, so that we don't have to revoke the CA certificate for _test_webhook_insecure.py_; we can just generate a bogus certificate instead. The CA certificate is still generated in the _test-server.sh_ script, as it needs to be installed into the OS certificate store.
Interestingly, the CA certificate installation wasn't actually working, because the certificates were written to the wrong location. This didn't cause any failures, as we weren't actually testing this behavior. This is now fixed with the other changes.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6363
GitOrigin-RevId: 0f277d374daa64f657257ed2a4c2057c74b911db
2022-10-20 21:58:36 +03:00
|
|
|
elif hge_ctx.webhook:
|
2019-05-14 14:20:55 +03:00
|
|
|
# Check whether the output is also forbidden when webhook returns forbidden
|
server/tests-py: Start webhook.py inside the test harness.
We use a helper service to start a webhook-based authentication service for some tests. This moves the initialization of the service out of _test-server.sh_ and into the Python test harness, as a fixture.
In order to do this, I had to make a few changes. The main deviation is that we no longer run _all_ tests against an HGE with this authentication service, just a few (those in _test_webhook.py_). Because this reduced coverage, I have added some more tests there, which actually cover some areas not exacerbated elsewhere (mainly trying to use webhook credentials to talk to an admin-only endpoint).
The webhook service can run both with and without TLS, and decide whether it's necessary to skip one of these based on the arguments passed and how HGE is started, according to the following logic:
* If a TLS CA certificate is passed in, it will run with TLS, otherwise it will skip it.
* If HGE was started externally and a TLS certificate is provided, it will skip running without TLS, as it will assume that HGE was configured to talk to a webhook over HTTPS.
* Some tests should only be run with TLS; this is marked with a `tls_webhook_server` marker.
* Some tests should only be run _without_ TLS; this is marked with a `no_tls_webhook_server` marker.
The actual parameterization of the webhook service configuration is done through test subclasses, because normal pytest parameterization doesn't work with the `hge_fixture_env` hack that we use. Because `hge_fixture_env` is not a sanctioned way of conveying data between fixtures (and, unfortunately, there isn't a sanctioned way of doing this when the fixtures in question may not know about each other directly), parameterizing the `webhook_server` fixture doesn't actually parameterize `hge_server` properly. Subclassing forces this to work correctly.
The certificate generation is moved to a Python fixture, so that we don't have to revoke the CA certificate for _test_webhook_insecure.py_; we can just generate a bogus certificate instead. The CA certificate is still generated in the _test-server.sh_ script, as it needs to be installed into the OS certificate store.
Interestingly, the CA certificate installation wasn't actually working, because the certificates were written to the wrong location. This didn't cause any failures, as we weren't actually testing this behavior. This is now fixed with the other changes.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6363
GitOrigin-RevId: 0f277d374daa64f657257ed2a4c2057c74b911db
2022-10-20 21:58:36 +03:00
|
|
|
validate.test_forbidden_webhook(hge_ctx, http_conf)
|
2019-05-14 14:20:55 +03:00
|
|
|
else:
|
|
|
|
assert True
|
|
|
|
|
|
|
|
|
2019-05-10 09:05:11 +03:00
|
|
|
@pytest.mark.parametrize('transport', ['http', 'websocket'])
|
|
|
|
def test_v1alpha1_validation_error(self, hge_ctx, transport):
|
|
|
|
gql_query = """
|
|
|
|
query {
|
|
|
|
author {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
notPresentCol
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
http_conf = {
|
|
|
|
'url': '/v1alpha1/graphql',
|
|
|
|
'status': 400,
|
|
|
|
'query': {'query': gql_query},
|
|
|
|
'response': {
|
|
|
|
"errors": [{
|
|
|
|
"extensions": {
|
|
|
|
"path": "$.selectionSet.author.selectionSet.notPresentCol",
|
|
|
|
"code": "validation-failed"
|
|
|
|
},
|
An `ErrorMessage` type, to encapsulate.
This introduces an `ErrorMessage` newtype which wraps `Text` in a manner which is designed to be easy to construct, and difficult to deconstruct.
It provides functionality similar to `Data.Text.Extended`, but designed _only_ for error messages. Error messages are constructed through `fromString`, concatenation, or the `toErrorValue` function, which is designed to be overridden for all meaningful domain types that might show up in an error message. Notably, there are not and should never be instances of `ToErrorValue` for `String`, `Text`, `Int`, etc. This is so that we correctly represent the value in a way that is specific to its type. For example, all `Name` values (from the _graphql-parser-hs_ library) are single-quoted now; no exceptions.
I have mostly had to add `instance ToErrorValue` for various backend types (and also add newtypes where necessary). Some of these are not strictly necessary for this changeset, as I had bigger aspirations when I started. These aspirations have been tempered by trying and failing twice.
As such, in this changeset, I have started by introducing this type to the `parseError` and `parseErrorWith` functions. In the future, I would like to extend this to the `QErr` record and the various `throwError` functions, but this is a much larger task and should probably be done in stages.
For now, `toErrorMessage` and `fromErrorMessage` are provided for conversion to and from `Text`, but the intent is to stop exporting these once all error messages are converted to the new type.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5018
GitOrigin-RevId: 84b37e238992e4312255a87ca44f41af65e2d89a
2022-07-18 23:26:01 +03:00
|
|
|
"message": "field 'notPresentCol' not found in type: 'author'"
|
2019-05-10 09:05:11 +03:00
|
|
|
}]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ws_conf = {
|
|
|
|
'url': '/v1alpha1/graphql',
|
|
|
|
'query': {'query': gql_query},
|
|
|
|
'response': {
|
|
|
|
"path": "$.selectionSet.author.selectionSet.notPresentCol",
|
|
|
|
"code": "validation-failed",
|
An `ErrorMessage` type, to encapsulate.
This introduces an `ErrorMessage` newtype which wraps `Text` in a manner which is designed to be easy to construct, and difficult to deconstruct.
It provides functionality similar to `Data.Text.Extended`, but designed _only_ for error messages. Error messages are constructed through `fromString`, concatenation, or the `toErrorValue` function, which is designed to be overridden for all meaningful domain types that might show up in an error message. Notably, there are not and should never be instances of `ToErrorValue` for `String`, `Text`, `Int`, etc. This is so that we correctly represent the value in a way that is specific to its type. For example, all `Name` values (from the _graphql-parser-hs_ library) are single-quoted now; no exceptions.
I have mostly had to add `instance ToErrorValue` for various backend types (and also add newtypes where necessary). Some of these are not strictly necessary for this changeset, as I had bigger aspirations when I started. These aspirations have been tempered by trying and failing twice.
As such, in this changeset, I have started by introducing this type to the `parseError` and `parseErrorWith` functions. In the future, I would like to extend this to the `QErr` record and the various `throwError` functions, but this is a much larger task and should probably be done in stages.
For now, `toErrorMessage` and `fromErrorMessage` are provided for conversion to and from `Text`, but the intent is to stop exporting these once all error messages are converted to the new type.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5018
GitOrigin-RevId: 84b37e238992e4312255a87ca44f41af65e2d89a
2022-07-18 23:26:01 +03:00
|
|
|
"error": "field 'notPresentCol' not found in type: 'author'"
|
2019-05-10 09:05:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if transport == 'http':
|
|
|
|
check_query(hge_ctx, http_conf, transport)
|
|
|
|
elif transport == 'websocket':
|
|
|
|
check_query(hge_ctx, ws_conf, transport)
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('transport', ['http', 'websocket'])
|
|
|
|
def test_v1alpha1_execution_error(self, hge_ctx, transport):
|
|
|
|
mutation = """
|
|
|
|
mutation {
|
|
|
|
insert_article (objects: [
|
|
|
|
{
|
|
|
|
title: "test 3"
|
|
|
|
content: "test 3 content"
|
|
|
|
author_id: 44
|
|
|
|
is_published: false
|
|
|
|
}
|
|
|
|
]) {
|
|
|
|
returning {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
http_conf = {
|
|
|
|
'url': '/v1alpha1/graphql',
|
|
|
|
'status': 400,
|
|
|
|
'query': {'query': mutation},
|
|
|
|
'response': {
|
|
|
|
"errors": [{
|
|
|
|
"extensions": {
|
|
|
|
"path": "$.selectionSet.insert_article.args.objects",
|
|
|
|
"code": "constraint-violation"
|
|
|
|
},
|
|
|
|
"message": "Foreign key violation. insert or update on table \"article\" violates foreign key constraint \"article_author_id_fkey\""
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ws_conf = {
|
|
|
|
'url': '/v1alpha1/graphql',
|
|
|
|
'query': {'query': mutation},
|
|
|
|
'response': {
|
|
|
|
'data': None,
|
|
|
|
"errors": [{
|
|
|
|
"path": "$.selectionSet.insert_article.args.objects",
|
|
|
|
"error": "Foreign key violation. insert or update on table \"article\" violates foreign key constraint \"article_author_id_fkey\"",
|
|
|
|
"code": "constraint-violation"
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if transport == 'http':
|
|
|
|
check_query(hge_ctx, http_conf, transport)
|
|
|
|
elif transport == 'websocket':
|
|
|
|
check_query(hge_ctx, ws_conf, transport)
|
|
|
|
|
|
|
|
|
|
|
|
def test_v1alpha1_ws_start_error(self, hge_ctx):
|
|
|
|
ws_client = GQLWsClient(hge_ctx, '/v1alpha1/graphql')
|
|
|
|
query = {'query': '{ author { name } }'}
|
|
|
|
frame = {
|
|
|
|
'id': '1',
|
|
|
|
'type': 'start',
|
|
|
|
'payload': query
|
|
|
|
}
|
|
|
|
ws_client.ws_active_query_ids.add('1')
|
|
|
|
ws_client.send(frame)
|
|
|
|
resp = ws_client.get_ws_query_event('1', 10)
|
|
|
|
print(resp)
|
|
|
|
assert 'type' in resp
|
|
|
|
assert resp['type'] == 'error'
|
|
|
|
assert 'payload' in resp
|
|
|
|
assert resp['payload'] == {
|
|
|
|
'path': '$',
|
|
|
|
'error': 'start received before the connection is initialised',
|
|
|
|
'code': 'start-failed'
|
|
|
|
}
|