bunsane 0.3.2 → 0.5.0

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 (220) hide show
  1. package/CHANGELOG.md +471 -370
  2. package/core/BatchLoader.ts +56 -32
  3. package/core/Entity.ts +93 -1020
  4. package/core/EntityHookManager.ts +52 -754
  5. package/core/Logger.ts +10 -0
  6. package/core/RequestContext.ts +94 -85
  7. package/core/RequestLoaders.ts +98 -5
  8. package/core/SchedulerManager.ts +28 -600
  9. package/core/app/cors.ts +2 -11
  10. package/core/app/preparedStatementWarmup.ts +9 -49
  11. package/core/app/requestRouter.ts +9 -8
  12. package/core/app/restRegistry.ts +8 -0
  13. package/core/archetype/fieldResolvers.ts +85 -40
  14. package/core/archetype/relationLoader.ts +135 -92
  15. package/core/cache/CacheManager.ts +91 -302
  16. package/core/cache/CompressionUtils.ts +34 -3
  17. package/core/cache/MemoryCache.ts +40 -37
  18. package/core/cache/RedisCache.ts +8 -7
  19. package/core/cache/health.ts +30 -0
  20. package/core/cache/invalidation.ts +96 -0
  21. package/core/cache/strategies/writeInvalidate.ts +111 -0
  22. package/core/cache/strategies/writeThrough.ts +233 -0
  23. package/core/components/BaseComponent.ts +25 -10
  24. package/core/components/ComponentRegistry.ts +28 -0
  25. package/core/decorators/IndexedField.ts +1 -1
  26. package/core/entity/cacheStrategies.ts +97 -0
  27. package/core/entity/componentAccess.ts +383 -0
  28. package/core/entity/finders.ts +202 -0
  29. package/core/entity/getCacheManager.ts +10 -0
  30. package/core/entity/pendingOps.ts +72 -0
  31. package/core/entity/saveEntity.ts +375 -0
  32. package/core/health.ts +93 -4
  33. package/core/hooks/dispatcher.ts +439 -0
  34. package/core/hooks/guards.ts +155 -0
  35. package/core/hooks/registry.ts +247 -0
  36. package/core/metadata/definitions/Component.ts +1 -1
  37. package/core/metadata/index.ts +15 -4
  38. package/core/middleware/RateLimit.ts +102 -105
  39. package/core/middleware/RequestId.ts +2 -9
  40. package/core/middleware/SecurityHeaders.ts +2 -11
  41. package/core/middleware/headers.ts +28 -0
  42. package/core/remote/OutboxWorker.ts +213 -183
  43. package/core/remote/RemoteManager.ts +401 -400
  44. package/core/remote/StreamConsumer.ts +535 -535
  45. package/core/remote/types.ts +153 -151
  46. package/core/requestScope.ts +34 -0
  47. package/core/scheduler/cronEvaluator.ts +174 -0
  48. package/core/scheduler/lifecycleHooks.ts +21 -0
  49. package/core/scheduler/lockCoordinator.ts +27 -0
  50. package/core/scheduler/metrics.ts +14 -0
  51. package/core/scheduler/taskRunner.ts +420 -0
  52. package/core/validateEnv.ts +10 -0
  53. package/database/DatabaseHelper.ts +128 -101
  54. package/database/IndexingStrategy.ts +72 -2
  55. package/database/PreparedStatementCache.ts +8 -2
  56. package/database/cancellable.ts +35 -22
  57. package/database/index.ts +29 -3
  58. package/database/instrumentedDb.ts +141 -141
  59. package/database/sqlHelpers.ts +3 -1
  60. package/endpoints/archetypes.ts +2 -8
  61. package/endpoints/tables.ts +6 -1
  62. package/gql/index.ts +1 -1
  63. package/gql/schema/index.ts +15 -4
  64. package/gql/visitors/ResolverGeneratorVisitor.ts +25 -4
  65. package/package.json +22 -1
  66. package/query/CTENode.ts +5 -3
  67. package/query/ComponentInclusionNode.ts +245 -14
  68. package/query/OrNode.ts +8 -19
  69. package/query/Query.ts +208 -79
  70. package/query/QueryContext.ts +6 -0
  71. package/query/QueryDAG.ts +7 -2
  72. package/query/membershipSource.ts +66 -0
  73. package/storage/LocalStorageProvider.ts +8 -3
  74. package/studio/dist/assets/index-BMZ67Npg.js +254 -0
  75. package/studio/dist/assets/index-BpbuYz9g.css +1 -0
  76. package/studio/{index.html → dist/index.html} +3 -2
  77. package/swagger/generator.ts +11 -1
  78. package/upload/UploadManager.ts +8 -6
  79. package/utils/uuid.ts +40 -10
  80. package/.claude/scheduled_tasks.lock +0 -1
  81. package/.claude/settings.local.json +0 -47
  82. package/.prettierrc +0 -4
  83. package/.serena/memories/architectural-decision-no-dependency-injection.md +0 -76
  84. package/.serena/memories/architecture.md +0 -154
  85. package/.serena/memories/cache-interface-refactoring-2026-01-24.md +0 -165
  86. package/.serena/memories/code_style_and_conventions.md +0 -76
  87. package/.serena/memories/project_overview.md +0 -43
  88. package/.serena/memories/schema-dsl-plan.md +0 -107
  89. package/.serena/memories/suggested_commands.md +0 -80
  90. package/.serena/memories/typescript-compilation-status.md +0 -54
  91. package/.serena/project.yml +0 -114
  92. package/BunSane.jpg +0 -0
  93. package/CLAUDE.md +0 -198
  94. package/TODO.md +0 -2
  95. package/bun.lock +0 -302
  96. package/bunfig.toml +0 -10
  97. package/docs/RFC_APP_REFACTOR.md +0 -248
  98. package/docs/RFC_REFACTOR_TARGETS.md +0 -251
  99. package/docs/SCALABILITY_PLAN.md +0 -175
  100. package/studio/bun.lock +0 -482
  101. package/studio/package.json +0 -39
  102. package/studio/postcss.config.js +0 -6
  103. package/studio/src/components/DataTable.tsx +0 -211
  104. package/studio/src/components/Layout.tsx +0 -13
  105. package/studio/src/components/PageContainer.tsx +0 -9
  106. package/studio/src/components/PageHeader.tsx +0 -13
  107. package/studio/src/components/SearchBar.tsx +0 -57
  108. package/studio/src/components/Sidebar.tsx +0 -294
  109. package/studio/src/components/ui/button.tsx +0 -56
  110. package/studio/src/components/ui/checkbox.tsx +0 -26
  111. package/studio/src/components/ui/input.tsx +0 -25
  112. package/studio/src/hooks/useDataTable.ts +0 -131
  113. package/studio/src/index.css +0 -36
  114. package/studio/src/lib/api.ts +0 -186
  115. package/studio/src/lib/utils.ts +0 -13
  116. package/studio/src/main.tsx +0 -17
  117. package/studio/src/pages/ArcheType.tsx +0 -239
  118. package/studio/src/pages/Components.tsx +0 -124
  119. package/studio/src/pages/EntityInspector.tsx +0 -302
  120. package/studio/src/pages/QueryRunner.tsx +0 -246
  121. package/studio/src/pages/Table.tsx +0 -94
  122. package/studio/src/pages/Welcome.tsx +0 -241
  123. package/studio/src/routes.tsx +0 -45
  124. package/studio/src/store/archeTypeSettings.ts +0 -30
  125. package/studio/src/store/studio.ts +0 -65
  126. package/studio/src/utils/columnHelpers.tsx +0 -114
  127. package/studio/studio-instructions.md +0 -81
  128. package/studio/tailwind.config.js +0 -77
  129. package/studio/utils.ts +0 -54
  130. package/studio/vite.config.js +0 -19
  131. package/tests/benchmark/BENCHMARK_DATABASES_PLAN.md +0 -338
  132. package/tests/benchmark/bunfig.toml +0 -9
  133. package/tests/benchmark/fixtures/EcommerceComponents.ts +0 -283
  134. package/tests/benchmark/fixtures/EcommerceDataGenerators.ts +0 -301
  135. package/tests/benchmark/fixtures/RelationTracker.ts +0 -159
  136. package/tests/benchmark/fixtures/index.ts +0 -6
  137. package/tests/benchmark/index.ts +0 -22
  138. package/tests/benchmark/noop-preload.ts +0 -3
  139. package/tests/benchmark/query-lateral-benchmark.test.ts +0 -372
  140. package/tests/benchmark/runners/BenchmarkLoader.ts +0 -132
  141. package/tests/benchmark/runners/index.ts +0 -4
  142. package/tests/benchmark/scenarios/query-benchmarks.test.ts +0 -465
  143. package/tests/benchmark/scripts/generate-db.ts +0 -344
  144. package/tests/benchmark/scripts/run-benchmarks.ts +0 -97
  145. package/tests/e2e/http.test.ts +0 -130
  146. package/tests/fixtures/archetypes/TestUserArchetype.ts +0 -21
  147. package/tests/fixtures/components/TestOrder.ts +0 -23
  148. package/tests/fixtures/components/TestProduct.ts +0 -23
  149. package/tests/fixtures/components/TestUser.ts +0 -20
  150. package/tests/fixtures/components/index.ts +0 -6
  151. package/tests/graphql/SchemaGeneration.test.ts +0 -90
  152. package/tests/graphql/builders/ResolverBuilder.test.ts +0 -223
  153. package/tests/graphql/builders/TypeDefBuilder.test.ts +0 -153
  154. package/tests/helpers/MockRedisClient.ts +0 -113
  155. package/tests/helpers/MockRedisStreamServer.ts +0 -448
  156. package/tests/integration/archetype/ArcheType.persistence.test.ts +0 -241
  157. package/tests/integration/cache/CacheInvalidation.test.ts +0 -259
  158. package/tests/integration/entity/Entity.persistence.test.ts +0 -333
  159. package/tests/integration/entity/Entity.saveTimeout.test.ts +0 -110
  160. package/tests/integration/loaders/RequestLoaders.abort.test.ts +0 -82
  161. package/tests/integration/query/Query.abort.test.ts +0 -66
  162. package/tests/integration/query/Query.complexAnalysis.test.ts +0 -557
  163. package/tests/integration/query/Query.edgeCases.test.ts +0 -595
  164. package/tests/integration/query/Query.exec.test.ts +0 -576
  165. package/tests/integration/query/Query.explainAnalyze.test.ts +0 -233
  166. package/tests/integration/query/Query.jsonbArray.test.ts +0 -214
  167. package/tests/integration/remote/dlq.test.ts +0 -175
  168. package/tests/integration/remote/event-dispatch.test.ts +0 -114
  169. package/tests/integration/remote/outbox.test.ts +0 -130
  170. package/tests/integration/remote/rpc.test.ts +0 -177
  171. package/tests/pglite-setup.ts +0 -62
  172. package/tests/setup.ts +0 -164
  173. package/tests/stress/BenchmarkRunner.ts +0 -203
  174. package/tests/stress/DataSeeder.ts +0 -190
  175. package/tests/stress/StressTestReporter.ts +0 -229
  176. package/tests/stress/cursor-perf-test.ts +0 -171
  177. package/tests/stress/fixtures/RealisticComponents.ts +0 -235
  178. package/tests/stress/fixtures/StressTestComponents.ts +0 -58
  179. package/tests/stress/index.ts +0 -7
  180. package/tests/stress/scenarios/query-benchmarks.test.ts +0 -285
  181. package/tests/stress/scenarios/realistic-scenarios.test.ts +0 -1081
  182. package/tests/stress/scenarios/timeout-investigation.test.ts +0 -522
  183. package/tests/unit/BatchLoader.test.ts +0 -196
  184. package/tests/unit/archetype/ArcheType.test.ts +0 -107
  185. package/tests/unit/cache/CacheManager.test.ts +0 -498
  186. package/tests/unit/cache/MemoryCache.test.ts +0 -260
  187. package/tests/unit/cache/RedisCache.test.ts +0 -411
  188. package/tests/unit/database/cancellable.test.ts +0 -81
  189. package/tests/unit/database/instrumentedDb.test.ts +0 -160
  190. package/tests/unit/entity/Entity.components.test.ts +0 -317
  191. package/tests/unit/entity/Entity.drainSideEffects.test.ts +0 -51
  192. package/tests/unit/entity/Entity.reload.test.ts +0 -63
  193. package/tests/unit/entity/Entity.requireComponents.test.ts +0 -72
  194. package/tests/unit/entity/Entity.test.ts +0 -345
  195. package/tests/unit/gql/depthLimit.test.ts +0 -203
  196. package/tests/unit/gql/operationMiddleware.test.ts +0 -293
  197. package/tests/unit/health/Health.test.ts +0 -129
  198. package/tests/unit/middleware/AccessLog.test.ts +0 -37
  199. package/tests/unit/middleware/Middleware.test.ts +0 -98
  200. package/tests/unit/middleware/RequestId.test.ts +0 -54
  201. package/tests/unit/middleware/SecurityHeaders.test.ts +0 -66
  202. package/tests/unit/query/FilterBuilder.test.ts +0 -111
  203. package/tests/unit/query/JsonbArrayBuilder.test.ts +0 -178
  204. package/tests/unit/query/Query.emptyString.test.ts +0 -69
  205. package/tests/unit/query/Query.test.ts +0 -310
  206. package/tests/unit/remote/CircuitBreaker.test.ts +0 -159
  207. package/tests/unit/remote/RemoteError.test.ts +0 -55
  208. package/tests/unit/remote/decorators.test.ts +0 -195
  209. package/tests/unit/remote/metrics.test.ts +0 -115
  210. package/tests/unit/remote/mockRedisStreamServer.test.ts +0 -104
  211. package/tests/unit/scheduler/DistributedLock.test.ts +0 -274
  212. package/tests/unit/scheduler/SchedulerManager.timeBased.test.ts +0 -95
  213. package/tests/unit/schema/schema-integration.test.ts +0 -426
  214. package/tests/unit/schema/schema.test.ts +0 -580
  215. package/tests/unit/storage/S3StorageProvider.test.ts +0 -567
  216. package/tests/unit/upload/RestUpload.test.ts +0 -267
  217. package/tests/unit/validateEnv.test.ts +0 -82
  218. package/tests/utils/entity-tracker.ts +0 -57
  219. package/tests/utils/index.ts +0 -13
  220. package/tests/utils/test-context.ts +0 -149
