splent-framework 1.30.0__tar.gz → 1.30.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. {splent_framework-1.30.0/src/splent_framework.egg-info → splent_framework-1.30.2}/PKG-INFO +2 -1
  2. {splent_framework-1.30.0 → splent_framework-1.30.2}/pyproject.toml +2 -1
  3. splent_framework-1.30.2/src/splent_framework/__init__.py +12 -0
  4. splent_framework-1.30.2/src/splent_framework/fixtures/fixtures.py +180 -0
  5. {splent_framework-1.30.0 → splent_framework-1.30.2/src/splent_framework.egg-info}/PKG-INFO +2 -1
  6. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework.egg-info/SOURCES.txt +1 -0
  7. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework.egg-info/requires.txt +1 -0
  8. splent_framework-1.30.2/tests/test_fixtures_reset_db.py +167 -0
  9. splent_framework-1.30.2/tests/test_lazy_package_init.py +27 -0
  10. splent_framework-1.30.0/src/splent_framework/__init__.py +0 -3
  11. splent_framework-1.30.0/src/splent_framework/fixtures/fixtures.py +0 -95
  12. splent_framework-1.30.0/tests/test_fixtures_reset_db.py +0 -69
  13. {splent_framework-1.30.0 → splent_framework-1.30.2}/COPYING +0 -0
  14. {splent_framework-1.30.0 → splent_framework-1.30.2}/COPYING.LESSER +0 -0
  15. {splent_framework-1.30.0 → splent_framework-1.30.2}/README.md +0 -0
  16. {splent_framework-1.30.0 → splent_framework-1.30.2}/setup.cfg +0 -0
  17. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/admin/__init__.py +0 -0
  18. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/admin/registry.py +0 -0
  19. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/api/__init__.py +0 -0
  20. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/app_factory.py +0 -0
  21. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/assets/__init__.py +0 -0
  22. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/assets/asset_registry.py +0 -0
  23. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/assets/feature_assets.py +0 -0
  24. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/audit/__init__.py +0 -0
  25. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/blueprints/__init__.py +0 -0
  26. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/blueprints/base_blueprint.py +0 -0
  27. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/bootstraps/__init__.py +0 -0
  28. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/bootstraps/locustfile_bootstrap.py +0 -0
  29. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/cache/__init__.py +0 -0
  30. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/configuration/__init__.py +0 -0
  31. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/configuration/configuration.py +0 -0
  32. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/configuration/default_config.py +0 -0
  33. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/configuration/layers.py +0 -0
  34. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/content/__init__.py +0 -0
  35. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/context/__init__.py +0 -0
  36. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/context/context_manager.py +0 -0
  37. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/dates/__init__.py +0 -0
  38. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/db.py +0 -0
  39. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/decorators/__init__.py +0 -0
  40. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/decorators/decorators.py +0 -0
  41. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/empty/__init__.py +0 -0
  42. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/environment/__init__.py +0 -0
  43. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/environment/host.py +0 -0
  44. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/fetch/__init__.py +0 -0
  45. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/fixtures/__init__.py +0 -0
  46. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/guard/__init__.py +0 -0
  47. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/health/__init__.py +0 -0
  48. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/helpers/__init__.py +0 -0
  49. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/helpers/test_helpers_auth.py +0 -0
  50. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/hooks/__init__.py +0 -0
  51. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/hooks/template_hooks.py +0 -0
  52. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/i18n/__init__.py +0 -0
  53. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/i18n/text.py +0 -0
  54. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/icons/__init__.py +0 -0
  55. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/keys/__init__.py +0 -0
  56. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/layout/__init__.py +0 -0
  57. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/layout/shells.py +0 -0
  58. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/locust/__init__.py +0 -0
  59. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/locust/common.py +0 -0
  60. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/__init__.py +0 -0
  61. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/config_manager.py +0 -0
  62. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/cookie_security_manager.py +0 -0
  63. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/csp.py +0 -0
  64. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/csrf_manager.py +0 -0
  65. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/db_manager.py +0 -0
  66. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/error_handler_manager.py +0 -0
  67. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/feature_loader.py +0 -0
  68. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/feature_manager.py +0 -0
  69. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/feature_order.py +0 -0
  70. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/health_manager.py +0 -0
  71. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/jinja_manager.py +0 -0
  72. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/locale_manager.py +0 -0
  73. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/logging_manager.py +0 -0
  74. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/mapper_guard.py +0 -0
  75. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/migration_manager.py +0 -0
  76. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/namespace_manager.py +0 -0
  77. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/proxy_manager.py +0 -0
  78. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/security_headers_manager.py +0 -0
  79. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/session_manager.py +0 -0
  80. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/static_cache_manager.py +0 -0
  81. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/managers/task_manager.py +0 -0
  82. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/markdown/__init__.py +0 -0
  83. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/markdown/render.py +0 -0
  84. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/migrations/__init__.py +0 -0
  85. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/migrations/feature_env.py +0 -0
  86. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/namespaces/__init__.py +0 -0
  87. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/nav/__init__.py +0 -0
  88. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/nav/menu_registry.py +0 -0
  89. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/nav/nav_registry.py +0 -0
  90. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/observability/__init__.py +0 -0
  91. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/optional.py +0 -0
  92. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/pages/__init__.py +0 -0
  93. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/params/__init__.py +0 -0
  94. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/params/runtime.py +0 -0
  95. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/params/schema.py +0 -0
  96. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/ratelimit/__init__.py +0 -0
  97. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/refinement/__init__.py +0 -0
  98. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/refinement/conflicts.py +0 -0
  99. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/refinement/facts.py +0 -0
  100. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/refinement/model_extender.py +0 -0
  101. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/refinement/parser.py +0 -0
  102. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/refinement/registry.py +0 -0
  103. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/refinement/templates.py +0 -0
  104. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/refinement/validator.py +0 -0
  105. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/repositories/BaseRepository.py +0 -0
  106. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/repositories/__init__.py +0 -0
  107. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/rich_text/__init__.py +0 -0
  108. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/rich_text/sanitize.py +0 -0
  109. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/roles/__init__.py +0 -0
  110. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/search/__init__.py +0 -0
  111. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/search/schema.py +0 -0
  112. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/search/search_registry.py +0 -0
  113. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/security/__init__.py +0 -0
  114. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/seeders/BaseSeeder.py +0 -0
  115. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/seeders/__init__.py +0 -0
  116. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/selenium/__init__.py +0 -0
  117. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/selenium/common.py +0 -0
  118. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/services/BaseService.py +0 -0
  119. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/services/__init__.py +0 -0
  120. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/services/file_access.py +0 -0
  121. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/services/service_locator.py +0 -0
  122. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/settings/__init__.py +0 -0
  123. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/settings/settings_schema.py +0 -0
  124. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/signals/__init__.py +0 -0
  125. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/signals/registry.py +0 -0
  126. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/signals/signal_utils.py +0 -0
  127. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/site/__init__.py +0 -0
  128. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/sitemap/__init__.py +0 -0
  129. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/sizes/__init__.py +0 -0
  130. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/storage/__init__.py +0 -0
  131. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/tasks/__init__.py +0 -0
  132. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/tasks/backends.py +0 -0
  133. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/tasks/dispatch.py +0 -0
  134. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/tasks/isolated.py +0 -0
  135. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/tasks/registry.py +0 -0
  136. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/tasks/runs.py +0 -0
  137. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/tasks/worker.py +0 -0
  138. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/translations/es/LC_MESSAGES/messages.mo +0 -0
  139. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/translations/es/LC_MESSAGES/messages.po +0 -0
  140. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/translations/messages.pot +0 -0
  141. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/ui/__init__.py +0 -0
  142. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/ui/static/splent-ui.js +0 -0
  143. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/urls/__init__.py +0 -0
  144. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/utils/__init__.py +0 -0
  145. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/utils/app_loader.py +0 -0
  146. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/utils/feature_utils.py +0 -0
  147. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/utils/form_helpers.py +0 -0
  148. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/utils/path_utils.py +0 -0
  149. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/utils/pyproject_reader.py +0 -0
  150. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/utils/text.py +0 -0
  151. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework/utils/uvl_resolver.py +0 -0
  152. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework.egg-info/dependency_links.txt +0 -0
  153. {splent_framework-1.30.0 → splent_framework-1.30.2}/src/splent_framework.egg-info/top_level.txt +0 -0
  154. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_api.py +0 -0
  155. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_app_version.py +0 -0
  156. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_audit.py +0 -0
  157. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_base_blueprint_security.py +0 -0
  158. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_base_repository.py +0 -0
  159. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_cache.py +0 -0
  160. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_config_layers.py +0 -0
  161. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_config_manager.py +0 -0
  162. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_config_manager_return.py +0 -0
  163. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_config_trace.py +0 -0
  164. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_content.py +0 -0
  165. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_context_manager.py +0 -0
  166. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_cookie_samesite.py +0 -0
  167. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_csp_compose.py +0 -0
  168. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_csp_nonce.py +0 -0
  169. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_csrf.py +0 -0
  170. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_dates.py +0 -0
  171. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_default_config.py +0 -0
  172. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_empty.py +0 -0
  173. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_error_handler_manager.py +0 -0
  174. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_feature_assets.py +0 -0
  175. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_feature_loader_imports.py +0 -0
  176. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_feature_manager_parse.py +0 -0
  177. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_feature_order.py +0 -0
  178. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_feature_utils.py +0 -0
  179. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_fetch.py +0 -0
  180. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_guard.py +0 -0
  181. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_has_role.py +0 -0
  182. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_health.py +0 -0
  183. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_health_listeners.py +0 -0
  184. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_helpers_host.py +0 -0
  185. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_helpers_locust.py +0 -0
  186. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_helpers_selenium.py +0 -0
  187. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_i18n_text.py +0 -0
  188. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_icons.py +0 -0
  189. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_isolated.py +0 -0
  190. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_keys.py +0 -0
  191. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_locale_manager.py +0 -0
  192. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_locustfile_bootstrap.py +0 -0
  193. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_mapper_guard.py +0 -0
  194. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_markdown.py +0 -0
  195. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_migration_manager.py +0 -0
  196. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_namespace_manager.py +0 -0
  197. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_observability_report.py +0 -0
  198. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_optional.py +0 -0
  199. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_pages.py +0 -0
  200. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_params_boot.py +0 -0
  201. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_params_schema.py +0 -0
  202. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_path_utils_env_file.py +0 -0
  203. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_prod_cookies.py +0 -0
  204. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_prod_db_engine_options.py +0 -0
  205. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_prod_factory_integration.py +0 -0
  206. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_prod_max_content_length.py +0 -0
  207. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_prod_operator_settings.py +0 -0
  208. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_prod_security_headers.py +0 -0
  209. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_prod_static_cache.py +0 -0
  210. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_protected_uploads_dir.py +0 -0
  211. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_proxy_manager.py +0 -0
  212. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_pyproject_reader.py +0 -0
  213. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_pyproject_reader_env.py +0 -0
  214. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_ratelimit.py +0 -0
  215. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_refinement.py +0 -0
  216. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_refinement_boot.py +0 -0
  217. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_refinement_conflicts.py +0 -0
  218. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_refinement_hooks.py +0 -0
  219. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_refinement_load_order.py +0 -0
  220. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_refinement_models.py +0 -0
  221. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_refinement_routes.py +0 -0
  222. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_refinement_services.py +0 -0
  223. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_refinement_templates.py +0 -0
  224. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_search_registry.py +0 -0
  225. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_search_schema.py +0 -0
  226. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_security.py +0 -0
  227. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_settings_schema.py +0 -0
  228. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_site.py +0 -0
  229. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_sitemap.py +0 -0
  230. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_sizes.py +0 -0
  231. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_storage.py +0 -0
  232. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_tasks.py +0 -0
  233. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_tasks_rq.py +0 -0
  234. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_test_store.py +0 -0
  235. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_text.py +0 -0
  236. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u1_app_factory.py +0 -0
  237. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u1_config_manager.py +0 -0
  238. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u1_csrf.py +0 -0
  239. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u1_error_handlers.py +0 -0
  240. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u1_error_page_fails.py +0 -0
  241. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u1_forbidden_page.py +0 -0
  242. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u1_logging_manager.py +0 -0
  243. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u2_app_loader.py +0 -0
  244. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u2_path_utils.py +0 -0
  245. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u2_session_manager.py +0 -0
  246. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u3_feature_integrator.py +0 -0
  247. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u3_feature_link_resolver.py +0 -0
  248. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u3_feature_loader.py +0 -0
  249. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u3_feature_sources_override.py +0 -0
  250. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u3_refiner_extensions.py +0 -0
  251. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u4_contracts_snapshot.py +0 -0
  252. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u4_feature_lists.py +0 -0
  253. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u4_feature_manager.py +0 -0
  254. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u5_asset_registry.py +0 -0
  255. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u5_asset_shells.py +0 -0
  256. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u5_base_blueprint.py +0 -0
  257. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u5_namespace_manager.py +0 -0
  258. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u6_admin_registry.py +0 -0
  259. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u6_jinja_manager.py +0 -0
  260. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u6_markdown.py +0 -0
  261. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u6_menu_registry.py +0 -0
  262. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u6_nav_registry.py +0 -0
  263. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u6_rich_text.py +0 -0
  264. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u6_shells.py +0 -0
  265. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u6_template_hooks.py +0 -0
  266. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u7_i18n_text.py +0 -0
  267. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u7_locale_manager.py +0 -0
  268. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u7_settings_schema.py +0 -0
  269. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u8_feature_env.py +0 -0
  270. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u8_file_access.py +0 -0
  271. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u8_migration_logging.py +0 -0
  272. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u8_model_extender.py +0 -0
  273. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u8_service_refinement.py +0 -0
  274. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u8_signals.py +0 -0
  275. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u9_base_repository.py +0 -0
  276. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u9_base_seeder.py +0 -0
  277. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u9_file_access.py +0 -0
  278. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_u9_form_helpers.py +0 -0
  279. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_ui.py +0 -0
  280. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_urls.py +0 -0
  281. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_urls_helpers.py +0 -0
  282. {splent_framework-1.30.0 → splent_framework-1.30.2}/tests/test_uvl_resolver.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_framework
