Files
group_fqcd_jr/tests/integration/test_config_release_mysql.py
T

121 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import UTC, datetime
from uuid import uuid4
import pytest
from sqlalchemy import delete, select
from app.infrastructure.db import SessionFactory
from app.model.audit import InteractionAudit
from app.model.configuration import ConfigRelease
from app.model.platform import DomainEventOutbox
from app.service.config_release_service import ConfigReleaseError, ConfigReleaseService
@pytest.mark.integration
@pytest.mark.asyncio
async def test_config_release_lifecycle_is_versioned_and_audited() -> None:
now = datetime.now(UTC).replace(tzinfo=None)
release_no = f"it-{uuid4().hex}"
peer_release_no = f"it-peer-{uuid4().hex}"
release_id = 0
peer_release_id = 0
rollback_id = 0
try:
async with SessionFactory() as session:
release = ConfigRelease(
release_no=release_no,
title="integration",
change_summary="integration",
status="draft",
created_by=101,
created_at=now,
updated_at=now,
)
session.add(release)
await session.flush()
release_id = release.id
service = ConfigReleaseService(session)
await service.submit_for_review(release_id, 101)
# 自审允许:创建人 101 审核自己提交的版本不报错,并如实写入 reviewer_id
# (chk_config_release_separation 已由 20260911_drop_review_separation 撤下)。
self_reviewed = await service.approve(release_id, 101)
assert self_reviewed.reviewer_id == 101
await service.activate(release_id, 202)
# 他人复核:`reviewer_id` 如实写入审核人,覆盖 approve 的另一条分支。
peer = ConfigRelease(
release_no=peer_release_no,
title="integration-peer",
change_summary="integration-peer",
status="draft",
created_by=101,
created_at=now,
updated_at=now,
)
session.add(peer)
await session.flush()
peer_release_id = peer.id
await service.submit_for_review(peer_release_id, 101)
assert (await service.approve(peer_release_id, 202)).reviewer_id == 202
with pytest.raises(ConfigReleaseError, match="not pending review"):
await service.approve(peer_release_id, 202)
await session.commit()
async with SessionFactory() as session:
service = ConfigReleaseService(session)
rollback = await service.rollback(release_id, 202)
rollback_id = rollback.id
await session.commit()
async with SessionFactory() as session:
release = await session.get(ConfigRelease, release_id)
rollback = await session.get(ConfigRelease, rollback_id)
events = list(
await session.scalars(
select(DomainEventOutbox).where(
DomainEventOutbox.event_type == "config.cache_invalidate_requested",
DomainEventOutbox.aggregate_id.in_([str(release_id), str(rollback_id)]),
)
)
)
audits = list(
await session.scalars(
select(InteractionAudit).where(
InteractionAudit.action_type.in_(
[
"config.submit_review",
"config.approve",
"config.activate",
"config.rollback",
]
),
InteractionAudit.detail["release_id"].in_([release_id, rollback_id]),
)
)
)
assert release is not None and release.status == "superseded"
assert rollback is not None and rollback.status == "active"
assert rollback.rollback_of_release_id == release_id
assert len(events) == 2
assert len(audits) == 4
finally:
async with SessionFactory() as session:
await session.execute(
delete(InteractionAudit).where(
InteractionAudit.detail["release_id"].in_(
[release_id, peer_release_id, rollback_id]
)
)
)
await session.execute(
delete(DomainEventOutbox).where(
DomainEventOutbox.aggregate_id.in_(
[str(release_id), str(peer_release_id), str(rollback_id)]
)
)
)
await session.execute(
delete(ConfigRelease).where(ConfigRelease.release_no.like("it-%"))
)
await session.commit()