| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- from __future__ import annotations
- import json
- import os
- import uuid
- from pathlib import Path
- import pytest
- from sqlalchemy import text
- pytestmark = pytest.mark.integration
- ROOT = Path(__file__).resolve().parents[2]
- def test_domain_template_import_diff_and_rollback_are_atomic(monkeypatch):
- database_url = os.environ.get("TEST_DATABASE_URL")
- if not database_url:
- pytest.skip("TEST_DATABASE_URL is required")
- monkeypatch.setenv("DATABASE_URL", database_url)
- from app import create_app, db
- from app.core.governance.domain_template_repository import (
- SqlAlchemyDomainTemplateRepository,
- )
- from app.core.governance.domain_templates import DomainTemplateService
- app = create_app()
- app.config.update(TESTING=True)
- actor_uid = str(uuid.uuid4())
- definition = json.loads(
- (
- ROOT / "docs/phase2/P2_WP01_SPARE_PARTS_DOMAIN_TEMPLATE.json"
- ).read_text(encoding="utf-8")
- )
- definition["template_code"] = f"spare_parts_{uuid.uuid4().hex[:8]}"
- definition["name"] = f"集成测试模板 {definition['template_code']}"
- template_uid = None
- try:
- with app.app_context():
- db.session.execute(
- text(
- """
- INSERT INTO public.users (
- id, username, display_name, password_hash, status
- ) VALUES (
- CAST(:id AS uuid), :username, :username,
- 'p2-wp01-integration-hash', 'active'
- )
- """
- ),
- {"id": actor_uid, "username": f"p2-wp01-{actor_uid[:8]}"},
- )
- db.session.commit()
- service = DomainTemplateService(
- SqlAlchemyDomainTemplateRepository(db.session)
- )
- first = service.import_template(definition, actor_uid=actor_uid)
- db.session.commit()
- template_uid = first["uid"]
- changed = dict(definition)
- changed["description"] = "集成测试第二版"
- preview = service.dry_run(changed)
- assert preview["diff"]["added_object_types"] == []
- class FailingRepository(SqlAlchemyDomainTemplateRepository):
- def apply_import(self, record):
- super().apply_import(record)
- raise RuntimeError("simulated failure after persistence")
- failing_service = DomainTemplateService(FailingRepository(db.session))
- with pytest.raises(RuntimeError, match="simulated failure"):
- failing_service.import_template(changed, actor_uid=actor_uid)
- db.session.rollback()
- assert service.get_template(
- definition["template_code"]
- )["current_version"] == 1
- second = service.import_template(changed, actor_uid=actor_uid)
- db.session.commit()
- rolled_back = service.rollback(
- definition["template_code"],
- target_version=1,
- actor_uid=actor_uid,
- )
- db.session.commit()
- assert first["current_version"] == 1
- assert second["current_version"] == 2
- assert rolled_back["current_version"] == 3
- assert rolled_back["description"] == definition["description"]
- assert len(rolled_back["object_types"]) == 6
- assert [item["operation"] for item in service.list_imports(
- definition["template_code"]
- )] == ["rollback", "import", "import"]
- counts = db.session.execute(
- text(
- """
- SELECT
- (SELECT count(*) FROM public.governance_object_types
- WHERE template_uid = CAST(:uid AS uuid)) AS object_types,
- (SELECT count(*) FROM public.governance_domain_template_versions
- WHERE template_uid = CAST(:uid AS uuid)) AS versions,
- (SELECT count(*) FROM public.governance_domain_template_imports
- WHERE template_uid = CAST(:uid AS uuid)) AS imports
- """
- ),
- {"uid": template_uid},
- ).mappings().one()
- assert dict(counts) == {
- "object_types": 6,
- "versions": 3,
- "imports": 3,
- }
- finally:
- with app.app_context():
- db.session.rollback()
- if template_uid:
- for statement in (
- "DELETE FROM public.governance_domain_template_imports "
- "WHERE template_uid = CAST(:uid AS uuid)",
- "DELETE FROM public.governance_object_types "
- "WHERE template_uid = CAST(:uid AS uuid)",
- "DELETE FROM public.governance_domain_template_versions "
- "WHERE template_uid = CAST(:uid AS uuid)",
- "DELETE FROM public.governance_domain_templates "
- "WHERE uid = CAST(:uid AS uuid)",
- ):
- db.session.execute(
- text(statement),
- {"uid": template_uid},
- )
- db.session.execute(
- text("DELETE FROM public.users WHERE id = CAST(:id AS uuid)"),
- {"id": actor_uid},
- )
- db.session.commit()
|