Files
postgrest/test/io/test_admin.py
2026-07-11 19:20:59 +00:00

207 lines
7.1 KiB
Python

"PostgREST admin server related tests"
import os
import re
import signal
import time
import pytest
from config import FIXTURES
from postgrest import (
freeport,
reset_statement_timeout,
run,
set_statement_timeout,
)
def test_admin_schema_cache(defaultenv):
"Should get a success response from the admin server containing current schema cache"
with run(env=defaultenv) as postgrest:
response = postgrest.admin.get("/schema_cache")
assert response.status_code == 200
assert '"dbTables":[[{"qiName":"authors_only"' in response.text
def test_admin_ready_w_channel(defaultenv):
"Should get a success response from the admin server ready endpoint when the LISTEN channel is enabled"
env = {
**defaultenv,
"PGRST_DB_CHANNEL_ENABLED": "true",
}
with run(env=env) as postgrest:
response = postgrest.admin.get("/ready")
assert response.status_code == 200
def test_admin_ready_wo_channel(defaultenv):
"Should get a success response from the admin server ready endpoint when the LISTEN channel is disabled"
env = {
**defaultenv,
"PGRST_DB_CHANNEL_ENABLED": "false",
}
with run(env=env) as postgrest:
response = postgrest.admin.get("/ready")
assert response.status_code == 200
def test_admin_ready_includes_schema_cache_state(defaultenv, metapostgrest):
"Should get a failed response from the admin server ready endpoint when the schema cache is not loaded"
role = "timeout_authenticator"
env = {
**defaultenv,
"PGUSER": role,
"PGRST_DB_ANON_ROLE": "postgrest_test_anonymous",
"PGRST_INTERNAL_SCHEMA_CACHE_QUERY_SLEEP": "500",
}
with run(env=env) as postgrest:
# The schema cache query takes at least 500ms, due to PGRST_INTERNAL_SCHEMA_CACHE_QUERY_SLEEP above.
# Make it impossible to load the schema cache, by setting statement timeout to 400ms.
set_statement_timeout(metapostgrest, role, 400)
# force a reconnection so the new role setting is picked up
postgrest.process.send_signal(signal.SIGUSR1)
postgrest.wait_until_scache_starts_loading()
response = postgrest.admin.get("/ready", timeout=1)
assert response.status_code == 503
response = postgrest.session.get("/projects", timeout=1)
assert response.status_code == 200
reset_statement_timeout(metapostgrest, role)
def test_admin_not_found(defaultenv):
"Should get a not found from a undefined endpoint on the admin server"
with run(env=defaultenv) as postgrest:
response = postgrest.admin.get("/notfound")
assert response.status_code == 404
def test_admin_ready_dependent_on_main_app(defaultenv):
"Should get a failure from the admin ready endpoint if the main app also fails"
with run(env=defaultenv) as postgrest:
# delete the unix socket to make the main app fail
os.remove(defaultenv["PGRST_SERVER_UNIX_SOCKET"])
response = postgrest.admin.get("/ready")
assert response.status_code == 500
def test_admin_live_good(defaultenv):
"Should get a success from the admin live endpoint if the main app is running"
with run(env=defaultenv, port=freeport()) as postgrest:
response = postgrest.admin.get("/live")
assert response.status_code == 200
def test_admin_live_dependent_on_main_app(defaultenv):
"Should get a failure from the admin live endpoint if the main app also fails"
with run(env=defaultenv) as postgrest:
# delete the unix socket to make the main app fail
os.remove(defaultenv["PGRST_SERVER_UNIX_SOCKET"])
response = postgrest.admin.get("/live")
assert response.status_code == 500
@pytest.mark.parametrize("specialhostvalue", FIXTURES["specialhostvalues"])
def test_admin_works_with_host_special_values(specialhostvalue, defaultenv):
"Should get a success from the admin live and ready endpoints when using special host values for the main app"
with run(env=defaultenv, port=freeport(), host=specialhostvalue) as postgrest:
response = postgrest.admin.get("/live")
assert response.status_code == 200
response = postgrest.admin.get("/ready")
assert response.status_code == 200
def test_admin_metrics(defaultenv):
"Should get metrics from the admin endpoint"
with run(env=defaultenv) as postgrest:
response = postgrest.admin.get("/metrics")
assert response.status_code == 200
assert response.headers["Content-Type"] == "text/plain; charset=utf-8"
assert "pgrst_schema_cache_query_time_seconds" in response.text
assert 'pgrst_schema_cache_loads_total{status="SUCCESS"}' in response.text
assert "pgrst_db_pool_max" in response.text
assert "pgrst_db_pool_waiting" in response.text
assert "pgrst_db_pool_available" in response.text
assert "pgrst_db_pool_timeouts_total" in response.text
def test_admin_metrics_include_ghc_runtime_metrics(defaultenv):
"Should get GHC runtime metrics from the admin endpoint when RTS stats are enabled"
with run(env=defaultenv, args=["+RTS", "-T", "-RTS"]) as postgrest:
response = postgrest.admin.get("/metrics")
assert response.status_code == 200
assert "# HELP ghc_gcs_total Total number of GCs" in response.text
assert "# TYPE ghc_gcs_total counter" in response.text
assert re.search(r"^ghc_gcs_total \d+(?:\.\d+)?$", response.text, re.MULTILINE)
assert re.search(
r"^ghc_allocated_bytes_total \d+(?:\.\d+)?$", response.text, re.MULTILINE
)
def test_admin_metrics_exclude_ghc_runtime_metrics_by_default(defaultenv):
"Should not get GHC runtime metrics unless RTS stats are enabled"
with run(env=defaultenv) as postgrest:
response = postgrest.admin.get("/metrics")
assert response.status_code == 200
assert "ghc_gcs_total" not in response.text
assert "ghc_allocated_bytes_total" not in response.text
def test_admin_metrics_include_schema_cache_fails(defaultenv, metapostgrest):
"Should get shema cache fails from the metrics endpoint"
role = "timeout_authenticator"
env = {
**defaultenv,
"PGUSER": role,
"PGRST_INTERNAL_SCHEMA_CACHE_QUERY_SLEEP": "50",
}
with run(env=env) as postgrest:
# The schema cache query takes at least 20ms, due to PGRST_INTERNAL_SCHEMA_CACHE_QUERY_SLEEP above.
# Make it impossible to load the schema cache, by setting statement timeout to 100ms.
set_statement_timeout(metapostgrest, role, 20)
# force a reconnection so the new role setting is picked up
postgrest.process.send_signal(signal.SIGUSR1)
# wait for some schema cache retries
time.sleep(1)
response = postgrest.admin.get("/ready", timeout=1)
assert response.status_code == 503
response = postgrest.admin.get("/metrics", timeout=1)
assert response.status_code == 200
metrics = float(
re.search(
r'pgrst_schema_cache_loads_total{status="FAIL"} (\d+)', response.text
).group(1)
)
assert metrics == 1.0
reset_statement_timeout(metapostgrest, role)