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 0377d20 commit 87e4b73
Show file tree
Hide file tree
Showing 30 changed files with 1,816 additions and 1,762 deletions.
110 changes: 48 additions & 62 deletions packages/test/src/helpers-integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,8 @@ import {
registerDefaultCustomSearchAttributes,
} from './helpers';

export interface Context<T extends TestWorkflowEnvironment> {
env: T;
workflowBundle: WorkflowBundle;
}

export interface MultiEnvContext<T extends TestWorkflowEnvironment> {
envs: T[];
export interface Context {
env: TestWorkflowEnvironment;
workflowBundle: WorkflowBundle;
}

Expand All @@ -58,16 +53,14 @@ const defaultDynamicConfigOptions = [
'worker.removableBuildIdDurationSinceDefault=1',
];

function setupRuntime(
recordedLogs?: { [workflowId: string]: LogEntry[] }
) {
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);
? 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: {
Expand All @@ -80,19 +73,21 @@ function setupRuntime(
});
}

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

export async function createDefaultTestEnvironment(opts?: LocalTestWorkflowEnvironmentOptions): Promise<TestWorkflowEnvironment> {
export async function createTestEnvironment(
opts?: LocalTestWorkflowEnvironmentOptions
): Promise<TestWorkflowEnvironment> {
return await TestWorkflowEnvironment.createLocal({
...(opts || {}), // Use provided options or default to an empty object
server: {
Expand All @@ -105,36 +100,18 @@ export async function createDefaultTestEnvironment(opts?: LocalTestWorkflowEnvir
});
}

export function makeMultiEnvironmentTest<T extends TestWorkflowEnvironment>(
opts: {
workflowsPath: string;
multiWorkflowEnvironmentOpts: LocalTestWorkflowEnvironmentOptions[];
workflowInterceptorModules?: string[];
recordedLogs?: { [workflowId: string]: LogEntry[] };
configureEnv: (opt: LocalTestWorkflowEnvironmentOptions) => T,
}
): TestFn<MultiEnvContext<T>> {
if (opts.multiWorkflowEnvironmentOpts.length < 1) {
throw new Error("must have at least 1 workflow environment options");
}

let test = anyTest as TestFn<MultiEnvContext<T>>;
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 = {
workflowBundle: await createDefaultTestWorkflowBundle(opts.workflowsPath, opts.workflowInterceptorModules),
envs: opts.multiWorkflowEnvironmentOpts.map(workflowEnvironmentOptions => opts.configureEnv(workflowEnvironmentOptions)),
}
for (const env of t.context.envs) {
// TODO(thomas): do environments create separate connections?
await registerDefaultCustomSearchAttributes(env.connection);
}
t.context = await opts.createTestContext(t);
});
test.after.always(async (t) => {
for (const env of t.context.envs) {
// TODO(thomas): reused state across test envs will cause this to break I think
await env.teardown();
}
await opts.teardown(t.context);
});
return test;
}
Expand All @@ -144,20 +121,21 @@ export function makeTestFunction(opts: {
workflowEnvironmentOpts?: LocalTestWorkflowEnvironmentOptions;
workflowInterceptorModules?: string[];
recordedLogs?: { [workflowId: string]: LogEntry[] };
}): TestFn<Context<TestWorkflowEnvironment>> {
let test = anyTest as TestFn<Context<TestWorkflowEnvironment>>;
test.before(async (t) => {
setupRuntime(opts.recordedLogs);
t.context = {
workflowBundle: await createDefaultTestWorkflowBundle(opts.workflowsPath, opts.workflowInterceptorModules),
env: await createDefaultTestEnvironment(opts.workflowEnvironmentOpts),
}
await registerDefaultCustomSearchAttributes(t.context.env.connection);
});
test.after.always(async (t) => {
await t.context.env.teardown();
}): TestFn<Context> {
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 @@ -179,15 +157,19 @@ export interface Helpers {
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 @@ -202,7 +184,7 @@ export function helpers(t: ExecutionContext<Context>, testEnv: TestWorkflowEnvir
): Promise<void> {
await Worker.runReplayHistory(
{
workflowBundle: t.context.workflowBundle,
workflowBundle,
...opts,
},
history
Expand Down Expand Up @@ -273,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 87e4b73

Please sign in to comment.