Skip to content

Commit

Permalink
Refactored old integration tests to use integration testing helpers.
Browse files Browse the repository at this point in the history
Extended integration testing helpers to allow for configurable test contexts.
  • Loading branch information
THardy98 committed Dec 11, 2024
1 parent 9c0052c commit 7e0aac3
Show file tree
Hide file tree
Showing 32 changed files with 1,869 additions and 1,943 deletions.
4 changes: 3 additions & 1 deletion packages/test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"build:ts": "tsc --build",
"build:protos": "node ./scripts/compile-proto.js",
"test": "ava ./lib/test-*.js",
"test.watch": "ava --watch ./lib/test-*.js"
"test.watch": "ava --watch ./lib/test-*.js",
"test-old": "RUN_INTEGRATION_TESTS=1 ava ./lib/test-integration-old.js",
"test-refactor": "RUN_INTEGRATION_TESTS=1 ava ./lib/test-integration-new.js"
},
"ava": {
"timeout": "60s",
Expand Down
142 changes: 92 additions & 50 deletions packages/test/src/helpers-integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
isGrpcServiceError,
WorkflowFailedError,
WorkflowHandle,
WorkflowHandleWithFirstExecutionRunId,
WorkflowStartOptions,
WorkflowUpdateFailedError,
} from '@temporalio/client';
Expand All @@ -20,6 +21,7 @@ import {
Runtime,
WorkerOptions,
WorkflowBundle,
WorkflowBundleWithSourceMap,
bundleWorkflowCode,
makeTelemetryFilterString,
} from '@temporalio/worker';
Expand Down Expand Up @@ -51,57 +53,89 @@ const defaultDynamicConfigOptions = [
'worker.removableBuildIdDurationSinceDefault=1',
];

function setupRuntime(recordedLogs?: { [workflowId: string]: LogEntry[] }) {
const logger = recordedLogs
? new DefaultLogger('DEBUG', (entry) => {
const workflowId = (entry.meta as any)?.workflowInfo?.workflowId ?? (entry.meta as any)?.workflowId;
recordedLogs![workflowId] ??= [];
recordedLogs![workflowId].push(entry);
})
: new DefaultLogger((process.env.TEST_LOG_LEVEL || 'DEBUG').toUpperCase() as LogLevel);
Runtime.install({
logger,
telemetryOptions: {
logging: {
filter: makeTelemetryFilterString({
core: (process.env.TEST_LOG_LEVEL || 'INFO').toUpperCase() as LogLevel,
}),
},
},
});
}

export async function createTestWorkflowBundle(
workflowsPath: string,
workflowInterceptorModules?: string[]
): Promise<WorkflowBundleWithSourceMap> {
return await bundleWorkflowCode({
...bundlerOptions,
workflowInterceptorModules: [...defaultWorkflowInterceptorModules, ...(workflowInterceptorModules ?? [])],
workflowsPath,
logger: new DefaultLogger('WARN'),
});
}

export async function createTestEnvironment(
opts?: LocalTestWorkflowEnvironmentOptions
): Promise<TestWorkflowEnvironment> {
return await TestWorkflowEnvironment.createLocal({
...(opts || {}), // Use provided options or default to an empty object
server: {
...(opts?.server || {}), // Use provided server options or default to an empty object
extraArgs: [
...defaultDynamicConfigOptions.flatMap((opt) => ['--dynamic-config-value', opt]),
...(opts?.server?.extraArgs ?? []),
],
},
});
}

export function makeConfigurableEnvironmentTest<T>(opts: {
recordedLogs?: { [workflowId: string]: LogEntry[] };
createTestContext: (t: ExecutionContext) => Promise<T>;
teardown: (t: T) => Promise<void>;
}): TestFn<T> {
const test = anyTest as TestFn<T>;
test.before(async (t) => {
setupRuntime(opts.recordedLogs);
t.context = await opts.createTestContext(t);
});
test.after.always(async (t) => {
await opts.teardown(t.context);
});
return test;
}

