test_models.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. from __future__ import annotations
  2. from sqlalchemy import Computed, UniqueConstraint
  3. import dms.models # noqa: F401
  4. from dms.common.enums import (
  5. AllowedModule,
  6. AuditAction,
  7. AuditResult,
  8. AuditTarget,
  9. ContentExtractStatus,
  10. RoleCode,
  11. SecurityLevel,
  12. SECURITY_LEVEL_VALUES,
  13. )
  14. from dms.extensions import db
  15. EXPECTED_TABLES = {
  16. "sys_organization",
  17. "sys_user",
  18. "doc_category",
  19. "doc_document",
  20. "doc_attachment_binding",
  21. "doc_document_relation",
  22. "doc_permission",
  23. "sys_audit_log",
  24. "sys_role",
  25. "sys_role_dept",
  26. "sys_role_permission",
  27. "sys_ip_entry",
  28. "sys_org_ip_assignment",
  29. }
  30. BUSINESS_TABLES = EXPECTED_TABLES - {"sys_audit_log"}
  31. COMMON_COLUMNS = {
  32. "id",
  33. "created_by",
  34. "updated_by",
  35. "created_at",
  36. "updated_at",
  37. "deleted_at",
  38. "is_deleted",
  39. "row_version",
  40. }
  41. def test_expected_models_are_registered():
  42. assert set(db.metadata.tables) == EXPECTED_TABLES
  43. assert len(db.metadata.tables) == 13
  44. def test_business_tables_have_common_columns():
  45. for table_name in BUSINESS_TABLES:
  46. columns = {column.name for column in db.metadata.tables[table_name].columns}
  47. assert COMMON_COLUMNS <= columns
  48. def test_audit_log_has_append_only_exception_fields():
  49. columns = {
  50. column.name for column in db.metadata.tables["sys_audit_log"].columns
  51. }
  52. assert "created_at" in columns
  53. assert {"updated_at", "deleted_at", "is_deleted", "row_version"}.isdisjoint(
  54. columns
  55. )
  56. def test_auth_version_field_exists_with_default_zero():
  57. column = db.metadata.tables["sys_user"].c.auth_version
  58. assert column.nullable is False
  59. assert str(column.server_default.arg) == "0"
  60. def test_attachment_type_field_exists():
  61. column = db.metadata.tables["doc_document"].c.attachment_type
  62. assert column.nullable is True
  63. def test_active_marker_columns_are_virtual_generated_columns():
  64. for table_name in (
  65. "doc_attachment_binding",
  66. "doc_permission",
  67. "sys_role",
  68. "sys_role_dept",
  69. "sys_role_permission",
  70. "sys_ip_entry",
  71. "sys_org_ip_assignment",
  72. "sys_user",
  73. ):
  74. column = db.metadata.tables[table_name].c.active_marker
  75. assert isinstance(column.computed, Computed)
  76. assert "IF(is_deleted = 0, 1, NULL)" in str(column.computed.sqltext)
  77. assert column.computed.persisted is False
  78. def test_role_default_marker_is_virtual_generated_column():
  79. column = db.metadata.tables["sys_role"].c.default_marker
  80. assert isinstance(column.computed, Computed)
  81. assert "IF(is_default = 1 AND is_deleted = 0, 1, NULL)" in str(
  82. column.computed.sqltext
  83. )
  84. assert column.computed.persisted is False
  85. def _unique_column_sets(table_name: str) -> set[tuple[str, ...]]:
  86. table = db.metadata.tables[table_name]
  87. return {
  88. tuple(column.name for column in constraint.columns)
  89. for constraint in table.constraints
  90. if isinstance(constraint, UniqueConstraint)
  91. }
  92. def test_active_binding_unique_constraint():
  93. assert (
  94. "main_document_id",
  95. "attachment_document_id",
  96. "active_marker",
  97. ) in _unique_column_sets("doc_attachment_binding")
  98. def test_active_permission_unique_constraint():
  99. assert (
  100. "document_id",
  101. "subject_type",
  102. "subject_id",
  103. "active_marker",
  104. ) in _unique_column_sets("doc_permission")
  105. def test_role_tables_unique_constraints():
  106. assert ("role_name", "active_marker") in _unique_column_sets("sys_role")
  107. assert ("default_marker",) in _unique_column_sets("sys_role")
  108. assert ("role_id", "organization_id", "active_marker") in _unique_column_sets(
  109. "sys_role_dept",
  110. )
  111. assert (
  112. "role_id",
  113. "security_level",
  114. "dept_scope",
  115. "active_marker",
  116. ) in _unique_column_sets("sys_role_permission")
  117. def test_ip_tables_unique_constraints():
  118. assert ("ip_value", "active_marker") in _unique_column_sets("sys_ip_entry")
  119. assert (
  120. "organization_id",
  121. "ip_entry_id",
  122. "active_marker",
  123. ) in _unique_column_sets("sys_org_ip_assignment")
  124. def test_portal_user_external_id_unique_constraint():
  125. assert ("external_id", "active_marker") in _unique_column_sets("sys_user")
  126. def test_no_foreign_key_uses_cascade_and_audit_has_no_foreign_keys():
  127. for table in db.metadata.tables.values():
  128. for foreign_key in table.foreign_keys:
  129. assert foreign_key.ondelete in {"RESTRICT", "NO ACTION"}
  130. assert foreign_key.ondelete != "CASCADE"
  131. assert not db.metadata.tables["sys_audit_log"].foreign_keys
  132. def test_created_and_updated_by_have_no_foreign_keys():
  133. for table_name in BUSINESS_TABLES:
  134. table = db.metadata.tables[table_name]
  135. assert not table.c.created_by.foreign_keys
  136. assert not table.c.updated_by.foreign_keys
  137. def test_required_permanent_unique_codes_exist():
  138. assert ("org_code", "active_marker") in _unique_column_sets("sys_organization")
  139. assert ("username",) in _unique_column_sets("sys_user")
  140. assert ("category_code", "active_marker") in _unique_column_sets("doc_category")
  141. def test_critical_indexes_exist():
  142. expected = {
  143. "doc_document": {
  144. "ix_doc_document_type_active_updated",
  145. "ix_doc_document_file_hash",
  146. "ix_doc_document_attachment_type_active_updated",
  147. },
  148. "sys_audit_log": {
  149. "ix_sys_audit_log_created_at",
  150. "ix_sys_audit_log_user_created",
  151. "ix_sys_audit_log_organization_created",
  152. "ix_sys_audit_log_action_created",
  153. "ix_sys_audit_log_target_created",
  154. "ix_sys_audit_log_result_created",
  155. "ix_sys_audit_log_request_id",
  156. },
  157. }
  158. for table_name, names in expected.items():
  159. actual = {index.name for index in db.metadata.tables[table_name].indexes}
  160. assert names <= actual
  161. def test_permission_model_has_no_inherited_source_column():
  162. assert "permission_source" not in db.metadata.tables["doc_permission"].c
  163. def test_contract_enums_are_exact():
  164. assert {item.value for item in RoleCode} == {"USER", "ADMIN"}
  165. assert {item.value for item in ContentExtractStatus} == {
  166. "SUCCESS",
  167. "EMPTY",
  168. "UNSUPPORTED",
  169. "FAILED",
  170. }
  171. assert {item.value for item in AllowedModule} == {
  172. "DOCUMENT_BROWSER",
  173. "BACKEND_MANAGEMENT",
  174. "AUDIT_LOG",
  175. "ORGANIZATION_USER_MANAGEMENT",
  176. }
  177. assert {item.value for item in AuditResult} == {"SUCCESS", "FAILURE"}
  178. assert {item.value for item in AuditAction} == {
  179. "LOGIN",
  180. "LOGOUT",
  181. "VIEW_DOCUMENT",
  182. "DOWNLOAD_DOCUMENT",
  183. "UPLOAD_DOCUMENT",
  184. "BATCH_IMPORT",
  185. "EDIT_DOCUMENT",
  186. "DELETE_DOCUMENT",
  187. "CREATE_CATEGORY",
  188. "EDIT_CATEGORY",
  189. "DELETE_CATEGORY",
  190. "CHANGE_PERMISSION",
  191. "BIND_ATTACHMENT",
  192. "UNBIND_ATTACHMENT",
  193. "RESTORE_DOCUMENT",
  194. "CREATE_ORGANIZATION",
  195. "EDIT_ORGANIZATION",
  196. "DELETE_ORGANIZATION",
  197. "CREATE_USER",
  198. "EDIT_USER",
  199. "DELETE_USER",
  200. "RESET_USER_PASSWORD",
  201. "REORDER_DOCUMENT",
  202. "REORDER_ATTACHMENT",
  203. "REORDER_CATEGORY",
  204. "CREATE_ROLE",
  205. "EDIT_ROLE",
  206. "DELETE_ROLE",
  207. "CHANGE_ROLE_PERMISSION",
  208. "CHANGE_ROLE_USER",
  209. "DOCUMENT_PASSWORD_SET",
  210. "DOCUMENT_PASSWORD_REMOVE",
  211. "CREATE_IP_ENTRY",
  212. "EDIT_IP_ENTRY",
  213. "DELETE_IP_ENTRY",
  214. "ASSIGN_ORG_IP",
  215. }
  216. assert {item.value for item in AuditTarget} == {
  217. "AUTH",
  218. "CATEGORY",
  219. "DOCUMENT",
  220. "ATTACHMENT",
  221. "PERMISSION",
  222. "ATTACHMENT_BINDING",
  223. "ORGANIZATION",
  224. "USER",
  225. "ROLE",
  226. "IP_ENTRY",
  227. }
  228. assert SECURITY_LEVEL_VALUES == {
  229. SecurityLevel.PUBLIC: 10,
  230. SecurityLevel.INTERNAL: 20,
  231. SecurityLevel.SECRET: 30,
  232. SecurityLevel.CONFIDENTIAL: 40,
  233. SecurityLevel.TOP_SECRET: 50,
  234. }