From 9c9e25b2af82881e94b7aeb3463c9502fc26c4eb Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Mon, 3 Jan 2022 10:34:19 +0100 Subject: [PATCH] test: Move io-tests SQL fixtures to a separate file --- .github/workflows/ci.yaml | 2 +- nix/tools/tests.nix | 8 +- test/fixtures/roles.sql | 55 -------------- test/fixtures/schema.sql | 53 ------------- test/io-tests/configs/sigusr2-settings.config | 2 +- test/io-tests/db_config.sql | 55 ++++++++++++++ test/io-tests/fixtures.sql | 75 +++++++++++++++++++ test/io-tests/test_io.py | 55 ++++---------- 8 files changed, 151 insertions(+), 154 deletions(-) create mode 100644 test/io-tests/db_config.sql create mode 100644 test/io-tests/fixtures.sql diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index efced73a0..c28e3eb1b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -82,7 +82,7 @@ jobs: - name: Run IO tests if: always() - run: postgrest-with-postgresql-${{ matrix.pgVersion }} postgrest-test-io + run: postgrest-with-postgresql-${{ matrix.pgVersion }} -f test/io-tests/fixtures.sql postgrest-test-io - name: Run query cost tests if: always() diff --git a/nix/tools/tests.nix b/nix/tools/tests.nix index 41d1404b6..b54604c51 100644 --- a/nix/tools/tests.nix +++ b/nix/tools/tests.nix @@ -92,8 +92,8 @@ let } '' ${cabal-install}/bin/cabal v2-build ${devCabalOptions} - ${cabal-install}/bin/cabal v2-exec ${withTools.withPg} \ - ${ioTestPython}/bin/pytest -- -v test/io-tests "''${_arg_leftovers[@]}" + ${cabal-install}/bin/cabal v2-exec -- ${withTools.withPg} -f test/io-tests/fixtures.sql \ + ${ioTestPython}/bin/pytest -v test/io-tests "''${_arg_leftovers[@]}" ''; dumpSchema = @@ -137,8 +137,8 @@ let # collect all tests HPCTIXFILE="$tmpdir"/io.tix \ - ${withTools.withPg} ${cabal-install}/bin/cabal v2-exec ${devCabalOptions} \ - ${ioTestPython}/bin/pytest -- -v test/io-tests + ${withTools.withPg} -f test/io-tests/fixtures.sql ${cabal-install}/bin/cabal v2-exec ${devCabalOptions} -- \ + ${ioTestPython}/bin/pytest -v test/io-tests HPCTIXFILE="$tmpdir"/spec.tix \ ${withTools.withPg} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec diff --git a/test/fixtures/roles.sql b/test/fixtures/roles.sql index 332f40e21..93b962d83 100644 --- a/test/fixtures/roles.sql +++ b/test/fixtures/roles.sql @@ -5,58 +5,3 @@ CREATE ROLE postgrest_test_default_role; CREATE ROLE postgrest_test_author; GRANT postgrest_test_anonymous, postgrest_test_default_role, postgrest_test_author TO :USER; - --- reloadable config options for io tests -ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_aud = 'https://example.org'; -ALTER ROLE postgrest_test_authenticator SET pgrst.openapi_server_proxy_uri = 'https://example.org/api'; -ALTER ROLE postgrest_test_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.db-config'; -ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_secret = 'REALLYREALLYREALLYREALLYVERYSAFE'; -ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_secret_is_base64 = 'true'; -ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_role_claim_key = '."a"."role"'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_tx_end = 'commit-allow-override'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_schemas = 'test, tenant1, tenant2'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_root_spec = 'root'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_prepared_statements = 'false'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_pre_request = 'test.custom_headers'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_max_rows = '1000'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_extra_search_path = 'public, extensions'; - --- override with database specific setting -ALTER ROLE postgrest_test_authenticator IN DATABASE :DBNAME SET pgrst.jwt_secret = 'OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE'; -ALTER ROLE postgrest_test_authenticator IN DATABASE :DBNAME SET pgrst.db_extra_search_path = 'public, extensions, private'; - --- other database settings that should be ignored -DROP DATABASE IF EXISTS other; -CREATE DATABASE other; -ALTER ROLE postgrest_test_authenticator IN DATABASE other SET pgrst.db_max_rows = '1111'; - --- non-reloadable configs for io tests -ALTER ROLE postgrest_test_authenticator SET pgrst.server_host = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.server_port = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.server_unix_socket = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.server_unix_socket_mode = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.log_level = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_anon_role = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_uri = 'postgresql://ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_channel_enabled = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_channel = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_pool = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_pool_timeout = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst.db_config = 'ignored'; - --- other authenticator reloadable config options for io tests -CREATE ROLE other_authenticator LOGIN NOINHERIT; -ALTER ROLE other_authenticator SET pgrst.jwt_aud = 'https://otherexample.org'; -ALTER ROLE other_authenticator SET pgrst.openapi_server_proxy_uri = 'https://otherexample.org/api'; -ALTER ROLE other_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.other-db-config'; -ALTER ROLE other_authenticator SET pgrst.jwt_secret = 'ODERREALLYREALLYREALLYREALLYVERYSAFE'; -ALTER ROLE other_authenticator SET pgrst.jwt_secret_is_base64 = 'true'; -ALTER ROLE other_authenticator SET pgrst.jwt_role_claim_key = '."other"."role"'; -ALTER ROLE other_authenticator SET pgrst.db_tx_end = 'rollback-allow-override'; -ALTER ROLE other_authenticator SET pgrst.db_schemas = 'test, other_tenant1, other_tenant2'; -ALTER ROLE other_authenticator SET pgrst.db_root_spec = 'other_root'; -ALTER ROLE other_authenticator SET pgrst.db_prepared_statements = 'false'; -ALTER ROLE other_authenticator SET pgrst.db_pre_request = 'test.other_custom_headers'; -ALTER ROLE other_authenticator SET pgrst.db_max_rows = '100'; -ALTER ROLE other_authenticator SET pgrst.db_extra_search_path = 'public, extensions, other'; -ALTER ROLE other_authenticator SET pgrst.openapi_mode = 'disabled'; diff --git a/test/fixtures/schema.sql b/test/fixtures/schema.sql index c13b88354..eb25c6cc0 100644 --- a/test/fixtures/schema.sql +++ b/test/fixtures/schema.sql @@ -2120,59 +2120,6 @@ returns setof v2.parents as $$ select * from v2.parents where id < $1; $$ language sql; --- Used to test if prepared statements are used -create function uses_prepared_statements() returns bool as $$ - select count(name) > 0 from pg_catalog.pg_prepared_statements -$$ language sql; - -create or replace function change_max_rows_config(val int, notify bool default false) returns void as $_$ -begin - execute format($$ - alter role postgrest_test_authenticator set pgrst.db_max_rows = %L; - $$, val); - if notify then - perform pg_notify('pgrst', 'reload config'); - end if; -end $_$ volatile security definer language plpgsql ; - -create or replace function reset_max_rows_config() returns void as $_$ -begin - alter role postgrest_test_authenticator set pgrst.db_max_rows = '1000'; -end $_$ volatile security definer language plpgsql ; - -create or replace function change_db_schema_and_full_reload(schemas text) returns void as $_$ -begin - execute format($$ - alter role postgrest_test_authenticator set pgrst.db_schemas = %L; - $$, schemas); - perform pg_notify('pgrst', 'reload config'); - perform pg_notify('pgrst', 'reload schema'); -end $_$ volatile security definer language plpgsql ; - -create or replace function v1.reset_db_schema_config() returns void as $_$ -begin - alter role postgrest_test_authenticator set pgrst.db_schemas = 'test'; - perform pg_notify('pgrst', 'reload config'); - perform pg_notify('pgrst', 'reload schema'); -end $_$ volatile security definer language plpgsql ; - -create or replace function test.invalid_role_claim_key_reload() returns void as $_$ -begin - alter role postgrest_test_authenticator set pgrst.jwt_role_claim_key = 'test'; - perform pg_notify('pgrst', 'reload config'); -end $_$ volatile security definer language plpgsql ; - -create or replace function test.reset_invalid_role_claim_key() returns void as $_$ -begin - alter role postgrest_test_authenticator set pgrst.jwt_role_claim_key = '."a"."role"'; - perform pg_notify('pgrst', 'reload config'); -end $_$ volatile security definer language plpgsql ; - -create or replace function test.reload_pgrst_config() returns void as $_$ -begin - perform pg_notify('pgrst', 'reload config'); -end $_$ language plpgsql ; - create table private.screens ( id serial primary key, name text not null default 'new screen' diff --git a/test/io-tests/configs/sigusr2-settings.config b/test/io-tests/configs/sigusr2-settings.config index 69784f06c..c8a1cb403 100644 --- a/test/io-tests/configs/sigusr2-settings.config +++ b/test/io-tests/configs/sigusr2-settings.config @@ -1,4 +1,4 @@ -db-schemas = "test" +db-schemas = "public" db-pool = 1 app.settings.name_var = "John" diff --git a/test/io-tests/db_config.sql b/test/io-tests/db_config.sql new file mode 100644 index 000000000..c0fee5a82 --- /dev/null +++ b/test/io-tests/db_config.sql @@ -0,0 +1,55 @@ +CREATE ROLE db_config_authenticator LOGIN NOINHERIT; + +-- reloadable config options +ALTER ROLE db_config_authenticator SET pgrst.jwt_aud = 'https://example.org'; +ALTER ROLE db_config_authenticator SET pgrst.openapi_server_proxy_uri = 'https://example.org/api'; +ALTER ROLE db_config_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.db-config'; +ALTER ROLE db_config_authenticator SET pgrst.jwt_secret = 'REALLYREALLYREALLYREALLYVERYSAFE'; +ALTER ROLE db_config_authenticator SET pgrst.jwt_secret_is_base64 = 'true'; +ALTER ROLE db_config_authenticator SET pgrst.jwt_role_claim_key = '."a"."role"'; +ALTER ROLE db_config_authenticator SET pgrst.db_tx_end = 'commit-allow-override'; +ALTER ROLE db_config_authenticator SET pgrst.db_schemas = 'test, tenant1, tenant2'; +ALTER ROLE db_config_authenticator SET pgrst.db_root_spec = 'root'; +ALTER ROLE db_config_authenticator SET pgrst.db_prepared_statements = 'false'; +ALTER ROLE db_config_authenticator SET pgrst.db_pre_request = 'test.custom_headers'; +ALTER ROLE db_config_authenticator SET pgrst.db_max_rows = '1000'; +ALTER ROLE db_config_authenticator SET pgrst.db_extra_search_path = 'public, extensions'; + +-- override with database specific setting +ALTER ROLE db_config_authenticator IN DATABASE :DBNAME SET pgrst.jwt_secret = 'OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE'; +ALTER ROLE db_config_authenticator IN DATABASE :DBNAME SET pgrst.db_extra_search_path = 'public, extensions, private'; + +-- other database settings that should be ignored +CREATE DATABASE other; +ALTER ROLE db_config_authenticator IN DATABASE other SET pgrst.db_max_rows = '1111'; + +-- non-reloadable configs +ALTER ROLE db_config_authenticator SET pgrst.server_host = 'ignored'; +ALTER ROLE db_config_authenticator SET pgrst.server_port = 'ignored'; +ALTER ROLE db_config_authenticator SET pgrst.server_unix_socket = 'ignored'; +ALTER ROLE db_config_authenticator SET pgrst.server_unix_socket_mode = 'ignored'; +ALTER ROLE db_config_authenticator SET pgrst.log_level = 'ignored'; +ALTER ROLE db_config_authenticator SET pgrst.db_anon_role = 'ignored'; +ALTER ROLE db_config_authenticator SET pgrst.db_uri = 'postgresql://ignored'; +ALTER ROLE db_config_authenticator SET pgrst.db_channel_enabled = 'ignored'; +ALTER ROLE db_config_authenticator SET pgrst.db_channel = 'ignored'; +ALTER ROLE db_config_authenticator SET pgrst.db_pool = 'ignored'; +ALTER ROLE db_config_authenticator SET pgrst.db_pool_timeout = 'ignored'; +ALTER ROLE db_config_authenticator SET pgrst.db_config = 'ignored'; + +-- other authenticator reloadable config options +CREATE ROLE other_authenticator LOGIN NOINHERIT; +ALTER ROLE other_authenticator SET pgrst.jwt_aud = 'https://otherexample.org'; +ALTER ROLE other_authenticator SET pgrst.openapi_server_proxy_uri = 'https://otherexample.org/api'; +ALTER ROLE other_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.other-db-config'; +ALTER ROLE other_authenticator SET pgrst.jwt_secret = 'ODERREALLYREALLYREALLYREALLYVERYSAFE'; +ALTER ROLE other_authenticator SET pgrst.jwt_secret_is_base64 = 'true'; +ALTER ROLE other_authenticator SET pgrst.jwt_role_claim_key = '."other"."role"'; +ALTER ROLE other_authenticator SET pgrst.db_tx_end = 'rollback-allow-override'; +ALTER ROLE other_authenticator SET pgrst.db_schemas = 'test, other_tenant1, other_tenant2'; +ALTER ROLE other_authenticator SET pgrst.db_root_spec = 'other_root'; +ALTER ROLE other_authenticator SET pgrst.db_prepared_statements = 'false'; +ALTER ROLE other_authenticator SET pgrst.db_pre_request = 'test.other_custom_headers'; +ALTER ROLE other_authenticator SET pgrst.db_max_rows = '100'; +ALTER ROLE other_authenticator SET pgrst.db_extra_search_path = 'public, extensions, other'; +ALTER ROLE other_authenticator SET pgrst.openapi_mode = 'disabled'; diff --git a/test/io-tests/fixtures.sql b/test/io-tests/fixtures.sql new file mode 100644 index 000000000..4c8a3a3cb --- /dev/null +++ b/test/io-tests/fixtures.sql @@ -0,0 +1,75 @@ +\ir db_config.sql + +CREATE ROLE postgrest_test_anonymous; +CREATE ROLE postgrest_test_author; + +GRANT postgrest_test_anonymous, postgrest_test_author TO :USER; + +CREATE SCHEMA v1; +GRANT USAGE ON SCHEMA v1 TO postgrest_test_anonymous; + +CREATE TABLE authors_only (); +GRANT SELECT ON authors_only TO postgrest_test_author; + +CREATE TABLE projects AS SELECT FROM generate_series(1,5); +GRANT SELECT ON projects TO postgrest_test_anonymous; + +create function get_guc_value(name text) returns text as $$ + select nullif(current_setting(name), '')::text; +$$ language sql; + +create function v1.get_guc_value(name text) returns text as $$ + select nullif(current_setting(name), '')::text; +$$ language sql; + +create function uses_prepared_statements() returns bool as $$ + select count(name) > 0 from pg_catalog.pg_prepared_statements +$$ language sql; + +create function change_max_rows_config(val int, notify bool default false) returns void as $_$ +begin + execute format($$ + alter role postgrest_test_authenticator set pgrst.db_max_rows = %L; + $$, val); + if notify then + perform pg_notify('pgrst', 'reload config'); + end if; +end $_$ volatile security definer language plpgsql ; + +create function reset_max_rows_config() returns void as $_$ +begin + alter role postgrest_test_authenticator reset pgrst.db_max_rows; +end $_$ volatile security definer language plpgsql ; + +create function change_db_schema_and_full_reload(schemas text) returns void as $_$ +begin + execute format($$ + alter role postgrest_test_authenticator set pgrst.db_schemas = %L; + $$, schemas); + perform pg_notify('pgrst', 'reload config'); + perform pg_notify('pgrst', 'reload schema'); +end $_$ volatile security definer language plpgsql ; + +create function v1.reset_db_schema_config() returns void as $_$ +begin + alter role postgrest_test_authenticator reset pgrst.db_schemas; + perform pg_notify('pgrst', 'reload config'); + perform pg_notify('pgrst', 'reload schema'); +end $_$ volatile security definer language plpgsql ; + +create function invalid_role_claim_key_reload() returns void as $_$ +begin + alter role postgrest_test_authenticator set pgrst.jwt_role_claim_key = 'test'; + perform pg_notify('pgrst', 'reload config'); +end $_$ volatile security definer language plpgsql ; + +create function reset_invalid_role_claim_key() returns void as $_$ +begin + alter role postgrest_test_authenticator reset pgrst.jwt_role_claim_key; + perform pg_notify('pgrst', 'reload config'); +end $_$ volatile security definer language plpgsql ; + +create function reload_pgrst_config() returns void as $_$ +begin + perform pg_notify('pgrst', 'reload config'); +end $_$ language plpgsql ; diff --git a/test/io-tests/test_io.py b/test/io-tests/test_io.py index b7c5a2a66..cc94ad2c1 100644 --- a/test/io-tests/test_io.py +++ b/test/io-tests/test_io.py @@ -85,7 +85,7 @@ def defaultenv(): "Default environment for PostgREST." return { "PGRST_DB_URI": os.environ["PGRST_DB_URI"], - "PGRST_DB_SCHEMAS": os.environ["PGRST_DB_SCHEMAS"], + "PGRST_DB_SCHEMAS": "public", "PGRST_DB_ANON_ROLE": os.environ["PGRST_DB_ANON_ROLE"], "PGRST_DB_CONFIG": "false", "PGRST_LOG_LEVEL": "info", @@ -288,7 +288,7 @@ def test_expected_config_from_environment(): @pytest.mark.parametrize( "role, expectedconfig", [ - ("postgrest_test_authenticator", "no-defaults-with-db.config"), + ("db_config_authenticator", "no-defaults-with-db.config"), ("other_authenticator", "no-defaults-with-db-other-authenticator.config"), ], ) @@ -314,23 +314,6 @@ def test_expected_config_from_db_settings(defaultenv, role, expectedconfig): assert dumpconfig(configpath=config, env=env) == expected -def test_read_db_setting(defaultenv): - """ - Should be able to read db settings with current_setting. - - See: https://github.com/PostgREST/postgrest/pull/1729#discussion_r572946461 - """ - env = { - **defaultenv, - "PGRST_DB_CONFIG": "true", - } - with run(env=env) as postgrest: - uri = "/rpc/get_guc_value?name=pgrst.jwt_secret" - response = postgrest.session.get(uri) - - assert response.text == '"OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE"' - - @pytest.mark.parametrize( "config", [conf for conf in CONFIGSDIR.iterdir() if conf.suffix == ".config"], @@ -590,16 +573,15 @@ def test_db_schema_reload(tmp_path, defaultenv): configfile = tmp_path / "test.config" configfile.write_text(config) - headers = {"Accept-Profile": "v1"} env = {key: value for key, value in defaultenv.items() if key != "PGRST_DB_SCHEMAS"} with run(configfile, env=env) as postgrest: - response = postgrest.session.get("/parents", headers=headers) - assert response.status_code == 404 + response = postgrest.session.get("/rpc/get_guc_value?name=search_path") + assert response.text == '"public, public"' # change setting configfile.write_text( - config.replace('db-schemas = "test"', 'db-schemas = "test, v1"') + config.replace('db-schemas = "public"', 'db-schemas = "v1"') ) # reload config @@ -610,23 +592,18 @@ def test_db_schema_reload(tmp_path, defaultenv): time.sleep(0.1) - response = postgrest.session.get("/parents", headers=headers) - assert response.status_code == 200 + response = postgrest.session.get("/rpc/get_guc_value?name=search_path") + assert response.text == '"v1, public"' def test_db_schema_notify_reload(defaultenv): "DB schema and config should be reloaded when PostgREST is sent a NOTIFY" - env = { - **defaultenv, - "PGRST_DB_CONFIG": "true", - "PGRST_DB_CHANNEL_ENABLED": "true", - "PGRST_DB_SCHEMAS": "test", - } + env = {**defaultenv, "PGRST_DB_CONFIG": "true", "PGRST_DB_CHANNEL_ENABLED": "true"} with run(env=env) as postgrest: - response = postgrest.session.get("/parents") - assert response.status_code == 404 + response = postgrest.session.get("/rpc/get_guc_value?name=search_path") + assert response.text == '"public, public"' # change db-schemas config on the db and reload config and cache with notify postgrest.session.post( @@ -635,11 +612,12 @@ def test_db_schema_notify_reload(defaultenv): time.sleep(0.5) - response = postgrest.session.get("/parents?select=*,children(*)") - assert response.status_code == 200 + response = postgrest.session.get("/rpc/get_guc_value?name=search_path") + assert response.text == '"v1, public"' # reset db-schemas config on the db - postgrest.session.post("/rpc/reset_db_schema_config") + response = postgrest.session.post("/rpc/reset_db_schema_config") + assert response.status_code == 200 def test_max_rows_reload(defaultenv): @@ -723,10 +701,7 @@ def test_invalid_role_claim_key_notify_reload(defaultenv): break time.sleep(0.1) - assert ( - "failed to parse role-claim-key value" - in output.decode() - ) + assert "failed to parse role-claim-key value" in output.decode() response = postgrest.session.post("/rpc/reset_invalid_role_claim_key") assert response.status_code == 200