more tests and fixes

This commit is contained in:
Eric Allam
2025-03-24 21:56:57 +00:00
parent ef871b0434
commit 2262a42632
3 changed files with 224 additions and 40 deletions
+18 -22
View File
@@ -57,7 +57,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.globalStartHooks.set(id, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -70,7 +70,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.taskStartHooks.set(taskId, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -89,7 +89,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
const registeredHook = {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
};
@@ -122,7 +122,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.globalFailureHooks.set(id, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -135,7 +135,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.taskFailureHooks.set(taskId, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -153,7 +153,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.globalSuccessHooks.set(id, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -166,7 +166,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.taskSuccessHooks.set(taskId, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -184,7 +184,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.globalCompleteHooks.set(id, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -197,7 +197,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.taskCompleteHooks.set(taskId, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -215,7 +215,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.globalWaitHooks.set(id, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -228,7 +228,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.taskWaitHooks.set(taskId, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -246,7 +246,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.globalResumeHooks.set(id, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -259,7 +259,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.taskResumeHooks.set(taskId, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -279,7 +279,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.globalCatchErrorHooks.set(id, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -292,7 +292,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.taskCatchErrorHooks.set(taskId, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -312,7 +312,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.globalMiddlewareHooks.set(id, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -325,7 +325,7 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
this.taskMiddlewareHooks.set(taskId, {
id,
name: hook.id ?? hook.fn.name ? (hook.fn.name === "" ? undefined : hook.fn.name) : undefined,
name: hook.id,
fn: hook.fn,
});
}
@@ -507,9 +507,5 @@ export class NoopLifecycleHooksManager implements LifecycleHooksManager {
}
function generateHookId(hook: RegisterHookFunctionParams<any>): string {
return hook.id ?? hook.fn.name
? hook.fn.name === ""
? hook.fn.toString()
: hook.fn.name
: hook.fn.toString();
return hook.id ?? hook.fn.toString();
}
+20 -16
View File
@@ -3,13 +3,8 @@ import { VERSION } from "../../version.js";
import { ApiError, RateLimitError } from "../apiClient/errors.js";
import { ConsoleInterceptor } from "../consoleInterceptor.js";
import { isInternalError, parseError, sanitizeError, TaskPayloadParsedError } from "../errors.js";
import {
flattenAttributes,
lifecycleHooks,
runMetadata,
TriggerConfig,
waitUntil,
} from "../index.js";
import { flattenAttributes, lifecycleHooks, runMetadata, waitUntil } from "../index.js";
import { TaskCompleteResult } from "../lifecycleHooks/types.js";
import { recordSpanException, TracingSDK } from "../otel/index.js";
import { runTimelineMetrics } from "../run-timeline-metrics-api.js";
import {
@@ -25,7 +20,6 @@ import { SemanticInternalAttributes } from "../semanticInternalAttributes.js";
import { taskContext } from "../task-context-api.js";
import { TriggerTracer } from "../tracer.js";
import { HandleErrorFunction, TaskMetadataWithFunctions } from "../types/index.js";
import { UsageMeasurement } from "../usage/types.js";
import {
conditionallyExportPacket,
conditionallyImportPacket,
@@ -34,7 +28,6 @@ import {
stringifyIO,
} from "../utils/ioSerialization.js";
import { calculateNextRetryDelay } from "../utils/retries.js";
import { TaskCompleteResult } from "../lifecycleHooks/types.js";
export type TaskExecutorOptions = {
tracingSDK: TracingSDK;
@@ -134,12 +127,12 @@ export class TaskExecutor {
try {
parsedPayload = await this.#parsePayload(parsedPayload);
if (execution.attempt.number === 1) {
await this.#callOnStartFunctions(parsedPayload, ctx, signal);
}
initOutput = await this.#callInitFunctions(parsedPayload, ctx, signal);
if (execution.attempt.number === 1) {
await this.#callOnStartFunctions(parsedPayload, ctx, initOutput, signal);
}
const output = await this.#callRun(parsedPayload, ctx, initOutput, signal);
try {
@@ -573,7 +566,12 @@ export class TaskExecutor {
}
}
async #callOnStartFunctions(payload: unknown, ctx: TaskRunContext, signal?: AbortSignal) {
async #callOnStartFunctions(
payload: unknown,
ctx: TaskRunContext,
initOutput: any,
signal?: AbortSignal
) {
const globalStartHooks = lifecycleHooks.getGlobalStartHooks();
const taskStartHook = lifecycleHooks.getTaskStartHook(this.task.id);
@@ -592,7 +590,7 @@ export class TaskExecutor {
await this._tracer.startActiveSpan(
hook.name ?? "global",
async (span) => {
await hook.fn({ payload, ctx, signal, task: this.task.id });
await hook.fn({ payload, ctx, signal, task: this.task.id, init: initOutput });
},
{
attributes: {
@@ -606,7 +604,13 @@ export class TaskExecutor {
await this._tracer.startActiveSpan(
"task",
async (span) => {
await taskStartHook({ payload, ctx, signal, task: this.task.id });
await taskStartHook({
payload,
ctx,
signal,
task: this.task.id,
init: initOutput,
});
},
{
attributes: {
+186 -2
View File
@@ -89,7 +89,7 @@ describe("TaskExecutor", () => {
run: {
id: "test-run-id",
payload: "{}",
payloadType: "json",
payloadType: "application/json",
metadata: {},
startedAt: new Date(),
tags: [],
@@ -261,7 +261,7 @@ describe("TaskExecutor", () => {
run: {
id: "test-run-id",
payload: "{}",
payloadType: "json",
payloadType: "application/json",
metadata: {},
startedAt: new Date(),
tags: [],
@@ -340,4 +340,188 @@ describe("TaskExecutor", () => {
},
});
});
test("should call onStart hooks in correct order with proper data", async () => {
const globalStartOrder: string[] = [];
const startPayloads: any[] = [];
const startInits: any[] = [];
// Register global init hook to provide init data
lifecycleHooks.registerGlobalInitHook({
id: "test-init",
fn: async () => {
return {
foo: "bar",
};
},
});
// Register two global start hooks
lifecycleHooks.registerGlobalStartHook({
id: "global-start-1",
fn: async ({ payload, ctx, init }) => {
console.log("Executing global start hook 1");
globalStartOrder.push("global-1");
startPayloads.push(payload);
startInits.push(init);
},
});
lifecycleHooks.registerGlobalStartHook({
id: "global-start-2",
fn: async ({ payload, ctx, init }) => {
console.log("Executing global start hook 2");
globalStartOrder.push("global-2");
startPayloads.push(payload);
startInits.push(init);
},
});
// Register task-specific start hook
lifecycleHooks.registerTaskStartHook("test-task", {
id: "task-start",
fn: async ({ payload, ctx, init }) => {
console.log("Executing task start hook");
globalStartOrder.push("task");
startPayloads.push(payload);
startInits.push(init);
},
});
// Verify hooks are registered
const globalHooks = lifecycleHooks.getGlobalStartHooks();
console.log(
"Registered global hooks:",
globalHooks.map((h) => h.id)
);
const taskHook = lifecycleHooks.getTaskStartHook("test-task");
console.log("Registered task hook:", taskHook ? "yes" : "no");
const task = {
id: "test-task",
fns: {
run: async (payload: any, params: RunFnParams<any>) => {
return {
output: "test-output",
init: params.init,
};
},
},
};
const tracingSDK = new TracingSDK({
url: "http://localhost:4318",
});
const tracer = new TriggerTracer({
name: "test-task",
version: "1.0.0",
tracer: tracingSDK.getTracer("test-task"),
logger: tracingSDK.getLogger("test-task"),
});
const consoleInterceptor = new ConsoleInterceptor(tracingSDK.getLogger("test-task"), false);
const executor = new TaskExecutor(task, {
tracingSDK,
tracer,
consoleInterceptor,
retries: {
enabledInDev: false,
default: {
maxAttempts: 1,
},
},
handleErrorFn: undefined,
});
const execution: TaskRunExecution = {
task: {
id: "test-task",
filePath: "test-task.ts",
},
attempt: {
number: 1,
startedAt: new Date(),
id: "test-attempt-id",
status: "success",
backgroundWorkerId: "test-background-worker-id",
backgroundWorkerTaskId: "test-background-worker-task-id",
},
run: {
id: "test-run-id",
payload: '{"test":"data"}',
payloadType: "application/json",
metadata: {},
startedAt: new Date(),
tags: [],
isTest: false,
createdAt: new Date(),
durationMs: 0,
costInCents: 0,
baseCostInCents: 0,
priority: 0,
},
machine: {
name: "micro",
cpu: 1,
memory: 1,
centsPerMs: 0,
},
queue: {
name: "test-queue",
id: "test-queue-id",
},
environment: {
type: "PRODUCTION",
id: "test-environment-id",
slug: "test-environment-slug",
},
organization: {
id: "test-organization-id",
name: "test-organization-name",
slug: "test-organization-slug",
},
project: {
id: "test-project-id",
name: "test-project-name",
slug: "test-project-slug",
ref: "test-project-ref",
},
};
const worker: ServerBackgroundWorker = {
id: "test-background-worker-id",
version: "1.0.0",
contentHash: "test-content-hash",
engine: "V2",
};
const result = await executor.execute(execution, worker, {});
// Verify hooks were called in correct order
expect(globalStartOrder).toEqual(["global-1", "global-2", "task"]);
// Verify each hook received the correct payload
startPayloads.forEach((payload) => {
expect(payload).toEqual({ test: "data" });
});
console.log("startInits", startInits);
// Verify each hook received the correct init data
startInits.forEach((init) => {
expect(init).toEqual({ foo: "bar" });
});
// Verify the final result
expect(result).toEqual({
result: {
ok: true,
id: "test-run-id",
output: '{"json":{"output":"test-output","init":{"foo":"bar"}}}',
outputType: "application/super+json",
},
});
});
});