bunsane 0.3.1 → 0.4.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 (224) hide show
  1. package/CHANGELOG.md +445 -318
  2. package/config/cache.config.ts +35 -1
  3. package/core/App.ts +24 -1064
  4. package/core/ArcheType.ts +78 -2110
  5. package/core/BatchLoader.ts +56 -32
  6. package/core/Entity.ts +85 -1043
  7. package/core/EntityHookManager.ts +52 -754
  8. package/core/Logger.ts +10 -0
  9. package/core/RequestContext.ts +64 -6
  10. package/core/RequestLoaders.ts +187 -36
  11. package/core/SchedulerManager.ts +28 -600
  12. package/core/app/bootstrap.ts +133 -0
  13. package/core/app/cors.ts +85 -0
  14. package/core/app/graphqlSetup.ts +56 -0
  15. package/core/app/healthEndpoints.ts +31 -0
  16. package/core/app/metricsCollector.ts +27 -0
  17. package/core/app/preparedStatementWarmup.ts +15 -0
  18. package/core/app/processHandlers.ts +43 -0
  19. package/core/app/requestRouter.ts +310 -0
  20. package/core/app/restRegistry.ts +80 -0
  21. package/core/app/shutdown.ts +97 -0
  22. package/core/app/studioRouter.ts +83 -0
  23. package/core/archetype/customTypes.ts +100 -0
  24. package/core/archetype/decorators.ts +171 -0
  25. package/core/archetype/fieldResolvers.ts +666 -0
  26. package/core/archetype/helpers.ts +29 -0
  27. package/core/archetype/relationLoader.ts +161 -0
  28. package/core/archetype/schemaBuilder.ts +141 -0
  29. package/core/archetype/weaver.ts +218 -0
  30. package/core/archetype/zodSchemaBuilder.ts +527 -0
  31. package/core/cache/CacheManager.ts +173 -267
  32. package/core/cache/CompressionUtils.ts +34 -3
  33. package/core/cache/MemoryCache.ts +40 -37
  34. package/core/cache/RedisCache.ts +4 -4
  35. package/core/cache/health.ts +30 -0
  36. package/core/cache/invalidation.ts +96 -0
  37. package/core/cache/strategies/writeInvalidate.ts +111 -0
  38. package/core/cache/strategies/writeThrough.ts +233 -0
  39. package/core/components/BaseComponent.ts +16 -8
  40. package/core/components/ComponentRegistry.ts +28 -0
  41. package/core/decorators/IndexedField.ts +1 -1
  42. package/core/entity/cacheStrategies.ts +97 -0
  43. package/core/entity/componentAccess.ts +364 -0
  44. package/core/entity/finders.ts +202 -0
  45. package/core/entity/pendingOps.ts +72 -0
  46. package/core/entity/saveEntity.ts +377 -0
  47. package/core/hooks/dispatcher.ts +439 -0
  48. package/core/hooks/guards.ts +155 -0
  49. package/core/hooks/registry.ts +247 -0
  50. package/core/metadata/definitions/Component.ts +1 -1
  51. package/core/metadata/index.ts +15 -4
  52. package/core/middleware/AccessLog.ts +8 -1
  53. package/core/middleware/RateLimit.ts +102 -105
  54. package/core/middleware/RequestId.ts +2 -9
  55. package/core/middleware/SecurityHeaders.ts +2 -11
  56. package/core/middleware/headers.ts +28 -0
  57. package/core/remote/OutboxWorker.ts +213 -183
  58. package/core/remote/RemoteManager.ts +401 -400
  59. package/core/remote/types.ts +153 -151
  60. package/core/requestScope.ts +34 -0
  61. package/core/scheduler/cronEvaluator.ts +174 -0
  62. package/core/scheduler/lifecycleHooks.ts +21 -0
  63. package/core/scheduler/lockCoordinator.ts +27 -0
  64. package/core/scheduler/metrics.ts +14 -0
  65. package/core/scheduler/taskRunner.ts +420 -0
  66. package/database/DatabaseHelper.ts +128 -101
  67. package/database/IndexingStrategy.ts +72 -2
  68. package/database/PreparedStatementCache.ts +20 -5
  69. package/database/cancellable.ts +35 -0
  70. package/database/index.ts +15 -3
  71. package/database/instrumentedDb.ts +141 -0
  72. package/endpoints/archetypes.ts +2 -8
  73. package/endpoints/tables.ts +6 -1
  74. package/gql/index.ts +1 -1
  75. package/gql/visitors/ResolverGeneratorVisitor.ts +25 -4
  76. package/package.json +22 -1
  77. package/query/CTENode.ts +5 -3
  78. package/query/ComponentInclusionNode.ts +240 -13
  79. package/query/OrNode.ts +6 -5
  80. package/query/Query.ts +203 -59
  81. package/query/QueryContext.ts +6 -0
  82. package/query/QueryDAG.ts +7 -2
  83. package/query/membershipSource.ts +66 -0
  84. package/storage/LocalStorageProvider.ts +8 -3
  85. package/studio/dist/assets/index-BMZ67Npg.js +254 -0
  86. package/studio/dist/assets/index-BpbuYz9g.css +1 -0
  87. package/studio/{index.html → dist/index.html} +3 -2
  88. package/swagger/generator.ts +11 -1
  89. package/upload/UploadManager.ts +8 -6
  90. package/utils/uuid.ts +40 -10
  91. package/.claude/settings.local.json +0 -47
  92. package/.prettierrc +0 -4
  93. package/.serena/memories/architectural-decision-no-dependency-injection.md +0 -76
  94. package/.serena/memories/architecture.md +0 -154
  95. package/.serena/memories/cache-interface-refactoring-2026-01-24.md +0 -165
  96. package/.serena/memories/code_style_and_conventions.md +0 -76
  97. package/.serena/memories/project_overview.md +0 -43
  98. package/.serena/memories/schema-dsl-plan.md +0 -107
  99. package/.serena/memories/suggested_commands.md +0 -80
  100. package/.serena/memories/typescript-compilation-status.md +0 -54
  101. package/.serena/project.yml +0 -114
  102. package/BunSane.jpg +0 -0
  103. package/CLAUDE.md +0 -198
  104. package/TODO.md +0 -2
  105. package/bun.lock +0 -302
  106. package/bunfig.toml +0 -10
  107. package/docs/SCALABILITY_PLAN.md +0 -175
  108. package/studio/bun.lock +0 -482
  109. package/studio/package.json +0 -39
  110. package/studio/postcss.config.js +0 -6
  111. package/studio/src/components/DataTable.tsx +0 -211
  112. package/studio/src/components/Layout.tsx +0 -13
  113. package/studio/src/components/PageContainer.tsx +0 -9
  114. package/studio/src/components/PageHeader.tsx +0 -13
  115. package/studio/src/components/SearchBar.tsx +0 -57
  116. package/studio/src/components/Sidebar.tsx +0 -294
  117. package/studio/src/components/ui/button.tsx +0 -56
  118. package/studio/src/components/ui/checkbox.tsx +0 -26
  119. package/studio/src/components/ui/input.tsx +0 -25
  120. package/studio/src/hooks/useDataTable.ts +0 -131
  121. package/studio/src/index.css +0 -36
  122. package/studio/src/lib/api.ts +0 -186
  123. package/studio/src/lib/utils.ts +0 -13
  124. package/studio/src/main.tsx +0 -17
  125. package/studio/src/pages/ArcheType.tsx +0 -239
  126. package/studio/src/pages/Components.tsx +0 -124
  127. package/studio/src/pages/EntityInspector.tsx +0 -302
  128. package/studio/src/pages/QueryRunner.tsx +0 -246
  129. package/studio/src/pages/Table.tsx +0 -94
  130. package/studio/src/pages/Welcome.tsx +0 -241
  131. package/studio/src/routes.tsx +0 -45
  132. package/studio/src/store/archeTypeSettings.ts +0 -30
  133. package/studio/src/store/studio.ts +0 -65
  134. package/studio/src/utils/columnHelpers.tsx +0 -114
  135. package/studio/studio-instructions.md +0 -81
  136. package/studio/tailwind.config.js +0 -77
  137. package/studio/utils.ts +0 -54
  138. package/studio/vite.config.js +0 -19
  139. package/tests/benchmark/BENCHMARK_DATABASES_PLAN.md +0 -338
  140. package/tests/benchmark/bunfig.toml +0 -9
  141. package/tests/benchmark/fixtures/EcommerceComponents.ts +0 -283
  142. package/tests/benchmark/fixtures/EcommerceDataGenerators.ts +0 -301
  143. package/tests/benchmark/fixtures/RelationTracker.ts +0 -159
  144. package/tests/benchmark/fixtures/index.ts +0 -6
  145. package/tests/benchmark/index.ts +0 -22
  146. package/tests/benchmark/noop-preload.ts +0 -3
  147. package/tests/benchmark/query-lateral-benchmark.test.ts +0 -372
  148. package/tests/benchmark/runners/BenchmarkLoader.ts +0 -132
  149. package/tests/benchmark/runners/index.ts +0 -4
  150. package/tests/benchmark/scenarios/query-benchmarks.test.ts +0 -465
  151. package/tests/benchmark/scripts/generate-db.ts +0 -344
  152. package/tests/benchmark/scripts/run-benchmarks.ts +0 -97
  153. package/tests/e2e/http.test.ts +0 -130
  154. package/tests/fixtures/archetypes/TestUserArchetype.ts +0 -21
  155. package/tests/fixtures/components/TestOrder.ts +0 -23
  156. package/tests/fixtures/components/TestProduct.ts +0 -23
  157. package/tests/fixtures/components/TestUser.ts +0 -20
  158. package/tests/fixtures/components/index.ts +0 -6
  159. package/tests/graphql/SchemaGeneration.test.ts +0 -90
  160. package/tests/graphql/builders/ResolverBuilder.test.ts +0 -223
  161. package/tests/graphql/builders/TypeDefBuilder.test.ts +0 -153
  162. package/tests/helpers/MockRedisClient.ts +0 -113
  163. package/tests/helpers/MockRedisStreamServer.ts +0 -448
  164. package/tests/integration/archetype/ArcheType.persistence.test.ts +0 -241
  165. package/tests/integration/cache/CacheInvalidation.test.ts +0 -259
  166. package/tests/integration/entity/Entity.persistence.test.ts +0 -333
  167. package/tests/integration/entity/Entity.saveTimeout.test.ts +0 -110
  168. package/tests/integration/query/Query.complexAnalysis.test.ts +0 -557
  169. package/tests/integration/query/Query.edgeCases.test.ts +0 -595
  170. package/tests/integration/query/Query.exec.test.ts +0 -576
  171. package/tests/integration/query/Query.explainAnalyze.test.ts +0 -233
  172. package/tests/integration/query/Query.jsonbArray.test.ts +0 -214
  173. package/tests/integration/remote/dlq.test.ts +0 -175
  174. package/tests/integration/remote/event-dispatch.test.ts +0 -114
  175. package/tests/integration/remote/outbox.test.ts +0 -130
  176. package/tests/integration/remote/rpc.test.ts +0 -177
  177. package/tests/pglite-setup.ts +0 -62
  178. package/tests/setup.ts +0 -164
  179. package/tests/stress/BenchmarkRunner.ts +0 -203
  180. package/tests/stress/DataSeeder.ts +0 -190
  181. package/tests/stress/StressTestReporter.ts +0 -229
  182. package/tests/stress/cursor-perf-test.ts +0 -171
  183. package/tests/stress/fixtures/RealisticComponents.ts +0 -235
  184. package/tests/stress/fixtures/StressTestComponents.ts +0 -58
  185. package/tests/stress/index.ts +0 -7
  186. package/tests/stress/scenarios/query-benchmarks.test.ts +0 -285
  187. package/tests/stress/scenarios/realistic-scenarios.test.ts +0 -1081
  188. package/tests/stress/scenarios/timeout-investigation.test.ts +0 -522
  189. package/tests/unit/BatchLoader.test.ts +0 -196
  190. package/tests/unit/archetype/ArcheType.test.ts +0 -107
  191. package/tests/unit/cache/CacheManager.test.ts +0 -367
  192. package/tests/unit/cache/MemoryCache.test.ts +0 -260
  193. package/tests/unit/cache/RedisCache.test.ts +0 -411
  194. package/tests/unit/entity/Entity.components.test.ts +0 -317
  195. package/tests/unit/entity/Entity.drainSideEffects.test.ts +0 -51
  196. package/tests/unit/entity/Entity.reload.test.ts +0 -63
  197. package/tests/unit/entity/Entity.requireComponents.test.ts +0 -72
  198. package/tests/unit/entity/Entity.test.ts +0 -345
  199. package/tests/unit/gql/depthLimit.test.ts +0 -203
  200. package/tests/unit/gql/operationMiddleware.test.ts +0 -293
  201. package/tests/unit/health/Health.test.ts +0 -129
  202. package/tests/unit/middleware/AccessLog.test.ts +0 -37
  203. package/tests/unit/middleware/Middleware.test.ts +0 -98
  204. package/tests/unit/middleware/RequestId.test.ts +0 -54
  205. package/tests/unit/middleware/SecurityHeaders.test.ts +0 -66
  206. package/tests/unit/query/FilterBuilder.test.ts +0 -111
  207. package/tests/unit/query/JsonbArrayBuilder.test.ts +0 -178
  208. package/tests/unit/query/Query.emptyString.test.ts +0 -69
  209. package/tests/unit/query/Query.test.ts +0 -310
  210. package/tests/unit/remote/CircuitBreaker.test.ts +0 -159
  211. package/tests/unit/remote/RemoteError.test.ts +0 -55
  212. package/tests/unit/remote/decorators.test.ts +0 -195
  213. package/tests/unit/remote/metrics.test.ts +0 -115
  214. package/tests/unit/remote/mockRedisStreamServer.test.ts +0 -104
  215. package/tests/unit/scheduler/DistributedLock.test.ts +0 -274
  216. package/tests/unit/scheduler/SchedulerManager.timeBased.test.ts +0 -95
  217. package/tests/unit/schema/schema-integration.test.ts +0 -426
  218. package/tests/unit/schema/schema.test.ts +0 -580
  219. package/tests/unit/storage/S3StorageProvider.test.ts +0 -567
  220. package/tests/unit/upload/RestUpload.test.ts +0 -267
  221. package/tests/unit/validateEnv.test.ts +0 -82
  222. package/tests/utils/entity-tracker.ts +0 -57
  223. package/tests/utils/index.ts +0 -13
  224. 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
- });