@@ -1,274 +0,0 @@
1
- /**
2
- * Unit tests for DistributedLock
3
- * Tests PostgreSQL advisory lock-based distributed locking functionality
4
- *
5
- * Note: These tests require a PostgreSQL database connection
6
- */
7
- import { describe, test, expect, beforeEach, afterEach, beforeAll, afterAll } from 'bun:test';
8
- import { DistributedLock, resetDistributedLock, DEFAULT_LOCK_CONFIG } from '../../../core/scheduler/DistributedLock';
9
-
10
- describe('DistributedLock', () => {
11
- let lock: DistributedLock;
12
-
13
- beforeEach(() => {
14
- // Reset singleton before each test
15
- resetDistributedLock();
16
- lock = new DistributedLock({
17
- enabled: true,
18
- enableLogging: false,
19
- lockTimeout: 0,
20
- retryInterval: 50,
21
- });
22
- });
23
-
24
- afterEach(async () => {
25
- // Release all locks and reset
26
- await lock.releaseAll();
27
- resetDistributedLock();
28
- });
29
-
30
- describe('constructor', () => {
31
- test('creates lock with default config', () => {
32
- const defaultLock = new DistributedLock();
33
- expect(defaultLock).toBeDefined();
34
- expect(defaultLock.getConfig().enabled).toBe(DEFAULT_LOCK_CONFIG.enabled);
35
- expect(defaultLock.getConfig().lockKeyPrefix).toBe(DEFAULT_LOCK_CONFIG.lockKeyPrefix);
36
- });
37
-
38
- test('creates lock with custom config', () => {
39
- const customLock = new DistributedLock({
40
- enabled: false,
41
- lockKeyPrefix: 0x12345678,
42
- lockTimeout: 5000,
43
- });
44
- const config = customLock.getConfig();
45
- expect(config.enabled).toBe(false);
46
- expect(config.lockKeyPrefix).toBe(0x12345678);
47
- expect(config.lockTimeout).toBe(5000);
48
- });
49
- });
50
-
51
- describe('tryAcquire()', () => {
52
- test('acquires lock for new task', async () => {
53
- const result = await lock.tryAcquire('test-task-1');
54
-
55
- expect(result.acquired).toBe(true);
56
- expect(result.taskId).toBe('test-task-1');
57
- expect(result.lockKey).toBeDefined();
58
- expect(typeof result.lockKey).toBe('bigint');
59
- });
60
-
61
- test('generates consistent lock keys for same task', async () => {
62
- const lock2 = new DistributedLock({ enabled: true, enableLogging: false });
63
-
64
- const result1 = await lock.tryAcquire('consistent-task');
65
- await lock.release('consistent-task');
66
-
67
- const result2 = await lock2.tryAcquire('consistent-task');
68
- await lock2.release('consistent-task');
69
-
70
- expect(result1.lockKey).toBe(result2.lockKey);
71
- });
72
-
73
- test('generates different lock keys for different tasks', async () => {
74
- const result1 = await lock.tryAcquire('task-a');
75
- const result2 = await lock.tryAcquire('task-b');
76
-
77
- expect(result1.lockKey).not.toBe(result2.lockKey);
78
-
79
- await lock.release('task-a');
80
- await lock.release('task-b');
81
- });
82
-
83
- test('fails to acquire already held lock', async () => {
84
- // First instance acquires lock
85
- const result1 = await lock.tryAcquire('exclusive-task');
86
- expect(result1.acquired).toBe(true);
87
-
88
- // Second instance tries to acquire same lock (simulated with same connection)
89
- // Note: In real scenario, this would be a different database session
90
- // For unit test, we verify the lock is tracked as held
91
- expect(lock.isHeld('exclusive-task')).toBe(true);
92
- });
93
-
94
- test('returns true immediately when locking disabled', async () => {
95
- const disabledLock = new DistributedLock({ enabled: false });
96
-
97
- const result = await lock.tryAcquire('disabled-test');
98
-
99
- // When disabled, lock is always "acquired" with key 0
100
- const disabledResult = await disabledLock.tryAcquire('disabled-test');
101
- expect(disabledResult.acquired).toBe(true);
102
- expect(disabledResult.lockKey).toBe(0n);
103
- });
104
-
105
- test('tracks held locks locally', async () => {
106
- expect(lock.getHeldLockCount()).toBe(0);
107
-
108
- await lock.tryAcquire('tracked-task-1');
109
- expect(lock.getHeldLockCount()).toBe(1);
110
- expect(lock.isHeld('tracked-task-1')).toBe(true);
111
-
112
- await lock.tryAcquire('tracked-task-2');
113
- expect(lock.getHeldLockCount()).toBe(2);
114
- expect(lock.isHeld('tracked-task-2')).toBe(true);
115
- });
116
- });
117
-
118
- describe('release()', () => {
119
- test('releases held lock', async () => {
120
- await lock.tryAcquire('release-test');
121
- expect(lock.isHeld('release-test')).toBe(true);
122
-
123
- const released = await lock.release('release-test');
124
-
125
- expect(released).toBe(true);
126
- expect(lock.isHeld('release-test')).toBe(false);
127
- expect(lock.getHeldLockCount()).toBe(0);
128
- });
129
-
130
- test('returns false for non-held lock', async () => {
131
- const released = await lock.release('never-acquired');
132
-
133
- // PostgreSQL returns false if lock wasn't held
134
- expect(released).toBe(false);
135
- });
136
-
137
- test('does nothing when disabled', async () => {
138
- const disabledLock = new DistributedLock({ enabled: false });
139
-
140
- const released = await disabledLock.release('any-task');
141
-
142
- expect(released).toBe(true);
143
- });
144
- });
145
-
146
- describe('releaseAll()', () => {
147
- test('releases all held locks', async () => {
148
- await lock.tryAcquire('multi-1');
149
- await lock.tryAcquire('multi-2');
150
- await lock.tryAcquire('multi-3');
151
-
152
- expect(lock.getHeldLockCount()).toBe(3);
153
-
154
- await lock.releaseAll();
155
-
156
- expect(lock.getHeldLockCount()).toBe(0);
157
- expect(lock.isHeld('multi-1')).toBe(false);
158
- expect(lock.isHeld('multi-2')).toBe(false);
159
- expect(lock.isHeld('multi-3')).toBe(false);
160
- });
161
-
162
- test('handles empty lock set gracefully', async () => {
163
- expect(lock.getHeldLockCount()).toBe(0);
164
-
165
- // Should not throw
166
- await lock.releaseAll();
167
-
168
- expect(lock.getHeldLockCount()).toBe(0);
169
- });
170
- });
171
-
172
- describe('updateConfig()', () => {
173
- test('updates configuration', () => {
174
- const originalConfig = lock.getConfig();
175
-
176
- lock.updateConfig({
177
- enabled: false,
178
- lockTimeout: 10000,
179
- });
180
-
181
- const newConfig = lock.getConfig();
182
- expect(newConfig.enabled).toBe(false);
183
- expect(newConfig.lockTimeout).toBe(10000);
184
- // Other values should remain
185
- expect(newConfig.lockKeyPrefix).toBe(originalConfig.lockKeyPrefix);
186
- });
187
- });
188
-
189
- describe('isHeld()', () => {
190
- test('returns false for never acquired task', () => {
191
- expect(lock.isHeld('unknown-task')).toBe(false);
192
- });
193
-
194
- test('returns true for acquired task', async () => {
195
- await lock.tryAcquire('held-task');
196
- expect(lock.isHeld('held-task')).toBe(true);
197
- });
198
-
199
- test('returns false after release', async () => {
200
- await lock.tryAcquire('was-held-task');
201
- expect(lock.isHeld('was-held-task')).toBe(true);
202
-
203
- await lock.release('was-held-task');
204
- expect(lock.isHeld('was-held-task')).toBe(false);
205
- });
206
- });
207
-
208
- describe('lock key generation', () => {
209
- test('generates positive bigint keys', async () => {
210
- const tasks = ['task-1', 'task-2', 'TaskWithCaps', 'task.with.dots', 'task-with-dashes'];
211
-
212
- for (const task of tasks) {
213
- const result = await lock.tryAcquire(task);
214
- expect(result.lockKey).toBeGreaterThan(0n);
215
- await lock.release(task);
216
- }
217
- });
218
-
219
- test('handles empty string task id', async () => {
220
- const result = await lock.tryAcquire('');
221
- expect(result.lockKey).toBeDefined();
222
- await lock.release('');
223
- });
224
-
225
- test('handles unicode task ids', async () => {
226
- const result = await lock.tryAcquire('task-日本語');
227
- expect(result.acquired).toBe(true);
228
- expect(result.lockKey).toBeGreaterThan(0n);
229
- await lock.release('task-日本語');
230
- });
231
-
232
- test('handles very long task ids', async () => {
233
- const longId = 'a'.repeat(1000);
234
- const result = await lock.tryAcquire(longId);
235
- expect(result.acquired).toBe(true);
236
- expect(result.lockKey).toBeGreaterThan(0n);
237
- await lock.release(longId);
238
- });
239
- });
240
- });
241
-
242
- describe('DistributedLock with timeout', () => {
243
- let lock: DistributedLock;
244
-
245
- beforeEach(() => {
246
- resetDistributedLock();
247
- lock = new DistributedLock({
248
- enabled: true,
249
- enableLogging: false,
250
- lockTimeout: 200, // 200ms timeout
251
- retryInterval: 50,
252
- });
253
- });
254
-
255
- afterEach(async () => {
256
- await lock.releaseAll();
257
- resetDistributedLock();
258
- });
259
-
260
- test('retries until timeout when lock not available', async () => {
261
- // This test verifies the retry mechanism is called
262
- // In a real multi-instance scenario, we'd test across database sessions
263
-
264
- const startTime = Date.now();
265
-
266
- // First acquire the lock
267
- const result1 = await lock.tryAcquire('timeout-task');
268
- expect(result1.acquired).toBe(true);
269
-
270
- const elapsed = Date.now() - startTime;
271
- // Should return quickly when lock is available
272
- expect(elapsed).toBeLessThan(100);
273
- });
274
- });
@@ -1,95 +0,0 @@
1
- /**
2
- * Unit tests for SchedulerManager time-based (entity-less) tasks.
3
- * Covers BUNSANE-002: @ScheduledTask without query/componentTarget.
4
- */
5
- import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
6
- import { SchedulerManager } from '../../../core/SchedulerManager';
7
- import { ScheduleInterval } from '../../../types/scheduler.types';
8
-
9
- describe('SchedulerManager time-based tasks', () => {
10
- let scheduler: SchedulerManager;
11
-
12
- beforeEach(() => {
13
- scheduler = SchedulerManager.getInstance();
14
- scheduler.updateConfig({
15
- enabled: true,
16
- enableLogging: false,
17
- runOnStart: false,
18
- distributedLocking: false,
19
- maxConcurrentTasks: 5,
20
- defaultTimeout: 5000,
21
- });
22
- });
23
-
24
- afterEach(async () => {
25
- await scheduler.stop().catch(() => {});
26
- });
27
-
28
- test('registers task with no query / no componentTarget', () => {
29
- let called = 0;
30
- const service = {
31
- tick: async () => {
32
- called++;
33
- },
34
- };
35
-
36
- expect(() =>
37
- scheduler.registerTask({
38
- id: 'test.timebased.register',
39
- name: 'timebased-register',
40
- interval: ScheduleInterval.MINUTE,
41
- options: {},
42
- service,
43
- methodName: 'tick',
44
- nextExecution: new Date(),
45
- executionCount: 0,
46
- isRunning: false,
47
- enabled: true,
48
- })
49
- ).not.toThrow();
50
- });
51
-
52
- test('executes handler with no entity argument', async () => {
53
- const receivedArgsBox: { args: unknown[] | null } = { args: null };
54
- const service = {
55
- tick: async (...args: unknown[]) => {
56
- receivedArgsBox.args = args;
57
- },
58
- };
59
-
60
- scheduler.registerTask({
61
- id: 'test.timebased.exec',
62
- name: 'timebased-exec',
63
- interval: ScheduleInterval.MINUTE,
64
- options: {},
65
- service,
66
- methodName: 'tick',
67
- nextExecution: new Date(),
68
- executionCount: 0,
69
- isRunning: false,
70
- enabled: true,
71
- });
72
-
73
- const ok = await scheduler.executeTaskNow('test.timebased.exec');
74
- expect(ok).toBe(true);
75
- expect(receivedArgsBox.args).toEqual([]);
76
- });
77
-
78
- test('rejects task still missing required fields', () => {
79
- const service = { tick: async () => {} };
80
- expect(() =>
81
- scheduler.registerTask({
82
- // missing id
83
- name: 'bad',
84
- interval: ScheduleInterval.MINUTE,
85
- options: {},
86
- service,
87
- methodName: 'tick',
88
- nextExecution: new Date(),
89
- executionCount: 0,
90
- isRunning: false,
91
- enabled: true,
92
- } as any)
93
- ).toThrow(/missing required fields/);
94
- });
95
- });