Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion packages/workflow-executor/src/executors/base-step-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,27 @@ export default abstract class BaseStepExecutor<TStep extends StepDefinition = St
}

async execute(): Promise<StepExecutionResult> {
const { runId, stepId, stepIndex, stepDefinition, baseRecordRef } = this.context;

this.context.logger.info('Step execution started', {
runId,
stepId,
stepIndex,
stepType: stepDefinition.type,
collection: baseRecordRef.collectionName,
});

try {
return await this.doExecute();
const result = await this.doExecute();

this.context.logger.info('Step execution completed', {
runId,
stepId,
stepIndex,
status: result.stepOutcome.status,
});

return result;
} catch (error) {
if (error instanceof WorkflowExecutorError) {
if (error.cause !== undefined) {
Expand Down
2 changes: 1 addition & 1 deletion packages/workflow-executor/src/ports/logger-port.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface Logger {
error(message: string, context: Record<string, unknown>): void;
info?(message: string, context: Record<string, unknown>): void;
info(message: string, context: Record<string, unknown>): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function makeMockRunStore(stepExecutions: StepExecutionData[] = []): RunStore {
}

function makeMockLogger(): Logger {
return { error: jest.fn() };
return { info: jest.fn(), error: jest.fn() };
}

function makeContext(overrides: Partial<ExecutionContext> = {}): ExecutionContext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function makeContext(
},
schemaCache: new SchemaCache(),
previousSteps: [],
logger: { error: jest.fn() },
logger: { info: jest.fn(), error: jest.fn() },
...overrides,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ function makeContext(
},
schemaCache: new SchemaCache(),
previousSteps: [],
logger: { error: jest.fn() },
logger: { info: jest.fn(), error: jest.fn() },
...overrides,
};
}
Expand Down Expand Up @@ -1295,7 +1295,7 @@ describe('LoadRelatedRecordStepExecutor', () => {
});

it('returns user message and logs cause when agentPort.getRelatedData throws an infra error', async () => {
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const agentPort = makeMockAgentPort();
(agentPort.getRelatedData as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
const mockModel = makeMockModel({ relationName: 'Order', reasoning: 'test' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ function makeContext(
},
schemaCache: new SchemaCache(),
previousSteps: [],
logger: { error: jest.fn() },
logger: { info: jest.fn(), error: jest.fn() },
...overrides,
};
}
Expand Down Expand Up @@ -218,7 +218,7 @@ describe('McpTaskStepExecutor', () => {
tool_calls: [{ name: 'send_notification', args: { message: 'Hi' }, id: 'call_1' }],
})
.mockResolvedValueOnce({ tool_calls: [] });
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const runStore = makeMockRunStore();
const context = makeContext({
model,
Expand Down Expand Up @@ -299,7 +299,7 @@ describe('McpTaskStepExecutor', () => {

it('returns error when saveStepExecution fails (Branch C)', async () => {
const { model } = makeMockModel('send_notification', { message: 'Hello' });
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const runStore = makeMockRunStore({
saveStepExecution: jest.fn().mockRejectedValue(new Error('DB unavailable')),
});
Expand Down Expand Up @@ -491,7 +491,7 @@ describe('McpTaskStepExecutor', () => {
invoke: invokeFn,
});
const { model } = makeMockModel('send_notification', { message: 'Hello' });
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const runStore = makeMockRunStore({
saveStepExecution: jest.fn().mockRejectedValue(new Error('Disk full')),
});
Expand Down Expand Up @@ -529,7 +529,7 @@ describe('McpTaskStepExecutor', () => {
userConfirmed: true,
},
};
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const runStore = makeMockRunStore({
getStepExecutions: jest.fn().mockResolvedValue([execution]),
saveStepExecution: jest.fn().mockRejectedValue(new Error('Disk full')),
Expand Down Expand Up @@ -638,7 +638,7 @@ describe('McpTaskStepExecutor', () => {
invoke: invokeFn,
});
const { model } = makeMockModel('send_notification', {});
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const context = makeContext({
model,
stepDefinition: makeStep({ automaticExecution: true }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ function makeContext(
},
schemaCache: new SchemaCache(),
previousSteps: [],
logger: { error: jest.fn() },
logger: { info: jest.fn(), error: jest.fn() },
...overrides,
};
}
Expand Down Expand Up @@ -668,7 +668,7 @@ describe('ReadRecordStepExecutor', () => {
});

it('returns user message and logs cause when agentPort.getRecord throws an infra error', async () => {
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const agentPort = makeMockAgentPort();
(agentPort.getRecord as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
const mockModel = makeMockModel({ fieldNames: ['email'] });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ function makeContext(
},
schemaCache: new SchemaCache(),
previousSteps: [],
logger: { error: jest.fn() },
logger: { info: jest.fn(), error: jest.fn() },
...overrides,
};
}
Expand Down Expand Up @@ -518,7 +518,7 @@ describe('TriggerRecordActionStepExecutor', () => {
});

it('returns user message and logs cause when agentPort.executeAction throws an infra error', async () => {
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const agentPort = makeMockAgentPort();
(agentPort.executeAction as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
const mockModel = makeMockModel({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function makeContext(
},
schemaCache: new SchemaCache(),
previousSteps: [],
logger: { error: jest.fn() },
logger: { info: jest.fn(), error: jest.fn() },
...overrides,
};
}
Expand Down Expand Up @@ -681,7 +681,7 @@ describe('UpdateRecordStepExecutor', () => {
});

it('returns user message and logs cause when agentPort.updateRecord throws an infra error', async () => {
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const agentPort = makeMockAgentPort();
(agentPort.updateRecord as jest.Mock).mockRejectedValue(new Error('DB connection lost'));
const mockModel = makeMockModel({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function createServer(
overrides: {
runner?: Runner;
workflowPort?: WorkflowPort;
logger?: { error: jest.Mock };
logger?: { info: jest.Mock; error: jest.Mock };
} = {},
) {
return new ExecutorHttpServer({
Expand Down Expand Up @@ -252,7 +252,7 @@ describe('ExecutorHttpServer', () => {
});

it('returns 503 when hasRunAccess throws', async () => {
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const workflowPort = createMockWorkflowPort({
hasRunAccess: jest.fn().mockRejectedValue(new Error('orchestrator down')),
});
Expand Down
8 changes: 4 additions & 4 deletions packages/workflow-executor/test/runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function createMockAiClient() {
}

function createMockLogger(): jest.Mocked<Required<Logger>> {
return { error: jest.fn(), info: jest.fn() };
return { info: jest.fn(), error: jest.fn() };
}

const VALID_ENV_SECRET = 'a'.repeat(64);
Expand Down Expand Up @@ -708,7 +708,7 @@ describe('StepExecutorFactory.create — factory', () => {
workflowPort: {} as WorkflowPort,
runStore: {} as RunStore,
schemaCache: new SchemaCache(),
logger: { error: jest.fn() },
logger: { info: jest.fn(), error: jest.fn() },
});

it('dispatches Condition steps to ConditionStepExecutor', async () => {
Expand Down Expand Up @@ -774,7 +774,7 @@ describe('StepExecutorFactory.create — factory', () => {
const rootCause = new Error('root cause');
const error = new Error('wrapper');
(error as Error & { cause: Error }).cause = rootCause;
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const contextConfig: StepContextConfig = {
...makeContextConfig(),
aiClient: {
Expand All @@ -796,7 +796,7 @@ describe('StepExecutorFactory.create — factory', () => {
it('logs cause as undefined when construction error cause is not an Error instance', async () => {
const error = new Error('wrapper');
(error as Error & { cause: string }).cause = 'plain string';
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
const contextConfig: StepContextConfig = {
...makeContextConfig(),
aiClient: {
Expand Down
4 changes: 2 additions & 2 deletions packages/workflow-executor/test/stores/database-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe('DatabaseStore (SQLite)', () => {
.spyOn(badSequelize.getQueryInterface(), 'createTable')
.mockRejectedValueOnce(new Error('disk full'));

const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
await expect(badStore.init(logger)).rejects.toThrow('disk full');
expect(logger.error).toHaveBeenCalledWith(
'Database migration failed',
Expand All @@ -129,7 +129,7 @@ describe('DatabaseStore (SQLite)', () => {
});

it('close() catches and logs errors instead of throwing', async () => {
const logger = { error: jest.fn() };
const logger = { info: jest.fn(), error: jest.fn() };
jest.spyOn(sequelize, 'close').mockRejectedValueOnce(new Error('close failed'));

await expect(store.close(logger)).resolves.toBeUndefined();
Expand Down
Loading