@gencow/core 0.1.27 → 0.1.29
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.
- package/dist/auth-config.d.ts +92 -5
- package/dist/config.d.ts +107 -0
- package/dist/config.js +12 -0
- package/dist/context.d.ts +139 -0
- package/dist/context.js +3 -0
- package/dist/crud.d.ts +5 -5
- package/dist/crud.js +19 -35
- package/dist/document-types.d.ts +65 -0
- package/dist/document-types.js +15 -0
- package/dist/grounded-answer-types.d.ts +62 -0
- package/dist/grounded-answer-types.js +6 -0
- package/dist/http-action.d.ts +77 -0
- package/dist/http-action.js +41 -0
- package/dist/index.d.ts +30 -5
- package/dist/index.js +15 -2
- package/dist/platform-capacity-profile.d.ts +19 -0
- package/dist/platform-capacity-profile.js +94 -0
- package/dist/procedure.d.ts +58 -0
- package/dist/procedure.js +115 -0
- package/dist/rag-ingest-types.d.ts +39 -0
- package/dist/rag-ingest-types.js +1 -0
- package/dist/rag-operations-types.d.ts +81 -0
- package/dist/rag-operations-types.js +1 -0
- package/dist/rag-schema.d.ts +1466 -0
- package/dist/rag-schema.js +87 -0
- package/dist/reactive-mutation-types.d.ts +11 -0
- package/dist/reactive-mutation-types.js +1 -0
- package/dist/reactive-mutation.d.ts +51 -0
- package/dist/reactive-mutation.js +75 -0
- package/dist/reactive-query-types.d.ts +12 -0
- package/dist/reactive-query-types.js +1 -0
- package/dist/reactive-query.d.ts +14 -0
- package/dist/reactive-query.js +28 -0
- package/dist/reactive-realtime.d.ts +48 -0
- package/dist/reactive-realtime.js +236 -0
- package/dist/reactive.d.ts +29 -5
- package/dist/reactive.js +65 -0
- package/dist/rls-db.d.ts +9 -2
- package/dist/runtime-env-policy.d.ts +5 -0
- package/dist/runtime-env-policy.js +56 -0
- package/dist/search-types.d.ts +83 -0
- package/dist/search-types.js +1 -0
- package/dist/server.d.ts +1 -2
- package/dist/server.js +0 -1
- package/dist/storage-metering.d.ts +13 -0
- package/dist/storage-metering.js +18 -0
- package/dist/storage-shared.d.ts +36 -0
- package/dist/storage-shared.js +39 -0
- package/dist/storage.d.ts +5 -27
- package/dist/storage.js +30 -22
- package/dist/wake-app-result.d.ts +22 -0
- package/dist/wake-app-result.js +11 -0
- package/dist/workflow-types.d.ts +16 -2
- package/dist/workflow.d.ts +1 -1
- package/dist/workflow.js +136 -11
- package/dist/workflows-api.js +71 -3
- package/package.json +11 -7
- package/src/auth-config.ts +104 -3
- package/src/config.ts +119 -0
- package/src/context.ts +152 -0
- package/src/crud.ts +18 -35
- package/src/document-types.ts +102 -0
- package/src/grounded-answer-types.ts +78 -0
- package/src/http-action.ts +101 -0
- package/src/index.ts +142 -19
- package/src/platform-capacity-profile.ts +114 -0
- package/src/procedure.ts +283 -0
- package/src/rag-ingest-types.ts +52 -0
- package/src/rag-operations-types.ts +90 -0
- package/src/rag-schema.ts +94 -0
- package/src/reactive-mutation-types.ts +13 -0
- package/src/reactive-mutation.ts +115 -0
- package/src/reactive-query-types.ts +14 -0
- package/src/reactive-query.ts +48 -0
- package/src/reactive-realtime.ts +267 -0
- package/src/rls-db.ts +9 -4
- package/src/runtime-env-policy.ts +66 -0
- package/src/search-types.ts +91 -0
- package/src/server.ts +6 -2
- package/src/storage-metering.ts +35 -0
- package/src/storage-shared.ts +74 -0
- package/src/storage.ts +44 -53
- package/src/wake-app-result.ts +37 -0
- package/src/workflow-types.ts +16 -2
- package/src/workflow.ts +166 -12
- package/src/workflows-api.ts +82 -3
- package/src/__tests__/auth.test.ts +0 -118
- package/src/__tests__/crons.test.ts +0 -83
- package/src/__tests__/crud-codegen-integration.test.ts +0 -246
- package/src/__tests__/crud-owner-rls.test.ts +0 -387
- package/src/__tests__/crud.test.ts +0 -930
- package/src/__tests__/dist-exports.test.ts +0 -176
- package/src/__tests__/fixtures/basic/auth.ts +0 -32
- package/src/__tests__/fixtures/basic/drizzle.config.ts +0 -12
- package/src/__tests__/fixtures/basic/index.ts +0 -6
- package/src/__tests__/fixtures/basic/migrations/0000_last_warstar.sql +0 -75
- package/src/__tests__/fixtures/basic/migrations/meta/0000_snapshot.json +0 -497
- package/src/__tests__/fixtures/basic/migrations/meta/_journal.json +0 -13
- package/src/__tests__/fixtures/basic/schema.ts +0 -51
- package/src/__tests__/fixtures/basic/tasks.ts +0 -15
- package/src/__tests__/fixtures/common/auth-schema.ts +0 -67
- package/src/__tests__/helpers/basic-rls-fixture.ts +0 -135
- package/src/__tests__/helpers/pglite-migrations.ts +0 -32
- package/src/__tests__/helpers/pglite-rls-session.ts +0 -51
- package/src/__tests__/helpers/seed-like-fill.ts +0 -202
- package/src/__tests__/helpers/test-gencow-ctx-rls.ts +0 -50
- package/src/__tests__/httpaction.test.ts +0 -122
- package/src/__tests__/image-optimization.test.ts +0 -648
- package/src/__tests__/load.test.ts +0 -389
- package/src/__tests__/network-sim.test.ts +0 -319
- package/src/__tests__/reactive.test.ts +0 -479
- package/src/__tests__/retry.test.ts +0 -113
- package/src/__tests__/rls-crud-basic.test.ts +0 -317
- package/src/__tests__/rls-crud-no-owner-rls-pglite.test.ts +0 -117
- package/src/__tests__/rls-custom-mutation-handlers.test.ts +0 -142
- package/src/__tests__/rls-custom-query-handlers.test.ts +0 -128
- package/src/__tests__/rls-db-leased-connection.test.ts +0 -118
- package/src/__tests__/rls-session-and-policies.test.ts +0 -228
- package/src/__tests__/scheduler-durable-v2.test.ts +0 -288
- package/src/__tests__/scheduler-durable.test.ts +0 -173
- package/src/__tests__/scheduler-exec.test.ts +0 -328
- package/src/__tests__/scheduler.test.ts +0 -187
- package/src/__tests__/storage.test.ts +0 -334
- package/src/__tests__/tsconfig.json +0 -8
- package/src/__tests__/validator.test.ts +0 -323
- package/src/__tests__/workflow.test.ts +0 -606
- package/src/__tests__/ws-integration.test.ts +0 -309
- package/src/__tests__/ws-scale.test.ts +0 -241
- package/src/auth.ts +0 -155
- package/src/reactive.ts +0 -580
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* packages/core/src/__tests__/scheduler-durable-v2.test.ts
|
|
3
|
-
*
|
|
4
|
-
* Scheduler Durable v2 테스트 — Platform DB 중앙화 구현 검증.
|
|
5
|
-
*
|
|
6
|
-
* DB/HTTP 의존 없이 검증 가능한 핵심 로직:
|
|
7
|
-
* - persistJob에 올바른 필드가 전달되는지
|
|
8
|
-
* - args가 persistJob 콜백까지 정확히 전달되는지
|
|
9
|
-
* - 다수의 job 동시 등록 시 id 충돌 없음
|
|
10
|
-
* - removeJob 실패 시 graceful 처리
|
|
11
|
-
* - runAfter(0) 즉시 실행 케이스
|
|
12
|
-
* - 대형 args 직렬화 안정성
|
|
13
|
-
*
|
|
14
|
-
* Run: bun test packages/core/src/__tests__/scheduler-durable-v2.test.ts
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { describe, it, expect } from "bun:test";
|
|
18
|
-
import { createScheduler } from "../scheduler.js";
|
|
19
|
-
import type { ScheduledJobRecord } from "../scheduler.js";
|
|
20
|
-
|
|
21
|
-
describe("Scheduler Durable v2 — Platform DB 중앙화", () => {
|
|
22
|
-
// ── args 전달 정확성 ──
|
|
23
|
-
|
|
24
|
-
it("persistJob에 args가 정확히 전달된다 (중첩 객체)", async () => {
|
|
25
|
-
const persisted: ScheduledJobRecord[] = [];
|
|
26
|
-
|
|
27
|
-
const scheduler = createScheduler({
|
|
28
|
-
persistJob: async (job) => {
|
|
29
|
-
persisted.push(job);
|
|
30
|
-
},
|
|
31
|
-
removeJob: async () => true,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
scheduler.registerAction("deep.args", async () => {});
|
|
35
|
-
|
|
36
|
-
const complexArgs = {
|
|
37
|
-
nested: { deeply: { value: 42 } },
|
|
38
|
-
array: [1, 2, { x: "y" }],
|
|
39
|
-
unicode: "한글테스트 🎯",
|
|
40
|
-
nullValue: null,
|
|
41
|
-
boolValue: false,
|
|
42
|
-
zero: 0,
|
|
43
|
-
emptyString: "",
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
scheduler.runAfter(1000, "deep.args", complexArgs);
|
|
47
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
48
|
-
|
|
49
|
-
expect(persisted.length).toBe(1);
|
|
50
|
-
expect(persisted[0].args).toEqual(complexArgs);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("persistJob에 args 미전달 시 undefined로 전달", async () => {
|
|
54
|
-
const persisted: ScheduledJobRecord[] = [];
|
|
55
|
-
|
|
56
|
-
const scheduler = createScheduler({
|
|
57
|
-
persistJob: async (job) => {
|
|
58
|
-
persisted.push(job);
|
|
59
|
-
},
|
|
60
|
-
removeJob: async () => true,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
scheduler.registerAction("no.args", async () => {});
|
|
64
|
-
scheduler.runAfter(1000, "no.args"); // args 생략
|
|
65
|
-
|
|
66
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
67
|
-
|
|
68
|
-
expect(persisted.length).toBe(1);
|
|
69
|
-
// args는 undefined 또는 빈 값이어야 함 (Platform에서 {} default 처리)
|
|
70
|
-
expect(
|
|
71
|
-
persisted[0].args === undefined ||
|
|
72
|
-
persisted[0].args === null ||
|
|
73
|
-
JSON.stringify(persisted[0].args) === "{}",
|
|
74
|
-
).toBe(true);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// ── 동시 등록 id 고유성 ──
|
|
78
|
-
|
|
79
|
-
it("10개 job 동시 등록 시 모든 id가 고유하다", async () => {
|
|
80
|
-
const persisted: ScheduledJobRecord[] = [];
|
|
81
|
-
|
|
82
|
-
const scheduler = createScheduler({
|
|
83
|
-
persistJob: async (job) => {
|
|
84
|
-
persisted.push(job);
|
|
85
|
-
},
|
|
86
|
-
removeJob: async () => true,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
for (let i = 0; i < 10; i++) {
|
|
90
|
-
scheduler.registerAction(`batch.${i}`, async () => {});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const ids: string[] = [];
|
|
94
|
-
for (let i = 0; i < 10; i++) {
|
|
95
|
-
ids.push(scheduler.runAfter(1000, `batch.${i}`, { idx: i }));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
99
|
-
|
|
100
|
-
// 모든 id 고유
|
|
101
|
-
const uniqueIds = new Set(ids);
|
|
102
|
-
expect(uniqueIds.size).toBe(10);
|
|
103
|
-
|
|
104
|
-
// 모든 persistJob 호출됨
|
|
105
|
-
expect(persisted.length).toBe(10);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// ── runAfter(0) — 즉시 실행 ──
|
|
109
|
-
|
|
110
|
-
it("runAfter(0) — durable mode에서도 즉시 persistJob 호출", async () => {
|
|
111
|
-
const persisted: ScheduledJobRecord[] = [];
|
|
112
|
-
|
|
113
|
-
const scheduler = createScheduler({
|
|
114
|
-
persistJob: async (job) => {
|
|
115
|
-
persisted.push(job);
|
|
116
|
-
},
|
|
117
|
-
removeJob: async () => true,
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
scheduler.registerAction("instant", async () => {});
|
|
121
|
-
|
|
122
|
-
const beforeTime = Date.now();
|
|
123
|
-
scheduler.runAfter(0, "instant", { immediate: true });
|
|
124
|
-
|
|
125
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
126
|
-
|
|
127
|
-
expect(persisted.length).toBe(1);
|
|
128
|
-
expect(persisted[0].args).toEqual({ immediate: true });
|
|
129
|
-
// runAt은 현재 시간과 거의 동일해야 함 (±2초 오차 허용)
|
|
130
|
-
expect(Math.abs(persisted[0].runAt.getTime() - beforeTime)).toBeLessThan(2000);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// ── removeJob 실패 시 graceful 처리 ──
|
|
134
|
-
|
|
135
|
-
it("removeJob 실패 시 cancel()이 false 반환 (throw 안 함)", async () => {
|
|
136
|
-
const scheduler = createScheduler({
|
|
137
|
-
persistJob: async () => {},
|
|
138
|
-
removeJob: async () => {
|
|
139
|
-
throw new Error("DB connection lost");
|
|
140
|
-
},
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
scheduler.registerAction("fail.cancel", async () => {});
|
|
144
|
-
const id = scheduler.runAfter(10000, "fail.cancel");
|
|
145
|
-
|
|
146
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
147
|
-
|
|
148
|
-
// suppress expected error
|
|
149
|
-
const orig = console.error;
|
|
150
|
-
console.error = () => {};
|
|
151
|
-
const result = scheduler.cancel(id);
|
|
152
|
-
console.error = orig;
|
|
153
|
-
|
|
154
|
-
// removeJob 실패 시 return false, throw 안 함
|
|
155
|
-
expect(typeof result).toBe("boolean");
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// ── 대형 args 안정성 ──
|
|
159
|
-
|
|
160
|
-
it("대형 args (10KB) 직렬화 안정성", async () => {
|
|
161
|
-
const persisted: ScheduledJobRecord[] = [];
|
|
162
|
-
|
|
163
|
-
const scheduler = createScheduler({
|
|
164
|
-
persistJob: async (job) => {
|
|
165
|
-
persisted.push(job);
|
|
166
|
-
},
|
|
167
|
-
removeJob: async () => true,
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
scheduler.registerAction("large.payload", async () => {});
|
|
171
|
-
|
|
172
|
-
// ~10KB args
|
|
173
|
-
const largeArgs = {
|
|
174
|
-
data: "x".repeat(10_000),
|
|
175
|
-
items: Array.from({ length: 100 }, (_, i) => ({ id: i, name: `item-${i}` })),
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
scheduler.runAfter(1000, "large.payload", largeArgs);
|
|
179
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
180
|
-
|
|
181
|
-
expect(persisted.length).toBe(1);
|
|
182
|
-
expect((persisted[0].args as any).data.length).toBe(10_000);
|
|
183
|
-
expect((persisted[0].args as any).items.length).toBe(100);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// ── onError + args 조합 ──
|
|
187
|
-
|
|
188
|
-
it("onError와 args가 함께 persistJob에 전달된다", async () => {
|
|
189
|
-
const persisted: ScheduledJobRecord[] = [];
|
|
190
|
-
|
|
191
|
-
const scheduler = createScheduler({
|
|
192
|
-
persistJob: async (job) => {
|
|
193
|
-
persisted.push(job);
|
|
194
|
-
},
|
|
195
|
-
removeJob: async () => true,
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
scheduler.registerAction("pipeline.step1", async () => {});
|
|
199
|
-
scheduler.registerAction("pipeline.onError", async () => {});
|
|
200
|
-
|
|
201
|
-
scheduler.runAfter(5000, "pipeline.step1", { step: 1, data: "test" }, { onError: "pipeline.onError" });
|
|
202
|
-
|
|
203
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
204
|
-
|
|
205
|
-
expect(persisted.length).toBe(1);
|
|
206
|
-
expect(persisted[0].action).toBe("pipeline.step1");
|
|
207
|
-
expect(persisted[0].args).toEqual({ step: 1, data: "test" });
|
|
208
|
-
expect(persisted[0].onErrorAction).toBe("pipeline.onError");
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// ── runAt + durable 조합 ──
|
|
212
|
-
|
|
213
|
-
it("runAt(과거 시점) — durable mode에서도 persistJob 호출 (즉시 실행 대상)", async () => {
|
|
214
|
-
const persisted: ScheduledJobRecord[] = [];
|
|
215
|
-
|
|
216
|
-
const scheduler = createScheduler({
|
|
217
|
-
persistJob: async (job) => {
|
|
218
|
-
persisted.push(job);
|
|
219
|
-
},
|
|
220
|
-
removeJob: async () => true,
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
scheduler.registerAction("past.task", async () => {});
|
|
224
|
-
|
|
225
|
-
const pastTime = new Date(Date.now() - 60_000); // 1분 전
|
|
226
|
-
scheduler.runAt(pastTime, "past.task", { expired: true });
|
|
227
|
-
|
|
228
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
229
|
-
|
|
230
|
-
expect(persisted.length).toBe(1);
|
|
231
|
-
expect(persisted[0].args).toEqual({ expired: true });
|
|
232
|
-
// runAt이 과거여도 persistJob은 호출되어야 함 (폴러가 즉시 픽업)
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
// ── 다중 cancel ──
|
|
236
|
-
|
|
237
|
-
it("같은 job을 2번 cancel — 첫 번째만 removeJob 성공", async () => {
|
|
238
|
-
let removeCount = 0;
|
|
239
|
-
|
|
240
|
-
const scheduler = createScheduler({
|
|
241
|
-
persistJob: async () => {},
|
|
242
|
-
removeJob: async () => {
|
|
243
|
-
removeCount++;
|
|
244
|
-
return removeCount === 1; // 첫 번째만 true
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
scheduler.registerAction("double.cancel", async () => {});
|
|
249
|
-
const id = scheduler.runAfter(10000, "double.cancel");
|
|
250
|
-
|
|
251
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
252
|
-
|
|
253
|
-
const r1 = scheduler.cancel(id);
|
|
254
|
-
await new Promise((r) => setTimeout(r, 20));
|
|
255
|
-
const r2 = scheduler.cancel(id);
|
|
256
|
-
|
|
257
|
-
// 첫 번째 cancel은 성공 (pendingJobs에서 제거)
|
|
258
|
-
expect(r1).toBe(true);
|
|
259
|
-
// 두 번째 cancel은 이미 제거되었으므로 false
|
|
260
|
-
expect(r2).toBe(false);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
// ── persistJob 순서 보장 ──
|
|
264
|
-
|
|
265
|
-
it("연속 runAfter 호출 시 persistJob 순서 보장", async () => {
|
|
266
|
-
const order: string[] = [];
|
|
267
|
-
|
|
268
|
-
const scheduler = createScheduler({
|
|
269
|
-
persistJob: async (job) => {
|
|
270
|
-
order.push(job.action);
|
|
271
|
-
},
|
|
272
|
-
removeJob: async () => true,
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
scheduler.registerAction("a", async () => {});
|
|
276
|
-
scheduler.registerAction("b", async () => {});
|
|
277
|
-
scheduler.registerAction("c", async () => {});
|
|
278
|
-
|
|
279
|
-
scheduler.runAfter(1000, "a");
|
|
280
|
-
scheduler.runAfter(2000, "b");
|
|
281
|
-
scheduler.runAfter(3000, "c");
|
|
282
|
-
|
|
283
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
284
|
-
|
|
285
|
-
// persistJob은 호출 순서대로 실행되어야 함
|
|
286
|
-
expect(order).toEqual(["a", "b", "c"]);
|
|
287
|
-
});
|
|
288
|
-
});
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* packages/core/src/__tests__/scheduler-durable.test.ts
|
|
3
|
-
*
|
|
4
|
-
* Scheduler Durable Mode 테스트 — createScheduler({ persistJob, removeJob })
|
|
5
|
-
* DB 콜백이 제공되면 runAfter()가 setTimeout 대신 persistJob을 호출하고,
|
|
6
|
-
* cancel()이 removeJob을 호출하는지 검증.
|
|
7
|
-
*
|
|
8
|
-
* Run: bun test packages/core/src/__tests__/scheduler-durable.test.ts
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, it, expect } from "bun:test";
|
|
12
|
-
import { createScheduler } from "../scheduler.js";
|
|
13
|
-
import type { ScheduledJobRecord } from "../scheduler.js";
|
|
14
|
-
|
|
15
|
-
describe("Scheduler Durable Mode — persistJob/removeJob", () => {
|
|
16
|
-
it("durable mode에서 runAfter → persistJob 콜백 호출", async () => {
|
|
17
|
-
const persisted: ScheduledJobRecord[] = [];
|
|
18
|
-
|
|
19
|
-
const scheduler = createScheduler({
|
|
20
|
-
persistJob: async (job) => {
|
|
21
|
-
persisted.push(job);
|
|
22
|
-
},
|
|
23
|
-
removeJob: async () => true,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
scheduler.registerAction("test.durable", async () => {});
|
|
27
|
-
const id = scheduler.runAfter(5000, "test.durable", { key: "value" });
|
|
28
|
-
|
|
29
|
-
// persistJob은 비동기이므로 약간 대기
|
|
30
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
31
|
-
|
|
32
|
-
expect(persisted.length).toBe(1);
|
|
33
|
-
expect(persisted[0].id).toBe(id);
|
|
34
|
-
expect(persisted[0].action).toBe("test.durable");
|
|
35
|
-
expect(persisted[0].args).toEqual({ key: "value" });
|
|
36
|
-
expect(persisted[0].runAt).toBeInstanceOf(Date);
|
|
37
|
-
expect(persisted[0].runAt.getTime()).toBeGreaterThan(Date.now() + 4000); // ~5초 후
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("durable mode에서 runAfter → setTimeout 실행 안 함 (외부 폴러에 위임)", async () => {
|
|
41
|
-
let actionExecuted = false;
|
|
42
|
-
|
|
43
|
-
const scheduler = createScheduler({
|
|
44
|
-
persistJob: async () => {},
|
|
45
|
-
removeJob: async () => true,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
scheduler.registerAction("no.exec", async () => {
|
|
49
|
-
actionExecuted = true;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
scheduler.runAfter(50, "no.exec");
|
|
53
|
-
|
|
54
|
-
// 100ms 대기 — durable mode에서는 action이 실행되지 않아야 함
|
|
55
|
-
await new Promise((r) => setTimeout(r, 150));
|
|
56
|
-
|
|
57
|
-
expect(actionExecuted).toBe(false);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("durable mode에서 cancel → removeJob 콜백 호출", async () => {
|
|
61
|
-
const removed: string[] = [];
|
|
62
|
-
|
|
63
|
-
const scheduler = createScheduler({
|
|
64
|
-
persistJob: async () => {},
|
|
65
|
-
removeJob: async (jobId) => {
|
|
66
|
-
removed.push(jobId);
|
|
67
|
-
return true;
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
scheduler.registerAction("noop", async () => {});
|
|
72
|
-
const id = scheduler.runAfter(10000, "noop");
|
|
73
|
-
|
|
74
|
-
// persistJob 완료 대기
|
|
75
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
76
|
-
|
|
77
|
-
scheduler.cancel(id);
|
|
78
|
-
|
|
79
|
-
// removeJob은 비동기이므로 대기
|
|
80
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
81
|
-
|
|
82
|
-
expect(removed.length).toBeGreaterThanOrEqual(1);
|
|
83
|
-
expect(removed).toContain(id);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("durable mode에서 onError action 이름이 persistJob에 전달", async () => {
|
|
87
|
-
const persisted: ScheduledJobRecord[] = [];
|
|
88
|
-
|
|
89
|
-
const scheduler = createScheduler({
|
|
90
|
-
persistJob: async (job) => {
|
|
91
|
-
persisted.push(job);
|
|
92
|
-
},
|
|
93
|
-
removeJob: async () => true,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
scheduler.registerAction("step1", async () => {});
|
|
97
|
-
scheduler.registerAction("onFail", async () => {});
|
|
98
|
-
|
|
99
|
-
scheduler.runAfter(1000, "step1", {}, { onError: "onFail" });
|
|
100
|
-
|
|
101
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
102
|
-
|
|
103
|
-
expect(persisted.length).toBe(1);
|
|
104
|
-
expect(persisted[0].onErrorAction).toBe("onFail");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("persistJob 실패 시 인메모리 fallback으로 실행", async () => {
|
|
108
|
-
let actionExecuted = false;
|
|
109
|
-
|
|
110
|
-
const scheduler = createScheduler({
|
|
111
|
-
persistJob: async () => {
|
|
112
|
-
throw new Error("DB connection failed");
|
|
113
|
-
},
|
|
114
|
-
removeJob: async () => true,
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
scheduler.registerAction("fallback.test", async () => {
|
|
118
|
-
actionExecuted = true;
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// suppress expected error log
|
|
122
|
-
const origError = console.error;
|
|
123
|
-
console.error = () => {};
|
|
124
|
-
|
|
125
|
-
scheduler.runAfter(50, "fallback.test");
|
|
126
|
-
|
|
127
|
-
// persistJob 실패 → fallback → setTimeout(50ms) → 실행
|
|
128
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
129
|
-
|
|
130
|
-
console.error = origError;
|
|
131
|
-
|
|
132
|
-
expect(actionExecuted).toBe(true);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("콜백 미설정 시 기존 인메모리 mode 유지", async () => {
|
|
136
|
-
let executed = false;
|
|
137
|
-
|
|
138
|
-
const scheduler = createScheduler(); // no options
|
|
139
|
-
|
|
140
|
-
scheduler.registerAction("mem.test", async () => {
|
|
141
|
-
executed = true;
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
scheduler.runAfter(50, "mem.test");
|
|
145
|
-
|
|
146
|
-
await new Promise((r) => setTimeout(r, 150));
|
|
147
|
-
|
|
148
|
-
expect(executed).toBe(true);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("runAt도 durable mode에서 persistJob 호출", async () => {
|
|
152
|
-
const persisted: ScheduledJobRecord[] = [];
|
|
153
|
-
|
|
154
|
-
const scheduler = createScheduler({
|
|
155
|
-
persistJob: async (job) => {
|
|
156
|
-
persisted.push(job);
|
|
157
|
-
},
|
|
158
|
-
removeJob: async () => true,
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
scheduler.registerAction("future.task", async () => {});
|
|
162
|
-
|
|
163
|
-
const futureTime = new Date(Date.now() + 60_000); // 1분 후
|
|
164
|
-
scheduler.runAt(futureTime, "future.task", { x: 1 });
|
|
165
|
-
|
|
166
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
167
|
-
|
|
168
|
-
expect(persisted.length).toBe(1);
|
|
169
|
-
expect(persisted[0].action).toBe("future.task");
|
|
170
|
-
// runAt → runAfter 변환이므로 runAt 시간과 근사해야 함
|
|
171
|
-
expect(Math.abs(persisted[0].runAt.getTime() - futureTime.getTime())).toBeLessThan(1000);
|
|
172
|
-
});
|
|
173
|
-
});
|