server: hasura on PG v13 (#125)

GitOrigin-RevId: 00fd91c250bcf3dc7ee638e3b152e0dab7281de7
This commit is contained in:
hasura-bot 2020-12-01 17:51:45 +05:30
parent 3736a569ad
commit f6bd354b40
10 changed files with 177 additions and 25 deletions

View File

@ -1,7 +1,7 @@
# anchor refs to be used elsewhere
refs:
constants:
- &server_builder_image hasura/graphql-engine-server-builder:2020-08-26
- &server_builder_image hasura/graphql-engine-server-builder:2020-11-05
skip_job_on_ciignore: &skip_job_on_ciignore
run:
name: checking if job should be terminated or not
@ -79,7 +79,7 @@ refs:
setup_remote_docker: &setup_remote_docker
setup_remote_docker:
version: 17.09.0-ce
version: 19.03.13
docker_layer_caching: true
# ref pg environment for testing
@ -105,7 +105,7 @@ refs:
command: |
mkdir -p /usr/share/man/man{1,7}
apt-get update
apt install --yes pgbouncer jq curl postgresql-client-12
apt install --yes pgbouncer jq curl postgresql-client-13
- run:
name: Ensure databases are present
environment:
@ -171,7 +171,7 @@ jobs:
name: Install latest postgresql client tools
command: |
apt-get -y update
apt-get -y install postgresql-client-12
apt-get -y install postgresql-client-13
- run:
name: Build the binary
working_directory: ./server
@ -236,6 +236,16 @@ jobs:
command: echo 'all server tests passed!'
# pytest the server with postgres versions >= 9.5
test_server_pg_13:
<<: *test_server
environment:
PG_VERSION: "13"
POSTGIS_VERSION: "3.0.0"
docker:
- image: *server_builder_image
- image: hasura/postgres-13.0-alpine-postgis3
<<: *test_pg_env
test_server_pg_12:
<<: *test_server
environment:
@ -609,6 +619,10 @@ workflows:
requires:
- build_server
- build_console
- test_server_pg_13:
<<: *filter_only_vtags
requires:
- build_server
- test_server_pg_12:
<<: *filter_only_vtags
requires:
@ -640,6 +654,7 @@ workflows:
- all_server_tests_pass:
<<: *filter_only_vtags
requires:
- test_server_pg_13
- test_server_pg_12
- test_server_pg_11
- test_server_pg_10

View File

@ -0,0 +1,2 @@
FROM circleci/postgres:13.0-postgis
ENV POSTGRES_PASSWORD="postgres"

View File

@ -1,9 +1,9 @@
# Don't update this without updating the
# packager imager of graphql-engine
# packager image of graphql-engine
FROM haskell:8.10.2-stretch
ARG docker_ver="17.09.0-ce"
ARG postgres_ver="12"
ARG docker_ver="19.03.13"
ARG postgres_ver="13"
ARG node_ver="12.x"
# Install GNU make, curl, git and docker client. Required to build the server

View File

@ -72,6 +72,7 @@ This release contains the [PDV refactor (#4111)](https://github.com/hasura/graph
- console: select first operator by default on the browse rows screen (close #5729) (#6032)
- cli: add missing global flags for seed command (#5565)
- cli: allow seeds as alias for seed command (#5693)
- build: add `test_server_pg_13` to the CI to run the server tests on Postgres v13 (#6070)
## v1.3.3

View File

@ -5,7 +5,7 @@ export VERSION
registry := hasura
packager_ver := 20190731
pg_dump_ver := 12
pg_dump_ver := 13
build_output := /build/_server_output
# Getting access to the built products with the `cabal v2-*` commands is really awkward; see

View File

@ -13,9 +13,17 @@ query:
}
}
response:
errors:
- extensions:
code: constraint-violation
path: $.selectionSet.insert_address.args.objects
message: "Not-NULL violation. null value in column \"door_no\" violates not-null constraint"
allowed_responses:
- response:
errors:
- extensions:
code: constraint-violation
path: $.selectionSet.insert_address.args.objects
message: "Not-NULL violation. null value in column \"door_no\" violates not-null constraint"
# postgres 13
- response:
errors:
- extensions:
code: constraint-violation
path: $.selectionSet.insert_address.args.objects
message: "Not-NULL violation. null value in column \"door_no\" of relation \"address\" violates not-null constraint"

View File

@ -379,9 +379,6 @@ class TestGraphqlUpdateBasic:
def test_column_in_multiple_operators(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + "/article_column_multiple_operators.yaml")
def test_column_in_multiple_operators(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + "/article_column_multiple_operators.yaml")
def test_author_by_pk(self, hge_ctx):
check_query_f(hge_ctx, self.dir() + "/author_by_pk.yaml")

View File

@ -8,6 +8,7 @@ resp_pg_version_map = {
'10': 'response_10_11',
'11': 'response_10_11',
'12': 'response_10_11',
'13': 'response_10_11',
'latest': 'response_10_11'
}

View File

@ -4,12 +4,17 @@
# tests are running correctly, or test our python test helpers.
import pytest
from validate import check_query_f, collapse_order_not_selset
from validate import (
check_query_f,
collapse_order_not_selset,
validate_http_anyq_with_allowed_responses,
)
from ruamel.yaml.comments import CommentedMap
usefixtures = pytest.mark.usefixtures
@usefixtures('per_class_tests_db_state')
@usefixtures("per_class_tests_db_state")
class TestTests1:
"""
Test various things about our test framework code. Validate that tests work
@ -161,3 +166,90 @@ class TestTests2:
@classmethod
def dir(cls):
return 'queries/graphql_query/permissions'
@usefixtures("per_class_tests_db_state")
class TestTests3:
"""
This test case is about testing validate_http_anyq_with_allowed_responses
with an empty list of allowed responses wherein it should throw an exception
"""
@pytest.mark.xfail(reason="expected, validating function working")
def test_tests_validate_http_anyq_with_allowed_responses_with_empty_list(
self, hge_ctx
):
# These values are being set as they are in address_not_null_constraint_error.yaml
url = "/v1/graphql"
query = {
"query": 'mutation {\n insert_address(objects: [{street: "koramangala"}]){\n returning{\n id\n street\n }\n affected_rows\n }\n} \n'
}
try:
resp, pass_test = validate_http_anyq_with_allowed_responses(
hge_ctx, url, query, {}, 200, []
)
except:
print("FAIL!")
assert 0, "Test failure, occurs as expected"
# It reaches here only if the exception wasn't caught, in which case
# this current test should fail
"""
This test case is about testing validate_http_anyq_with_allowed_responses
with a list of allowed responses which are incorrect wherein it should fail the test
"""
@pytest.mark.xfail(reason="expected, validating test code")
def test_tests_validate_http_anyq_with_allowed_responses_with_no_correct_response(
self, hge_ctx
):
# These values are being set as they are in address_not_null_constraint_error.yaml
url = "/v1/graphql"
query = {
"query": 'mutation {\n insert_address(objects: [{street: "koramangala"}]){\n returning{\n id\n street\n }\n affected_rows\n }\n} \n'
}
allowed_response_1 = {
"response": {
"errors": [
{
"extensions": {
"code": "constraint-violation",
"path": "$.selectionSet.insert_address.args.objects",
},
"message": 'Not-NULL. null value in column "door_no" violates not-null constraint',
}
]
}
}
allowed_response_2 = {
"response": {
"errors": [
{
"extensions": {
"code": "constraint-violation",
"path": "$.selectionSet.insert_address.args.objects",
},
"message": 'Not-NULL violation. null value in column "door_no" of relation "address" not-null constraint',
}
]
}
}
allowed_responses = [allowed_response_1, allowed_response_2]
try:
resp, err = validate_http_anyq_with_allowed_responses(
hge_ctx, url, query, {}, 200, allowed_responses
)
except:
print("FAIL!")
assert 0, "Test failed, as expected"
# Re-use setup and teardown from where we adapted this test case:
@classmethod
def dir(cls):
return "queries/graphql_mutation/insert/constraints"

View File

@ -199,7 +199,11 @@ def check_query(hge_ctx, conf, transport='http', add_auth=True, claims_namespace
assert transport in ['http', 'websocket', 'subscription'], "Unknown transport type " + transport
if transport == 'http':
print('running on http')
return validate_http_anyq(hge_ctx, conf['url'], conf['query'], headers,
if 'allowed_responses' in conf:
return validate_http_anyq_with_allowed_responses(hge_ctx, conf['url'], conf['query'], headers,
conf['status'], conf.get('allowed_responses'))
else:
return validate_http_anyq(hge_ctx, conf['url'], conf['query'], headers,
conf['status'], conf.get('response'))
elif transport == 'websocket':
print('running on websocket')
@ -274,18 +278,44 @@ def validate_http_anyq(hge_ctx, url, query, headers, exp_code, exp_response):
else:
return resp, True
def validate_http_anyq_with_allowed_responses(hge_ctx, url, query, headers, exp_code, allowed_responses):
code, resp, resp_hdrs = hge_ctx.anyq(url, query, headers)
print(headers)
assert code == exp_code, resp
print('http resp: ', resp)
if isinstance(allowed_responses, list) and len(allowed_responses) > 0:
resp_res = {}
test_passed = False
for response in allowed_responses:
dict_resp = json.loads(json.dumps(response))
exp_resp = dict_resp['response']
resp_result, pass_test = assert_graphql_resp_expected(resp, exp_resp, query, resp_hdrs, hge_ctx.avoid_err_msg_checks, True)
if pass_test == True:
test_passed = True
resp_res = resp_result
break
if test_passed == True:
return resp_res, test_passed
else:
# test should fail if none of the allowed responses work
raise Exception("allowed_responses did not contain the response that was expected. Please check your allowed_responses")
else:
raise Exception("allowed_responses was not a list of permissible responses")
# Check the actual graphql response is what we expected, also taking into
# consideration the ordering of keys that we expect to be preserved, based on
# 'query'.
#
# Returns 'resp' and a bool indicating whether the test passed or not (this
# will always be True unless we are `--accepting`)
def assert_graphql_resp_expected(resp_orig, exp_response_orig, query, resp_hdrs={}, skip_if_err_msg=False):
# Prepare actual and respected responses so comparison takes into
def assert_graphql_resp_expected(resp_orig, exp_response_orig, query, resp_hdrs={}, skip_if_err_msg=False, skip_assertion=False):
# Prepare actual and expected responses so comparison takes into
# consideration only the ordering that we care about:
resp = collapse_order_not_selset(resp_orig, query)
exp_response = collapse_order_not_selset(exp_response_orig, query)
matched = equal_CommentedMap(resp, exp_response)
matched = equal_CommentedMap(resp, exp_response)
if PytestConf.config.getoption("--accept"):
print('skipping assertion since we chose to --accept new output')
@ -307,7 +337,10 @@ def assert_graphql_resp_expected(resp_orig, exp_response_orig, query, resp_hdrs=
test_output['request id'] = resp_hdrs['x-request-id']
yml.dump(test_output, stream=dump_str)
if not skip_if_err_msg:
assert matched, '\n' + dump_str.getvalue()
if skip_assertion:
return resp, matched
else:
assert matched, '\n' + dump_str.getvalue()
elif matched:
return resp, matched
else:
@ -323,7 +356,10 @@ def assert_graphql_resp_expected(resp_orig, exp_response_orig, query, resp_hdrs=
warnings.warn("Response does not have the expected error message\n" + dump_str.getvalue())
return resp, matched
else:
assert matched_, '\n' + dump_str.getvalue()
if skip_assertion:
return resp, matched_
else:
assert matched_, '\n' + dump_str.getvalue()
return resp, matched # matched always True unless --accept
# This really sucks; newer ruamel made __eq__ ignore ordering: