Skip to content

Commit ba53c1c

Browse files
authored
fix: exclude initializeState from TaskEntity method dispatch (#278)
1 parent 9ee163f commit ba53c1c

2 files changed

Lines changed: 54 additions & 1 deletion

File tree

packages/durabletask-js/src/entities/task-entity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export abstract class TaskEntity<TState> implements ITaskEntity {
169169
if (name.toLowerCase() === operationName) {
170170
const prop = (this as unknown as Record<string, unknown>)[name];
171171
// Skip non-functions and built-in methods
172-
if (typeof prop === "function" && name !== "constructor" && name !== "run") {
172+
if (typeof prop === "function" && name !== "constructor" && name !== "run" && name !== "initializeState") {
173173
return name;
174174
}
175175
}

packages/durabletask-js/test/task-entity.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,4 +422,57 @@ describe("TaskEntity", () => {
422422
);
423423
});
424424
});
425+
426+
describe("lifecycle method exclusion", () => {
427+
it("should not dispatch to overridden initializeState as an operation", async () => {
428+
const entity = new CounterEntity();
429+
const { operation } = createMockOperation("initializeState", undefined, { count: 42 });
430+
431+
// initializeState is a lifecycle method, not a user-facing operation.
432+
// Even though CounterEntity overrides it, it should not be callable.
433+
await expect(entity.run(operation)).rejects.toThrow(
434+
"No suitable method found for entity operation 'initializeState'",
435+
);
436+
});
437+
438+
it("should not dispatch to initializeState case-insensitively", async () => {
439+
const entity = new CounterEntity();
440+
const { operation } = createMockOperation("INITIALIZESTATE", undefined, { count: 42 });
441+
442+
await expect(entity.run(operation)).rejects.toThrow(
443+
"No suitable method found for entity operation 'INITIALIZESTATE'",
444+
);
445+
});
446+
447+
it("should still call initializeState internally when state is null", async () => {
448+
const entity = new CounterEntity();
449+
// No initial state — initializeState should be called internally
450+
const { operation } = createMockOperation("get", undefined, undefined);
451+
452+
const result = await entity.run(operation);
453+
454+
// initializeState returns { count: 0 }, so get() returns 0
455+
expect(result).toBe(0);
456+
});
457+
458+
it("should not dispatch to 'run' as an operation", async () => {
459+
// Verify that 'run' remains excluded from dispatch
460+
class SimpleEntity extends TaskEntity<{ value: number }> {
461+
getValue(): number {
462+
return this.state.value;
463+
}
464+
465+
protected initializeState(): { value: number } {
466+
return { value: 0 };
467+
}
468+
}
469+
470+
const entity = new SimpleEntity();
471+
const { operation } = createMockOperation("run", undefined, { value: 10 });
472+
473+
await expect(entity.run(operation)).rejects.toThrow(
474+
"No suitable method found for entity operation 'run'",
475+
);
476+
});
477+
});
425478
});

0 commit comments

Comments
 (0)