test_responsibilities.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. from __future__ import annotations
  2. import pytest
  3. USER_A = "01900000-0000-7000-8000-000000000101"
  4. USER_B = "01900000-0000-7000-8000-000000000102"
  5. def test_device_scope_requires_one_accountable_asset_manager():
  6. from app.core.governance.responsibilities import (
  7. ResponsibilityValidationError,
  8. validate_matrix,
  9. )
  10. with pytest.raises(
  11. ResponsibilityValidationError,
  12. match="one accountable asset manager",
  13. ):
  14. validate_matrix(
  15. "device_asset",
  16. [
  17. {
  18. "user_id": USER_A,
  19. "responsibility_role": "data_steward",
  20. "raci_role": "responsible",
  21. }
  22. ],
  23. )
  24. validated = validate_matrix(
  25. "device_asset",
  26. [
  27. {
  28. "user_id": USER_A,
  29. "responsibility_role": "asset_manager",
  30. "raci_role": "accountable",
  31. },
  32. {
  33. "user_id": USER_B,
  34. "responsibility_role": "data_steward",
  35. "raci_role": "responsible",
  36. },
  37. ],
  38. )
  39. assert [item.user_id for item in validated] == [USER_A, USER_B]
  40. quality = validate_matrix(
  41. "device_quality",
  42. [
  43. {
  44. "user_id": USER_A,
  45. "responsibility_role": "asset_manager",
  46. "raci_role": "accountable",
  47. }
  48. ],
  49. )
  50. assert quality[0].user_id == USER_A
  51. def test_responsibility_matrix_rejects_duplicates_and_unknown_values():
  52. from app.core.governance.responsibilities import (
  53. ResponsibilityValidationError,
  54. validate_matrix,
  55. )
  56. duplicate = {
  57. "user_id": USER_A,
  58. "responsibility_role": "asset_manager",
  59. "raci_role": "accountable",
  60. }
  61. with pytest.raises(ResponsibilityValidationError, match="duplicate"):
  62. validate_matrix("device_mapping", [duplicate, duplicate])
  63. with pytest.raises(ResponsibilityValidationError, match="resource type"):
  64. validate_matrix("dashboard", [duplicate])
  65. with pytest.raises(ResponsibilityValidationError, match="responsibility role"):
  66. validate_matrix(
  67. "business_domain",
  68. [
  69. {
  70. "user_id": USER_A,
  71. "responsibility_role": "supervisor",
  72. "raci_role": "accountable",
  73. }
  74. ],
  75. )
  76. assert validate_matrix("data_asset", []) == ()
  77. with pytest.raises(
  78. ResponsibilityValidationError,
  79. match="one accountable asset manager",
  80. ):
  81. validate_matrix("device_asset", [])
  82. def test_replace_is_revision_guarded_and_rejects_inactive_users():
  83. from app.core.governance.responsibilities import (
  84. ResponsibilityConflict,
  85. ResponsibilityService,
  86. ResponsibilityUserUnavailable,
  87. )
  88. class Repository:
  89. def replace(self, **kwargs):
  90. assert kwargs["expected_revision"] == 3
  91. if kwargs["assignments"][0].user_id == USER_B:
  92. raise ResponsibilityUserUnavailable("inactive user")
  93. raise ResponsibilityConflict("revision conflict")
  94. service = ResponsibilityService(Repository())
  95. with pytest.raises(ResponsibilityConflict):
  96. service.replace(
  97. resource_type="device_ontology",
  98. resource_uid="device-domain",
  99. assignments=[
  100. {
  101. "user_id": USER_A,
  102. "responsibility_role": "asset_manager",
  103. "raci_role": "accountable",
  104. }
  105. ],
  106. expected_revision=3,
  107. actor_uid=USER_A,
  108. )
  109. with pytest.raises(ResponsibilityUserUnavailable):
  110. service.replace(
  111. resource_type="device_ontology",
  112. resource_uid="device-domain",
  113. assignments=[
  114. {
  115. "user_id": USER_B,
  116. "responsibility_role": "asset_manager",
  117. "raci_role": "accountable",
  118. }
  119. ],
  120. expected_revision=3,
  121. actor_uid=USER_A,
  122. )