cov: Add io tests for basic cli commands and invalid config options

Full code coverage for Config.hs except expected overlays.
This commit is contained in:
Wolfgang Walther
2021-01-14 16:40:29 +01:00
committed by Wolfgang Walther
parent ab6f90aa78
commit 97d8456382
5 changed files with 251 additions and 12 deletions
@@ -0,0 +1,25 @@
db-anon-role = "required"
db-channel = "pgrst"
db-channel-enabled = false
db-extra-search-path = "public"
db-max-rows = ""
db-pool = 10
db-pool-timeout = 10
db-pre-request = ""
db-prepared-statements = true
db-root-spec = ""
db-schemas = "required"
db-tx-end = "commit"
db-uri = "required"
jwt-aud = ""
jwt-role-claim-key = ".\"role\""
jwt-secret = ""
jwt-secret-is-base64 = false
log-level = "error"
openapi-server-proxy-uri = ""
raw-media-types = ""
server-host = "!4"
server-port = 3000
server-unix-socket = ""
server-unix-socket-mode = "660"
app.settings.test = "Bool False"
+4
View File
@@ -0,0 +1,4 @@
yaml:
is:
not:
supported:
+16
View File
@@ -0,0 +1,16 @@
# tests how config options fall back with invalid types
db-anon-role = "required"
db-schemas = "required"
db-uri = "required"
# expects string
app.settings.test = false
# expects boolean or string
db-channel-enabled = 13
# expects integer or string
db-max-rows = true
# expects string
raw-media-types = true
+142
View File
@@ -1,3 +1,144 @@
cli:
# success: valid commands
- name: help long
args: ['--help']
- name: help short
args: ['-h']
- name: example long
args: ['--example']
- name: example short
args: ['-e']
- name: dump config
args: ['--dump-config']
use_defaultenv: true
- name: dump schema
args: ['--dump-schema']
use_defaultenv: true
# failures: config files
- name: no config
expect: error
- name: non-existant config file
expect: error
args: ['does_not_exist.conf']
- name: invalid config file
expect: error
args: ['test/io-tests/configs/invalid.yaml']
# failures: required config options
- name: missing db-anon-role
expect: error
env:
PGRST_DB_URI: required
PGRST_DB_SCHEMAS: required
- name: missing db-schemas
expect: error
env:
PGRST_DB_ANON_ROLE: required
PGRST_DB_URI: required
- name: missing db-uri
expect: error
env:
PGRST_DB_ANON_ROLE: required
PGRST_DB_SCHEMAS: required
# failures: wrong config values
- name: invalid server-unix-socket-mode not octal
expect: error
use_defaultenv: true
env:
PGRST_SERVER_UNIX_SOCKET_MODE: '800'
- name: invalid server-unix-socket-mode < 600
expect: error
use_defaultenv: true
env:
PGRST_SERVER_UNIX_SOCKET_MODE: '599'
- name: invalid server-unix-socket-mode > 777
expect: error
use_defaultenv: true
env:
PGRST_SERVER_UNIX_SOCKET_MODE: '778'
# TODO: Bug needs to be fixed
# - name: invalid jwt-aud
# expect: error
# use_defaultenv: true
# env:
# PGRST_JWT_AUD: 'htp:/@@localhorst.invalid'
- name: invalid log-level
expect: error
use_defaultenv: true
env:
PGRST_LOG_LEVEL: never
- name: invalid db-tx-end
expect: error
use_defaultenv: true
env:
PGRST_DB_TX_END: random
- name: invalid openapi-server-proxy-uri
expect: error
use_defaultenv: true
env:
PGRST_OPENAPI_SERVER_PROXY_URI: 'htp:/@@localhorst.invalid'
- name: invalid jwt-secret not base64
expect: error
use_defaultenv: true
env:
PGRST_JWT_SECRET_IS_BASE64: 'true'
PGRST_JWT_SECRET: 'no base-64!'
# success: parsing config values
- name: log-level=
expect: 'log-level = "error"'
use_defaultenv: true
env:
PGRST_LOG_LEVEL: ""
- name: log-level=crit
expect: 'log-level = "crit"'
use_defaultenv: true
env:
PGRST_LOG_LEVEL: crit
- name: log-level=error
expect: 'log-level = "error"'
use_defaultenv: true
env:
PGRST_LOG_LEVEL: error
- name: log-level=warn
expect: 'log-level = "warn"'
use_defaultenv: true
env:
PGRST_LOG_LEVEL: warn
- name: log-level=info
expect: 'log-level = "info"'
use_defaultenv: true
env:
PGRST_LOG_LEVEL: info
- name: db-tx-end=
expect: 'db-tx-end = "commit"'
use_defaultenv: true
env:
PGRST_DB_TX_END: ""
- name: db-tx-end=commit
expect: 'db-tx-end = "commit"'
use_defaultenv: true
env:
PGRST_DB_TX_END: commit
- name: db-tx-end=commit-allow-override
expect: 'db-tx-end = "commit-allow-override"'
use_defaultenv: true
env:
PGRST_DB_TX_END: commit-allow-override
- name: db-tx-end=rollback-allow-override
expect: 'db-tx-end = "rollback-allow-override"'
use_defaultenv: true
env:
PGRST_DB_TX_END: rollback-allow-override
- name: db-tx-end=rollback
expect: 'db-tx-end = "rollback"'
use_defaultenv: true
env:
PGRST_DB_TX_END: rollback
- name: raw-media-types list
expect: 'raw-media-types = "image/png,image/jpeg"'
use_defaultenv: true
env:
PGRST_RAW_MEDIA_TYPES: ' image/png , image/jpeg '
roleclaims: roleclaims:
- key: '.postgrest.a_role' - key: '.postgrest.a_role'
data: data:
@@ -26,6 +167,7 @@ roleclaims:
data: data:
role: postgrest_test_author role: postgrest_test_author
expected_status: 401 expected_status: 401
invalidroleclaimkeys: invalidroleclaimkeys:
- 'role.other' - 'role.other'
- '.role##' - '.role##'
+64 -12
View File
@@ -3,6 +3,7 @@
import contextlib import contextlib
import dataclasses import dataclasses
from datetime import datetime from datetime import datetime
from itertools import repeat
from operator import attrgetter from operator import attrgetter
import os import os
import pathlib import pathlib
@@ -28,6 +29,22 @@ POSTGREST_BIN = shutil.which("postgrest")
SECRET = "reallyreallyreallyreallyverysafe" SECRET = "reallyreallyreallyreallyverysafe"
def itemgetter(*items):
"operator.itemgetter with None as fallback when key does not exist"
if len(items) == 1:
item = items[0]
def g(obj):
return obj.get(item)
else:
def g(obj):
return tuple(obj.get(item) for item in items)
return g
class PostgrestTimedOut(Exception): class PostgrestTimedOut(Exception):
"Connecting to PostgREST endpoint timed out." "Connecting to PostgREST endpoint timed out."
@@ -83,27 +100,36 @@ def hpctixfile():
return tixfile.with_suffix(f".{test}.tix") return tixfile.with_suffix(f".{test}.tix")
def dumpconfig(configpath=None, env=None, stdin=None): def cli(args, env=None, stdin=None):
"Dump the config as parsed by PostgREST." "Run PostgREST and return stdout."
env = env or {} env = env or {}
command = [POSTGREST_BIN, "--dump-config"] command = [POSTGREST_BIN] + args
env["HPCTIXFILE"] = hpctixfile() env["HPCTIXFILE"] = hpctixfile()
if configpath:
command.append(configpath)
process = subprocess.Popen( process = subprocess.Popen(
command, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE command, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE
) )
process.stdin.write(stdin or b"") process.stdin.write(stdin or b"")
result = process.communicate(timeout=5)[0] try:
process.kill() result = process.communicate(timeout=5)[0]
process.wait() if process.returncode != 0:
if process.returncode != 0: raise PostgrestError()
raise PostgrestError() return result.decode("utf-8")
return result.decode("utf-8") finally:
process.kill()
process.wait()
def dumpconfig(configpath=None, env=None, stdin=None):
"Dump the config as parsed by PostgREST."
args = ["--dump-config"]
if configpath:
args.append(configpath)
return cli(args, env=env, stdin=stdin)
@contextlib.contextmanager @contextlib.contextmanager
@@ -181,6 +207,32 @@ def jwtauthheader(claim, secret):
return authheader(jwt.encode(claim, secret).decode("utf-8")) return authheader(jwt.encode(claim, secret).decode("utf-8"))
@pytest.mark.parametrize(
"args,env,use_defaultenv,expect",
map(itemgetter("args", "env", "use_defaultenv", "expect"), FIXTURES["cli"]),
ids=map(itemgetter("name"), FIXTURES["cli"]),
)
def test_cli(args, env, use_defaultenv, expect, defaultenv):
"""
When PostgREST is run with <args> arguments and <env>/<defaultenv> environment variabales
it should return. Exit code should be according to <expect_error>.
"""
# use --dump-config by default to make sure that the postgrest process will terminate for sure
args = args or ["--dump-config"]
env = env or {}
if use_defaultenv:
env = {**defaultenv, **env}
if expect == "error":
with pytest.raises(PostgrestError):
print(cli(args, env=env))
else:
dump = cli(args, env=env).split("\n")
if expect:
assert expect in dump
@pytest.mark.parametrize( @pytest.mark.parametrize(
"expectedconfig", (CONFIGSDIR / "expected").iterdir(), ids=attrgetter("name") "expectedconfig", (CONFIGSDIR / "expected").iterdir(), ids=attrgetter("name")
) )