@tstdl/base 0.93.87 → 0.93.90

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 (314) hide show
  1. package/ai/genkit/helpers.d.ts +3 -1
  2. package/ai/genkit/helpers.js +3 -3
  3. package/api/server/gateway.d.ts +3 -0
  4. package/api/server/gateway.js +15 -4
  5. package/api/server/middlewares/catch-error.middleware.js +2 -4
  6. package/api/server/middlewares/cors.middleware.js +2 -3
  7. package/api/server/middlewares/csrf.middleware.d.ts +41 -0
  8. package/api/server/middlewares/csrf.middleware.js +108 -0
  9. package/api/server/middlewares/index.d.ts +1 -0
  10. package/api/server/middlewares/index.js +1 -0
  11. package/api/server/module.d.ts +8 -2
  12. package/api/server/module.js +14 -8
  13. package/api/server/tests/csrf.middleware.test.js +91 -0
  14. package/audit/drizzle/{0000_bored_stick.sql → 0000_lumpy_thunderball.sql} +3 -3
  15. package/audit/drizzle/meta/0000_snapshot.json +4 -4
  16. package/audit/drizzle/meta/_journal.json +2 -9
  17. package/audit/module.d.ts +4 -1
  18. package/audit/module.js +3 -2
  19. package/audit/schemas.d.ts +1 -1
  20. package/audit/types.d.ts +1 -1
  21. package/audit/types.js +1 -1
  22. package/authentication/client/authentication.service.d.ts +14 -1
  23. package/authentication/client/authentication.service.js +82 -23
  24. package/authentication/client/http-client.middleware.d.ts +6 -0
  25. package/authentication/client/http-client.middleware.js +36 -0
  26. package/authentication/client/module.js +8 -2
  27. package/authentication/models/service-account.model.d.ts +2 -2
  28. package/authentication/models/service-account.model.js +10 -5
  29. package/authentication/models/subject.model.d.ts +20 -5
  30. package/authentication/models/subject.model.js +34 -29
  31. package/authentication/models/system-account.model.d.ts +3 -2
  32. package/authentication/models/system-account.model.js +11 -5
  33. package/authentication/models/user.model.d.ts +2 -11
  34. package/authentication/models/user.model.js +5 -16
  35. package/authentication/server/authentication-api-request-token.provider.d.ts +0 -2
  36. package/authentication/server/authentication-api-request-token.provider.js +3 -11
  37. package/authentication/server/authentication.api-controller.d.ts +1 -2
  38. package/authentication/server/authentication.api-controller.js +8 -9
  39. package/authentication/server/authentication.audit.d.ts +3 -2
  40. package/authentication/server/authentication.service.d.ts +27 -1
  41. package/authentication/server/authentication.service.js +67 -18
  42. package/authentication/server/drizzle/{0000_normal_paper_doll.sql → 0000_soft_tag.sql} +25 -32
  43. package/authentication/server/drizzle/meta/0000_snapshot.json +180 -205
  44. package/authentication/server/drizzle/meta/_journal.json +2 -2
  45. package/authentication/server/helper.js +9 -2
  46. package/authentication/server/module.d.ts +4 -1
  47. package/authentication/server/module.js +9 -5
  48. package/authentication/server/schemas.d.ts +2 -1
  49. package/authentication/server/schemas.js +2 -2
  50. package/authentication/server/subject.service.d.ts +17 -11
  51. package/authentication/server/subject.service.js +86 -84
  52. package/authentication/tests/authentication-ancillary.service.test.d.ts +1 -0
  53. package/authentication/tests/authentication-ancillary.service.test.js +13 -0
  54. package/authentication/tests/authentication-secret-requirements.validator.test.d.ts +1 -0
  55. package/authentication/tests/authentication-secret-requirements.validator.test.js +29 -0
  56. package/authentication/tests/authentication.api-controller.test.d.ts +1 -0
  57. package/authentication/tests/authentication.api-controller.test.js +88 -0
  58. package/authentication/tests/authentication.api-request-token.provider.test.d.ts +1 -0
  59. package/authentication/tests/authentication.api-request-token.provider.test.js +48 -0
  60. package/authentication/tests/authentication.client-middleware.test.d.ts +1 -0
  61. package/authentication/tests/authentication.client-middleware.test.js +23 -0
  62. package/authentication/tests/authentication.client-service.test.d.ts +1 -0
  63. package/authentication/tests/authentication.client-service.test.js +70 -0
  64. package/authentication/tests/authentication.service.test.d.ts +1 -0
  65. package/authentication/tests/authentication.service.test.js +186 -0
  66. package/authentication/tests/authentication.test-ancillary-service.d.ts +9 -0
  67. package/authentication/tests/authentication.test-ancillary-service.js +27 -0
  68. package/authentication/tests/helper.test.d.ts +1 -0
  69. package/authentication/tests/helper.test.js +107 -0
  70. package/authentication/tests/secret-requirements.error.test.d.ts +1 -0
  71. package/authentication/tests/secret-requirements.error.test.js +14 -0
  72. package/authentication/tests/subject.service.test.d.ts +1 -0
  73. package/authentication/tests/subject.service.test.js +140 -0
  74. package/circuit-breaker/postgres/drizzle/meta/0000_snapshot.json +1 -1
  75. package/circuit-breaker/postgres/drizzle/meta/_journal.json +2 -2
  76. package/circuit-breaker/postgres/module.d.ts +7 -1
  77. package/circuit-breaker/postgres/module.js +8 -6
  78. package/circuit-breaker/tests/circuit-breaker.test.js +2 -22
  79. package/document-management/api/document-management.api.js +2 -6
  80. package/document-management/server/services/document-validation.service.js +6 -5
  81. package/document-management/server/services/document-workflow.service.js +5 -5
  82. package/document-management/service-models/document-folders.view-model.d.ts +5 -2
  83. package/document-management/service-models/document-folders.view-model.js +42 -9
  84. package/document-management/service-models/enriched/enriched-document-management-data.view.js +1 -1
  85. package/examples/document-management/main.js +4 -4
  86. package/http/client/adapters/undici.adapter.d.ts +7 -5
  87. package/http/client/adapters/undici.adapter.js +13 -10
  88. package/http/client/module.d.ts +3 -1
  89. package/http/client/module.js +8 -9
  90. package/http/server/http-server.d.ts +2 -0
  91. package/http/server/node/module.d.ts +6 -2
  92. package/http/server/node/module.js +6 -4
  93. package/http/server/node/node-http-server.d.ts +2 -0
  94. package/http/server/node/node-http-server.js +7 -0
  95. package/http/types.d.ts +1 -1
  96. package/key-value-store/postgres/module.d.ts +7 -1
  97. package/key-value-store/postgres/module.js +7 -3
  98. package/lock/postgres/lock.js +0 -1
  99. package/lock/postgres/module.d.ts +7 -1
  100. package/lock/postgres/module.js +9 -5
  101. package/logger/formatter.d.ts +2 -0
  102. package/logger/formatters/json.js +2 -2
  103. package/logger/formatters/pretty-print.js +8 -10
  104. package/logger/logger.d.ts +1 -1
  105. package/logger/logger.js +15 -12
  106. package/message-bus/local/module.d.ts +5 -2
  107. package/message-bus/local/module.js +5 -4
  108. package/module/module.d.ts +2 -1
  109. package/module/module.js +3 -0
  110. package/module/modules/web-server.module.d.ts +11 -6
  111. package/module/modules/web-server.module.js +15 -10
  112. package/orm/decorators.d.ts +24 -1
  113. package/orm/decorators.js +40 -4
  114. package/orm/query/base.d.ts +17 -17
  115. package/orm/query/base.js +1 -1
  116. package/orm/repository.types.d.ts +45 -1
  117. package/orm/schemas/tsvector.js +1 -1
  118. package/orm/server/drizzle/schema-converter.d.ts +3 -1
  119. package/orm/server/drizzle/schema-converter.js +120 -14
  120. package/orm/server/index.d.ts +1 -0
  121. package/orm/server/index.js +1 -0
  122. package/orm/server/module.d.ts +4 -2
  123. package/orm/server/module.js +6 -5
  124. package/orm/server/query-converter.d.ts +6 -3
  125. package/orm/server/query-converter.js +32 -20
  126. package/orm/server/repository-config.d.ts +8 -0
  127. package/orm/server/repository-config.js +8 -0
  128. package/orm/server/repository.d.ts +117 -43
  129. package/orm/server/repository.js +757 -253
  130. package/orm/server/transaction.d.ts +4 -2
  131. package/orm/server/transaction.js +14 -5
  132. package/orm/server/transactional.d.ts +6 -2
  133. package/orm/server/transactional.js +39 -9
  134. package/orm/server/types.d.ts +2 -0
  135. package/orm/sqls/case-when.d.ts +3 -3
  136. package/orm/sqls/case-when.js +2 -2
  137. package/orm/sqls/sqls.d.ts +31 -5
  138. package/orm/sqls/sqls.js +69 -6
  139. package/orm/tests/data-types.test.d.ts +1 -0
  140. package/orm/tests/data-types.test.js +39 -0
  141. package/orm/tests/decorators.test.d.ts +1 -0
  142. package/orm/tests/decorators.test.js +77 -0
  143. package/orm/tests/encryption.test.d.ts +1 -0
  144. package/orm/tests/encryption.test.js +34 -0
  145. package/orm/tests/query-complex.test.d.ts +1 -0
  146. package/orm/tests/query-complex.test.js +203 -0
  147. package/orm/tests/query-converter-complex.test.d.ts +1 -0
  148. package/orm/tests/query-converter-complex.test.js +126 -0
  149. package/orm/tests/query-converter.test.d.ts +1 -0
  150. package/orm/tests/query-converter.test.js +123 -0
  151. package/orm/tests/repository-advanced.test.d.ts +1 -0
  152. package/orm/tests/repository-advanced.test.js +232 -0
  153. package/orm/tests/repository-attributes.test.d.ts +1 -0
  154. package/orm/tests/repository-attributes.test.js +99 -0
  155. package/orm/tests/repository-comprehensive.test.d.ts +1 -0
  156. package/orm/tests/repository-comprehensive.test.js +187 -0
  157. package/orm/tests/repository-coverage.test.d.ts +1 -0
  158. package/orm/tests/repository-coverage.test.js +303 -0
  159. package/orm/tests/repository-cti-complex.test.d.ts +1 -0
  160. package/orm/tests/repository-cti-complex.test.js +170 -0
  161. package/orm/tests/repository-cti-embedded.test.d.ts +1 -0
  162. package/orm/tests/repository-cti-embedded.test.js +188 -0
  163. package/orm/tests/repository-cti-extensive.test.d.ts +1 -0
  164. package/orm/tests/repository-cti-extensive.test.js +308 -0
  165. package/orm/tests/repository-cti-mapping.test.d.ts +1 -0
  166. package/orm/tests/repository-cti-mapping.test.js +121 -0
  167. package/orm/tests/repository-cti-search.test.d.ts +1 -0
  168. package/orm/tests/repository-cti-search.test.js +152 -0
  169. package/orm/tests/repository-cti-soft-delete.test.d.ts +1 -0
  170. package/orm/tests/repository-cti-soft-delete.test.js +115 -0
  171. package/orm/tests/repository-cti-transactions.test.d.ts +1 -0
  172. package/orm/tests/repository-cti-transactions.test.js +126 -0
  173. package/orm/tests/repository-cti-upsert-many.test.d.ts +1 -0
  174. package/orm/tests/repository-cti-upsert-many.test.js +127 -0
  175. package/orm/tests/repository-cti.test.d.ts +1 -0
  176. package/orm/tests/repository-cti.test.js +456 -0
  177. package/orm/tests/repository-edge-cases.test.d.ts +1 -0
  178. package/orm/tests/repository-edge-cases.test.js +216 -0
  179. package/orm/tests/repository-expiration.test.d.ts +1 -0
  180. package/orm/tests/repository-expiration.test.js +153 -0
  181. package/orm/tests/repository-extra-coverage.test.d.ts +1 -0
  182. package/orm/tests/repository-extra-coverage.test.js +546 -0
  183. package/orm/tests/repository-mapping.test.d.ts +1 -0
  184. package/orm/tests/repository-mapping.test.js +71 -0
  185. package/orm/tests/repository-regression.test.d.ts +1 -0
  186. package/orm/tests/repository-regression.test.js +330 -0
  187. package/orm/tests/repository-search-coverage.test.d.ts +1 -0
  188. package/orm/tests/repository-search-coverage.test.js +129 -0
  189. package/orm/tests/repository-search.test.d.ts +1 -0
  190. package/orm/tests/repository-search.test.js +116 -0
  191. package/orm/tests/repository-soft-delete.test.d.ts +1 -0
  192. package/orm/tests/repository-soft-delete.test.js +143 -0
  193. package/orm/tests/repository-transactions-nested.test.d.ts +1 -0
  194. package/orm/tests/repository-transactions-nested.test.js +202 -0
  195. package/orm/tests/repository-types.test.d.ts +1 -0
  196. package/orm/tests/repository-types.test.js +218 -0
  197. package/orm/tests/schema-converter.test.d.ts +1 -0
  198. package/orm/tests/schema-converter.test.js +81 -0
  199. package/orm/tests/schema-generation.test.d.ts +1 -0
  200. package/orm/tests/schema-generation.test.js +127 -0
  201. package/orm/tests/sql-helpers.test.d.ts +1 -0
  202. package/orm/tests/sql-helpers.test.js +67 -0
  203. package/orm/tests/transaction-safety.test.d.ts +1 -0
  204. package/orm/tests/transaction-safety.test.js +81 -0
  205. package/orm/tests/transactional.test.d.ts +1 -0
  206. package/orm/tests/transactional.test.js +224 -0
  207. package/orm/tests/utils.test.d.ts +1 -0
  208. package/orm/tests/utils.test.js +70 -0
  209. package/orm/utils.d.ts +7 -0
  210. package/orm/utils.js +26 -6
  211. package/package.json +12 -7
  212. package/pool/pool.js +1 -1
  213. package/rate-limit/index.d.ts +2 -0
  214. package/rate-limit/index.js +2 -0
  215. package/rate-limit/postgres/drizzle/0000_watery_rage.sql +7 -0
  216. package/{queue → rate-limit}/postgres/drizzle/meta/0000_snapshot.json +14 -39
  217. package/rate-limit/postgres/drizzle/meta/_journal.json +13 -0
  218. package/{queue → rate-limit}/postgres/drizzle.config.js +1 -1
  219. package/rate-limit/postgres/index.d.ts +4 -0
  220. package/rate-limit/postgres/index.js +4 -0
  221. package/rate-limit/postgres/module.d.ts +12 -0
  222. package/rate-limit/postgres/module.js +28 -0
  223. package/rate-limit/postgres/postgres-rate-limiter.d.ts +9 -0
  224. package/rate-limit/postgres/postgres-rate-limiter.js +56 -0
  225. package/rate-limit/postgres/rate-limit.model.d.ts +8 -0
  226. package/rate-limit/postgres/rate-limit.model.js +35 -0
  227. package/rate-limit/postgres/rate-limiter.provider.d.ts +6 -0
  228. package/rate-limit/postgres/rate-limiter.provider.js +21 -0
  229. package/rate-limit/postgres/schemas.d.ts +3 -0
  230. package/rate-limit/postgres/schemas.js +4 -0
  231. package/rate-limit/provider.d.ts +9 -0
  232. package/rate-limit/provider.js +2 -0
  233. package/rate-limit/rate-limiter.d.ts +35 -0
  234. package/rate-limit/rate-limiter.js +3 -0
  235. package/rate-limit/tests/postgres-rate-limiter.test.d.ts +1 -0
  236. package/rate-limit/tests/postgres-rate-limiter.test.js +92 -0
  237. package/signals/implementation/configure.d.ts +3 -0
  238. package/signals/implementation/configure.js +3 -0
  239. package/sse/data-stream-source.d.ts +1 -1
  240. package/sse/data-stream-source.js +6 -6
  241. package/task-queue/enqueue-batch.d.ts +17 -0
  242. package/task-queue/enqueue-batch.js +24 -0
  243. package/{queue → task-queue}/index.d.ts +1 -1
  244. package/{queue → task-queue}/index.js +1 -1
  245. package/task-queue/postgres/drizzle/0000_thin_black_panther.sql +74 -0
  246. package/task-queue/postgres/drizzle/meta/0000_snapshot.json +592 -0
  247. package/task-queue/postgres/drizzle/meta/_journal.json +13 -0
  248. package/task-queue/postgres/drizzle.config.d.ts +2 -0
  249. package/task-queue/postgres/drizzle.config.js +11 -0
  250. package/task-queue/postgres/index.d.ts +4 -0
  251. package/task-queue/postgres/index.js +4 -0
  252. package/task-queue/postgres/module.d.ts +12 -0
  253. package/task-queue/postgres/module.js +28 -0
  254. package/task-queue/postgres/schemas.d.ts +16 -0
  255. package/task-queue/postgres/schemas.js +8 -0
  256. package/task-queue/postgres/task-queue.d.ts +83 -0
  257. package/task-queue/postgres/task-queue.js +1054 -0
  258. package/task-queue/postgres/task-queue.provider.d.ts +7 -0
  259. package/{queue/postgres/queue.provider.js → task-queue/postgres/task-queue.provider.js} +8 -8
  260. package/task-queue/postgres/task.model.d.ts +39 -0
  261. package/task-queue/postgres/task.model.js +178 -0
  262. package/{queue → task-queue}/provider.d.ts +3 -3
  263. package/task-queue/provider.js +2 -0
  264. package/{queue → task-queue}/task-context.d.ts +7 -7
  265. package/{queue → task-queue}/task-context.js +8 -8
  266. package/{queue/queue.d.ts → task-queue/task-queue.d.ts} +128 -59
  267. package/task-queue/task-queue.js +200 -0
  268. package/task-queue/tests/complex.test.d.ts +1 -0
  269. package/task-queue/tests/complex.test.js +299 -0
  270. package/task-queue/tests/dependencies.test.d.ts +1 -0
  271. package/task-queue/tests/dependencies.test.js +174 -0
  272. package/task-queue/tests/queue.test.d.ts +1 -0
  273. package/task-queue/tests/queue.test.js +334 -0
  274. package/task-queue/tests/worker.test.d.ts +1 -0
  275. package/task-queue/tests/worker.test.js +163 -0
  276. package/test1.js +1 -1
  277. package/test4.js +2 -2
  278. package/unit-test/index.d.ts +1 -0
  279. package/unit-test/index.js +1 -0
  280. package/unit-test/integration-setup.d.ts +55 -0
  281. package/unit-test/integration-setup.js +182 -0
  282. package/utils/patterns.d.ts +3 -0
  283. package/utils/patterns.js +6 -1
  284. package/audit/drizzle/0001_previous_network.sql +0 -2
  285. package/audit/drizzle/meta/0001_snapshot.json +0 -195
  286. package/queue/enqueue-batch.d.ts +0 -17
  287. package/queue/enqueue-batch.js +0 -18
  288. package/queue/postgres/drizzle/0000_zippy_moondragon.sql +0 -11
  289. package/queue/postgres/drizzle/0001_certain_wild_pack.sql +0 -2
  290. package/queue/postgres/drizzle/0002_dear_meggan.sql +0 -2
  291. package/queue/postgres/drizzle/0003_tricky_venom.sql +0 -30
  292. package/queue/postgres/drizzle/meta/0001_snapshot.json +0 -103
  293. package/queue/postgres/drizzle/meta/0002_snapshot.json +0 -90
  294. package/queue/postgres/drizzle/meta/0003_snapshot.json +0 -288
  295. package/queue/postgres/drizzle/meta/_journal.json +0 -34
  296. package/queue/postgres/index.d.ts +0 -4
  297. package/queue/postgres/index.js +0 -4
  298. package/queue/postgres/module.d.ts +0 -9
  299. package/queue/postgres/module.js +0 -29
  300. package/queue/postgres/queue.d.ts +0 -60
  301. package/queue/postgres/queue.js +0 -681
  302. package/queue/postgres/queue.provider.d.ts +0 -7
  303. package/queue/postgres/schemas.d.ts +0 -14
  304. package/queue/postgres/schemas.js +0 -6
  305. package/queue/postgres/task.model.d.ts +0 -24
  306. package/queue/postgres/task.model.js +0 -115
  307. package/queue/provider.js +0 -2
  308. package/queue/queue.js +0 -131
  309. package/queue/tests/queue.test.js +0 -623
  310. package/test3.d.ts +0 -1
  311. package/test3.js +0 -47
  312. /package/{queue/tests/queue.test.d.ts → api/server/tests/csrf.middleware.test.d.ts} +0 -0
  313. /package/circuit-breaker/postgres/drizzle/{0000_hard_shocker.sql → 0000_cooing_korath.sql} +0 -0
  314. /package/{queue → rate-limit}/postgres/drizzle.config.d.ts +0 -0
