fastapi-modular 0.3.0__tar.gz → 0.3.1__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 (200) hide show
  1. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/CHANGELOG.md +28 -0
  2. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/PKG-INFO +3 -3
  3. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/README.md +2 -2
  4. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/README.vi.md +2 -2
  5. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/architecture.md +1 -1
  6. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/background.md +21 -0
  7. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/__init__.py +1 -1
  8. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/container.py +22 -0
  9. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/workers.py +5 -1
  10. fastapi_modular-0.3.1/tests/test_ghi_du_lieu_tu_worker.py +254 -0
  11. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/.gitignore +0 -0
  12. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/LICENSE +0 -0
  13. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/Makefile +0 -0
  14. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/alembic.ini +0 -0
  15. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/README.md +0 -0
  16. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/config.md +0 -0
  17. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/database.md +0 -0
  18. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/kafka.md +0 -0
  19. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/migrations.md +0 -0
  20. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/mongodb.md +0 -0
  21. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/mqtt.md +0 -0
  22. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/operations.md +0 -0
  23. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/providers.md +0 -0
  24. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/rabbitmq.md +0 -0
  25. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/redis.md +0 -0
  26. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/rpc.md +0 -0
  27. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/docs/websocket.md +0 -0
  28. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/__init__.py +0 -0
  29. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/clean.py +0 -0
  30. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/configure_env.py +0 -0
  31. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/info.py +0 -0
  32. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/install.py +0 -0
  33. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/main.py +0 -0
  34. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/new_module.py +0 -0
  35. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/new_project.py +0 -0
  36. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/new_provider.py +0 -0
  37. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/requirements.py +0 -0
  38. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/serve.py +0 -0
  39. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/cli/tools.py +0 -0
  40. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/__init__.py +0 -0
  41. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/clock.py +0 -0
  42. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/compat.py +0 -0
  43. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/config.py +0 -0
  44. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/context.py +0 -0
  45. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/controller.py +0 -0
  46. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/cron.py +0 -0
  47. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/error_handlers.py +0 -0
  48. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/events.py +0 -0
  49. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/exceptions.py +0 -0
  50. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/guards.py +0 -0
  51. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/jobs.py +0 -0
  52. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/lifespan.py +0 -0
  53. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/locks.py +0 -0
  54. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/logging.py +0 -0
  55. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/metrics.py +0 -0
  56. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/providers.py +0 -0
  57. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/rpc.py +0 -0
  58. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/scheduler.py +0 -0
  59. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/schemas.py +0 -0
  60. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/websocket/__init__.py +0 -0
  61. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/websocket/adapter.py +0 -0
  62. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/websocket/gateway.py +0 -0
  63. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/websocket/namespace.py +0 -0
  64. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/websocket/protocol.py +0 -0
  65. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/websocket/server.py +0 -0
  66. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/core/websocket/socket.py +0 -0
  67. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/discovery.py +0 -0
  68. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/factory.py +0 -0
  69. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/__init__.py +0 -0
  70. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/database/__init__.py +0 -0
  71. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/database/base.py +0 -0
  72. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/database/circuit.py +0 -0
  73. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/database/factory.py +0 -0
  74. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/database/memory.py +0 -0
  75. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/database/mongo.py +0 -0
  76. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/database/query.py +0 -0
  77. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/database/repository.py +0 -0
  78. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/database/sql.py +0 -0
  79. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/kafka/__init__.py +0 -0
  80. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/kafka/broker.py +0 -0
  81. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/kafka/consumers.py +0 -0
  82. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/kafka/metrics.py +0 -0
  83. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/kafka/responders.py +0 -0
  84. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/mqtt/__init__.py +0 -0
  85. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/mqtt/client.py +0 -0
  86. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/mqtt/consumers.py +0 -0
  87. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/mqtt/metrics.py +0 -0
  88. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/mqtt/patterns.py +0 -0
  89. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/mqtt/responders.py +0 -0
  90. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/rabbitmq/__init__.py +0 -0
  91. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/rabbitmq/broker.py +0 -0
  92. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/rabbitmq/consumers.py +0 -0
  93. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/rabbitmq/metrics.py +0 -0
  94. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/rabbitmq/patterns.py +0 -0
  95. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/rabbitmq/responders.py +0 -0
  96. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/redis/__init__.py +0 -0
  97. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/redis/client.py +0 -0
  98. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/redis/metrics.py +0 -0
  99. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/redis/pubsub.py +0 -0
  100. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/infrastructure/redis/responders.py +0 -0
  101. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/middleware/__init__.py +0 -0
  102. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/middleware/request_context.py +0 -0
  103. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/fastapi_modular/py.typed +0 -0
  104. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/migrations/env.py +0 -0
  105. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/migrations/script.py.mako +0 -0
  106. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/migrations/versions/.gitkeep +0 -0
  107. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/migrations/versions/20260821_1020_tao_bang_users_va_devices.py +0 -0
  108. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/pyproject.toml +0 -0
  109. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/pytest.ini +0 -0
  110. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/ruff.toml +0 -0
  111. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/__init__.py +0 -0
  112. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/__init__.py +0 -0
  113. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/chat/__init__.py +0 -0
  114. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/chat/chat_controller.py +0 -0
  115. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/chat/chat_gateway.py +0 -0
  116. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/chat/dto/__init__.py +0 -0
  117. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/chat/dto/chat_dto.py +0 -0
  118. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/devices/__init__.py +0 -0
  119. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/devices/device_controller.py +0 -0
  120. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/devices/device_service.py +0 -0
  121. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/devices/dto/__init__.py +0 -0
  122. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/devices/dto/device_dto.py +0 -0
  123. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/devices/entities/__init__.py +0 -0
  124. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/devices/entities/device_model.py +0 -0
  125. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/health/__init__.py +0 -0
  126. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/health/health_controller.py +0 -0
  127. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/kafka_test/__init__.py +0 -0
  128. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/kafka_test/kafka_consumer.py +0 -0
  129. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/kafka_test/kafka_controller.py +0 -0
  130. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/metrics/__init__.py +0 -0
  131. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/metrics/metrics_controller.py +0 -0
  132. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/mqtt_test/__init__.py +0 -0
  133. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/mqtt_test/mqtt_controller.py +0 -0
  134. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/mqtt_test/mqtt_listener.py +0 -0
  135. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/rabbitmq_test/__init__.py +0 -0
  136. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/rabbitmq_test/rabbitmq_consumer.py +0 -0
  137. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/rabbitmq_test/rabbitmq_controller.py +0 -0
  138. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/redis_test/__init__.py +0 -0
  139. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/redis_test/redis_controller.py +0 -0
  140. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/redis_test/redis_listener.py +0 -0
  141. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/users/__init__.py +0 -0
  142. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/users/dto/__init__.py +0 -0
  143. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/users/dto/user_dto.py +0 -0
  144. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/users/entities/__init__.py +0 -0
  145. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/users/entities/user_model.py +0 -0
  146. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/users/user_controller.py +0 -0
  147. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/api/users/user_service.py +0 -0
  148. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/core/__init__.py +0 -0
  149. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/core/config.py +0 -0
  150. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/core/lifespan.py +0 -0
  151. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/src/main.py +0 -0
  152. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/conftest.py +0 -0
  153. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/fixtures/cron_vectors.json +0 -0
  154. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/fixtures/nestjs_patterns.json +0 -0
  155. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_circuit.py +0 -0
  156. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_cli.py +0 -0
  157. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_configure_env.py +0 -0
  158. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_container.py +0 -0
  159. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_controller_dong_bo.py +0 -0
  160. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_cron.py +0 -0
  161. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_cross_module.py +0 -0
  162. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_database.py +0 -0
  163. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_drivers.py +0 -0
  164. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_enum_parity.py +0 -0
  165. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_events.py +0 -0
  166. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_foreign_keys.py +0 -0
  167. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_guards.py +0 -0
  168. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_ha_tang_that.py +0 -0
  169. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_health.py +0 -0
  170. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_injection.py +0 -0
  171. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_jobs.py +0 -0
  172. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_kafka.py +0 -0
  173. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_kafka_offline.py +0 -0
  174. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_lifespan_rieng.py +0 -0
  175. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_metrics.py +0 -0
  176. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_module_generator.py +0 -0
  177. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_mongo_query.py +0 -0
  178. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_mqtt.py +0 -0
  179. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_mqtt_offline.py +0 -0
  180. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_providers.py +0 -0
  181. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_query_builder.py +0 -0
  182. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_rabbitmq.py +0 -0
  183. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_rabbitmq_offline.py +0 -0
  184. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_redis.py +0 -0
  185. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_redis_offline.py +0 -0
  186. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_requirements_ghi_nho.py +0 -0
  187. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_resilience.py +0 -0
  188. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_rpc.py +0 -0
  189. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_scheduler.py +0 -0
  190. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_schema_evolution.py +0 -0
  191. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_schemas.py +0 -0
  192. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_settings_mo_rong.py +0 -0
  193. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_sqlite_pragmas.py +0 -0
  194. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_transaction.py +0 -0
  195. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_tuong_thich_phien_ban.py +0 -0
  196. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_uniqueness.py +0 -0
  197. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_users_api.py +0 -0
  198. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_websocket.py +0 -0
  199. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_workers.py +0 -0
  200. {fastapi_modular-0.3.0 → fastapi_modular-0.3.1}/tests/test_ws_adapter.py +0 -0
