graphql-engine/server/tests-py/test_logging.py
Alexis King 3ef6feb394
server: Fix flaky logging test (#3081)
The changes in 0c74839934 adjusted the
format of error logs slightly to omit fields instead of including them
with null values. However, this was rarely triggered by this test
because it only looks at the first log message, but log messages can
sometimes be written out of order. This makes the test order-agnostic.
2019-10-10 08:40:19 -05:00

201 lines
8.0 KiB
Python

#!/usr/bin/env python3
import json
import os
import time
import pytest
if not pytest.config.getoption("--test-logging"):
pytest.skip("--test-logging missing, skipping tests", allow_module_level=True)
class TestLogging():
dir = 'queries/logging'
def _teardown(self, hge_ctx):
st_code, resp = hge_ctx.v1q_f(self.dir + '/teardown.yaml')
assert st_code == 200, resp
@pytest.fixture(autouse=True)
def transact(self, hge_ctx):
# setup some tables
st_code, resp = hge_ctx.v1q_f(self.dir + '/setup.yaml')
assert st_code == 200, resp
try:
# make a successful query
q = {'query': 'query { hello {code name} }'}
headers = {'x-request-id': 'successful-query-log-test'}
if hge_ctx.hge_key:
headers['x-hasura-admin-secret'] = hge_ctx.hge_key
resp = hge_ctx.http.post(hge_ctx.hge_url + '/v1/graphql', json=q,
headers=headers)
assert resp.status_code == 200 and 'data' in resp.json()
# make a query where JSON body parsing fails
q = {'quer': 'query { hello {code name} }'}
headers = {'x-request-id': 'json-parse-fail-log-test'}
if hge_ctx.hge_key:
headers['x-hasura-admin-secret'] = hge_ctx.hge_key
resp = hge_ctx.http.post(hge_ctx.hge_url + '/v1/graphql', json=q,
headers=headers)
assert resp.status_code == 200 and 'errors' in resp.json()
# make an unthorized query where admin secret/access token is empty
q = {'query': 'query { hello {code name} }'}
headers = {'x-request-id': 'unauthorized-query-test'}
resp = hge_ctx.http.post(hge_ctx.hge_url + '/v1/graphql', json=q,
headers=headers)
assert resp.status_code == 200 and 'errors' in resp.json()
# make an unthorized metadata request where admin secret/access token is empty
q = {
'query': {
'type': 'select',
'args': {
"table": {
"name": "hdb_function",
"schema": "hdb_catalog"
},
"columns": ["function_name", "function_schema", "is_system_defined"],
"where": { "function_schema": "public" }
}
}
}
headers = {'x-request-id': 'unauthorized-metadata-test'}
resp = hge_ctx.http.post(hge_ctx.hge_url + '/v1/query', json=q,
headers=headers)
assert resp.status_code == 401 and 'error' in resp.json()
# gather and parse the logs now
self.logs = self._parse_logs(hge_ctx)
# sometimes the log might take time to buffer
time.sleep(2)
yield
finally:
self._teardown(hge_ctx)
def _parse_logs(self, hge_ctx):
# parse the log file into a json list
log_file = os.getenv('LOGGING_TEST_LOGFILE_PATH', None)
if not log_file:
print('Could not determine log file path to test logging!')
assert False
loglines = []
with open(log_file, 'r') as f:
loglines = f.readlines()
logs = list(map(lambda x: json.loads(x.strip()), loglines))
assert len(logs) > 0
return logs
def test_startup_logs(self, hge_ctx):
def _get_server_config(x):
return x['type'] == 'startup' and \
'kind' in x['detail'] and \
x['detail']['kind'] == 'server_configuration'
config_logs = list(filter(_get_server_config, self.logs))
print(config_logs)
assert len(config_logs) == 1
config_log = config_logs[0]
print(config_log)
info = config_log['detail']['info']
# we can improve this later by checking the actual value and
# cross-checking with current config of the server
assert 'enable_console' in info
assert 'port' in info
assert 'server_host' in info
#assert 'transaction_isolation' in info
assert 'admin_secret_set' in info
if hge_ctx.hge_key:
assert info['admin_secret_set'] == True
assert 'auth_hook' in info
assert 'auth_hook_mode' in info
assert 'jwt_secret' in info
assert 'unauth_role' in info
assert 'cors_config' in info
assert 'enable_console' in info
assert 'console_assets_dir' in info
assert 'enable_telemetry' in info
assert 'use_prepared_statements' in info
assert 'stringify_numeric_types' in info
assert 'enabled_apis' in info
assert 'live_query_options' in info
assert 'enable_allowlist' in info
assert 'enabled_log_types' in info
def test_http_log(self, hge_ctx):
def _get_http_logs(x):
return x['type'] == 'http-log'
print('all logs gathered', self.logs)
http_logs = list(filter(_get_http_logs, self.logs))
print('http logs', http_logs)
assert len(http_logs) > 0
for http_log in http_logs:
print(http_log)
http_info = http_log['detail']['http_info']
assert 'url' in http_info
assert 'status' in http_info
assert 'ip' in http_info
operation = http_log['detail']['operation']
assert 'request_id' in operation
if operation['request_id'] == 'successful-query-log-test':
assert 'query_execution_time' in operation
assert 'user_vars' in operation
def test_query_log(self, hge_ctx):
def _get_query_logs(x):
return x['type'] == 'query-log'
query_logs = list(filter(_get_query_logs, self.logs))
assert len(query_logs) > 0
onelog = query_logs[0]['detail']
assert 'request_id' in onelog
assert 'query' in onelog
assert 'query' in onelog['query']
assert 'generated_sql' in onelog
def test_http_parse_failed_log(self, hge_ctx):
def _get_parse_failed_logs(x):
return x['type'] == 'http-log' and \
x['detail']['operation']['request_id'] == 'json-parse-fail-log-test'
http_logs = list(filter(_get_parse_failed_logs, self.logs))
print('parse failed logs', http_logs)
assert len(http_logs) > 0
print(http_logs[0])
assert 'error' in http_logs[0]['detail']['operation']
assert http_logs[0]['detail']['operation']['error']['code'] == 'parse-failed'
def test_http_unthorized_query(self, hge_ctx):
def _get_failed_logs(x):
return x['type'] == 'http-log' and \
x['detail']['operation']['request_id'] == 'unauthorized-query-test'
http_logs = list(filter(_get_failed_logs, self.logs))
print('unauthorized failed logs', http_logs)
assert len(http_logs) > 0
print(http_logs[0])
assert 'error' in http_logs[0]['detail']['operation']
assert http_logs[0]['detail']['operation']['error']['code'] == 'access-denied'
assert http_logs[0]['detail']['operation'].get('query') is None
assert http_logs[0]['detail']['operation']['raw_query'] is not None
def test_http_unthorized_metadata(self, hge_ctx):
def _get_failed_logs(x):
return x['type'] == 'http-log' and \
x['detail']['operation']['request_id'] == 'unauthorized-metadata-test'
http_logs = list(filter(_get_failed_logs, self.logs))
print('unauthorized failed logs', http_logs)
assert len(http_logs) > 0
print(http_logs[0])
assert 'error' in http_logs[0]['detail']['operation']
assert http_logs[0]['detail']['operation']['error']['code'] == 'access-denied'
assert http_logs[0]['detail']['operation'].get('query') is None
assert http_logs[0]['detail']['operation']['raw_query'] is not None