feat: new settings, CLI skeleton and interface skeletons
Adds the pydantic-settings Settings with validated GITEA_URL, OAuth scopes and toolsets, the argparse CLI skeleton, catalog.model, identity and GiteaAPIError. Test fixtures now isolate env, cwd and state paths in tmp.
This commit is contained in:
1 parent
4ac9626eb5
commit
b82e2866cb
24 files changed
+1512
-605
No files matched your search
+358
-105
@@ -1,138 +1,391 @@
|
||||
"""Tests for configuration management."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from aegis_gitea_mcp.config import get_settings, reset_settings
|
||||
from aegis_gitea_mcp.config import (
|
||||
DEFAULT_OAUTH_SCOPES,
|
||||
GITEA_SCOPES,
|
||||
Settings,
|
||||
get_settings,
|
||||
is_loopback_host,
|
||||
reset_settings,
|
||||
)
|
||||
|
||||
|
||||
def test_settings_from_env(mock_env: None) -> None:
|
||||
"""Test loading settings from environment variables."""
|
||||
def make(monkeypatch: pytest.MonkeyPatch, **env: str) -> Settings:
|
||||
"""Build fresh settings from the given environment."""
|
||||
for key, value in env.items():
|
||||
monkeypatch.setenv(key, value)
|
||||
reset_settings()
|
||||
return get_settings()
|
||||
|
||||
|
||||
def test_defaults(mock_env: None, tmp_path) -> None:
|
||||
settings = get_settings()
|
||||
|
||||
assert settings.gitea_base_url == "https://gitea.example.com"
|
||||
assert settings.gitea_token == "test-token-12345"
|
||||
assert settings.gitea_url == "https://gitea.example.com"
|
||||
assert settings.gitea_api_url == "https://gitea.example.com/api/v1"
|
||||
assert settings.gitea_token is not None
|
||||
assert settings.gitea_token.get_secret_value() == "test-token-12345"
|
||||
assert settings.mcp_transport == "stdio"
|
||||
assert settings.mcp_host == "127.0.0.1"
|
||||
assert settings.mcp_port == 8080
|
||||
assert settings.log_level == "DEBUG"
|
||||
|
||||
|
||||
def test_settings_defaults(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Test default values when not specified."""
|
||||
monkeypatch.setenv("GITEA_URL", "https://gitea.example.com")
|
||||
monkeypatch.setenv("GITEA_TOKEN", "test-token")
|
||||
monkeypatch.setenv("MCP_API_KEYS", "a" * 64)
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
assert settings.mcp_host == "127.0.0.1"
|
||||
assert settings.mcp_port == 8080
|
||||
assert settings.log_level == "INFO"
|
||||
assert settings.max_file_size_bytes == 1_048_576
|
||||
assert settings.mcp_path == "/mcp"
|
||||
assert settings.mcp_bearer_token is None
|
||||
assert settings.public_base_url is None
|
||||
assert settings.allowed_hosts == []
|
||||
assert settings.oauth_enabled is False
|
||||
assert settings.read_only is False
|
||||
assert settings.toolsets == ["all"]
|
||||
assert settings.disabled_tools == []
|
||||
assert settings.request_timeout_seconds == 30
|
||||
assert settings.max_tool_response_items == 200
|
||||
assert settings.max_tool_response_chars == 60000
|
||||
assert settings.max_binary_bytes == 5_242_880
|
||||
assert settings.secret_detection_mode == "mask"
|
||||
assert settings.log_level == "INFO"
|
||||
assert settings.expose_error_details is False
|
||||
assert settings.rate_limit_per_minute == 120
|
||||
assert ",".join(settings.oauth_scopes) == DEFAULT_OAUTH_SCOPES
|
||||
|
||||
|
||||
def test_settings_validation_missing_required(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None:
|
||||
"""Test that missing required fields raise validation errors."""
|
||||
def test_data_dir_defaults_and_audit_path(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("DATA_DIR")
|
||||
monkeypatch.delenv("AUDIT_LOG_PATH")
|
||||
settings = make(monkeypatch, GITEA_URL="https://g.example.com")
|
||||
|
||||
monkeypatch.delenv("GITEA_URL", raising=False)
|
||||
monkeypatch.delenv("GITEA_TOKEN", raising=False)
|
||||
|
||||
# Change to tmp directory so .env file won't be found
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
reset_settings()
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
assert str(settings.data_dir).endswith(".local/state/aegis-gitea-mcp")
|
||||
assert "~" not in str(settings.data_dir)
|
||||
assert settings.resolved_audit_log_path == settings.data_dir / "audit.log"
|
||||
|
||||
|
||||
def test_settings_invalid_log_level(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Test that invalid log levels are rejected."""
|
||||
monkeypatch.setenv("GITEA_URL", "https://gitea.example.com")
|
||||
monkeypatch.setenv("GITEA_TOKEN", "test-token")
|
||||
monkeypatch.setenv("MCP_API_KEYS", "a" * 64)
|
||||
monkeypatch.setenv("LOG_LEVEL", "INVALID")
|
||||
def test_audit_path_follows_data_dir(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None:
|
||||
monkeypatch.delenv("AUDIT_LOG_PATH")
|
||||
settings = make(monkeypatch, GITEA_URL="https://g.example.com", DATA_DIR=str(tmp_path / "d"))
|
||||
|
||||
reset_settings()
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
assert settings.resolved_audit_log_path == tmp_path / "d" / "audit.log"
|
||||
|
||||
|
||||
def test_settings_empty_token(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Test that empty tokens are rejected."""
|
||||
monkeypatch.setenv("GITEA_URL", "https://gitea.example.com")
|
||||
monkeypatch.setenv("GITEA_TOKEN", " ")
|
||||
monkeypatch.setenv("MCP_API_KEYS", "a" * 64)
|
||||
def test_blank_values_are_unset(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
settings = make(
|
||||
monkeypatch,
|
||||
GITEA_URL="https://g.example.com",
|
||||
GITEA_TOKEN="",
|
||||
MCP_BEARER_TOKEN=" ",
|
||||
PUBLIC_BASE_URL="",
|
||||
AUDIT_LOG_PATH="",
|
||||
)
|
||||
|
||||
reset_settings()
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
assert settings.gitea_token is None
|
||||
assert settings.mcp_bearer_token is None
|
||||
assert settings.public_base_url is None
|
||||
assert settings.audit_log_path is None
|
||||
|
||||
|
||||
def test_settings_singleton(mock_env: None) -> None:
|
||||
"""Test that get_settings returns same instance."""
|
||||
settings1 = get_settings()
|
||||
settings2 = get_settings()
|
||||
|
||||
assert settings1 is settings2
|
||||
def test_env_names_are_case_insensitive(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
settings = make(monkeypatch, gitea_url="https://g.example.com", mcp_port="9000")
|
||||
assert settings.mcp_port == 9000
|
||||
|
||||
|
||||
def test_write_mode_requires_whitelist_or_allow_all(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Write mode without whitelist must be rejected unless allow-all is enabled."""
|
||||
monkeypatch.setenv("GITEA_URL", "https://gitea.example.com")
|
||||
monkeypatch.setenv("GITEA_TOKEN", "test-token")
|
||||
monkeypatch.setenv("MCP_API_KEYS", "a" * 64)
|
||||
monkeypatch.setenv("WRITE_MODE", "true")
|
||||
monkeypatch.delenv("WRITE_REPOSITORY_WHITELIST", raising=False)
|
||||
monkeypatch.setenv("WRITE_ALLOW_ALL_TOKEN_REPOS", "false")
|
||||
|
||||
reset_settings()
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
|
||||
|
||||
def test_write_mode_allows_all_token_repos(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Allow-all mode should pass validation without explicit repository whitelist."""
|
||||
monkeypatch.setenv("GITEA_URL", "https://gitea.example.com")
|
||||
monkeypatch.setenv("GITEA_TOKEN", "test-token")
|
||||
monkeypatch.setenv("MCP_API_KEYS", "a" * 64)
|
||||
monkeypatch.setenv("WRITE_MODE", "true")
|
||||
monkeypatch.delenv("WRITE_REPOSITORY_WHITELIST", raising=False)
|
||||
monkeypatch.setenv("WRITE_ALLOW_ALL_TOKEN_REPOS", "true")
|
||||
|
||||
def test_reads_dotenv_file(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None:
|
||||
(tmp_path / ".env").write_text("GITEA_URL=https://dotenv.example.com\nREAD_ONLY=true\n")
|
||||
reset_settings()
|
||||
settings = get_settings()
|
||||
assert settings.write_allow_all_token_repos is True
|
||||
|
||||
assert settings.gitea_url == "https://dotenv.example.com"
|
||||
assert settings.read_only is True
|
||||
|
||||
|
||||
def _oauth_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Apply a minimal, valid OAuth-mode environment."""
|
||||
monkeypatch.setenv("GITEA_URL", "https://gitea.example.com")
|
||||
monkeypatch.setenv("OAUTH_MODE", "true")
|
||||
monkeypatch.setenv("GITEA_OAUTH_CLIENT_ID", "test-client-id")
|
||||
monkeypatch.setenv("GITEA_OAUTH_CLIENT_SECRET", "test-client-secret")
|
||||
monkeypatch.setenv("STARTUP_VALIDATE_GITEA", "false")
|
||||
|
||||
|
||||
def test_oauth_state_secret_too_short_is_rejected(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""OAUTH_STATE_SECRET shorter than 32 characters must fail validation."""
|
||||
_oauth_env(monkeypatch)
|
||||
monkeypatch.setenv("OAUTH_STATE_SECRET", "short-secret")
|
||||
|
||||
reset_settings()
|
||||
with pytest.raises(ValidationError, match="at least 32 characters"):
|
||||
def test_missing_gitea_url_is_rejected() -> None:
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
|
||||
|
||||
def test_oauth_state_secret_minimum_length_accepted(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""A 32+ character OAUTH_STATE_SECRET passes validation."""
|
||||
_oauth_env(monkeypatch)
|
||||
monkeypatch.setenv("OAUTH_STATE_SECRET", "x" * 32)
|
||||
|
||||
def test_settings_singleton_and_reset(mock_env: None) -> None:
|
||||
first = get_settings()
|
||||
assert get_settings() is first
|
||||
reset_settings()
|
||||
settings = get_settings()
|
||||
assert settings.oauth_mode is True
|
||||
assert len(settings.oauth_state_secret) == 32
|
||||
assert get_settings() is not first
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("raw", "expected"),
|
||||
[
|
||||
("https://gitea.example.com", "https://gitea.example.com"),
|
||||
("https://gitea.example.com/", "https://gitea.example.com"),
|
||||
("http://localhost:3000", "http://localhost:3000"),
|
||||
("https://gitea.example.com/api/v1", "https://gitea.example.com"),
|
||||
("https://gitea.example.com/api/v1/", "https://gitea.example.com"),
|
||||
("https://example.com/gitea", "https://example.com/gitea"),
|
||||
("https://example.com/gitea/api/v1", "https://example.com/gitea"),
|
||||
],
|
||||
)
|
||||
def test_gitea_url_normalised(monkeypatch: pytest.MonkeyPatch, raw: str, expected: str) -> None:
|
||||
assert make(monkeypatch, GITEA_URL=raw).gitea_url == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"raw",
|
||||
[
|
||||
"ftp://gitea.example.com",
|
||||
"gitea.example.com",
|
||||
"https://",
|
||||
"https://user:pw@gitea.example.com",
|
||||
"https://user@gitea.example.com",
|
||||
"https://gitea.example.com?x=1",
|
||||
"https://gitea.example.com/#frag",
|
||||
"https://gitea.example.com:99999",
|
||||
"file:///etc/passwd",
|
||||
],
|
||||
)
|
||||
def test_gitea_url_rejected(monkeypatch: pytest.MonkeyPatch, raw: str) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", raw)
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
|
||||
|
||||
def test_bearer_token_minimum_length(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", "https://g.example.com")
|
||||
monkeypatch.setenv("MCP_BEARER_TOKEN", "short")
|
||||
with pytest.raises(ValidationError, match="at least 32"):
|
||||
get_settings()
|
||||
|
||||
settings = make(monkeypatch, MCP_BEARER_TOKEN="x" * 32)
|
||||
assert settings.mcp_bearer_token is not None
|
||||
|
||||
|
||||
def test_encryption_key_minimum_length(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", "https://g.example.com")
|
||||
monkeypatch.setenv("TOKEN_ENCRYPTION_KEY", "short")
|
||||
with pytest.raises(ValidationError, match="TOKEN_ENCRYPTION_KEY"):
|
||||
get_settings()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"raw",
|
||||
[
|
||||
"https://mcp.example.com",
|
||||
"https://mcp.example.com/",
|
||||
"http://localhost:8080",
|
||||
"http://127.0.0.1:8080",
|
||||
"http://[::1]:8080",
|
||||
],
|
||||
)
|
||||
def test_public_base_url_accepted(monkeypatch: pytest.MonkeyPatch, raw: str) -> None:
|
||||
settings = make(monkeypatch, GITEA_URL="https://g.example.com", PUBLIC_BASE_URL=raw)
|
||||
assert settings.public_base_url == raw.rstrip("/")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"raw",
|
||||
[
|
||||
"http://mcp.example.com",
|
||||
"mcp.example.com",
|
||||
"https://mcp.example.com?a=b",
|
||||
"https://u:p@mcp.example.com",
|
||||
],
|
||||
)
|
||||
def test_public_base_url_rejected(monkeypatch: pytest.MonkeyPatch, raw: str) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", "https://g.example.com")
|
||||
monkeypatch.setenv("PUBLIC_BASE_URL", raw)
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
|
||||
|
||||
def test_mcp_path_validation(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", "https://g.example.com")
|
||||
assert make(monkeypatch, MCP_PATH="/api/mcp/").mcp_path == "/api/mcp"
|
||||
for bad in ("mcp", "//evil", "/a?b", "/", "/a b"):
|
||||
monkeypatch.setenv("MCP_PATH", bad)
|
||||
reset_settings()
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
|
||||
|
||||
def test_port_and_transport_validation(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", "https://g.example.com")
|
||||
monkeypatch.setenv("MCP_PORT", "70000")
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
monkeypatch.setenv("MCP_PORT", "8080")
|
||||
monkeypatch.setenv("MCP_TRANSPORT", "sse")
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
assert make(monkeypatch, MCP_TRANSPORT="http").mcp_transport == "http"
|
||||
|
||||
|
||||
def test_comma_separated_lists(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
settings = make(
|
||||
monkeypatch,
|
||||
GITEA_URL="https://g.example.com",
|
||||
ALLOWED_HOSTS="mcp.example.com, other.example.com:8443,mcp.example.com",
|
||||
TOOLSETS="Repo, issue",
|
||||
DISABLED_TOOLS="Gitea_Repo_Delete, gitea_admin_delete_user",
|
||||
)
|
||||
|
||||
assert settings.allowed_hosts == ["mcp.example.com", "other.example.com:8443"]
|
||||
assert settings.toolsets == ["repo", "issue"]
|
||||
assert settings.disabled_tools == ["gitea_repo_delete", "gitea_admin_delete_user"]
|
||||
|
||||
|
||||
def test_unknown_toolset_rejected(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", "https://g.example.com")
|
||||
monkeypatch.setenv("TOOLSETS", "repo,bogus")
|
||||
with pytest.raises(ValidationError, match="bogus"):
|
||||
get_settings()
|
||||
|
||||
|
||||
def test_empty_toolsets_rejected(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", "https://g.example.com")
|
||||
monkeypatch.setenv("TOOLSETS", " , ")
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
|
||||
|
||||
def test_default_oauth_scopes_are_valid() -> None:
|
||||
assert set(DEFAULT_OAUTH_SCOPES.split(",")) <= GITEA_SCOPES
|
||||
assert len(GITEA_SCOPES) == 18
|
||||
|
||||
|
||||
def test_oauth_scopes_custom_and_dedup(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
settings = make(
|
||||
monkeypatch,
|
||||
GITEA_URL="https://g.example.com",
|
||||
OAUTH_SCOPES="read:repository write:repository,read:repository",
|
||||
)
|
||||
assert settings.oauth_scopes == ["read:repository", "write:repository"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"scope", ["all", "repo", "read:bogus", "write:repository,admin", "public-only"]
|
||||
)
|
||||
def test_unknown_oauth_scope_rejected(monkeypatch: pytest.MonkeyPatch, scope: str) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", "https://g.example.com")
|
||||
monkeypatch.setenv("OAUTH_SCOPES", scope)
|
||||
with pytest.raises(ValidationError, match="unknown scope"):
|
||||
get_settings()
|
||||
|
||||
|
||||
def test_empty_oauth_scopes_rejected(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", "https://g.example.com")
|
||||
monkeypatch.setenv("OAUTH_SCOPES", ",")
|
||||
with pytest.raises(ValidationError, match="must not be empty"):
|
||||
get_settings()
|
||||
|
||||
|
||||
def test_log_level_normalised_and_validated(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
assert (
|
||||
make(monkeypatch, GITEA_URL="https://g.example.com", LOG_LEVEL="debug").log_level == "DEBUG"
|
||||
)
|
||||
monkeypatch.setenv("LOG_LEVEL", "loud")
|
||||
reset_settings()
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
|
||||
|
||||
def test_secret_detection_mode_validated(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITEA_URL", "https://g.example.com")
|
||||
monkeypatch.setenv("SECRET_DETECTION_MODE", "block")
|
||||
assert get_settings().secret_detection_mode == "block"
|
||||
reset_settings()
|
||||
monkeypatch.setenv("SECRET_DETECTION_MODE", "weird")
|
||||
with pytest.raises(ValidationError):
|
||||
get_settings()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("host", "expected"),
|
||||
[
|
||||
("127.0.0.1", True),
|
||||
("127.5.6.7", True),
|
||||
("::1", True),
|
||||
("[::1]", True),
|
||||
("localhost", True),
|
||||
("LOCALHOST", True),
|
||||
("0.0.0.0", False), # noqa: S104
|
||||
("::", False),
|
||||
("192.168.1.5", False),
|
||||
("gitea.example.com", False),
|
||||
],
|
||||
)
|
||||
def test_is_loopback(monkeypatch: pytest.MonkeyPatch, host: str, expected: bool) -> None:
|
||||
assert is_loopback_host(host) is expected
|
||||
settings = make(monkeypatch, GITEA_URL="https://g.example.com", MCP_HOST=host)
|
||||
assert settings.is_loopback_bind() is expected
|
||||
|
||||
|
||||
def test_oauth_config_errors(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
settings = make(monkeypatch, GITEA_URL="https://g.example.com")
|
||||
assert settings.oauth_config_errors() == []
|
||||
|
||||
settings = make(monkeypatch, OAUTH_ENABLED="true")
|
||||
errors = settings.oauth_config_errors()
|
||||
assert len(errors) == 4
|
||||
assert any("GITEA_OAUTH_CLIENT_ID" in e for e in errors)
|
||||
assert any("GITEA_OAUTH_CLIENT_SECRET" in e for e in errors)
|
||||
assert any("TOKEN_ENCRYPTION_KEY" in e for e in errors)
|
||||
assert any("PUBLIC_BASE_URL" in e for e in errors)
|
||||
|
||||
settings = make(
|
||||
monkeypatch,
|
||||
GITEA_OAUTH_CLIENT_ID="cid",
|
||||
GITEA_OAUTH_CLIENT_SECRET="csecret",
|
||||
TOKEN_ENCRYPTION_KEY="k" * 32,
|
||||
PUBLIC_BASE_URL="https://mcp.example.com",
|
||||
)
|
||||
assert settings.oauth_config_errors() == []
|
||||
|
||||
|
||||
def test_http_auth_configured(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
assert make(monkeypatch, GITEA_URL="https://g.example.com").http_auth_configured() is False
|
||||
assert make(monkeypatch, MCP_BEARER_TOKEN="b" * 32).http_auth_configured() is True
|
||||
|
||||
monkeypatch.delenv("MCP_BEARER_TOKEN")
|
||||
assert make(monkeypatch, OAUTH_ENABLED="true").http_auth_configured() is False
|
||||
settings = make(
|
||||
monkeypatch,
|
||||
GITEA_OAUTH_CLIENT_ID="cid",
|
||||
GITEA_OAUTH_CLIENT_SECRET="csecret",
|
||||
TOKEN_ENCRYPTION_KEY="k" * 32,
|
||||
PUBLIC_BASE_URL="https://mcp.example.com",
|
||||
)
|
||||
assert settings.http_auth_configured() is True
|
||||
|
||||
|
||||
def test_secrets_never_appear_in_repr_or_dump(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
secrets = {
|
||||
"GITEA_TOKEN": "gitea-token-SECRET-aaaaaaaaaaaa",
|
||||
"MCP_BEARER_TOKEN": "bearer-SECRET-bbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"GITEA_OAUTH_CLIENT_SECRET": "client-SECRET-cccccccccccc",
|
||||
"TOKEN_ENCRYPTION_KEY": "encryption-SECRET-dddddddddddddddddddd",
|
||||
}
|
||||
settings = make(monkeypatch, GITEA_URL="https://g.example.com", **secrets)
|
||||
|
||||
rendered = [
|
||||
repr(settings),
|
||||
str(settings),
|
||||
str(settings.model_dump()),
|
||||
settings.model_dump_json(),
|
||||
str(settings.model_dump(mode="json")),
|
||||
]
|
||||
for text in rendered:
|
||||
for secret in secrets.values():
|
||||
assert secret not in text
|
||||
assert settings.gitea_token is not None
|
||||
assert settings.gitea_token.get_secret_value() == secrets["GITEA_TOKEN"]
|
||||
|
||||
|
||||
def test_env_example_is_valid() -> None:
|
||||
"""The documented example file must load as-is (blank values mean unset)."""
|
||||
from pathlib import Path
|
||||
|
||||
example = Path(__file__).parent.parent / ".env.example"
|
||||
settings = Settings(_env_file=example) # type: ignore[call-arg]
|
||||
|
||||
assert settings.gitea_url == "https://gitea.example.com"
|
||||
assert settings.gitea_token is None
|
||||
assert settings.toolsets == ["all"]
|
||||
assert ",".join(settings.oauth_scopes) == DEFAULT_OAUTH_SCOPES
|
||||
Reference in new issue
Block a user