Files
ss-tools/backend/tests/api/test_git_gitea_routes.py
busya a32ca0631b feat(037): capture, verification lifecycle, inheritance + close 036 stabilization
- Authoritative candidate capture with server-issued artifacts and raw-byte
  immutability hashing (source_response_hash server-owned)
- Closed-period lifecycle: request-hash bound approvals, persisted closure
  immutability violations, byte-for-byte catalog stability on reclosure
- Verification runs: persisted VerificationRun model + FK migration,
  publish gate (block_publish), scheduled observability runs (02:00 UTC)
- FR-013 baseline inheritance: prior_release_id migration, plan_inheritance/
  execute_inheritance classification and re-extraction, API endpoints
- Visual executor bound to release-deployment environment; caller mismatch
  rejected; visual SSIM/reconciliation modules
- Query execution decomposed: envelope/model/executor split, no direct SQL
- AgentRun approvals extracted to submodule; evidence adapter; _utils
- Dashboard testing service decomposed into 30+ modules (all <400 LOC)
- Five Feature-037 agent tools with permission guards (tools_037.py)
- API readiness endpoint; Alembic env/migrations; test fixture repos
- Specs 036/037 contracts, openapi.yaml, schema.json, tasks/traceability
  updated; semantic index rebuilt with 0 parse warnings
- Fix ADR-0003 parser ambiguity: remove [DEF🆔ADR] prose example
- Add axiom-mcp-agent-feedback.md: agent findings for MCP rework plan
- Tests: 298 service + 1464 API + 45 agent passing; ruff clean
2026-07-31 11:28:50 +03:00

241 lines
9.2 KiB
Python