@@ -1,623 +0,0 @@
1
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
- import { configurePostgresCircuitBreaker, migratePostgresCircuitBreaker } from '../../circuit-breaker/postgres/module.js';
3
- import { Injector, runInInjectionContext } from '../../injector/index.js';
4
- import { ConsoleLogTransport, LogFormatter, Logger, LogTransport, PrettyPrintLogFormatter } from '../../logger/index.js';
5
- import { configureLocalMessageBus } from '../../message-bus/index.js';
6
- import { configureOrm } from '../../orm/server/index.js';
7
- import { QueueProvider, TaskState, UniqueTagStrategy } from '../../queue/index.js';
8
- import { configurePostgresQueue, migratePostgresQueueSchema } from '../../queue/postgres/index.js';
9
- import * as configParser from '../../utils/config-parser.js';
10
- import { currentTimestamp } from '../../utils/date-time.js';
11
- import { timeout } from '../../utils/timing.js';
12
- /**
13
- * Encapsulates the boilerplate for setting up the integration environment.
14
- */
15
- async function setupIntegrationTest() {
16
- const injector = new Injector('TestInjector');
17
- injector.register(LogFormatter, { useToken: PrettyPrintLogFormatter });
18
- injector.register(LogTransport, { useToken: ConsoleLogTransport });
19
- // 2. Configuration
20
- const dbConfig = {
21
- host: configParser.string('DATABASE_HOST', '127.0.0.1'),
22
- port: configParser.positiveInteger('DATABASE_PORT', 5432),
23
- user: configParser.string('DATABASE_USER', 'tstdl'),
24
- password: configParser.string('DATABASE_PASS', 'wf7rq6glrk5jykne'),
25
- database: configParser.string('DATABASE_NAME', 'tstdl'),
26
- };
27
- // 3. Configure Modules
28
- configureOrm({
29
- repositoryConfig: { schema: 'test' },
30
- connection: dbConfig,
31
- });
32
- configureLocalMessageBus();
33
- configurePostgresQueue();
34
- configurePostgresCircuitBreaker();
35
- // 4. Run Migrations
36
- // We need to resolve the module config once to ensure migrations run against the correct DB
37
- await runInInjectionContext(injector, migratePostgresQueueSchema);
38
- await runInInjectionContext(injector, migratePostgresCircuitBreaker);
39
- return injector;
40
- }
41
- describe('Queue Integration Tests', () => {
42
- let injector;
43
- let queue;
44
- let logger;
45
- const queueName = `test-queue-${Date.now()}`;
46
- // Helper to verify state in DB
47
- async function assertTaskState(id, state, message) {
48
- const task = await queue.get(id);
49
- expect(task?.status, message).toBe(state);
50
- }
51
- beforeAll(async () => {
52
- injector = await setupIntegrationTest();
53
- logger = injector.resolve(Logger);
54
- // Use the QueueProvider to Create/Retrieve the queue.
55
- // This is the cleanest way to get a configured queue instance without
56
- // messing with `injectArgument` manually in the test body.
57
- const queueProvider = injector.resolve(QueueProvider);
58
- queue = queueProvider.get(queueName, {
59
- processTimeout: 5000,
60
- retryDelayMinimum: 100, // Fast retries for testing
61
- retryDelayGrowth: 1, // Linear/No growth for predictable tests
62
- });
63
- logger.info(`Queue initialized: ${queueName}`);
64
- });
65
- afterEach(async () => {
66
- // Drain the queue to prevent state leakage between tests
67
- await queue.clear();
68
- });
69
- afterAll(async () => {
70
- try {
71
- await queue.clear();
72
- await injector.dispose();
73
- }
74
- catch (error) {
75
- // Ignore known double-dispose issue from MessageBus interaction
76
- if (error instanceof Error && error.message === 'MessageBus is disposed.') {
77
- return;
78
- }
79
- throw error;
80
- }
81
- });
82
- it('Basic FIFO Flow', async () => {
83
- logger.info('--- Test: Basic FIFO Flow ---');
84
- const t1 = await queue.enqueue({ value: 'first' });
85
- const t2 = await queue.enqueue({ value: 'second' });
86
- const d1 = await queue.dequeue();
87
- expect(d1?.id).toBe(t1.id);
88
- expect(d1?.data.value).toBe('first');
89
- await queue.acknowledge(d1, { success: true });
90
- await assertTaskState(t1.id, TaskState.Completed, 'Task 1 completed');
91
- const d2 = await queue.dequeue();
92
- expect(d2?.id).toBe(t2.id);
93
- await queue.acknowledge(d2, { success: true });
94
- });
95
- it('Priorities', async () => {
96
- logger.info('--- Test: Prioritization ---');
97
- // Priority 1000 (default)
98
- const low = await queue.enqueue({ value: 'low' });
99
- // Priority 1
100
- const high = await queue.enqueue({ value: 'high' }, { priority: 1 });
101
- const first = await queue.dequeue();
102
- expect(first?.id).toBe(high.id);
103
- await queue.acknowledge(first);
104
- const second = await queue.dequeue();
105
- expect(second?.id).toBe(low.id);
106
- await queue.acknowledge(second);
107
- });
108
- it('Deduplication (Unique Tags)', async () => {
109
- logger.info('--- Test: Deduplication (Unique Tags) ---');
110
- const tag = `unique-${Date.now()}`;
111
- // 1. Initial Insert
112
- const t1 = await queue.enqueue({ value: 'original' }, { tag, uniqueTag: UniqueTagStrategy.KeepOld });
113
- // 2. KeepOld Strategy: Should return existing task, ignore new data
114
- const t2 = await queue.enqueue({ value: 'ignored' }, { tag, uniqueTag: UniqueTagStrategy.KeepOld });
115
- expect(t1.id, 'KeepOld returned same ID').toBe(t2.id);
116
- const check1 = await queue.get(t1.id);
117
- expect(check1?.data.value).toBe('original');
118
- // 3. TakeNew Strategy: Should replace existing task with new data (new ID generated)
119
- const t3 = await queue.enqueue({ value: 'updated' }, { tag, uniqueTag: UniqueTagStrategy.TakeNew });
120
- expect(t1.id, 'TakeNew generated a NEW ID').not.toBe(t3.id);
121
- // Old ID should be gone (updated to new ID)
122
- const checkOld = await queue.get(t1.id);
123
- expect(checkOld).toBeUndefined();
124
- // New ID should have new data
125
- const checkNew = await queue.get(t3.id);
126
- expect(checkNew?.data.value).toBe('updated');
127
- expect(checkNew?.tries).toBe(0);
128
- });
129
- it('Retries and Failures', async () => {
130
- logger.info('--- Test: Retries and Dead Letter ---');
131
- const task = await queue.enqueue({ value: 'fail-me' });
132
- // Try 1
133
- const attempt1 = await queue.dequeue();
134
- expect(attempt1?.id).toBe(task.id);
135
- await queue.fail(attempt1, { message: 'oops' });
136
- // Force reschedule to now to bypass retryDelay
137
- await queue.reschedule(task.id, Date.now());
138
- // Try 2
139
- const attempt2 = await queue.dequeue();
140
- expect(attempt2?.id).toBe(task.id);
141
- expect(attempt2?.tries).toBe(2);
142
- // Fail fatally
143
- await queue.fail(attempt2, { message: 'fatal error' }, true);
144
- await assertTaskState(task.id, TaskState.Dead, 'Task is Dead after fatal error');
145
- });
146
- it('Hierarchy (Parent/Child)', async () => {
147
- logger.info('--- Test: Hierarchy (Parent/Child) ---');
148
- // A. Create Parent
149
- const p = await queue.enqueue({ value: 'parent-manual' });
150
- // B. Dequeue Parent
151
- const pTask = await queue.dequeue();
152
- expect(pTask?.id).toBe(p.id);
153
- // C. Parent spawns child
154
- const child = await queue.enqueue({ value: 'child-manual' }, { parentId: p.id });
155
- // D. "Finish" Parent execution. It should enter WAITING.
156
- await queue.acknowledge(pTask);
157
- await assertTaskState(p.id, TaskState.Waiting, 'Parent entered WAITING state');
158
- // E. Dequeue & Finish Child
159
- const cTask = await queue.dequeue();
160
- expect(cTask?.id).toBe(child.id);
161
- await queue.acknowledge(cTask);
162
- // F. Check Parent State -> PENDING (Fan-In triggered)
163
- await assertTaskState(p.id, TaskState.Pending, 'Parent rescheduled (PENDING)');
164
- // G. Dequeue Parent Again (Resume) & Finish
165
- const pTaskResumed = await queue.dequeue();
166
- expect(pTaskResumed?.id).toBe(p.id);
167
- await queue.acknowledge(pTaskResumed);
168
- await assertTaskState(p.id, TaskState.Completed, 'Parent COMPLETED');
169
- });
170
- it('Batching', async () => {
171
- logger.info('--- Test: Batch Operations ---');
172
- const batch = queue.batch();
173
- for (let i = 0; i < 5; i++) {
174
- batch.add({ value: `batch-${i}` });
175
- }
176
- const tasks = await batch.enqueue(true);
177
- expect(tasks.length).toBe(5);
178
- const dequeuedBatch = await queue.dequeueMany(5);
179
- expect(dequeuedBatch.length).toBe(5);
180
- await queue.acknowledgeMany(dequeuedBatch);
181
- const leftover = await queue.dequeue();
182
- expect(leftover).toBeUndefined();
183
- });
184
- it('Partial Success: Parent resumes after one child fails and another succeeds', async () => {
185
- logger.info('--- Test: Partial Success ---');
186
- // 1. Create Parent
187
- const p = await queue.enqueue({ value: 'parent' });
188
- // 2. Dequeue Parent
189
- const pTask = await queue.dequeue();
190
- expect(pTask?.id).toBe(p.id);
191
- // 3. Parent spawns two children
192
- const child1 = await queue.enqueue({ value: 'child-success' }, { parentId: p.id });
193
- const child2 = await queue.enqueue({ value: 'child-fail' }, { parentId: p.id });
194
- // 4. Acknowledge Parent -> Waiting
195
- await queue.acknowledge(pTask);
196
- await assertTaskState(p.id, TaskState.Waiting, 'Parent is WAITING');
197
- // 5. Dequeue & Succeed Child 1
198
- const c1Task = await queue.dequeue();
199
- expect(c1Task?.id).toBe(child1.id);
200
- await queue.acknowledge(c1Task);
201
- // Parent should still be waiting
202
- await assertTaskState(p.id, TaskState.Waiting, 'Parent still WAITING after first child');
203
- // 6. Dequeue & Fail Child 2 (Fatal)
204
- const c2Task = await queue.dequeue();
205
- expect(c2Task?.id).toBe(child2.id);
206
- await queue.fail(c2Task, { message: 'intentional failure' }, true);
207
- // 7. Check Parent State -> PENDING (Woken up despite child failure)
208
- await assertTaskState(p.id, TaskState.Pending, 'Parent woken up (PENDING) despite child failure');
209
- // 8. Dequeue Parent Again & Complete
210
- const pTaskResumed = await queue.dequeue();
211
- expect(pTaskResumed?.id).toBe(p.id);
212
- await queue.acknowledge(pTaskResumed);
213
- await assertTaskState(p.id, TaskState.Completed, 'Parent COMPLETED successfully');
214
- });
215
- it('Acknowledge with pre-existing failed child: Parent completes', async () => {
216
- logger.info('--- Test: Acknowledge with pre-existing failed child ---');
217
- // 1. Create Parent
218
- const p = await queue.enqueue({ value: 'parent-2' });
219
- // 2. Dequeue Parent
220
- const pTask = await queue.dequeue();
221
- // 3. Parent spawns a child that fails
222
- const child = await queue.enqueue({ value: 'child-fail-early' }, { parentId: p.id });
223
- const cTask = await queue.dequeue();
224
- await queue.fail(cTask, { message: 'fail early' }, true);
225
- await assertTaskState(child.id, TaskState.Dead, 'Child is DEAD');
226
- // 4. Parent acknowledges its own execution.
227
- // It should COMPLETE now, even though child is dead, because there are no ACTIVE children.
228
- await queue.acknowledge(pTask);
229
- await assertTaskState(p.id, TaskState.Completed, 'Parent COMPLETED even with dead child');
230
- });
231
- it('Fan-In & Result Persistence', async () => {
232
- logger.info('--- Test: Fan-In & Result Persistence ---');
233
- // 1. Create Parent
234
- const p = await queue.enqueue({ value: 'parent' });
235
- // 2. Dequeue Parent
236
- const pTask = await queue.dequeue();
237
- expect(pTask?.id).toBe(p.id);
238
- // 3. Parent spawns children
239
- await queue.enqueueMany([
240
- { data: { value: 'child-1' }, parentId: p.id },
241
- { data: { value: 'child-2' }, parentId: p.id },
242
- ]);
243
- // 4. Acknowledge Parent with a result. This result should be saved as 'state' since we move to Waiting.
244
- const partialResult = { step: 'children-spawned' };
245
- await queue.acknowledge(pTask, partialResult);
246
- // 5. Verify Parent is WAITING and has the state
247
- const pWaiting = await queue.get(p.id);
248
- expect(pWaiting?.status).toBe(TaskState.Waiting);
249
- expect(pWaiting?.state).toEqual(partialResult);
250
- // 6. Complete children
251
- const c1 = await queue.dequeue();
252
- const c2 = await queue.dequeue();
253
- await queue.acknowledge(c1);
254
- await queue.acknowledge(c2);
255
- // 7. Verify Parent is PENDING
256
- await assertTaskState(p.id, TaskState.Pending, 'Parent woken up');
257
- // 8. Dequeue Parent again and check its state
258
- const pResumed = await queue.dequeue();
259
- expect(pResumed?.id).toBe(p.id);
260
- expect(pResumed?.state).toEqual(partialResult);
261
- await queue.acknowledge(pResumed);
262
- await assertTaskState(p.id, TaskState.Completed, 'Parent COMPLETED');
263
- });
264
- it('Circuit Breaker Probe Logic', async () => {
265
- logger.info('--- Test: Circuit Breaker Probe Logic ---');
266
- const cbQueueName = `cb-test-${Date.now()}`;
267
- const queueProvider = injector.resolve(QueueProvider);
268
- const cbQueue = queueProvider.get(cbQueueName, {
269
- circuitBreakerThreshold: 2,
270
- circuitBreakerResetTimeout: 500,
271
- });
272
- // 1. Trip the breaker (Threshold = 2)
273
- await cbQueue.enqueueMany([
274
- { data: { value: 'fail-1' } },
275
- { data: { value: 'fail-2' } },
276
- ]);
277
- const fail1 = await cbQueue.dequeue();
278
- const fail2 = await cbQueue.dequeue();
279
- await cbQueue.fail(fail1, 'fail');
280
- await cbQueue.fail(fail2, 'fail');
281
- // 2. Enqueue more tasks
282
- await cbQueue.enqueueMany([
283
- { data: { value: 't1' } },
284
- { data: { value: 't2' } },
285
- ]);
286
- // 3. Dequeue should return nothing while Open
287
- const tasksOpen = await cbQueue.dequeueMany(5);
288
- expect(tasksOpen.length).toBe(0);
289
- // 4. Wait for reset timeout
290
- await timeout(600);
291
- // 5. First dequeue after timeout should be a PROBE (only 1 task)
292
- const tasksProbe = await cbQueue.dequeueMany(5);
293
- expect(tasksProbe.length).toBe(1);
294
- expect(tasksProbe[0]?.data.value).toBe('t1');
295
- // 6. While Half-Open (waiting for probe result), subsequent dequeues should return nothing
296
- const tasksHalfOpen = await cbQueue.dequeueMany(5);
297
- expect(tasksHalfOpen.length).toBe(0);
298
- // 7. Acknowledge probe -> Breaker closes
299
- await cbQueue.acknowledge(tasksProbe[0]);
300
- // 8. Now closed, we should get the rest
301
- const remaining = await cbQueue.dequeueMany(5);
302
- expect(remaining.length).toBe(1);
303
- expect(remaining[0]?.data.value).toBe('t2');
304
- await cbQueue.clear();
305
- });
306
- it('Bulk failMany & Circuit Breaker', async () => {
307
- logger.info('--- Test: Bulk failMany & Circuit Breaker ---');
308
- const cbQueueName = `bulk-fail-test-${Date.now()}`;
309
- const queueProvider = injector.resolve(QueueProvider);
310
- const cbQueue = queueProvider.get(cbQueueName, {
311
- circuitBreakerThreshold: 5,
312
- circuitBreakerResetTimeout: 5000,
313
- maxTries: 1, // Fail immediately
314
- });
315
- // 1. Enqueue 5 tasks (Threshold)
316
- const tasks = await cbQueue.enqueueMany([
317
- { data: { value: 'f1' } },
318
- { data: { value: 'f2' } },
319
- { data: { value: 'f3' } },
320
- { data: { value: 'f4' } },
321
- { data: { value: 'f5' } },
322
- ], { returnTasks: true });
323
- // 2. Dequeue all
324
- const dequeued = await cbQueue.dequeueMany(5);
325
- expect(dequeued.length).toBe(5);
326
- // 3. Fail all
327
- await cbQueue.failMany(dequeued, ['e1', 'e2', 'e3', 'e4', 'e5']);
328
- // 4. Verify all are Dead
329
- for (const t of tasks) {
330
- const stored = await cbQueue.get(t.id);
331
- expect(stored?.status, `Task ${t.id} is dead`).toBe(TaskState.Dead);
332
- expect(stored?.error?.message).toMatch(/e\d/);
333
- }
334
- // 5. Verify Circuit Breaker is OPEN
335
- // Enqueue a new task
336
- await cbQueue.enqueue({ value: 'probe' });
337
- // Try to dequeue - should fail because CB is open (Threshold 5 reached by batch of 5)
338
- const probe = await cbQueue.dequeue();
339
- expect(probe).toBeUndefined();
340
- await cbQueue.clear();
341
- });
342
- it('Bulk failMany with Fan-In', async () => {
343
- logger.info('--- Test: Bulk failMany with Fan-In ---');
344
- // 1. Create Parent
345
- const p = await queue.enqueue({ value: 'parent-bulk' });
346
- const pTask = await queue.dequeue();
347
- // 2. Create Children
348
- const children = await queue.enqueueMany([
349
- { data: { value: 'c1' }, parentId: p.id },
350
- { data: { value: 'c2' }, parentId: p.id },
351
- { data: { value: 'c3' }, parentId: p.id },
352
- ], { returnTasks: true });
353
- await queue.acknowledge(pTask); // Parent -> Waiting
354
- // 3. Dequeue Children
355
- const dequeuedChildren = await queue.dequeueMany(3);
356
- expect(dequeuedChildren.length).toBe(3);
357
- // 4. Fail all children (Fatal)
358
- const fiQueueName = `fanin-test-${Date.now()}`;
359
- const queueProvider = injector.resolve(QueueProvider);
360
- const fiQueue = queueProvider.get(fiQueueName, {
361
- maxTries: 1,
362
- });
363
- const p2 = await fiQueue.enqueue({ value: 'parent-fi' });
364
- const p2Task = await fiQueue.dequeue();
365
- const children2 = await fiQueue.enqueueMany([
366
- { data: { value: 'c1' }, parentId: p2.id },
367
- { data: { value: 'c2' }, parentId: p2.id },
368
- ], { returnTasks: true });
369
- await fiQueue.acknowledge(p2Task); // Waiting
370
- const dequeued2 = await fiQueue.dequeueMany(2);
371
- // Fail many
372
- await fiQueue.failMany(dequeued2, ['error', 'error']);
373
- // Check children are dead
374
- const c1State = await fiQueue.get(children2[0].id);
375
- expect(c1State?.status).toBe(TaskState.Dead);
376
- // Check Parent Fan-In -> Should be Pending
377
- const p2State = await fiQueue.get(p2.id);
378
- expect(p2State?.status).toBe(TaskState.Pending);
379
- await fiQueue.clear();
380
- });
381
- });
382
- describe('PostgresQueue (Distributed Task Orchestration)', () => {
383
- let injector;
384
- let queue;
385
- beforeAll(async () => {
386
- injector = await setupIntegrationTest();
387
- });
388
- beforeEach(() => {
389
- const queueProvider = injector.resolve(QueueProvider);
390
- const queueName = `pg-test-queue-${Date.now()}-${Math.random()}`;
391
- queue = queueProvider.get(queueName, {
392
- processTimeout: 200, // Short timeout for testing
393
- retryDelayMinimum: 50,
394
- retryDelayGrowth: 1,
395
- circuitBreakerThreshold: 2,
396
- circuitBreakerResetTimeout: 200,
397
- });
398
- });
399
- afterEach(async () => {
400
- await queue.clear();
401
- });
402
- afterAll(async () => {
403
- await injector?.dispose();
404
- });
405
- describe('Basic Lifecycle', () => {
406
- it('should enqueue and dequeue a task', async () => {
407
- await queue.enqueue({ foo: 'bar' });
408
- const task = await queue.dequeue();
409
- expect(task).toBeDefined();
410
- expect(task?.data).toEqual({ foo: 'bar' });
411
- expect(task?.status).toBe(TaskState.Running);
412
- expect(task?.tries).toBe(1);
413
- });
414
- it('should acknowledge a task successfully', async () => {
415
- const task = await queue.enqueue({ foo: 'bar' });
416
- const dequeued = await queue.dequeue();
417
- await queue.acknowledge(dequeued, { result: true });
418
- const updated = await queue.get(task.id);
419
- expect(updated?.status).toBe(TaskState.Completed);
420
- expect(updated?.result).toEqual({ result: true });
421
- expect(updated?.completeTimestamp).toBeGreaterThan(0);
422
- });
423
- it('should fail a task and increment tries', async () => {
424
- const task = await queue.enqueue({ foo: 'bar' });
425
- const dequeued = await queue.dequeue();
426
- await queue.fail(dequeued, new Error('temp failure'));
427
- const updated = await queue.get(task.id);
428
- expect(updated?.status).toBe(TaskState.Pending);
429
- expect(updated?.tries).toBe(1);
430
- expect(updated?.error).toBeDefined();
431
- });
432
- });
433
- describe('Deduplication (Unique Tags)', () => {
434
- it('should keep the old task when using UniqueTagStrategy.KeepOld', async () => {
435
- const tag = 'unique-1';
436
- await queue.enqueue({ foo: 'first' }, { tag, uniqueTag: UniqueTagStrategy.KeepOld });
437
- await queue.enqueue({ foo: 'second' }, { tag, uniqueTag: UniqueTagStrategy.KeepOld });
438
- const count = await queue.countByTag(tag);
439
- const task = (await queue.getByTag(tag))[0];
440
- expect(count).toBe(1);
441
- expect(task?.data).toEqual({ foo: 'first' });
442
- });
443
- it('should overwrite with new data when using UniqueTagStrategy.TakeNew', async () => {
444
- const tag = 'unique-2';
445
- await queue.enqueue({ foo: 'first' }, { tag, uniqueTag: UniqueTagStrategy.TakeNew });
446
- await queue.enqueue({ foo: 'second' }, { tag, uniqueTag: UniqueTagStrategy.TakeNew });
447
- const count = await queue.countByTag(tag);
448
- const task = (await queue.getByTag(tag))[0];
449
- expect(count).toBe(1);
450
- expect(task?.data).toEqual({ foo: 'second' });
451
- expect(task?.tries).toBe(0); // Should reset
452
- });
453
- });
454
- describe('Priority and Scheduling', () => {
455
- it('should dequeue tasks in priority order (lower number first)', async () => {
456
- await queue.enqueue({ foo: 'low' }, { priority: 2000 });
457
- await queue.enqueue({ foo: 'high' }, { priority: 10 });
458
- await queue.enqueue({ foo: 'mid' }, { priority: 1000 });
459
- const t1 = await queue.dequeue();
460
- const t2 = await queue.dequeue();
461
- const t3 = await queue.dequeue();
462
- expect(t1?.data.foo).toBe('high');
463
- expect(t2?.data.foo).toBe('mid');
464
- expect(t3?.data.foo).toBe('low');
465
- });
466
- it('should not dequeue a task scheduled in the future', async () => {
467
- const future = currentTimestamp() + 500;
468
- await queue.enqueue({ foo: 'future' }, { scheduleTimestamp: future });
469
- const task = await queue.dequeue();
470
- expect(task).toBeUndefined();
471
- await timeout(600);
472
- const taskLater = await queue.dequeue();
473
- expect(taskLater).toBeDefined();
474
- });
475
- });
476
- describe('Concurrency Control', () => {
477
- it('should respect global concurrency limits', async () => {
478
- const queueProvider = injector.resolve(QueueProvider);
479
- const limitedQueue = queueProvider.get(`limit-test-${Date.now()}`, { globalConcurrency: 2 });
480
- await limitedQueue.enqueueMany([{ data: { foo: '1' } }, { data: { foo: '2' } }, { data: { foo: '3' } }]);
481
- const t1 = await limitedQueue.dequeue();
482
- const t2 = await limitedQueue.dequeue();
483
- const t3 = await limitedQueue.dequeue();
484
- expect(t1).toBeDefined();
485
- expect(t2).toBeDefined();
486
- expect(t3).toBeUndefined(); // Limit reached
487
- await limitedQueue.acknowledge(t1);
488
- const t3Retry = await limitedQueue.dequeue();
489
- expect(t3Retry).toBeDefined(); // Slot opened
490
- await limitedQueue.clear();
491
- });
492
- });
493
- describe('Circuit Breaker', () => {
494
- it('should trip the breaker after threshold failures', async () => {
495
- // Config: circuitBreakerThreshold: 2 (set in beforeEach)
496
- await queue.enqueue({ foo: '1' });
497
- await queue.enqueue({ foo: '2' });
498
- await queue.enqueue({ foo: '3' });
499
- await queue.fail((await queue.dequeue()), 'err');
500
- await queue.fail((await queue.dequeue()), 'err');
501
- // Breaker should be Open
502
- const t3Attempt = await queue.dequeue();
503
- expect(t3Attempt).toBeUndefined();
504
- });
505
- it('should allow a single probe in Half-Open state', async () => {
506
- await queue.enqueueMany([{ data: { foo: '1' } }, { data: { foo: '2' } }]);
507
- await queue.fail((await queue.dequeue()), 'err');
508
- await queue.fail((await queue.dequeue()), 'err');
509
- // Breaker is Open. Wait for reset timeout (200ms)
510
- await timeout(250);
511
- const probe = await queue.dequeue();
512
- expect(probe).toBeDefined();
513
- const secondAttempt = await queue.dequeue();
514
- expect(secondAttempt).toBeUndefined(); // Only 1 probe allowed in Half-Open
515
- });
516
- });
517
- describe('Task Hierarchy (Orchestration)', () => {
518
- it('should move parent to Waiting if children are spawned', async () => {
519
- const parent = await queue.enqueue({ foo: 'parent' });
520
- const pDequeued = await queue.dequeue();
521
- // Spawn child
522
- await queue.enqueue({ foo: 'child' }, { parentId: pDequeued.id });
523
- await queue.acknowledge(pDequeued);
524
- const pStatus = await queue.get(parent.id);
525
- expect(pStatus?.status).toBe(TaskState.Waiting);
526
- });
527
- it('should wake up parent when all children are completed', async () => {
528
- const parent = await queue.enqueue({ foo: 'parent' });
529
- const child = await queue.enqueue({ foo: 'child' }, { parentId: parent.id });
530
- // Parent is in Waiting (simulated by manual acknowledge)
531
- const pDequeued = await queue.dequeue();
532
- await queue.acknowledge(pDequeued);
533
- // Complete child
534
- const cDequeued = await queue.dequeue();
535
- await queue.acknowledge(cDequeued);
536
- const pStatus = await queue.get(parent.id);
537
- expect(pStatus?.status).toBe(TaskState.Pending); // Woken up
538
- });
539
- });
540
- describe('Timeouts and Maintenance (Pruning)', () => {
541
- it('should recover "Zombie" tasks (crashed workers)', async () => {
542
- const task = await queue.enqueue({ foo: 'zombie' });
543
- await queue.dequeue(); // Task is now Running with a lease
544
- // processTimeout is 200ms. Wait for it to expire.
545
- await timeout(300);
546
- const recovered = await queue.dequeue();
547
- expect(recovered?.id).toBe(task.id);
548
- expect(recovered?.tries).toBe(2);
549
- });
550
- it('should fail tasks that exceed Hard Execution Timeout via prune', async () => {
551
- // Re-configure queue with very short execution timeout
552
- const queueProvider = injector.resolve(QueueProvider);
553
- const shortQueue = queueProvider.get(`prune-test-${Date.now()}`, { executionTimeout: 100 });
554
- const task = await shortQueue.enqueue({ foo: 'long-running' });
555
- await shortQueue.dequeue();
556
- await timeout(200);
557
- await shortQueue.prune();
558
- const updated = await shortQueue.get(task.id);
559
- expect(updated?.status).toBe(TaskState.Dead);
560
- expect(updated?.error?.message).toContain('Hard Execution Timeout');
561
- await shortQueue.clear();
562
- });
563
- it('should touch a task to extend lease', async () => {
564
- const task = await queue.enqueue({ foo: 'work' });
565
- const dequeued = await queue.dequeue();
566
- const initialLock = dequeued.lockExpirationTimestamp;
567
- await timeout(50);
568
- const touched = await queue.touch(dequeued);
569
- expect(touched?.lockExpirationTimestamp).toBeGreaterThan(initialLock);
570
- });
571
- it('should prevent touching if lease is lost (stolen by another worker)', async () => {
572
- await queue.enqueue({ foo: 'work' });
573
- const dequeued = await queue.dequeue();
574
- expect(dequeued).toBeDefined();
575
- // processTimeout is 200ms. Wait for it to expire.
576
- await timeout(300);
577
- await queue.dequeue(); // Stolen by another worker (tries=2)
578
- // Original worker tries to touch
579
- const touchResult = await queue.touch(dequeued);
580
- expect(touchResult).toBeUndefined();
581
- });
582
- });
583
- describe('Batch Operations', () => {
584
- it('should acknowledge many tasks efficiently', async () => {
585
- const tasks = await queue.enqueueMany([
586
- { data: { foo: '1' } },
587
- { data: { foo: '2' } },
588
- ], { returnTasks: true });
589
- const d1 = await queue.dequeue();
590
- const d2 = await queue.dequeue();
591
- await queue.acknowledgeMany([d1, d2]);
592
- const t1 = await queue.get(tasks[0].id);
593
- const t2 = await queue.get(tasks[1].id);
594
- expect(t1?.status).toBe(TaskState.Completed);
595
- expect(t2?.status).toBe(TaskState.Completed);
596
- });
597
- });
598
- describe('Rescheduling', () => {
599
- it('should reschedule and refund tries if running', async () => {
600
- const task = await queue.enqueue({ foo: 'reschedule-me' });
601
- const dequeued = await queue.dequeue();
602
- expect(dequeued?.tries).toBe(1);
603
- const inFuture = currentTimestamp() + 1000;
604
- await queue.reschedule(dequeued.id, inFuture);
605
- const updated = await queue.get(task.id);
606
- expect(updated?.status).toBe(TaskState.Pending);
607
- expect(updated?.tries).toBe(0); // Refunded
608
- expect(updated?.scheduleTimestamp).toBe(inFuture);
609
- });
610
- });
611
- describe('TaskContext (Worker DX)', () => {
612
- it('checkpoint() should update progress and handle lease loss', async () => {
613
- const task = await queue.enqueue({ foo: 'progress' });
614
- const dequeued = await queue.dequeue();
615
- // In real scenarios TaskContext wraps the queue logic.
616
- // Here we just verify touch/checkpoint effects on the DB.
617
- await queue.touch(dequeued, { progress: 0.5, state: { step: 1 } });
618
- const updated = await queue.get(task.id);
619
- expect(updated?.progress).toBe(0.5);
620
- expect(updated?.state).toEqual({ step: 1 });
621
- });
622
- });
623
- });
package/test3.d.ts DELETED
@@ -1 +0,0 @@
1
- import './polyfills.js';
package/test3.js DELETED
@@ -1,47 +0,0 @@
1
- import './polyfills.js';
2
- import { Application } from './application/application.js';
3
- import { provideInitializer, provideModule, provideSignalHandler } from './application/providers.js';
4
- import { inject } from './injector/inject.js';
5
- import { configureLocalMessageBus } from './message-bus/index.js';
6
- import { configureOrm } from './orm/server/index.js';
7
- import { configurePostgresQueue, migratePostgresQueueSchema } from './queue/postgres/module.js';
8
- import { Queue, UniqueTagStrategy } from './queue/queue.js';
9
- import * as configParser from './utils/config-parser.js';
10
- if (1 + 1 == 2)
11
- process.exit();
12
- const config = {
13
- database: {
14
- host: configParser.string('DATABASE_HOST', '127.0.0.1'),
15
- port: configParser.positiveInteger('DATABASE_PORT', 5432),
16
- user: configParser.string('DATABASE_USER', 'vitrass'),
17
- pass: configParser.string('DATABASE_PASS', '6zv7edvqv9vvzz6u4kuk'),
18
- database: configParser.string('DATABASE_NAME', 'vitrass'),
19
- schema: configParser.string('DATABASE_SCHEMA', 'vitrass')
20
- }
21
- };
22
- async function bootstrap() {
23
- configureLocalMessageBus();
24
- configureOrm({
25
- connection: {
26
- host: config.database.host,
27
- port: config.database.port,
28
- user: config.database.user,
29
- password: config.database.pass,
30
- database: config.database.database
31
- },
32
- repositoryConfig: { schema: 'vitrass' }
33
- });
34
- configurePostgresQueue({});
35
- await migratePostgresQueueSchema();
36
- }
37
- async function main(_cancellationSignal) {
38
- const queue = inject((Queue), 'echos');
39
- await queue.enqueue({ name: 'Max 1' }, { tag: 'foo', uniqueTag: UniqueTagStrategy.TakeNew });
40
- await queue.enqueue({ name: 'Max 2' }, { tag: 'foo', uniqueTag: UniqueTagStrategy.TakeNew });
41
- await queue.enqueue({ name: 'Max 3' }, { tag: 'foo', uniqueTag: UniqueTagStrategy.TakeNew });
42
- }
43
- Application.run('Test', [
44
- provideInitializer(bootstrap),
45
- provideModule(main),
46
- provideSignalHandler(),
47
- ]);