Files
TJWaterServerBinary/tests/unit/test_project_routing.py
T
jiang 90b02057bc
Generic Container CI/CD / test-build-publish (push) Successful in 1m13s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 1m13s
feat(projects): automate project infrastructure provisioning
2026-09-11 10:57:51 +08:00

126 lines
4.1 KiB
Python

import pytest
from psycopg.conninfo import conninfo_to_dict
from app.infra.db.project_routing import (
ActiveProjectRouting,
activate_project_routing,
get_active_project_routing,
get_project_database_name,
get_project_pgconn_string,
get_schema_template_database_name,
get_project_template_database_name,
get_project_timescale_pgconn_string,
)
def _routing(project_code: str = "project_a") -> ActiveProjectRouting:
return ActiveProjectRouting(
project_code=project_code,
business_dsn=(
"postgresql://biz_user:biz_password@biz.example:5432/biz_database"
"?sslmode=require"
),
timescale_dsn=(
"postgresql://ts_user:ts_password@timescale.example:5433/ts_database"
"?sslmode=require"
),
)
def test_project_database_uses_exact_routing_dsn_for_project_code() -> None:
routing = _routing()
with activate_project_routing(routing):
assert get_project_pgconn_string("project_a") == routing.business_dsn
assert (
get_project_timescale_pgconn_string("project_a")
== routing.timescale_dsn
)
def test_business_template_follows_physical_project_and_timescale_uses_routing(
monkeypatch,
) -> None:
monkeypatch.setattr(
"app.infra.db.project_routing.settings.WNDB_SCHEMA_TEMPLATE_DB_NAME",
"tjwater_v2_schema_template",
)
with activate_project_routing(_routing()):
template_name = get_project_template_database_name("project_a")
business = conninfo_to_dict(get_project_pgconn_string(template_name))
timescale = conninfo_to_dict(
get_project_timescale_pgconn_string("temporary_scheme")
)
assert business == {
"user": "biz_user",
"password": "biz_password",
"dbname": "biz_database_template",
"host": "biz.example",
"port": "5432",
"sslmode": "require",
}
assert timescale == {
"user": "ts_user",
"password": "ts_password",
"dbname": "ts_database",
"host": "timescale.example",
"port": "5433",
"sslmode": "require",
}
def test_project_code_resolves_to_physical_business_database(monkeypatch) -> None:
monkeypatch.setattr(
"app.infra.db.project_routing.settings.WNDB_SCHEMA_TEMPLATE_DB_NAME",
"tjwater_v2_schema_template",
)
with activate_project_routing(_routing()):
assert get_project_database_name("project_a") == "biz_database"
assert get_project_database_name("temporary_run") == "temporary_run"
assert (
get_project_template_database_name("project_a")
== "biz_database_template"
)
def test_project_template_uses_database_name_outside_project_routing() -> None:
assert (
get_project_template_database_name("physical_project")
== "physical_project_template"
)
def test_schema_template_comes_from_config(monkeypatch) -> None:
monkeypatch.setattr(
"app.infra.db.project_routing.settings.WNDB_SCHEMA_TEMPLATE_DB_NAME",
"tjwater_v2_schema_template",
)
assert get_schema_template_database_name() == "tjwater_v2_schema_template"
def test_project_routing_is_nested_and_request_local() -> None:
first = _routing("project_a")
second = _routing("project_b")
assert get_active_project_routing() is None
with activate_project_routing(first):
assert get_active_project_routing() is first
with activate_project_routing(second):
assert get_active_project_routing() is second
assert get_active_project_routing() is first
assert get_active_project_routing() is None
def test_timescale_access_requires_iot_routing_in_project_request() -> None:
business_only = _routing()
business_only = ActiveProjectRouting(
project_code=business_only.project_code,
business_dsn=business_only.business_dsn,
)
with activate_project_routing(business_only):
with pytest.raises(RuntimeError, match="TimescaleDB routing is not configured"):
get_project_timescale_pgconn_string()