Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/brave-functions-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopify/shopify-function-test-helpers": minor
---

Add function run metadata to `runFunction`, including instruction count, memory usage, and module size.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,12 @@ describe("Default Integration Test", () => {
);

expect(runResult.error).toBeNull();
expect(runResult.result.output).toEqual(fixture.expectedOutput);
expect(runResult.result?.output).toEqual(fixture.expectedOutput);
expect(runResult.metadata).toEqual({
instructionCount: expect.any(Number),
memoryUsageKiB: expect.any(Number),
moduleSizeKiB: expect.any(Number)
});
}, 10000);
});
});
Expand Down
91 changes: 69 additions & 22 deletions src/methods/run-function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,74 @@ import { spawn } from "child_process";

import { FixtureData } from "./load-fixture.js";

/**
* Metadata reported by function-runner for a function execution.
*/
export interface FunctionRunMetadata {
instructionCount: number;
memoryUsageKiB: number;
moduleSizeKiB: number;
}

/**
* Interface for the run function result
*/
export interface RunFunctionResult {
result: { output: any } | null;
metadata: FunctionRunMetadata | null;
error: string | null;
}

function parseFunctionRunnerResult(stdout: string): {
result: RunFunctionResult["result"];
metadata: FunctionRunMetadata | null;
error: string | null;
} {
let result: unknown;

try {
result = JSON.parse(stdout);
} catch (parseError) {
// function-runner is not guaranteed to return JSON when it fails.
return {
result: null,
metadata: null,
error: `Failed to parse function-runner output: ${parseError instanceof Error ? parseError.message : "Unknown error"}`,
};
}

const invalidShapeResult = {
result: null,
metadata: null,
error: `function-runner returned unexpected format. Received: ${JSON.stringify(result)}`,
};

if (typeof result !== "object" || result === null) {
return invalidShapeResult;
}

const resultObject = result as Record<string, unknown>;
const { output, instructions, size: moduleSize } = resultObject;
const memoryUsage = resultObject.memory_usage;

if (
output === undefined ||
typeof instructions !== "number" ||
typeof memoryUsage !== "number" ||
typeof moduleSize !== "number"
) {
return invalidShapeResult;
}

return {
result: { output },
metadata: {
instructionCount: instructions,
memoryUsageKiB: memoryUsage,
moduleSizeKiB: moduleSize,
},
error: null,
};
}

/**
Expand Down Expand Up @@ -68,41 +130,24 @@ export async function runFunction(
});

runnerProcess.on("close", (code) => {
const functionRunnerResult = parseFunctionRunnerResult(stdout);

if (code !== 0) {
resolve({
result: null,
metadata: functionRunnerResult.metadata,
error: `function-runner failed with exit code ${code}: ${stderr}`,
});
return;
}

try {
const result = JSON.parse(stdout);

// function-runner output format: { output: {...} }
if (!result.output) {
resolve({
result: null,
error: `function-runner returned unexpected format - missing 'output' field. Received: ${JSON.stringify(result)}`,
});
return;
}

resolve({
result: { output: result.output },
error: null,
});
} catch (parseError) {
resolve({
result: null,
error: `Failed to parse function-runner output: ${parseError instanceof Error ? parseError.message : "Unknown error"}`,
});
}
resolve(functionRunnerResult);
});

runnerProcess.on("error", (error) => {
resolve({
result: null,
metadata: null,
error: `Failed to start function-runner: ${error.message}`,
});
});
Expand All @@ -114,11 +159,13 @@ export async function runFunction(
if (error instanceof Error) {
return {
result: null,
metadata: null,
error: error.message,
};
} else {
return {
result: null,
metadata: null,
error: "Unknown error occurred",
};
}
Expand Down
5 changes: 4 additions & 1 deletion src/wasm-testing-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export { validateFixtureInput } from "./methods/validate-fixture-input.js";
// Export types for consumers
export type { FixtureData } from "./methods/load-fixture.js";
export type { BuildFunctionResult } from "./methods/build-function.js";
export type { RunFunctionResult } from "./methods/run-function.js";
export type {
FunctionRunMetadata,
RunFunctionResult,
} from "./methods/run-function.js";
export type { FunctionInfo } from "./methods/get-function-info.js";
export type {
ValidateTestAssetsOptions,
Expand Down
Loading
Loading