Pipeline/backend/tests/test_forgejo_connections_ap...

218 lines
7.2 KiB
Python
Raw Normal View History

2026-05-20 01:22:16 -05:00
# ruff: noqa: INP001
"""Integration tests for Forgejo connection CRUD behavior."""
from __future__ import annotations
from datetime import datetime
from uuid import uuid4
import pytest
from fastapi import APIRouter, FastAPI
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine
from sqlmodel import SQLModel, col, select
from sqlmodel.ext.asyncio.session import AsyncSession
from app import models as _models
from app.api.deps import require_org_member
from app.api.forgejo_connections import router as forgejo_connections_router
from app.db.session import get_session
from app.models.board_repository_links import BoardRepositoryLink
from app.models.boards import Board
from app.models.forgejo_connections import ForgejoConnection
from app.models.forgejo_issues import ForgejoIssue
from app.models.forgejo_repositories import ForgejoRepository
from app.models.organization_members import OrganizationMember
from app.models.organizations import Organization
from app.services.organizations import OrganizationContext
async def _make_engine() -> AsyncEngine:
engine = create_async_engine("sqlite+aiosqlite:///:memory:")
async with engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)
return engine
def _build_test_app(
session_maker: async_sessionmaker[AsyncSession],
ctx: OrganizationContext,
) -> FastAPI:
app = FastAPI()
api_v1 = APIRouter(prefix="/api/v1")
api_v1.include_router(forgejo_connections_router)
app.include_router(api_v1)
async def _override_get_session() -> AsyncSession:
async with session_maker() as session:
yield session
async def _override_require_org_member() -> OrganizationContext:
return ctx
app.dependency_overrides[get_session] = _override_get_session
app.dependency_overrides[require_org_member] = _override_require_org_member
return app
@pytest.mark.asyncio
async def test_delete_connection_removes_repositories_and_dependents() -> None:
engine = await _make_engine()
session_maker = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
organization = Organization(id=uuid4(), name="Pipeline")
member = OrganizationMember(
id=uuid4(),
organization_id=organization.id,
user_id=uuid4(),
role="owner",
)
app = _build_test_app(
session_maker,
OrganizationContext(organization=organization, member=member),
)
connection = ForgejoConnection(
id=uuid4(),
organization_id=organization.id,
name="Dream Forgejo",
base_url="https://forgejo.example.local",
)
repository = ForgejoRepository(
id=uuid4(),
organization_id=organization.id,
connection_id=connection.id,
owner="openclaw",
repo="pipeline",
display_name="Pipeline",
)
board = Board(
id=uuid4(),
organization_id=organization.id,
name="Pipeline Board",
slug="pipeline-board",
)
issue = ForgejoIssue(
id=uuid4(),
organization_id=organization.id,
repository_id=repository.id,
forgejo_issue_number=42,
title="Sync issue",
body_preview="Cached issue body",
state="open",
is_pull_request=False,
labels=[],
assignees=[],
author="kaspa",
html_url="https://forgejo.example.local/openclaw/pipeline/issues/42",
forgejo_created_at=datetime(2026, 5, 19, 12, 0, 0),
forgejo_updated_at=datetime(2026, 5, 19, 12, 5, 0),
)
link = BoardRepositoryLink(
id=uuid4(),
organization_id=organization.id,
board_id=board.id,
repository_id=repository.id,
)
try:
async with session_maker() as session:
session.add(organization)
session.add(connection)
session.add(repository)
session.add(board)
session.add(issue)
session.add(link)
await session.commit()
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://testserver",
) as client:
response = await client.delete(f"/api/v1/forgejo/connections/{connection.id}")
assert response.status_code == 200
assert response.json() == {"ok": True}
async with session_maker() as session:
assert (
await session.exec(
select(ForgejoConnection).where(col(ForgejoConnection.id) == connection.id)
)
).first() is None
assert (
await session.exec(
select(ForgejoRepository).where(col(ForgejoRepository.id) == repository.id)
)
).first() is None
assert (
await session.exec(select(ForgejoIssue).where(col(ForgejoIssue.id) == issue.id))
).first() is None
assert (
await session.exec(
select(BoardRepositoryLink).where(col(BoardRepositoryLink.id) == link.id)
)
).first() is None
assert (
await session.exec(select(Board).where(col(Board.id) == board.id))
).first() is not None
finally:
await engine.dispose()
2026-05-20 02:29:58 -05:00
@pytest.mark.asyncio
async def test_update_connection_blank_token_keeps_existing_token() -> None:
engine = await _make_engine()
session_maker = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
organization = Organization(id=uuid4(), name="Pipeline")
member = OrganizationMember(
id=uuid4(),
organization_id=organization.id,
user_id=uuid4(),
role="owner",
)
app = _build_test_app(
session_maker,
OrganizationContext(organization=organization, member=member),
)
connection = ForgejoConnection(
id=uuid4(),
organization_id=organization.id,
name="Dream Forgejo",
base_url="https://forgejo.example.local",
token="temp-token-12345678",
token_last_eight="12345678",
)
try:
async with session_maker() as session:
session.add(organization)
session.add(connection)
await session.commit()
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://testserver",
) as client:
response = await client.patch(
f"/api/v1/forgejo/connections/{connection.id}",
json={"name": "Dream Forgejo Updated", "token": ""},
)
assert response.status_code == 200
data = response.json()
assert data["name"] == "Dream Forgejo Updated"
assert data["has_token"] is True
assert data["token_last_eight"] == "12345678"
async with session_maker() as session:
saved = (
await session.exec(
select(ForgejoConnection).where(col(ForgejoConnection.id) == connection.id)
)
).one()
assert saved.token == "temp-token-12345678"
assert saved.token_last_eight == "12345678"
finally:
await engine.dispose()