3
- Version: 1.30.0
3
+ Version: 1.30.2
4
4
  Summary: SPLENT-FRAMEWORK is a set of libraries for agile product development within SPLENT.
5
5
  Author-email: DiversoLab <diversolab@us.es>
6
6
  License-Expression: LGPL-3.0-or-later
@@ -33,6 +33,7 @@ Requires-Dist: rq<3,>=2.0; extra == "rq"
33
33
  Provides-Extra: dev
34
34
  Requires-Dist: pytest>=8.3; extra == "dev"
35
35
  Requires-Dist: pytest-cov>=6.0; extra == "dev"
36
+ Requires-Dist: pytest-xdist>=3.6; extra == "dev"
36
37
  Requires-Dist: Faker>=33.0; extra == "dev"
37
38
  Requires-Dist: rq<3,>=2.0; extra == "dev"
38
39
  Requires-Dist: fakeredis[lua]>=2.20; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splent_framework"
7
- version = "1.30.0"
7
+ version = "1.30.2"
8
8
  description = "SPLENT-FRAMEWORK is a set of libraries for agile product development within SPLENT."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -38,6 +38,7 @@ rq = ["rq>=2.0,<3"]
38
38
  dev = [
39
39
  "pytest>=8.3",
40
40
  "pytest-cov>=6.0",
41
+ "pytest-xdist>=3.6",
41
42
  "Faker>=33.0",
42
43
  "rq>=2.0,<3",
43
44
  "fakeredis[lua]>=2.20",
@@ -0,0 +1,12 @@
1
+ __all__ = ["create_splent_app"]
2
+
3
+
4
+ def __getattr__(name):
5
+ # Importing any splent_framework module runs this file first. Loading the
6
+ # app factory here pulled Flask, SQLAlchemy and Alembic into every import,
7
+ # so it is loaded when asked for.
8
+ if name == "create_splent_app":
9
+ from splent_framework.app_factory import create_splent_app
10
+
11
+ return create_splent_app
12
+ raise AttributeError(f"module 'splent_framework' has no attribute {name!r}")
@@ -0,0 +1,180 @@
1
+ import hashlib
2
+
3
+ import pytest
4
+ from sqlalchemy import inspect, text
5
+ from splent_framework.db import db
6
+ from splent_framework.decorators.decorators import isolated_access_policies
7
+ from splent_framework.managers.migration_manager import SPLENT_MIGRATIONS_TABLE
8
+ from splent_framework.utils.app_loader import get_create_app_in_testing_mode
9
+
10
+ # What the database looked like right after the last full reset, per
11
+ # database URL. While it still looks the same, emptying the tables is
12
+ # enough.
13
+ _schema_after_reset: dict[str, str] = {}
14
+
15
+ _SCHEMA_QUERIES = tuple(
16
+ text(sql)
17
+ for sql in (
18
+ "SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES "
19
+ "WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME",
20
+ "SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, "
21
+ "COLLATION_NAME, COLUMN_DEFAULT, EXTRA FROM information_schema.COLUMNS "
22
+ "WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME, ORDINAL_POSITION",
23
+ "SELECT TABLE_NAME, INDEX_NAME, NON_UNIQUE, SEQ_IN_INDEX, COLUMN_NAME "
24
+ "FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() "
25
+ "ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX",
26
+ "SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, "
27
+ "REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE "
28
+ "WHERE TABLE_SCHEMA = DATABASE() "
29
+ "ORDER BY TABLE_NAME, CONSTRAINT_NAME, ORDINAL_POSITION",
30
+ "SELECT TRIGGER_NAME, EVENT_OBJECT_TABLE, ACTION_TIMING, "
31
+ "EVENT_MANIPULATION, ACTION_STATEMENT FROM information_schema.TRIGGERS "
32
+ "WHERE TRIGGER_SCHEMA = DATABASE() ORDER BY TRIGGER_NAME",
33
+ )
34
+ )
35
+
36
+ _TABLES_WITH_MOVED_COUNTER = text(
37
+ "SELECT TABLE_NAME FROM information_schema.TABLES "
38
+ "WHERE TABLE_SCHEMA = DATABASE() AND AUTO_INCREMENT > 1"
39
+ )
40
+
41
+
42
+ def _schema(conn):
43
+ """A digest of the database's schema and of the tables the app declares."""
44
+ rows = [conn.execute(query).all() for query in _SCHEMA_QUERIES]
45
+ declared = [
46
+ (table.name, [column.name for column in table.columns])
47
+ for table in db.metadata.sorted_tables
48
+ ]
49
+ return hashlib.sha1(repr((rows, declared)).encode()).hexdigest()
50
+
51
+
52
+ def _drop_and_create():
53
+ """Drop every table of the test database and create the app's.
54
+
55
+ The test database is shared: a product's tests and the neutral app of
56
+ a feature's release gate, which loads a few features, run against the
57
+ same one. Dropping only the app's own tables left the other apps'
58
+ behind, with the rows their last test left, and those pointed at the
59
+ ids the reset hands out again: a neutral app recreated ``user``, the
60
+ user_profile row of the previous product run then named its new user
61
+ 1, and deleting that account failed on the foreign key. splent_migrations,
62
+ which the app made when it started, stays.
63
+ """
64
+ with db.engine.connect() as conn:
65
+ conn.execute(text("SET FOREIGN_KEY_CHECKS=0"))
66
+ db.metadata.drop_all(bind=conn)
67
+ quote = conn.dialect.identifier_preparer.quote
68
+ for name in inspect(conn).get_table_names():
69
+ if name != SPLENT_MIGRATIONS_TABLE:
70
+ conn.execute(
71
+ text(f"DROP TABLE {quote(name)}")
72
+ ) # splent: allow sql-from-string
73
+ conn.execute(text("SET FOREIGN_KEY_CHECKS=1"))
74
+ db.metadata.create_all(bind=conn)
75
+ conn.commit()
76
+
77
+
78
+ def _empty_tables(conn):
79
+ """Delete every row of the app's tables and restart the id counters
80
+ that moved, so the next test sees what a fresh create_all leaves."""
81
+ quote = conn.dialect.identifier_preparer.quote
82
+ conn.execute(text("SET FOREIGN_KEY_CHECKS=0"))
83
+ for table in db.metadata.sorted_tables:
84
+ conn.execute(
85
+ text(f"DELETE FROM {quote(table.name)}")
86
+ ) # splent: allow sql-from-string
87
+ for (name,) in conn.execute(_TABLES_WITH_MOVED_COUNTER).all():
88
+ conn.execute(
89
+ text(f"ALTER TABLE {quote(name)} AUTO_INCREMENT = 1")
90
+ ) # splent: allow sql-from-string
91
+ conn.execute(text("SET FOREIGN_KEY_CHECKS=1"))
92
+
93
+
94
+ def _reset_db():
95
+ """Leave the test database with the app's tables, empty.
96
+
97
+ Dropping and creating every table costs most of a short test, so it
98
+ happens once per process and database. After that the tables are only
99
+ emptied, as long as the schema still matches the one the full reset
100
+ left: if a migration test, another app sharing the database or a model
101
+ declared later changed it, the tables are dropped and created again.
102
+ Databases other than MariaDB and MySQL always get the full reset.
103
+ """
104
+ db.session.remove()
105
+ engine = db.engine
106
+ if engine.dialect.name != "mysql":
107
+ _drop_and_create()
108
+ return
109
+ key = str(engine.url)
110
+ with engine.begin() as conn:
111
+ if not getattr(engine.dialect, "is_mariadb", False):
112
+ # MySQL 8 caches AUTO_INCREMENT in information_schema for a day.
113
+ conn.execute(text("SET SESSION information_schema_stats_expiry = 0"))
114
+ if _schema_after_reset.get(key) == _schema(conn):
115
+ _empty_tables(conn)
116
+ return
117
+ _drop_and_create()
118
+ with engine.connect() as conn:
119
+ _schema_after_reset[key] = _schema(conn)
120
+
121
+
122
+ @pytest.fixture(scope="session")
123
+ def test_app():
124
+ """
125
+ Creates and initializes the Flask app for the test session.
126
+ Drops and creates all tables once per session.
127
+ """
128
+ app = get_create_app_in_testing_mode()
129
+ with app.app_context():
130
+ _reset_db()
131
+ yield app
132
+
133
+
134
+ @pytest.fixture(scope="function")
135
+ def test_client(test_app):
136
+ """
137
+ Provides a test client with a clean database for each test.
138
+ Tables are dropped and recreated before the test runs.
139
+ """
140
+ with test_app.app_context():
141
+ _reset_db()
142
+ with test_app.test_client() as client:
143
+ yield client
144
+
145
+
146
+ @pytest.fixture(scope="module")
147
+ def test_client_module(test_app):
148
+ """
149
+ Shared test client for the entire module.
150
+ Resets the database once at the start of the module.
151
+ Tests within the module share database state.
152
+ """
153
+ with test_app.app_context():
154
+ _reset_db()
155
+
156
+ with test_app.test_client() as client:
157
+ yield client
158
+
159
+
160
+ @pytest.fixture(scope="function")
161
+ def clean_database(test_app):
162
+ """
163
+ Manually resets the database within a test.
164
+ Combine with test_client_module when you need a selective
165
+ reset inside a module-scoped session.
166
+ """
167
+ with test_app.app_context():
168
+ _reset_db()
169
+ yield
170
+
171
+
172
+ @pytest.fixture(scope="function")
173
+ def no_access_policies():
174
+ """
175
+ Runs the test with no access policies registered, and restores the ones
176
+ other features registered at startup when it ends. Use it to test a
177
+ feature's own role gate, or register a policy inside the test.
178
+ """
179
+ with isolated_access_policies():
180
+ yield
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_framework
3
- Version: 1.30.0
3
+ Version: 1.30.2
4
4
  Summary: SPLENT-FRAMEWORK is a set of libraries for agile product development within SPLENT.
5
5
  Author-email: DiversoLab <diversolab@us.es>
6
6
  License-Expression: LGPL-3.0-or-later
@@ -33,6 +33,7 @@ Requires-Dist: rq<3,>=2.0; extra == "rq"
33
33
  Provides-Extra: dev
34
34
  Requires-Dist: pytest>=8.3; extra == "dev"
35
35
  Requires-Dist: pytest-cov>=6.0; extra == "dev"
36
+ Requires-Dist: pytest-xdist>=3.6; extra == "dev"
36
37
  Requires-Dist: Faker>=33.0; extra == "dev"
37
38
  Requires-Dist: rq<3,>=2.0; extra == "dev"
38
39
  Requires-Dist: fakeredis[lua]>=2.20; extra == "dev"
@@ -182,6 +182,7 @@ tests/test_i18n_text.py
182
182
  tests/test_icons.py
183
183
  tests/test_isolated.py
184
184
  tests/test_keys.py
185
+ tests/test_lazy_package_init.py
185
186
  tests/test_locale_manager.py
186
187
  tests/test_locustfile_bootstrap.py
187
188
  tests/test_mapper_guard.py
@@ -20,6 +20,7 @@ pygments>=2.17
20
20
  [dev]
21
21
  pytest>=8.3
22
22
  pytest-cov>=6.0
23
+ pytest-xdist>=3.6
23
24
  Faker>=33.0
24
25
  rq<3,>=2.0
25
26
  fakeredis[lua]>=2.20
@@ -0,0 +1,167 @@
1
+ """The test fixtures start every test on an empty test database.
2
+
3
+ The full reset drops and creates every table once per process and database;
4
+ after that the tables are only emptied while the schema stays the one the
5
+ full reset left.
6
+
7
+ The full reset used to drop and recreate only the tables of the app under test. The
8
+ test database is shared by a product's tests and by the neutral app a
9
+ feature's release gate builds, which loads a few features: the tables only
10
+ the product knows survived a neutral run with the rows their last test
11
+ left, pointing at the ids the reset hands out again. legal's test that
12
+ deletes an account failed on a user_profile row of user 1 left by the
13
+ product's run.
14
+
15
+ Run on SQLite with the app's db replaced, the MariaDB statements read as
16
+ SQLite's.
17
+ """
18
+
19
+ from contextlib import contextmanager
20
+ from types import SimpleNamespace
21
+
22
+ import sqlalchemy as sa
23
+ from sqlalchemy import text
24
+
25
+ from splent_framework.fixtures import fixtures
26
+ from splent_framework.managers.migration_manager import SPLENT_MIGRATIONS_TABLE
27
+
28
+
29
+ def _engine(path):
30
+ engine = sa.create_engine(f"sqlite:///{path}")
31
+
32
+ @sa.event.listens_for(engine, "before_cursor_execute", retval=True)
33
+ def as_sqlite(conn, cursor, statement, parameters, context, executemany):
34
+ if statement.startswith("SET FOREIGN_KEY_CHECKS"):
35
+ state = "OFF" if statement.rstrip().endswith("0") else "ON"
36
+ statement = f"PRAGMA foreign_keys={state}"
37
+ return statement, parameters
38
+
39
+ return engine
40
+
41
+
42
+ def test_the_tables_another_app_left_are_dropped(tmp_path, monkeypatch):
43
+ engine = _engine(tmp_path / "test.db")
44
+ with engine.begin() as conn:
45
+ conn.execute(text("CREATE TABLE user (id INTEGER PRIMARY KEY)"))
46
+ conn.execute(
47
+ text(
48
+ "CREATE TABLE user_profile (id INTEGER PRIMARY KEY, "
49
+ "user_id INTEGER REFERENCES user (id))"
50
+ )
51
+ )
52
+ conn.execute(
53
+ text(f"CREATE TABLE {SPLENT_MIGRATIONS_TABLE} (feature TEXT PRIMARY KEY)")
54
+ )
55
+ conn.execute(text("INSERT INTO user VALUES (1)"))
56
+ conn.execute(text("INSERT INTO user_profile VALUES (1, 1)"))
57
+ ours = sa.MetaData()
58
+ sa.Table("user", ours, sa.Column("id", sa.Integer, primary_key=True))
59
+ monkeypatch.setattr(
60
+ fixtures,
61
+ "db",
62
+ SimpleNamespace(
63
+ session=SimpleNamespace(remove=lambda: None), engine=engine, metadata=ours
64
+ ),
65
+ )
66
+
67
+ fixtures._reset_db()
68
+
69
+ # The app's tables, empty, and the table the app makes when it starts.
70
+ assert sorted(sa.inspect(engine).get_table_names()) == sorted(
71
+ [SPLENT_MIGRATIONS_TABLE, "user"]
72
+ )
73
+ with engine.connect() as conn:
74
+ assert conn.execute(text("SELECT COUNT(*) FROM user")).scalar() == 0
75
+
76
+
77
+ class _Engine:
78
+ """Just what _reset_db reads from an engine: dialect, url and begin()."""
79
+
80
+ def __init__(self, name="mysql", url="mysql://test/db_test", mariadb=True):
81
+ self.dialect = SimpleNamespace(name=name, is_mariadb=mariadb)
82
+ self.url = url
83
+ self.statements = []
84
+
85
+ @contextmanager
86
+ def begin(self):
87
+ yield SimpleNamespace(execute=lambda st: self.statements.append(str(st)))
88
+
89
+ connect = begin
90
+
91
+
92
+ def _wire(monkeypatch, engine, schema):
93
+ calls = []
94
+ monkeypatch.setattr(
95
+ fixtures,
96
+ "db",
97
+ SimpleNamespace(session=SimpleNamespace(remove=lambda: None), engine=engine),
98
+ )
99
+ monkeypatch.setattr(fixtures, "_schema_after_reset", {})
100
+ monkeypatch.setattr(fixtures, "_schema", lambda conn: schema[0])
101
+ monkeypatch.setattr(fixtures, "_drop_and_create", lambda: calls.append("full"))
102
+ monkeypatch.setattr(fixtures, "_empty_tables", lambda conn: calls.append("empty"))
103
+ return calls
104
+
105
+
106
+ def test_the_tables_are_emptied_while_the_schema_stays(monkeypatch):
107
+ schema = ["a"]
108
+ calls = _wire(monkeypatch, _Engine(), schema)
109
+
110
+ fixtures._reset_db()
111
+ fixtures._reset_db()
112
+ fixtures._reset_db()
113
+
114
+ assert calls == ["full", "empty", "empty"]
115
+
116
+
117
+ def test_a_changed_schema_gets_the_full_reset_again(monkeypatch):
118
+ schema = ["a"]
119
+ calls = _wire(monkeypatch, _Engine(), schema)
120
+
121
+ fixtures._reset_db()
122
+ schema[0] = "b" # a migration test altered a table
123
+ fixtures._reset_db()
124
+ fixtures._reset_db()
125
+
126
+ assert calls == ["full", "full", "empty"]
127
+
128
+
129
+ def test_each_database_gets_its_own_first_full_reset(monkeypatch):
130
+ schema = ["a"]
131
+ first = _Engine(url="mysql://test/db_test")
132
+ calls = _wire(monkeypatch, first, schema)
133
+
134
+ fixtures._reset_db()
135
+ fixtures.db.engine = _Engine(url="mysql://test/db_test_2")
136
+ fixtures._reset_db()
137
+ fixtures.db.engine = first
138
+ fixtures._reset_db()
139
+
140
+ assert calls == ["full", "full", "empty"]
141
+
142
+
143
+ def test_other_databases_always_get_the_full_reset(monkeypatch):
144
+ calls = _wire(monkeypatch, _Engine(name="sqlite"), ["a"])
145
+
146
+ fixtures._reset_db()
147
+ fixtures._reset_db()
148
+
149
+ assert calls == ["full", "full"]
150
+
151
+
152
+ def test_mysql_reads_the_id_counters_uncached(monkeypatch):
153
+ mysql = _Engine(mariadb=False)
154
+ _wire(monkeypatch, mysql, ["a"])
155
+
156
+ fixtures._reset_db()
157
+
158
+ assert "SET SESSION information_schema_stats_expiry = 0" in mysql.statements
159
+
160
+
161
+ def test_mariadb_needs_no_stats_setting(monkeypatch):
162
+ mariadb = _Engine()
163
+ _wire(monkeypatch, mariadb, ["a"])
164
+
165
+ fixtures._reset_db()
166
+
167
+ assert not any("stats_expiry" in st for st in mariadb.statements)
@@ -0,0 +1,27 @@
1
+ """Importing a framework module does not load the app factory and its stack."""
2
+
3
+ import subprocess
4
+ import sys
5
+
6
+
7
+ def _run(code):
8
+ return subprocess.run(
9
+ [sys.executable, "-c", code], capture_output=True, text=True, check=True
10
+ ).stdout.strip()
11
+
12
+
13
+ def test_a_framework_module_imports_without_flask():
14
+ loaded = _run(
15
+ "import sys, splent_framework.utils.path_utils;"
16
+ "print('flask' in sys.modules, 'splent_framework.app_factory' in sys.modules)"
17
+ )
18
+ assert loaded == "False False"
19
+
20
+
21
+ def test_the_app_factory_is_still_importable_from_the_package():
22
+ assert (
23
+ _run(
24
+ "from splent_framework import create_splent_app; print(create_splent_app.__name__)"
25
+ )
26
+ == "create_splent_app"
27
+ )
@@ -1,3 +0,0 @@
1
- from splent_framework.app_factory import create_splent_app
2
-
3
- __all__ = ["create_splent_app"]
@@ -1,95 +0,0 @@
1
- import pytest
2
- from sqlalchemy import inspect, text
3
- from splent_framework.db import db
4
- from splent_framework.decorators.decorators import isolated_access_policies
5
- from splent_framework.managers.migration_manager import SPLENT_MIGRATIONS_TABLE
6
- from splent_framework.utils.app_loader import get_create_app_in_testing_mode
7
-
8
-
9
- def _reset_db():
10
- """Drop every table of the test database and create the app's.
11
- Disables FK checks for MariaDB.
12
-
13
- The test database is shared: a product's tests and the neutral app of
14
- a feature's release gate, which loads a few features, run against the
15
- same one. Dropping only the app's own tables left the other apps'
16
- behind, with the rows their last test left, and those pointed at the
17
- ids the reset hands out again: a neutral app recreated ``user``, the
18
- user_profile row of the previous product run then named its new user
19
- 1, and deleting that account failed on the foreign key. splent_migrations,
20
- which the app made when it started, stays.
21
- """
22
- db.session.remove()
23
- with db.engine.connect() as conn:
24
- conn.execute(text("SET FOREIGN_KEY_CHECKS=0"))
25
- db.metadata.drop_all(bind=conn)
26
- quote = conn.dialect.identifier_preparer.quote
27
- for name in inspect(conn).get_table_names():
28
- if name != SPLENT_MIGRATIONS_TABLE:
29
- conn.execute(
30
- text(f"DROP TABLE {quote(name)}")
31
- ) # splent: allow sql-from-string
32
- conn.execute(text("SET FOREIGN_KEY_CHECKS=1"))
33
- db.metadata.create_all(bind=conn)
34
- conn.commit()
35
-
36
-
37
- @pytest.fixture(scope="session")
38
- def test_app():
39
- """
40
- Creates and initializes the Flask app for the test session.
41
- Drops and creates all tables once per session.
42
- """
43
- app = get_create_app_in_testing_mode()
44
- with app.app_context():
45
- _reset_db()
46
- yield app
47
-
48
-
49
- @pytest.fixture(scope="function")
50
- def test_client(test_app):
51
- """
52
- Provides a test client with a clean database for each test.
53
- Tables are dropped and recreated before the test runs.
54
- """
55
- with test_app.app_context():
56
- _reset_db()
57
- with test_app.test_client() as client:
58
- yield client
59
-
60
-
61
- @pytest.fixture(scope="module")
62
- def test_client_module(test_app):
63
- """
64
- Shared test client for the entire module.
65
- Resets the database once at the start of the module.
66
- Tests within the module share database state.
67
- """
68
- with test_app.app_context():
69
- _reset_db()
70
-
71
- with test_app.test_client() as client:
72
- yield client
73
-
74
-
75
- @pytest.fixture(scope="function")
76
- def clean_database(test_app):
77
- """
78
- Manually resets the database within a test.
79
- Combine with test_client_module when you need a selective
80
- reset inside a module-scoped session.
81
- """
82
- with test_app.app_context():
83
- _reset_db()
84
- yield
85
-
86
-
87
- @pytest.fixture(scope="function")
88
- def no_access_policies():
89
- """
90
- Runs the test with no access policies registered, and restores the ones
91
- other features registered at startup when it ends. Use it to test a
92
- feature's own role gate, or register a policy inside the test.
93
- """
94
- with isolated_access_policies():
95
- yield
@@ -1,69 +0,0 @@
1
- """The test fixtures start every test on an empty test database.
2
-
3
- _reset_db dropped and recreated the tables of the app under test only. The
4
- test database is shared by a product's tests and by the neutral app a
5
- feature's release gate builds, which loads a few features: the tables only
6
- the product knows survived a neutral run with the rows their last test
7
- left, pointing at the ids the reset hands out again. legal's test that
8
- deletes an account failed on a user_profile row of user 1 left by the
9
- product's run.
10
-
11
- Run on SQLite with the app's db replaced, the MariaDB statements read as
12
- SQLite's.
13
- """
14
-
15
- from types import SimpleNamespace
16
-
17
- import sqlalchemy as sa
18
- from sqlalchemy import text
19
-
20
- from splent_framework.fixtures import fixtures
21
- from splent_framework.managers.migration_manager import SPLENT_MIGRATIONS_TABLE
22
-
23
-
24
- def _engine(path):
25
- engine = sa.create_engine(f"sqlite:///{path}")
26
-
27
- @sa.event.listens_for(engine, "before_cursor_execute", retval=True)
28
- def as_sqlite(conn, cursor, statement, parameters, context, executemany):
29
- if statement.startswith("SET FOREIGN_KEY_CHECKS"):
30
- state = "OFF" if statement.rstrip().endswith("0") else "ON"
31
- statement = f"PRAGMA foreign_keys={state}"
32
- return statement, parameters
33
-
34
- return engine
35
-
36
-
37
- def test_the_tables_another_app_left_are_dropped(tmp_path, monkeypatch):
38
- engine = _engine(tmp_path / "test.db")
39
- with engine.begin() as conn:
40
- conn.execute(text("CREATE TABLE user (id INTEGER PRIMARY KEY)"))
41
- conn.execute(
42
- text(
43
- "CREATE TABLE user_profile (id INTEGER PRIMARY KEY, "
44
- "user_id INTEGER REFERENCES user (id))"
45
- )
46
- )
47
- conn.execute(
48
- text(f"CREATE TABLE {SPLENT_MIGRATIONS_TABLE} (feature TEXT PRIMARY KEY)")
49
- )
50
- conn.execute(text("INSERT INTO user VALUES (1)"))
51
- conn.execute(text("INSERT INTO user_profile VALUES (1, 1)"))
52
- ours = sa.MetaData()
53
- sa.Table("user", ours, sa.Column("id", sa.Integer, primary_key=True))
54
- monkeypatch.setattr(
55
- fixtures,
56
- "db",
57
- SimpleNamespace(
58
- session=SimpleNamespace(remove=lambda: None), engine=engine, metadata=ours
59
- ),
60
- )
61
-
62
- fixtures._reset_db()
63
-
64
- # The app's tables, empty, and the table the app makes when it starts.
65
- assert sorted(sa.inspect(engine).get_table_names()) == sorted(
66
- [SPLENT_MIGRATIONS_TABLE, "user"]
67
- )
68
- with engine.connect() as conn:
69
- assert conn.execute(text("SELECT COUNT(*) FROM user")).scalar() == 0