export function makeTestFunction(opts: {
workflowsPath: string;
workflowEnvironmentOpts?: LocalTestWorkflowEnvironmentOptions;
workflowInterceptorModules?: string[];
recordedLogs?: { [workflowId: string]: LogEntry[] };
}): TestFn<Context> {
const test = anyTest as TestFn<Context>;
test.before(async (t) => {
const workflowBundle = await bundleWorkflowCode({
...bundlerOptions,
workflowInterceptorModules: [...defaultWorkflowInterceptorModules, ...(opts.workflowInterceptorModules ?? [])],
workflowsPath: opts.workflowsPath,
logger: new DefaultLogger('WARN'),
});
const logger = opts.recordedLogs
? new DefaultLogger('DEBUG', (entry) => {
const workflowId = (entry.meta as any)?.workflowInfo?.workflowId ?? (entry.meta as any)?.workflowId;
opts.recordedLogs![workflowId] ??= [];
opts.recordedLogs![workflowId].push(entry);
})
: new DefaultLogger((process.env.TEST_LOG_LEVEL || 'DEBUG').toUpperCase() as LogLevel);
Runtime.install({
logger,
telemetryOptions: {
logging: {
filter: makeTelemetryFilterString({
core: (process.env.TEST_LOG_LEVEL || 'INFO').toUpperCase() as LogLevel,
}),
},
},
});
const env = await TestWorkflowEnvironment.createLocal({
...opts.workflowEnvironmentOpts,
server: {
...opts.workflowEnvironmentOpts?.server,
extraArgs: [
...defaultDynamicConfigOptions.flatMap((opt) => ['--dynamic-config-value', opt]),
...(opts.workflowEnvironmentOpts?.server?.extraArgs ?? []),
],
},
});
await registerDefaultCustomSearchAttributes(env.connection);
t.context = {
env,
workflowBundle,
};
});
test.after.always(async (t) => {
await t.context.env.teardown();
return makeConfigurableEnvironmentTest<Context>({
recordedLogs: opts.recordedLogs,
createTestContext: async (_t: ExecutionContext): Promise<Context> => {
const env = await createTestEnvironment(opts.workflowEnvironmentOpts);
await registerDefaultCustomSearchAttributes(env.connection);
return {
workflowBundle: await createTestWorkflowBundle(opts.workflowsPath, opts.workflowInterceptorModules),
env,
};
},
teardown: async (c: Context) => {
await c.env.teardown();
},
});
return test;
}

export interface Helpers {
Expand All @@ -113,25 +147,29 @@ export interface Helpers {
fn: T,
opts: Omit<WorkflowStartOptions<T>, 'taskQueue' | 'workflowId'>
): Promise<workflow.WorkflowResultType<T>>;
startWorkflow<T extends () => Promise<any>>(workflowType: T): Promise<WorkflowHandle<T>>;
startWorkflow<T extends () => Promise<any>>(workflowType: T): Promise<WorkflowHandleWithFirstExecutionRunId<T>>;
startWorkflow<T extends workflow.Workflow>(
fn: T,
opts: Omit<WorkflowStartOptions<T>, 'taskQueue' | 'workflowId'>
): Promise<WorkflowHandle<T>>;
): Promise<WorkflowHandleWithFirstExecutionRunId<T>>;
assertWorkflowUpdateFailed(p: Promise<any>, causeConstructor: ErrorConstructor, message?: string): Promise<void>;
assertWorkflowFailedError(p: Promise<any>, causeConstructor: ErrorConstructor, message?: string): Promise<void>;
updateHasBeenAdmitted(handle: WorkflowHandle<workflow.Workflow>, updateId: string): Promise<boolean>;
}

export function helpers(t: ExecutionContext<Context>, testEnv: TestWorkflowEnvironment = t.context.env): Helpers {
export function configurableHelpers<T>(
t: ExecutionContext<T>,
workflowBundle: WorkflowBundle,
testEnv: TestWorkflowEnvironment
): Helpers {
const taskQueue = t.title.replace(/ /g, '_');

return {
taskQueue,
async createWorker(opts?: Partial<WorkerOptions>): Promise<Worker> {
return await Worker.create({
connection: testEnv.nativeConnection,
workflowBundle: t.context.workflowBundle,
workflowBundle,
taskQueue,
interceptors: {
activity: [() => ({ inbound: new ConnectionInjectorInterceptor(testEnv.connection) })],
Expand All @@ -146,7 +184,7 @@ export function helpers(t: ExecutionContext<Context>, testEnv: TestWorkflowEnvir
): Promise<void> {
await Worker.runReplayHistory(
{
workflowBundle: t.context.workflowBundle,
workflowBundle,
...opts,
},
history
Expand All @@ -165,7 +203,7 @@ export function helpers(t: ExecutionContext<Context>, testEnv: TestWorkflowEnvir
async startWorkflow(
fn: workflow.Workflow,
opts?: Omit<WorkflowStartOptions, 'taskQueue' | 'workflowId'>
): Promise<WorkflowHandle<workflow.Workflow>> {
): Promise<WorkflowHandleWithFirstExecutionRunId<workflow.Workflow>> {
return await testEnv.client.workflow.start(fn, {
taskQueue,
workflowId: randomUUID(),
Expand Down Expand Up @@ -217,3 +255,7 @@ export function helpers(t: ExecutionContext<Context>, testEnv: TestWorkflowEnvir
},
};
}

export function helpers(t: ExecutionContext<Context>, testEnv: TestWorkflowEnvironment = t.context.env): Helpers {
return configurableHelpers(t, t.context.workflowBundle, testEnv);
}
1 change: 1 addition & 0 deletions packages/test/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export const bundlerOptions = {
'uuid',
'net',
'fs/promises',
require.resolve('./activities'),
],
};

Expand Down
Loading

0 comments on commit 7e0aac3

Please sign in to comment.