Исправлены ошибки в ходе раскатки
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -10,6 +11,20 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
class InfrastructureConfigTests(unittest.TestCase):
|
||||
def test_structlog_event_is_not_passed_twice(self) -> None:
|
||||
log_methods = {"debug", "info", "warning", "error", "exception", "critical"}
|
||||
for source_path in (ROOT / "api-backend/app").rglob("*.py"):
|
||||
tree = ast.parse(source_path.read_text(encoding="utf-8"), filename=str(source_path))
|
||||
for node in ast.walk(tree):
|
||||
if (
|
||||
isinstance(node, ast.Call)
|
||||
and isinstance(node.func, ast.Attribute)
|
||||
and node.func.attr in log_methods
|
||||
and node.args
|
||||
and any(keyword.arg == "event" for keyword in node.keywords)
|
||||
):
|
||||
self.fail(f"duplicate structlog event in {source_path}:{node.lineno}")
|
||||
|
||||
def test_root_compose_uses_only_infra_fragments(self) -> None:
|
||||
compose = (ROOT / "docker-compose.yml").read_text(encoding="utf-8")
|
||||
for fragment in (
|
||||
@@ -22,6 +37,23 @@ class InfrastructureConfigTests(unittest.TestCase):
|
||||
self.assertIn(fragment, compose)
|
||||
self.assertNotIn("postgres:", compose.lower())
|
||||
|
||||
def test_external_dependencies_use_dedicated_egress_network(self) -> None:
|
||||
root = (ROOT / "docker-compose.yml").read_text(encoding="utf-8")
|
||||
self.assertIn("egress:\n name: han-chat-egress", root)
|
||||
|
||||
application = (ROOT / "infra/compose/application.yml").read_text(encoding="utf-8")
|
||||
self.assertIn("networks: [backend, observability, egress]", application)
|
||||
self.assertIn("networks: [public, backend, observability]", application)
|
||||
|
||||
jobs = (ROOT / "deployment/docker-compose.jobs.yml").read_text(encoding="utf-8")
|
||||
self.assertEqual(jobs.count("networks: [backend, egress]"), 4)
|
||||
|
||||
observability = (ROOT / "observability/docker-compose.yml").read_text(encoding="utf-8")
|
||||
self.assertIn("networks: [observability, backend, egress]", observability)
|
||||
|
||||
redis = (ROOT / "redis/docker-compose.yml").read_text(encoding="utf-8")
|
||||
self.assertNotIn("egress", redis)
|
||||
|
||||
def test_only_nginx_fragment_publishes_ports(self) -> None:
|
||||
forbidden = (
|
||||
ROOT / "infra/compose/application.yml",
|
||||
@@ -38,6 +70,12 @@ class InfrastructureConfigTests(unittest.TestCase):
|
||||
|
||||
def test_nginx_internal_denies_precede_spa(self) -> None:
|
||||
site = (ROOT / "nginx/templates/site-tls.conf.template").read_text(encoding="utf-8")
|
||||
compose = (ROOT / "nginx/docker-compose.yml").read_text(encoding="utf-8")
|
||||
config = (ROOT / "nginx/nginx.conf.template").read_text(encoding="utf-8")
|
||||
proxy_common = (ROOT / "nginx/snippets/proxy-common.conf").read_text(encoding="utf-8")
|
||||
proxy_keycloak = (ROOT / "nginx/snippets/proxy-keycloak.conf").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
internal = site.index("location ^~ /internal/")
|
||||
api = site.index("location ^~ /api/")
|
||||
frontend = site.index("include /etc/nginx/generated/frontend-location.conf")
|
||||
@@ -45,6 +83,17 @@ class InfrastructureConfigTests(unittest.TestCase):
|
||||
self.assertLess(api, frontend)
|
||||
self.assertIn("location = /api/v1/realtime", site)
|
||||
self.assertNotIn("message-safety:", site)
|
||||
self.assertIn("uid=0,gid=0", compose)
|
||||
self.assertIn('cap_add: ["CHOWN", "NET_BIND_SERVICE", "SETUID", "SETGID"]', compose)
|
||||
self.assertTrue(config.startswith("user nginx;\n"))
|
||||
self.assertIn("proxy_read_timeout 30s;", config)
|
||||
self.assertNotIn("proxy_read_timeout", proxy_common)
|
||||
self.assertNotIn("proxy_send_timeout", proxy_common)
|
||||
self.assertIn("include /etc/nginx/snippets/proxy-keycloak.conf;", site)
|
||||
self.assertIn("location ^~ /auth/resources/", site)
|
||||
self.assertIn("protocol/openid-connect/3p-cookies/", site)
|
||||
self.assertNotIn("security-headers.conf", proxy_keycloak)
|
||||
self.assertNotIn("X-Frame-Options", proxy_keycloak)
|
||||
|
||||
def test_redis_persistence_acl_and_no_host_port(self) -> None:
|
||||
config = (ROOT / "redis/redis.conf").read_text(encoding="utf-8")
|
||||
@@ -94,13 +143,42 @@ class InfrastructureConfigTests(unittest.TestCase):
|
||||
nginx = (ROOT / "nginx/docker-compose.yml").read_text(encoding="utf-8")
|
||||
self.assertIn("frontend-static: {condition: service_completed_successfully}", nginx)
|
||||
|
||||
def test_smoke_script_does_not_source_env_as_shell(self) -> None:
|
||||
smoke = (ROOT / "deployment/scripts/smoke.sh").read_text(encoding="utf-8")
|
||||
self.assertNotIn('. "./$ENV_FILE"', smoke)
|
||||
for variable in ("PUBLIC_HOST", "PUBLIC_WEB_URL", "KEYCLOAK_REALM"):
|
||||
self.assertIn(f"{variable}=$(env_value {variable})", smoke)
|
||||
|
||||
def test_expo_public_environment_uses_static_property_access(self) -> None:
|
||||
config = (ROOT / "frontend-test-site/src/config.ts").read_text(encoding="utf-8")
|
||||
self.assertNotIn("process.env[name]", config)
|
||||
self.assertIn("process.env.EXPO_PUBLIC_API_BASE_URL", config)
|
||||
self.assertIn("process.env.EXPO_PUBLIC_AUTH_BASE_URL", config)
|
||||
|
||||
def test_alembic_escapes_percent_encoded_dsn_options(self) -> None:
|
||||
for relative_path in (
|
||||
"api-backend/alembic/env.py",
|
||||
"bitrix-local-app/alembic/env.py",
|
||||
"bitrix-sync/alembic/env.py",
|
||||
):
|
||||
env_script = (ROOT / relative_path).read_text(encoding="utf-8")
|
||||
self.assertIn('.replace("%", "%%")', env_script, relative_path)
|
||||
self.assertIn("create_postgres_engine", env_script, relative_path)
|
||||
|
||||
def test_keycloak_management_health_and_bridge_environment(self) -> None:
|
||||
standalone = (ROOT / "keycloak/docker-compose.yml").read_text(encoding="utf-8")
|
||||
self.assertIn("GET /health/ready", standalone)
|
||||
self.assertNotIn("GET /auth/health/ready", standalone)
|
||||
self.assertIn("GET /auth/health/ready", standalone)
|
||||
|
||||
dockerfile = (ROOT / "keycloak/Dockerfile").read_text(encoding="utf-8")
|
||||
self.assertLess(
|
||||
dockerfile.index("COPY realm ./realm"),
|
||||
dockerfile.index("mvn -B -ntp clean verify"),
|
||||
)
|
||||
|
||||
application = (ROOT / "infra/compose/application.yml").read_text(encoding="utf-8")
|
||||
self.assertIn("GET /auth/health/ready", application)
|
||||
for variable in (
|
||||
"KC_DB_SCHEMA",
|
||||
"KEYCLOAK_OTP_MOCK_ENABLED",
|
||||
"KEYCLOAK_OTP_MOCK_CODE",
|
||||
"KEYCLOAK_OTP_HMAC_KEY",
|
||||
@@ -114,6 +192,10 @@ class InfrastructureConfigTests(unittest.TestCase):
|
||||
|
||||
def test_env_validator_accepts_materialized_example(self) -> None:
|
||||
example = (ROOT / ".env.example").read_text(encoding="utf-8")
|
||||
self.assertNotIn("options=-csearch_path", example)
|
||||
self.assertNotIn("currentSchema=", example)
|
||||
self.assertIn("KEYCLOAK_DB_SCHEMA=keycloak", example)
|
||||
self.assertIn("HAN_PG_PORT=5433", example)
|
||||
for required in (
|
||||
"CURSOR_HMAC_SECRET=",
|
||||
"BITRIX_TOKEN_ENCRYPTION_KEY=",
|
||||
|
||||
Reference in New Issue
Block a user