test_ontology_publication.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. from __future__ import annotations
  2. import pytest
  3. from tests.data_research.test_ontology_validation import base_graph
  4. def repository():
  5. from app.core.data_research.ontology.repository import MemoryOntologyRepository
  6. ids = iter(("ontology-1", "version-1", "run-1", "version-2"))
  7. repo = MemoryOntologyRepository(uid_factory=ids.__next__)
  8. ontology = repo.create(
  9. code="CUSTOMER",
  10. name="客户本体",
  11. owner_uid="owner-1",
  12. domain_links=base_graph()["domain_links"],
  13. )
  14. version = repo.save_draft(ontology.uid, base_graph(), expected_revision=0, actor_uid="editor-1")
  15. return repo, ontology, version
  16. def test_publish_is_idempotent_atomic_and_emits_outbox_event():
  17. from app.core.data_research.ontology.publication import OntologyPublicationService
  18. repo, ontology, version = repository()
  19. events = []
  20. service = OntologyPublicationService(
  21. repo,
  22. outbox_enqueue=lambda **event: events.append(event),
  23. commit=lambda: events.append({"committed": True}),
  24. )
  25. first = service.publish(version.uid, idempotency_key="publish-1", actor_uid="admin-1")
  26. second = service.publish(version.uid, idempotency_key="publish-1", actor_uid="admin-1")
  27. assert first == second
  28. assert repo.get(ontology.uid).active_version_uid == version.uid
  29. assert [event.get("event_type") for event in events].count("ontology.version_published") == 1
  30. assert first.status == "published"
  31. def test_invalid_graph_rolls_back_without_activation():
  32. from app.core.data_research.ontology.publication import (
  33. OntologyPublicationService,
  34. OntologyValidationFailed,
  35. )
  36. repo, ontology, version = repository()
  37. repo.replace_version(version.uid, {**base_graph(), "domain_links": []})
  38. rollbacks = []
  39. with pytest.raises(OntologyValidationFailed):
  40. OntologyPublicationService(repo, rollback=lambda: rollbacks.append(True)).publish(
  41. version.uid, idempotency_key="invalid", actor_uid="admin-1"
  42. )
  43. assert repo.get(ontology.uid).active_version_uid is None
  44. assert rollbacks == [True]
  45. class GraphSession:
  46. def __init__(self, fail=False):
  47. self.fail = fail
  48. self.calls = []
  49. def run(self, statement, **params):
  50. if self.fail:
  51. raise ConnectionError("neo4j unavailable")
  52. self.calls.append((statement, params))
  53. def test_projection_uses_active_version_uid_and_failure_is_retryable():
  54. from app.core.data_research.ontology.projection import OntologyGraphProjector
  55. graph = GraphSession()
  56. payload = {
  57. "ontology_uid": "ontology-1",
  58. "version_uid": "version-1",
  59. "version": 1,
  60. "graph_document": base_graph(),
  61. }
  62. OntologyGraphProjector(graph).project(payload)
  63. assert "active_version_uid" in graph.calls[0][0]
  64. assert graph.calls[0][1]["version_uid"] == "version-1"
  65. with pytest.raises(ConnectionError, match="neo4j unavailable"):
  66. OntologyGraphProjector(GraphSession(fail=True)).project(payload)
  67. def test_version_diff_and_rollback_create_new_draft():
  68. from app.core.data_research.ontology.publication import OntologyPublicationService, diff_graphs
  69. repo, ontology, version = repository()
  70. service = OntologyPublicationService(repo)
  71. service.publish(version.uid, idempotency_key="publish-1", actor_uid="admin-1")
  72. changed = base_graph()
  73. changed["classes"] = changed["classes"] + [{"uid": "account", "name": "Account"}]
  74. assert diff_graphs(base_graph(), changed)["classes"]["added"] == ["account"]
  75. rollback = service.create_rollback(
  76. ontology.uid,
  77. target_version_uid=version.uid,
  78. expected_revision=1,
  79. actor_uid="admin-1",
  80. )
  81. assert rollback.version == 2
  82. assert rollback.parent_version_uid == version.uid
  83. assert rollback.status == "draft"