# #region Test.Api.GitGiteaRoutes [C:3] [TYPE Module] [SEMANTICS test,git,gitea,routes]
# @BRIEF Unit tests for Gitea-specific Git API routes — list, create, delete repos.
# @RELATION BINDS_TO -> [Api.GiteaRoutes.GitGiteaRoutes]
# @TEST_EDGE: config_not_found -> 404
# @TEST_EDGE: wrong_provider -> 400
# @TEST_EDGE: unsupported_provider -> 501
import os
os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:")
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-tests")
import sys
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
_src = str(Path(__file__).resolve().parent.parent.parent / "src")
if _src not in sys.path:
sys.path.insert(0, _src)
def _make_mock_config(provider: str = "GITEA", pat: str = "pat-123"):
cfg = MagicMock()
cfg.id = "cfg-1"
cfg.url = "https://gitea.example.com"
cfg.pat = pat
cfg.provider = provider
return cfg
def _make_mock_gitea_repo(name: str = "my-repo") -> dict:
return {
"name": name,
"full_name": f"org/{name}",
"private": False,
"clone_url": f"https://gitea.example.com/org/{name}.git",
"html_url": f"https://gitea.example.com/org/{name}",
"ssh_url": f"git@gitea.example.com:org/{name}.git",
"default_branch": "main",
}
def _make_client(overrides: dict | None = None) -> TestClient:
from src.api.routes.git._gitea_routes import router
from src.api.routes.git._router import router as parent_router
from src.core.database import get_db
from src.dependencies import get_current_user, has_permission
from src.schemas.auth import User, RoleSchema
app = FastAPI()
app.include_router(parent_router)
app.include_router(router)
mock_user = User(
id="admin-1", username="admin", email="admin@x.com",
auth_source="LOCAL",
created_at=__import__("datetime").datetime.now(),
roles=[RoleSchema(id="r1", name="Admin", description="", is_admin=True, permissions=[])],
)
defaults = {
get_db: lambda: MagicMock(),
get_current_user: lambda: mock_user,
}
if overrides:
defaults.update(overrides)
for dep, mock_fn in defaults.items():
app.dependency_overrides[dep] = mock_fn
return TestClient(app)
# ── list_gitea_repositories ──
class TestListGiteaRepositories:
"""GET /config/{config_id}/gitea/repos"""
def test_list_success(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = _make_mock_config()
mock_gs = MagicMock()
mock_gs.list_gitea_repositories = AsyncMock(return_value=[_make_mock_gitea_repo()])
with patch("src.api.routes.git._gitea_routes.get_git_service", return_value=mock_gs):
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.get("/config/cfg-1/gitea/repos")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
assert data[0]["name"] == "my-repo"
def test_list_config_not_found(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = None
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.get("/config/cfg-999/gitea/repos")
assert resp.status_code == 404
def test_list_wrong_provider(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = _make_mock_config(provider="GITHUB")
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.get("/config/cfg-1/gitea/repos")
assert resp.status_code == 400
assert "GITEA provider only" in resp.text
# ── create_gitea_repository ──
class TestCreateGiteaRepository:
"""POST /config/{config_id}/gitea/repos"""
def test_create_success(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = _make_mock_config()
mock_gs = MagicMock()
mock_gs.create_gitea_repository = AsyncMock(return_value=_make_mock_gitea_repo())
with patch("src.api.routes.git._gitea_routes.get_git_service", return_value=mock_gs):
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.post("/config/cfg-1/gitea/repos", json={
"name": "new-repo", "private": True, "auto_init": True,
})
assert resp.status_code == 200
data = resp.json()
assert data["name"] == "my-repo"
def test_create_wrong_provider(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = _make_mock_config(provider="GITLAB")
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.post("/config/cfg-1/gitea/repos", json={
"name": "new-repo", "private": True,
})
assert resp.status_code == 400
# ── delete_gitea_repository ──
class TestDeleteGiteaRepository:
"""DELETE /config/{config_id}/gitea/repos/{owner}/{repo_name}"""
def test_delete_success(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = _make_mock_config()
mock_gs = MagicMock()
mock_gs.delete_gitea_repository = AsyncMock(return_value=None)
with patch("src.api.routes.git._gitea_routes.get_git_service", return_value=mock_gs):
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.delete("/config/cfg-1/gitea/repos/org/my-repo")
assert resp.status_code == 200
assert resp.json()["status"] == "success"
def test_delete_wrong_provider(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = _make_mock_config(provider="GITHUB")
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.delete("/config/cfg-1/gitea/repos/org/my-repo")
assert resp.status_code == 400
# ── create_remote_repository ──
class TestCreateRemoteRepository:
"""POST /config/{config_id}/repositories"""
def test_create_gitea_success(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = _make_mock_config()
mock_gs = MagicMock()
mock_gs.create_gitea_repository = AsyncMock(return_value=_make_mock_gitea_repo())
with patch("src.api.routes.git._gitea_routes.get_git_service", return_value=mock_gs):
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.post("/config/cfg-1/repositories", json={
"name": "remote-repo", "private": False,
})
assert resp.status_code == 200
data = resp.json()
assert data["name"] == "my-repo"
assert data["provider"] == "GITEA"
def test_create_github_success(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = _make_mock_config(provider="GITHUB")
mock_gs = MagicMock()
mock_gs.create_github_repository = AsyncMock(return_value=_make_mock_gitea_repo(name="gh-repo"))
with patch("src.api.routes.git._gitea_routes.get_git_service", return_value=mock_gs):
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.post("/config/cfg-1/repositories", json={
"name": "gh-repo", "private": False,
})
assert resp.status_code == 200
assert resp.json()["provider"] == "GITHUB"
def test_create_gitlab_success(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = _make_mock_config(provider="GITLAB")
mock_gs = MagicMock()
mock_gs.create_gitlab_repository = AsyncMock(return_value=_make_mock_gitea_repo(name="gl-repo"))
with patch("src.api.routes.git._gitea_routes.get_git_service", return_value=mock_gs):
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.post("/config/cfg-1/repositories", json={
"name": "gl-repo", "private": False,
})
assert resp.status_code == 200
assert resp.json()["provider"] == "GITLAB"
def test_create_unsupported_provider(self):
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = _make_mock_config(provider="UNKNOWN")
from src.core.database import get_db
client = _make_client({get_db: lambda: mock_db})
resp = client.post("/config/cfg-1/repositories", json={
"name": "repo", "private": False,
})
assert resp.status_code == 501
# #endregion Test.Api.GitGiteaRoutes