# 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() @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()