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