Files
han-app/infratest/test_infratest.py
T

143 lines
5.1 KiB
Python

from __future__ import annotations
import io
import json
import os
import tempfile
import unittest
from contextlib import redirect_stdout
from pathlib import Path
from unittest.mock import patch
from infratest import ConfigError, Reporter, Settings, as_bool, sanitize
def valid_env(ca_path: Path) -> str:
return f"""
HAN_PG_HOST=db.example.test
HAN_PG_PORT=6432
HAN_PG_DATABASE=han_chat
HAN_PG_SSLMODE=verify-full
HAN_PG_SSLROOTCERT={ca_path}
HAN_PG_PASSWORD_HAN_APP=han-password
HAN_PG_PASSWORD_BITRIX=bitrix-password
HAN_PG_PASSWORD_BITRIX_SYNC=sync-password
HAN_PG_PASSWORD_MESSAGE_SAFETY=safety-password
HAN_PG_PASSWORD_KEYCLOAK=keycloak-password
SELECTEL_S3_ENDPOINT_URL=https://s3.storage.selcloud.ru
SELECTEL_S3_BUCKET_QUARANTINE=quarantine
SELECTEL_S3_BUCKET_ATTACHMENTS=attachments
SELECTEL_S3_BUCKET_DOCUMENTS=documents
SELECTEL_S3_ACCESS_KEY=api-access
SELECTEL_S3_SECRET_KEY=api-secret
SELECTEL_S3_QUARANTINE_READ_ACCESS_KEY=read-access
SELECTEL_S3_QUARANTINE_READ_SECRET_KEY=read-secret
PUBLIC_WEB_URL=https://chat.example.test
INFRATEST_CHECK_CORS=true
INFRATEST_CHECK_MIGRATIONS=false
INFRATEST_TIMEOUT_SECONDS=10
""".strip()
class SettingsTests(unittest.TestCase):
def test_loads_complete_configuration_and_default_roles(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
ca = root / "ca.pem"
ca.write_text("test ca", encoding="utf-8")
env_file = root / ".env"
env_file.write_text(valid_env(ca), encoding="utf-8")
with patch.dict(os.environ, {}, clear=True):
settings = Settings.from_env_file(env_file)
self.assertEqual(settings.pg_host, "db.example.test")
self.assertEqual(settings.pg_port, 6432)
self.assertEqual(
[(role.user, role.schema) for role in settings.pg_roles],
[
("han_app", "han_app"),
("bitrix_local_app", "bitrix_local"),
("bitrix_sync_user", "bitrix_sync"),
("message_safety_app", "message_safety"),
("keycloak_user", "keycloak"),
],
)
self.assertEqual(settings.buckets["documents"], "documents")
self.assertTrue(settings.check_cors)
self.assertFalse(settings.check_migrations)
def test_rejects_non_verifying_postgres_tls(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
ca = root / "ca.pem"
ca.write_text("test ca", encoding="utf-8")
env_file = root / ".env"
env_file.write_text(
valid_env(ca).replace(
"HAN_PG_SSLMODE=verify-full",
"HAN_PG_SSLMODE=require",
),
encoding="utf-8",
)
with patch.dict(os.environ, {}, clear=True):
with self.assertRaisesRegex(ConfigError, "verify-full"):
Settings.from_env_file(env_file)
def test_requires_public_url_when_cors_enabled(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
ca = root / "ca.pem"
ca.write_text("test ca", encoding="utf-8")
env_file = root / ".env"
env_file.write_text(
valid_env(ca).replace(
"PUBLIC_WEB_URL=https://chat.example.test",
"PUBLIC_WEB_URL=",
),
encoding="utf-8",
)
with patch.dict(os.environ, {}, clear=True):
with self.assertRaisesRegex(ConfigError, "PUBLIC_WEB_URL"):
Settings.from_env_file(env_file)
class SafetyTests(unittest.TestCase):
def test_sanitize_redacts_secret_and_url_query(self) -> None:
value = (
"password=do-not-print "
"url=https://bucket.example/object?X-Amz-Credential=secret&X-Amz-Signature=x"
)
sanitized = sanitize(value)
self.assertNotIn("do-not-print", sanitized)
self.assertNotIn("X-Amz", sanitized)
self.assertNotIn("Signature", sanitized)
self.assertIn("password=<redacted>", sanitized)
self.assertIn("https://bucket.example/object", sanitized)
def test_reporter_exit_code_and_json_are_secret_free(self) -> None:
reporter = Reporter()
with redirect_stdout(io.StringIO()):
reporter.add("ok", "PASS", "url=https://example.test/a?token=secret")
reporter.add("bad", "FAIL", "password=hidden")
with tempfile.TemporaryDirectory() as directory:
report_path = Path(directory) / "report.json"
reporter.write_json(report_path)
data = json.loads(report_path.read_text(encoding="utf-8"))
self.assertEqual(reporter.exit_code, 1)
serialized = json.dumps(data)
self.assertNotIn("hidden", serialized)
self.assertNotIn("?token", serialized)
def test_boolean_parser(self) -> None:
self.assertTrue(as_bool("yes"))
self.assertFalse(as_bool("OFF"))
with self.assertRaises(ConfigError):
as_bool("sometimes")
if __name__ == "__main__":
unittest.main()