Skip to content

Commit 956aa0b

Browse files
Merge pull request #39 from backpine/test-handlers
Test handlers
2 parents a2ae077 + 7423c59 commit 956aa0b

6 files changed

Lines changed: 1702 additions & 118 deletions

File tree

packages/jobs/src/registry/registry.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,7 @@ export function toRuntimeRegistry<
148148
string,
149149
StoredContinuousDefinition
150150
>,
151-
debounce: registry.debounce as Record<
152-
string,
153-
StoredDebounceDefinition
154-
>,
151+
debounce: registry.debounce as Record<string, StoredDebounceDefinition>,
155152
workerPool: registry.workerPool as Record<
156153
string,
157154
StoredWorkerPoolDefinition

packages/jobs/test/handlers/continuous.test.ts

Lines changed: 57 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
// packages/jobs/test/handlers/continuous.test.ts
22

33
import { describe, it, expect, beforeEach } from "vitest";
4-
import { Effect, Layer, Schema, Duration } from "effect";
5-
import { createTestRuntime, NoopTrackerLayer } from "@durable-effect/core";
6-
import {
7-
ContinuousHandler,
8-
ContinuousHandlerLayer,
9-
type ContinuousResponse,
10-
} from "../../src/handlers/continuous";
11-
import { MetadataService, MetadataServiceLayer } from "../../src/services/metadata";
12-
import { AlarmService, AlarmServiceLayer } from "../../src/services/alarm";
13-
import { RegistryService, RegistryServiceLayer } from "../../src/services/registry";
14-
import { JobExecutionServiceLayer } from "../../src/services/execution";
15-
import { CleanupServiceLayer } from "../../src/services/cleanup";
16-
import { RetryExecutorLayer } from "../../src/retry";
4+
import { Effect, Schema, Duration } from "effect";
5+
import { ContinuousHandler } from "../../src/handlers/continuous";
176
import { Continuous } from "../../src/definitions/continuous";
18-
import type { RuntimeJobRegistry } from "../../src/registry/typed";
7+
import {
8+
createTestRegistry,
9+
createContinuousTestLayer,
10+
runWithLayer,
11+
runExitWithLayer,
12+
} from "./test-utils";
1913

2014
// =============================================================================
2115
// Test Fixtures
@@ -98,86 +92,15 @@ const terminatingPrimitive = Continuous.make({
9892
}),
9993
});
10094

101-
// Create test registry using object types (RuntimeJobRegistry)
102-
const createTestRegistry = (): RuntimeJobRegistry => ({
103-
continuous: {
104-
"counter": { ...counterPrimitive, name: "counter" },
105-
"failing": { ...failingPrimitive, name: "failing" },
106-
"no-immediate": { ...noImmediateStartPrimitive, name: "no-immediate" },
107-
"terminating": { ...terminatingPrimitive, name: "terminating" },
108-
} as Record<string, any>,
109-
debounce: {} as Record<string, any>,
110-
workerPool: {} as Record<string, any>,
111-
task: {} as Record<string, any>,
112-
});
113-
114-
// =============================================================================
115-
// Test Helpers
116-
// =============================================================================
117-
118-
// Helper to run Effect with layer, bypassing strict R parameter checking
119-
// This is needed because the test registry uses `as Record<string, any>` which
120-
// causes `any` to leak into Effect R parameters
121-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
122-
const runWithLayer = <A, E>(
123-
effect: Effect.Effect<A, E, any>,
124-
layer: Layer.Layer<ContinuousHandler>
125-
): Promise<A> =>
126-
Effect.runPromise(
127-
effect.pipe(Effect.provide(layer)) as Effect.Effect<A, E, never>
128-
);
129-
130-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
131-
const runExitWithLayer = <A, E>(
132-
effect: Effect.Effect<A, E, any>,
133-
layer: Layer.Layer<ContinuousHandler>
134-
) =>
135-
Effect.runPromiseExit(
136-
effect.pipe(Effect.provide(layer)) as Effect.Effect<A, E, never>
137-
);
138-
139-
// =============================================================================
140-
// Test Setup
141-
// =============================================================================
142-
143-
const createTestLayer = (initialTime = 1000000) => {
144-
const { layer: coreLayer, time, handles } = createTestRuntime("test-instance", initialTime);
145-
const registry = createTestRegistry();
146-
147-
const servicesLayer = Layer.mergeAll(
148-
MetadataServiceLayer,
149-
AlarmServiceLayer
150-
).pipe(
151-
Layer.provideMerge(NoopTrackerLayer),
152-
Layer.provideMerge(coreLayer)
153-
);
154-
155-
// RetryExecutor depends on AlarmService from servicesLayer
156-
const retryLayer = RetryExecutorLayer.pipe(
157-
Layer.provideMerge(servicesLayer)
158-
);
159-
160-
// CleanupService depends on AlarmService and StorageAdapter
161-
const cleanupLayer = CleanupServiceLayer.pipe(
162-
Layer.provideMerge(servicesLayer)
163-
);
164-
165-
// JobExecutionService depends on RetryExecutor, CleanupService, and core adapters
166-
const executionLayer = JobExecutionServiceLayer.pipe(
167-
Layer.provideMerge(retryLayer),
168-
Layer.provideMerge(cleanupLayer),
169-
Layer.provideMerge(coreLayer)
170-
);
171-
172-
const handlerLayer = ContinuousHandlerLayer.pipe(
173-
Layer.provideMerge(RegistryServiceLayer(registry)),
174-
Layer.provideMerge(servicesLayer),
175-
Layer.provideMerge(retryLayer),
176-
Layer.provideMerge(executionLayer)
177-
) as Layer.Layer<ContinuousHandler>;
178-
179-
return { layer: handlerLayer, time, handles, coreLayer };
180-
};
95+
const createRegistry = () =>
96+
createTestRegistry({
97+
continuous: {
98+
counter: { ...counterPrimitive, name: "counter" },
99+
failing: { ...failingPrimitive, name: "failing" },
100+
"no-immediate": { ...noImmediateStartPrimitive, name: "no-immediate" },
101+
terminating: { ...terminatingPrimitive, name: "terminating" },
102+
},
103+
});
181104