@@ -5,6 +5,34 @@ Theo [Keep a Changelog](https://keepachangelog.com/vi/1.1.0/); phiên bản theo
5
5
 
6
6
  ## [Chưa phát hành]
7
7
 
8
+ ## [0.3.1] — 2026-08-27
9
+
10
+ ### Sửa
11
+
12
+ - **Worker ghi database không được commit.** `contextvars` được sao chép khi tạo
13
+ Task/Thread, nên `@worker` sinh ra từ trong một HTTP request — hoặc từ
14
+ `@interval`/`@job`, vốn cũng mở request scope — thừa hưởng đúng store của
15
+ request đó. Mà `SqlUnitOfWork` là provider request-scoped: nó mở transaction
16
+ rồi chỉ commit ở `on_request_end`. Worker sống lâu hơn request nên transaction
17
+ ấy không bao giờ được commit.
18
+
19
+ Kiểu hỏng này im lặng đến khó chịu:
20
+
21
+ ```python
22
+ print("Deleted:", await repo.delete(row.id)) # -> True
23
+ ```
24
+
25
+ `True` là đúng — DELETE khớp một dòng, và câu SELECT ngay sau cũng thấy dữ
26
+ liệu mới vì cùng một connection. Chỉ có điều trên đĩa không đổi gì, và tắt app
27
+ là mất sạch.
28
+
29
+ Nay worker được cắt khỏi request scope thừa hưởng (`detach_request_scope`),
30
+ nên mỗi thao tác tự commit như docs vẫn mô tả. Gộp nhiều lệnh ghi thì bọc
31
+ `async with db.transaction():` — vẫn dùng được trong `ctx.run`.
32
+
33
+ Hệ quả có thể thấy: `container.resolve(<provider Scope.REQUEST>)` trong worker
34
+ giờ báo lỗi thay vì trả về một instance mồ côi. Đó là cố ý.
35
+
8
36
  ## [0.3.0] — 2026-08-27
9
37
 
10
38
  Bản này thêm **query builder** (JOIN, khoá ngoại, transaction, dữ liệu lồng
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fastapi-modular
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: NestJS-style modular architecture for FastAPI: DI container, class-based controllers, auto-discovered modules, WebSocket gateway, RabbitMQ/Redis/MQTT/Kafka
5
5
  Project-URL: Homepage, https://github.com/quanglinh2909/fastapi-modular
6
6
  Project-URL: Documentation, https://github.com/quanglinh2909/fastapi-modular/tree/main/docs
@@ -165,7 +165,7 @@ same job `package.json` does for `npm i`.
165
165
 
166
166
  ```
167
167
  # requirements.txt, after `fam install sqlite` and `fam install redis`
168
- fastapi-modular[redis,sqlite]>=0.3.0
168
+ fastapi-modular[redis,sqlite]>=0.3.1
169
169
  ```
170
170
 
171
171
  It records the extras, not the individual packages: the version ranges of
@@ -511,7 +511,7 @@ src/ SAMPLE APPLICATION — not shipped in the package; delete fr
511
511
  core/config.py AppSettings: subclass Settings to add your own .env variables
512
512
  core/lifespan.py application-specific startup / shutdown work
513
513
  api/ business modules; every subdirectory is one module
514
- tests/ 1097 tests that need no infrastructure, 292 more with real drivers/servers
514
+ tests/ 1101 tests that need no infrastructure, 292 more with real drivers/servers
515
515
  docs/ reference documentation (Vietnamese)
516
516
  ```
517
517
 
@@ -95,7 +95,7 @@ same job `package.json` does for `npm i`.
95
95
 
96
96
  ```
97
97
  # requirements.txt, after `fam install sqlite` and `fam install redis`
98
- fastapi-modular[redis,sqlite]>=0.3.0
98
+ fastapi-modular[redis,sqlite]>=0.3.1
99
99
  ```
100
100
 
101
101
  It records the extras, not the individual packages: the version ranges of
@@ -441,7 +441,7 @@ src/ SAMPLE APPLICATION — not shipped in the package; delete fr
441
441
  core/config.py AppSettings: subclass Settings to add your own .env variables
442
442
  core/lifespan.py application-specific startup / shutdown work
443
443
  api/ business modules; every subdirectory is one module
444
- tests/ 1097 tests that need no infrastructure, 292 more with real drivers/servers
444
+ tests/ 1101 tests that need no infrastructure, 292 more with real drivers/servers
445
445
  docs/ reference documentation (Vietnamese)
446
446
  ```
447
447
 
@@ -87,7 +87,7 @@ phần vào `requirements.txt`** để đồng nghiệp clone repo về chỉ c
87
87
 
88
88
  ```
89
89
  # requirements.txt, sau khi `fam install sqlite` rồi `fam install redis`
90
- fastapi-modular[redis,sqlite]>=0.3.0
90
+ fastapi-modular[redis,sqlite]>=0.3.1
91
91
  ```
92
92
 
93
93
  Nó ghi extras chứ không liệt kê từng gói con: khoảng phiên bản của
@@ -439,7 +439,7 @@ src/ ỨNG DỤNG MẪU — không nằm trong gói cài; xoá th
439
439
  core/config.py AppSettings: kế thừa Settings để thêm biến .env của bạn
440
440
  core/lifespan.py việc lúc khởi động / lúc tắt của riêng ứng dụng
441
441
  api/ các module nghiệp vụ; mỗi thư mục con là một module
442
- tests/ 1097 test chạy không cần hạ tầng, 292 test nữa cần driver/server thật
442
+ tests/ 1101 test chạy không cần hạ tầng, 292 test nữa cần driver/server thật
443
443
  docs/ tài liệu tra cứu
444
444
  ```
445
445
 
@@ -288,7 +288,7 @@ truy vấn database thật (1–10 ms) thì dưới 2%.
288
288
  fam lint # ruff trên `src` (mặc định): F, E, W, I, B, UP, SIM, RUF, BLE
289
289
  fam lint fastapi_modular src tests # soi cả thư viện và test — dùng cái này khi phát triển repo
290
290
  fam lint --fix # tự sửa phần sửa được
291
- fam test # 1097 test trên backend memory (292 test nữa cần hạ tầng hoặc driver thật)
291
+ fam test # 1101 test trên backend memory (292 test nữa cần hạ tầng hoặc driver thật)
292
292
  ```
293
293
 
294
294
  Cấu hình ở [`ruff.toml`](../ruff.toml). Rule `BLE` được bật có chủ ý: mỗi
@@ -642,6 +642,26 @@ ctx.run(self._repo.save(event))
642
642
  Đừng gọi `asyncio.run()` — nó tạo một event loop MỚI, mà connection pool của
643
643
  database thuộc về loop cũ, và nó sẽ hỏng theo những cách rất khó hiểu.
644
644
 
645
+ **Mỗi lời gọi tự commit.** Worker không nằm trong request nào, nên mỗi
646
+ `save`/`delete` là một transaction riêng, xong là xong. Muốn nhiều lệnh ghi
647
+ "cùng thành công hoặc cùng không" thì bọc lại — `db.transaction()` dùng được
648
+ trong `ctx.run` như thường:
649
+
650
+ ```python
651
+ async def chuyen_kho(self, tu: str, den: str) -> None:
652
+ async with self._db.transaction():
653
+ await self._repo.save(...)
654
+ await self._repo.delete(...)
655
+
656
+ # trong worker:
657
+ ctx.run(self._service.chuyen_kho("A", "B"))
658
+ ```
659
+
660
+ **Đừng resolve provider request-scoped trong worker.** Worker sống lâu hơn thứ
661
+ sinh ra nó, nên khung cắt nó khỏi request scope — `container.resolve(Principal)`
662
+ hay bất cứ provider `Scope.REQUEST` nào cũng sẽ báo lỗi ngay. Đó là cố ý: không
663
+ có request thì không có "danh tính của request này".
664
+
645
665
  ---
646
666
 
647
667
  ## Hỏng thì tra ở đây
@@ -663,6 +683,7 @@ database thuộc về loop cũ, và nó sẽ hỏng theo những cách rất kh
663
683
  | **`ctx.blocking` chậm dần khi thêm worker** | vượt trần pool — nới `APP_WORKERS__THREAD_POOL_SIZE` |
664
684
  | **`workers_restarted_total` tăng đều** | camera rớt mạng, hoặc vòng lặp ném lỗi mỗi lượt — xem log `worker.crashed` |
665
685
  | **`worker.stop_timeout` lúc tắt app** | vòng lặp không kiểm `ctx.running`, hoặc lời gọi chặn không có timeout |
686
+ | **Ghi/xoá trong worker: `delete()` trả `True` mà dữ liệu vẫn còn** | Bản **trước 0.3.1**: worker thừa hưởng transaction của request sinh ra nó và không ai commit. Nâng cấp: `pip install -U fastapi-modular`. Nhận ra nó bằng cách đọc database bằng **công cụ khác** (`sqlite3 app.db "SELECT …"`), chứ hỏi lại repository thì vẫn thấy "xong rồi" |
666
687
  | **Ctrl+C bấm mà không có gì xảy ra** | xem ngay dưới |
667
688
 
668
689
  ### Ctrl+C như không ăn
@@ -42,7 +42,7 @@ from fastapi_modular.discovery import DEFAULT_PACKAGE, register_routes
42
42
  from fastapi_modular.factory import add_middleware, bind_settings, create_app, new_fastapi
43
43
  from fastapi_modular.infrastructure.database.base import Entity, reference
44
44
 
45
- __version__ = "0.3.0"
45
+ __version__ = "0.3.1"
46
46
 
47
47
  __all__ = [
48
48
  "DEFAULT_PACKAGE",
@@ -363,6 +363,28 @@ async def request_scope() -> AsyncIterator[dict[str, Any]]:
363
363
  _request_store.reset(token)
364
364
 
365
365
 
366
+ def detach_request_scope() -> None:
367
+ """Cắt khỏi request scope THỪA HƯỞNG từ nơi sinh ra, cho việc chạy nền.
368
+
369
+ Vì sao cần: `contextvars` được sao chép khi tạo Task hay Thread, nên một
370
+ `@worker` sinh ra từ trong một HTTP request — hoặc từ một `@interval` /
371
+ `@job`, vốn cũng mở request scope — sẽ thấy đúng cái store đó, dù request ấy
372
+ đã đóng từ lâu.
373
+
374
+ Hậu quả đo được, và nó im lặng đến khó chịu: `SqlUnitOfWork` là provider
375
+ request-scoped, mở một transaction rồi chỉ commit ở `on_request_end`. Worker
376
+ thừa hưởng nó thì mọi lệnh ghi rơi vào một transaction KHÔNG BAO GIỜ được
377
+ commit — `repo.delete()` vẫn trả `True` (đúng, nó khớp một dòng), câu SELECT
378
+ ngay sau đó vẫn thấy dữ liệu mới (cùng connection), nhưng trên đĩa không có
379
+ gì đổi và tắt app là mất sạch.
380
+
381
+ Cắt rồi thì `_conn()` rơi về nhánh "ngoài request": mỗi thao tác một
382
+ `engine.begin()`, tự commit — đúng như docs mô tả cho worker/job/script.
383
+ Cần gộp nhiều lệnh ghi thì bọc `async with db.transaction():`.
384
+ """
385
+ _request_store.set(None)
386
+
387
+
366
388
  def Inject(token: type[T] | str) -> Any:
367
389
  """Cầu nối container -> FastAPI Depends.
368
390
 
@@ -79,7 +79,7 @@ from dataclasses import dataclass
79
79
  from typing import Any
80
80
 
81
81
  from fastapi_modular.core.config import Settings
82
- from fastapi_modular.core.container import container, injectable
82
+ from fastapi_modular.core.container import container, detach_request_scope, injectable
83
83
  from fastapi_modular.core.context import new_request_id, reset_request_id, set_request_id
84
84
  from fastapi_modular.core.exceptions import BadRequestError, ServiceUnavailableError
85
85
  from fastapi_modular.core.locks import NoLock, SingleFlight, build_lock
@@ -621,6 +621,10 @@ class WorkerPool:
621
621
  self, spec: WorkerSpec, instance: Any, data: Any, context: WorkerContext
622
622
  ) -> None:
623
623
  """Chạy vòng lặp, và dựng lại khi nó hỏng."""
624
+ # Worker sống lâu hơn thứ sinh ra nó, nên không được dùng chung request
625
+ # scope với nó — xem `detach_request_scope`. Đặt ở đây là đủ cho cả
626
+ # thread (`copy_context` lấy từ context của Task này) lẫn `ctx.run`.
627
+ detach_request_scope()
624
628
  if spec.single and not await self._become_owner(spec, context):
625
629
  return
626
630
 
@@ -0,0 +1,254 @@
1
+ """Worker ghi database thì dữ liệu phải THẬT SỰ tới đĩa.
2
+
3
+ Vì sao có file này. `contextvars` được sao chép khi tạo Task/Thread, nên một
4
+ `@worker` sinh ra từ trong một HTTP request — hoặc từ `@interval`/`@job`, vốn
5
+ cũng mở request scope — thừa hưởng đúng cái store của request đó. Mà
6
+ `SqlUnitOfWork` là provider request-scoped: nó mở một transaction rồi chỉ commit
7
+ ở `on_request_end`. Worker sống lâu hơn request, nên transaction ấy KHÔNG BAO
8
+ GIỜ được commit.
9
+
10
+ Kiểu hỏng này im lặng đến khó chịu, và người dùng thật đã mất buổi vì nó:
11
+
12
+ print("Deleted device offline:", await repo.delete(row.id))
13
+ # -> True
14
+
15
+ `True` là đúng: DELETE khớp một dòng. Câu SELECT ngay sau cũng thấy dữ liệu mới,
16
+ vì cùng một connection. Chỉ có điều trên đĩa không có gì đổi, và tắt app là mất
17
+ sạch.
18
+
19
+ Nên mọi phép kiểm ở đây đọc bằng **một kết nối sqlite KHÁC** (`tren_dia`), chứ
20
+ không hỏi lại chính repository — hỏi lại repository là hỏi đúng cái connection
21
+ đang giữ transaction chưa commit, và nó sẽ trả lời "xong rồi" trong mọi trường
22
+ hợp.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import importlib.util
28
+ import os
29
+ import sqlite3
30
+ import time
31
+ import uuid
32
+ from dataclasses import dataclass, field
33
+ from datetime import datetime
34
+
35
+ import pytest
36
+ from fastapi.testclient import TestClient
37
+
38
+ from fastapi_modular import Entity, entity, injectable
39
+ from fastapi_modular.core.clock import utcnow
40
+ from fastapi_modular.core.config import (
41
+ DatabaseSettings,
42
+ KafkaSettings,
43
+ MqttSettings,
44
+ RabbitSettings,
45
+ RedisSettings,
46
+ Settings,
47
+ WebSocketSettings,
48
+ )
49
+ from fastapi_modular.core.controller import build_router, controller, post
50
+ from fastapi_modular.core.workers import WorkerContext, worker
51
+ from fastapi_modular.factory import create_app
52
+ from fastapi_modular.infrastructure.database import Repository
53
+ from fastapi_modular.infrastructure.database.repository import Database
54
+
55
+ CO_SQLITE = importlib.util.find_spec("aiosqlite") is not None
56
+ pytestmark = pytest.mark.skipif(not CO_SQLITE, reason="cần aiosqlite")
57
+
58
+ DA_CHAY: set[str] = set()
59
+ DB_PATH = ""
60
+
61
+
62
+ @entity(name="wk_rows")
63
+ @dataclass(slots=True)
64
+ class WkRow(Entity):
65
+ id: str
66
+ nguon: str = ""
67
+ created_at: datetime = field(default_factory=utcnow)
68
+
69
+
70
+ def tren_dia() -> list[str]:
71
+ """Đọc bằng kết nối KHÁC — chỉ thấy thứ đã COMMIT."""
72
+ conn = sqlite3.connect(DB_PATH)
73
+ try:
74
+ return sorted(r[0] for r in conn.execute("SELECT id FROM wk_rows"))
75
+ finally:
76
+ conn.close()
77
+
78
+
79
+ @injectable
80
+ class WkService:
81
+ def __init__(self, repo: Repository[WkRow], db: Database) -> None:
82
+ self._repo = repo
83
+ self._db = db
84
+
85
+ async def them(self, id_: str) -> None:
86
+ await self._repo.save(WkRow(id=id_, nguon="worker"))
87
+
88
+ async def xoa(self, id_: str) -> bool:
89
+ return await self._repo.delete(id_)
90
+
91
+ async def them_roi_huy(self, id_: str) -> None:
92
+ """Ghi trong transaction rồi ném lỗi — không được để lại gì."""
93
+ try:
94
+ async with self._db.transaction():
95
+ await self._repo.save(WkRow(id=id_, nguon="worker"))
96
+ raise RuntimeError("hỏng giữa chừng")
97
+ except RuntimeError:
98
+ pass
99
+
100
+
101
+ @injectable
102
+ class WkWorkers:
103
+ def __init__(self, service: WkService) -> None:
104
+ self._service = service
105
+
106
+ @worker("wk-ghi", thread=True)
107
+ def ghi_trong_thread(self, data: dict, ctx: WorkerContext) -> None:
108
+ """Đúng hình dạng của worker thật: `def` thường + `ctx.run(...)`."""
109
+ ctx.run(self._service.them(data["id"]))
110
+ DA_CHAY.add("da-ghi")
111
+ while ctx.running:
112
+ ctx.wait(0.02)
113
+
114
+ @worker("wk-xoa", thread=True)
115
+ def ghi_roi_xoa(self, data: dict, ctx: WorkerContext) -> None:
116
+ ctx.run(self._service.them(data["id"]))
117
+ DA_CHAY.add("da-ghi-lan-hai")
118
+ ctx.run(self._service.xoa(data["id"]))
119
+ DA_CHAY.add("da-xoa")
120
+ while ctx.running:
121
+ ctx.wait(0.02)
122
+
123
+ @worker("wk-tx", thread=True)
124
+ def ghi_roi_huy(self, data: dict, ctx: WorkerContext) -> None:
125
+ ctx.run(self._service.them_roi_huy(data["id"]))
126
+ DA_CHAY.add("da-huy")
127
+ while ctx.running:
128
+ ctx.wait(0.02)
129
+
130
+
131
+ @controller(prefix="/wk")
132
+ class WkController:
133
+ def __init__(self, workers: WkWorkers) -> None:
134
+ self._workers = workers
135
+
136
+ @post("/ghi")
137
+ async def ghi(self, id_: str) -> dict:
138
+ """Sinh worker TỪ TRONG một HTTP request — ca nguy hiểm nhất.
139
+
140
+ Ở đây request scope chắc chắn đang mở, nên worker sao chép được nó.
141
+ """
142
+ await self._workers.ghi_trong_thread(id_, {"id": id_})
143
+ return {"ok": True}
144
+
145
+ @post("/ghi-xoa")
146
+ async def ghi_xoa(self, id_: str) -> dict:
147
+ await self._workers.ghi_roi_xoa(id_, {"id": id_})
148
+ return {"ok": True}
149
+
150
+ @post("/ghi-huy")
151
+ async def ghi_huy(self, id_: str) -> dict:
152
+ await self._workers.ghi_roi_huy(id_, {"id": id_})
153
+ return {"ok": True}
154
+
155
+
156
+ @pytest.fixture
157
+ def client(tmp_path):
158
+ global DB_PATH
159
+ DB_PATH = str(tmp_path / f"{uuid.uuid4().hex}.db")
160
+ DA_CHAY.clear()
161
+
162
+ settings = Settings(
163
+ APP_ENV="local",
164
+ APP_DEBUG=True,
165
+ APP_DB=DatabaseSettings(driver="sqlite", dsn=f"sqlite+aiosqlite:///{DB_PATH}"),
166
+ APP_RABBITMQ=RabbitSettings(enabled=False),
167
+ APP_REDIS=RedisSettings(enabled=False),
168
+ APP_MQTT=MqttSettings(enabled=False),
169
+ APP_KAFKA=KafkaSettings(enabled=False),
170
+ APP_WS=WebSocketSettings(adapter="local"),
171
+ )
172
+ app = create_app(settings, package="tests")
173
+ app.include_router(build_router(WkController), prefix="/api")
174
+ with TestClient(app) as client:
175
+ yield client
176
+ assert os.path.exists(DB_PATH)
177
+
178
+
179
+ def cho(dau_moc: str, han: float = 5.0) -> None:
180
+ """Chờ worker chạy tới mốc, tối đa `han` giây (đừng ngủ theo đồng hồ)."""
181
+ moc = time.monotonic()
182
+ while dau_moc not in DA_CHAY and time.monotonic() - moc < han:
183
+ time.sleep(0.01)
184
+ assert dau_moc in DA_CHAY, f"worker không tới được mốc {dau_moc!r}"
185
+
186
+
187
+ def cho_tren_dia(mong_doi: list[str], han: float = 5.0) -> None:
188
+ moc = time.monotonic()
189
+ while tren_dia() != mong_doi and time.monotonic() - moc < han:
190
+ time.sleep(0.01)
191
+ assert tren_dia() == mong_doi
192
+
193
+
194
+ def test_worker_ghi_thi_du_lieu_toi_dia(client):
195
+ """Ca chính: trước đây câu INSERT nằm trong transaction không ai commit."""
196
+ client.post("/api/wk/ghi", params={"id_": "r1"})
197
+ cho("da-ghi")
198
+ cho_tren_dia(["r1"])
199
+
200
+
201
+ def test_worker_xoa_thi_du_lieu_bien_khoi_dia(client):
202
+ """`delete()` trả True mà dòng vẫn còn trên đĩa — đúng lỗi người dùng gặp."""
203
+ client.post("/api/wk/ghi-xoa", params={"id_": "r2"})
204
+ cho("da-xoa")
205
+ cho_tren_dia([])
206
+
207
+
208
+ def test_worker_khong_dung_chung_UnitOfWork_voi_request_sinh_ra_no(client):
209
+ """Chốt đúng cơ chế, không chỉ chốt hệ quả.
210
+
211
+ Worker phải KHÔNG resolve được provider request-scoped: nó sống lâu hơn
212
+ request sinh ra nó, nên dùng chung là dùng một transaction đã mồ côi.
213
+ """
214
+ from fastapi_modular.core.container import container
215
+ from fastapi_modular.infrastructure.database.sql import SqlUnitOfWork
216
+
217
+ ket_qua: list[str] = []
218
+
219
+ @injectable
220
+ class Soi:
221
+ @worker("wk-soi", thread=True)
222
+ def soi(self, data: dict, ctx: WorkerContext) -> None:
223
+ try:
224
+ container.resolve(SqlUnitOfWork)
225
+ ket_qua.append("resolve được")
226
+ except RuntimeError:
227
+ ket_qua.append("đã cắt")
228
+ DA_CHAY.add("da-soi")
229
+ while ctx.running:
230
+ ctx.wait(0.02)
231
+
232
+ @controller(prefix="/soi")
233
+ class SoiController:
234
+ def __init__(self, soi: Soi) -> None:
235
+ self._soi = soi
236
+
237
+ @post("")
238
+ async def chay(self) -> dict:
239
+ await self._soi.soi("k", {})
240
+ return {"ok": True}
241
+
242
+ client.app.include_router(build_router(SoiController), prefix="/api")
243
+ client.post("/api/soi")
244
+ cho("da-soi")
245
+
246
+ assert ket_qua == ["đã cắt"]
247
+
248
+
249
+ def test_transaction_trong_worker_van_huy_duoc(client):
250
+ """Cắt request scope không được làm mất `db.transaction()` của worker."""
251
+ client.post("/api/wk/ghi-huy", params={"id_": "r3"})
252
+ cho("da-huy")
253
+ time.sleep(0.2) # cho nó cơ hội ghi sai, nếu có
254
+ assert tren_dia() == [], "khối transaction ném lỗi thì không được để lại gì"
File without changes