from pathlib import Path import pytest from app.core.config import Settings from app.core.secrets import SecretFileError, read_secret_file def test_secret_file_strips_single_trailing_newline(tmp_path: Path) -> None: secret_path = tmp_path / "secret" secret_path.write_text("test-only-value\n", encoding="utf-8") assert read_secret_file(secret_path) == "test-only-value" def test_secret_error_never_contains_secret_value(tmp_path: Path) -> None: secret_path = tmp_path / "secret" secret_path.write_text("first\nsecond\n", encoding="utf-8") with pytest.raises(SecretFileError) as captured: read_secret_file(secret_path) assert "first" not in str(captured.value) assert "second" not in str(captured.value) def test_database_url_reads_password_from_file(tmp_path: Path) -> None: password_path = tmp_path / "postgres_password" password_path.write_text("local-test-password", encoding="utf-8") settings = Settings(postgres_password_file=password_path) url = settings.database_url() assert url.password == "local-test-password" assert "local-test-password" not in str(url) def test_base_urls_drop_trailing_slashes() -> None: settings = Settings( bailian_openai_base_url="https://example.invalid/compatible-mode/v1/", bailian_native_base_url="https://example.invalid/api/v1/", bailian_rerank_base_url="https://example.invalid/compatible-api/v1/", ) assert settings.bailian_openai_base_url.endswith("/v1") assert settings.bailian_rerank_base_url.endswith("/v1") def test_model_gateway_url_is_fixed_to_internal_service() -> None: settings = Settings(model_gateway_base_url="http://model-gateway:8000/") assert settings.model_gateway_base_url == "http://model-gateway:8000" @pytest.mark.parametrize( "value", [ "https://model-gateway:8000", "http://model-gateway:9000", "http://127.0.0.1:8000", "http://model-gateway:8000/proxy", "http://user:model-token@model-gateway:8000", ], ) def test_model_gateway_url_rejects_ssrf_and_credential_variants(value: str) -> None: with pytest.raises(ValueError, match="fixed internal service URL"): Settings(model_gateway_base_url=value) def test_embedding_dimension_accepts_compose_string(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("EMBEDDING_DIMENSION", "1024") settings = Settings(_env_file=None) assert settings.embedding_dimension == 1024 assert isinstance(settings.embedding_dimension, int) @pytest.mark.parametrize("configured", ["1536", "1024.0", "01024", " 1024 "]) def test_embedding_dimension_rejects_any_other_environment_value( monkeypatch: pytest.MonkeyPatch, configured: str, ) -> None: monkeypatch.setenv("EMBEDDING_DIMENSION", configured) with pytest.raises(ValueError, match="EMBEDDING_DIMENSION must be exactly 1024"): Settings(_env_file=None) @pytest.mark.parametrize("configured", [1024.0, True, None]) def test_embedding_dimension_rejects_non_integer_programmatic_values(configured: object) -> None: with pytest.raises(ValueError, match="EMBEDDING_DIMENSION must be exactly 1024"): Settings.model_validate({"embedding_dimension": configured}) def test_live_endpoints_must_share_one_beijing_workspace(tmp_path: Path) -> None: key_path = tmp_path / "key" key_path.write_text("test-key-value", encoding="utf-8") settings = Settings( dashscope_api_key_file=key_path, bailian_openai_base_url=( "https://workspace-a.cn-beijing.maas.aliyuncs.com/compatible-mode/v1" ), bailian_rerank_base_url=( "https://workspace-b.cn-beijing.maas.aliyuncs.com/compatible-api/v1" ), ) with pytest.raises(ValueError, match="same workspace"): settings.bailian_api_key() def test_live_endpoint_rejects_non_bailian_host_before_reading_key(tmp_path: Path) -> None: settings = Settings( dashscope_api_key_file=tmp_path / "missing", bailian_openai_base_url="https://attacker.invalid/compatible-mode/v1", bailian_rerank_base_url="https://attacker.invalid/compatible-api/v1", ) with pytest.raises(ValueError, match="approved Beijing"): settings.bailian_api_key()