182105
// =============================================================================
183106
// Tests
@@ -192,7 +115,8 @@ describe("ContinuousHandler", () => {
192115

193116
describe("start action", () => {
194117
it("creates a new instance and executes immediately by default", async () => {
195-
const { layer } = createTestLayer(1000000);
118+
const registry = createRegistry();
119+
const { layer } = createContinuousTestLayer(registry, 1000000);
196120

197121
const result = await runWithLayer(
198122
Effect.gen(function* () {
@@ -222,7 +146,8 @@ describe("ContinuousHandler", () => {
222146
});
223147

224148
it("returns existing instance if already started", async () => {
225-
const { layer } = createTestLayer();
149+
const registry = createRegistry();
150+
const { layer } = createContinuousTestLayer(registry);
226151

227152
const result = await runWithLayer(
228153
Effect.gen(function* () {
@@ -258,7 +183,8 @@ describe("ContinuousHandler", () => {
258183
});
259184

260185
it("respects startImmediately: false", async () => {
261-
const { layer } = createTestLayer();
186+
const registry = createRegistry();
187+
const { layer } = createContinuousTestLayer(registry);
262188

263189
const result = await runWithLayer(
264190
Effect.gen(function* () {
@@ -284,7 +210,8 @@ describe("ContinuousHandler", () => {
284210

285211
describe("terminate action", () => {
286212
it("terminates a running instance and deletes all storage", async () => {
287-
const { layer } = createTestLayer();
213+
const registry = createRegistry();
214+
const { layer } = createContinuousTestLayer(registry);
288215

289216
const result = await runWithLayer(
290217
Effect.gen(function* () {
@@ -330,7 +257,8 @@ describe("ContinuousHandler", () => {
330257
});
331258

332259
it("returns not_found for non-existent instance", async () => {
333-
const { layer } = createTestLayer();
260+
const registry = createRegistry();
261+
const { layer } = createContinuousTestLayer(registry);
334262

335263
const result = await runWithLayer(
336264
Effect.gen(function* () {
@@ -352,7 +280,8 @@ describe("ContinuousHandler", () => {
352280
});
353281

354282
it("returns not_found when called twice (since first terminate deletes all storage)", async () => {
355-
const { layer } = createTestLayer();
283+
const registry = createRegistry();
284+
const { layer } = createContinuousTestLayer(registry);
356285

357286
const result = await runWithLayer(
358287
Effect.gen(function* () {
@@ -396,7 +325,8 @@ describe("ContinuousHandler", () => {
396325

397326
describe("trigger action", () => {
398327
it("triggers immediate execution", async () => {
399-
const { layer } = createTestLayer();
328+
const registry = createRegistry();
329+
const { layer } = createContinuousTestLayer(registry);
400330

401331
const result = await runWithLayer(
402332
Effect.gen(function* () {
@@ -431,7 +361,8 @@ describe("ContinuousHandler", () => {
431361
});
432362

433363
it("returns triggered: false for non-existent instance", async () => {
434-
const { layer } = createTestLayer();
364+
const registry = createRegistry();
365+
const { layer } = createContinuousTestLayer(registry);
435366

436367
const result = await runWithLayer(
437368
Effect.gen(function* () {
@@ -451,7 +382,8 @@ describe("ContinuousHandler", () => {
451382
});
452383

453384
it("returns triggered: false for terminated instance", async () => {
454-
const { layer } = createTestLayer();
385+
const registry = createRegistry();
386+
const { layer } = createContinuousTestLayer(registry);
455387

456388
const result = await runWithLayer(
457389
Effect.gen(function* () {
@@ -490,7 +422,8 @@ describe("ContinuousHandler", () => {
490422

491423
describe("status action", () => {
492424
it("returns status for running instance", async () => {
493-
const { layer } = createTestLayer();
425+
const registry = createRegistry();
426+
const { layer } = createContinuousTestLayer(registry);
494427

495428
const result = await runWithLayer(
496429
Effect.gen(function* () {
@@ -520,7 +453,8 @@ describe("ContinuousHandler", () => {
520453
});
521454

522455
it("returns not_found for non-existent instance", async () => {
523-
const { layer } = createTestLayer();
456+
const registry = createRegistry();
457+
const { layer } = createContinuousTestLayer(registry);
524458

525459
const result = await runWithLayer(
526460
Effect.gen(function* () {
@@ -542,7 +476,8 @@ describe("ContinuousHandler", () => {
542476

543477
describe("getState action", () => {
544478
it("returns current state", async () => {
545-
const { layer } = createTestLayer();
479+
const registry = createRegistry();
480+
const { layer } = createContinuousTestLayer(registry);
546481

547482
const result = await runWithLayer(
548483
Effect.gen(function* () {
@@ -574,7 +509,8 @@ describe("ContinuousHandler", () => {
574509

575510
describe("handleAlarm", () => {
576511
it("executes on alarm and schedules next", async () => {
577-
const { layer, time, handles } = createTestLayer(1000000);
512+
const registry = createRegistry();
513+
const { layer, time, handles } = createContinuousTestLayer(registry, 1000000);
578514

579515
await runWithLayer(
580516
Effect.gen(function* () {
@@ -609,7 +545,8 @@ describe("ContinuousHandler", () => {
609545
});
610546

611547
it("does nothing when terminated", async () => {
612-
const { layer, time } = createTestLayer();
548+
const registry = createRegistry();
549+
const { layer, time } = createContinuousTestLayer(registry);
613550

614551
await runWithLayer(
615552
Effect.gen(function* () {
@@ -646,7 +583,8 @@ describe("ContinuousHandler", () => {
646583

647584
describe("error handling", () => {
648585
it("fails with ExecutionError when execute fails without retry config", async () => {
649-
const { layer } = createTestLayer();
586+
const registry = createRegistry();
587+
const { layer } = createContinuousTestLayer(registry);
650588

651589
const resultExit = await runExitWithLayer(
652590
Effect.gen(function* () {
@@ -670,7 +608,8 @@ describe("ContinuousHandler", () => {
670608

671609
describe("ctx.terminate", () => {
672610
it("terminates on first run when condition met (purges state by default)", async () => {
673-
const { layer } = createTestLayer();
611+
const registry = createRegistry();
612+
const { layer } = createContinuousTestLayer(registry);
674613

675614
const result = await runWithLayer(
676615
Effect.gen(function* () {
@@ -713,7 +652,8 @@ describe("ContinuousHandler", () => {
713652
});
714653

715654
it("terminates during alarm and stops further alarms", async () => {
716-
const { layer, time, handles } = createTestLayer(1000000);
655+
const registry = createRegistry();
656+
const { layer, time, handles } = createContinuousTestLayer(registry, 1000000);
717657

718658
await runWithLayer(
719659
Effect.gen(function* () {
@@ -763,7 +703,8 @@ describe("ContinuousHandler", () => {
763703
});
764704

765705
it("trigger action returns terminated: true when terminate called", async () => {
766-
const { layer } = createTestLayer();
706+
const registry = createRegistry();
707+
const { layer } = createContinuousTestLayer(registry);
767708

768709
const result = await runWithLayer(
769710
Effect.gen(function* () {
@@ -797,7 +738,8 @@ describe("ContinuousHandler", () => {
797738
});
798739

799740
it("trigger action returns triggered: false for terminated instance", async () => {
800-
const { layer } = createTestLayer();
741+
const registry = createRegistry();
742+
const { layer } = createContinuousTestLayer(registry);
801743

802744
const result = await runWithLayer(
803745
Effect.gen(function* () {
@@ -830,7 +772,8 @@ describe("ContinuousHandler", () => {
830772
});
831773

832774
it("handleAlarm does nothing for terminated instance", async () => {
833-
const { layer, time } = createTestLayer();
775+
const registry = createRegistry();
776+
const { layer, time } = createContinuousTestLayer(registry);
834777

835778
await runWithLayer(
836779
Effect.gen(function* () {

0 commit comments

Comments
 (0)