Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ed7e29d
fix(sdk): correct FAILURE_TOLERANCE_EXCEEDED behavior in concurrent o…
ParidelPooya Dec 3, 2025
fe6a8d1
debug: add execution history logging to parallel-tolerated-failure-pe…
ParidelPooya Dec 3, 2025
e1cb0a3
Fix tolerated failure tests by disabling step retries
ParidelPooya Dec 3, 2025
edaa096
Fix retry strategy syntax in tolerated failure examples
ParidelPooya Dec 3, 2025
0d88f63
Remove parentheses from failure threshold exceeded example names
ParidelPooya Dec 3, 2025
3c7d7e0
Fix force-checkpointing-invoke test timeout by disabling retries
ParidelPooya Dec 3, 2025
657abd9
Reduce sleep time in force-checkpointing-invoke test
ParidelPooya Dec 3, 2025
aa3036f
Fix force-checkpointing-invoke timeout by disabling invoke retries
ParidelPooya Dec 3, 2025
cbe4fa8
Increase test timeout and update duration expectation for force-check…
ParidelPooya Dec 3, 2025
7d5d1bb
Add unit tests for FAILURE_TOLERANCE_EXCEEDED logic in replay mode
ParidelPooya Dec 3, 2025
cd34617
Refactor: Extract duplicate getCompletionReason logic into private me…
ParidelPooya Dec 3, 2025
fc89a61
Enhance tolerated failure tests with individual branch/item result as…
ParidelPooya Dec 3, 2025
6ba34ff
WIP: Add enhanced test assertions (needs fixes for type errors and lo…
ParidelPooya Dec 3, 2025
52f0668
Complete enhanced test assertions for completion config examples
ParidelPooya Dec 3, 2025
72858e7
Enhanced failure threshold exceeded tests with individual result asse…
ParidelPooya Dec 3, 2025
208bf26
Fixed retry configuration to prevent test timeouts in failure thresho…
ParidelPooya Dec 3, 2025
8a946ed
Optimized retry attempts to 1 retry (2 total attempts) for failure th…
ParidelPooya Dec 3, 2025
509f504
Optimize failure threshold exceeded tests with retryPresets.noRetry
ParidelPooya Dec 4, 2025
df40701
Use createRetryStrategy for failure threshold exceeded tests
ParidelPooya Dec 4, 2025
132931f
Fix failure threshold exceeded tests
ParidelPooya Dec 5, 2025
3b63af0
Replace historyEvents.find with runner.getOperation() in map tests
ParidelPooya Dec 5, 2025
85f00b8
Refactor operation status checks to use array-based approach
ParidelPooya Dec 5, 2025
86179df
Remove unused test-helpers.ts file
ParidelPooya Dec 5, 2025
1ab8d65
Remove unused retryPresets import from force-checkpointing-invoke.ts
ParidelPooya Dec 5, 2025
a7c17ff
Increase integration test timeout to 120 seconds
ParidelPooya Dec 5, 2025
a61483d
Add retry strategy to failing steps in parallel tests
ParidelPooya Dec 5, 2025
949e994
Merge remote-tracking branch 'origin/main' into feat/completion-confi…
ParidelPooya Dec 5, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const defaultPreset = createDefaultPreset();
module.exports = {
...defaultPreset,
testMatch: ["**/src/examples/**/*.test.ts"],
testTimeout: 90000,
testTimeout: 120000,
testNamePattern: "cloud",
bail: true,
};
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,6 @@ createTests({
// Verify operations were tracked
const operations = execution.getOperations();
expect(operations.length).toBeGreaterThan(0);
}, 30000); // 30 second timeout
}, 60000); // 60 second timeout
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { handler } from "./map-failure-threshold-exceeded-count";
import { createTests } from "../../../utils/test-helper";
import { OperationStatus } from "@aws/durable-execution-sdk-js-testing";

createTests({
name: "Map failure threshold exceeded count",
functionName: "map-failure-threshold-exceeded-count",
handler,
tests: (runner) => {
it("should return FAILURE_TOLERANCE_EXCEEDED when failure count exceeds threshold", async () => {
const execution = await runner.run();
const result = execution.getResult() as any;

expect(result.completionReason).toBe("FAILURE_TOLERANCE_EXCEEDED");
expect(result.successCount).toBe(2); // Items 4 and 5 succeed
expect(result.failureCount).toBe(3); // Items 1, 2, 3 fail (exceeds threshold of 2)
expect(result.totalCount).toBe(5);

// Verify individual operation statuses
[
{ name: "process-0", status: OperationStatus.FAILED },
{ name: "process-1", status: OperationStatus.FAILED },
{ name: "process-2", status: OperationStatus.FAILED },
{ name: "process-3", status: OperationStatus.SUCCEEDED },
{ name: "process-4", status: OperationStatus.SUCCEEDED },
].forEach(({ name, status }) => {
expect(runner.getOperation(name)?.getStatus()).toBe(status);
});
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
DurableContext,
withDurableExecution,
createRetryStrategy,
} from "@aws/durable-execution-sdk-js";
import { ExampleConfig } from "../../../types";

export const config: ExampleConfig = {
name: "Map failure threshold exceeded count",
description: "Map operation where failure count exceeds tolerance threshold",
};

export const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
const items = [1, 2, 3, 4, 5];

const result = await context.map(
"failure-threshold-items",
items,
async (ctx: DurableContext, item: number, index: number) => {
return await ctx.step(
`process-${index}`,
async () => {
if (item <= 3) {
throw new Error(`Item ${item} failed`);
}
return item * 2;
},
{ retryStrategy: createRetryStrategy({ maxAttempts: 2 }) },
);
},
{
completionConfig: {
toleratedFailureCount: 2, // Allow only 2 failures, but we'll have 3
},
},
);

await context.wait({ seconds: 1 });

return {
completionReason: result.completionReason,
successCount: result.successCount,
failureCount: result.failureCount,
totalCount: result.totalCount,
};
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { handler } from "./map-failure-threshold-exceeded-percentage";
import { createTests } from "../../../utils/test-helper";
import { OperationStatus } from "@aws/durable-execution-sdk-js-testing";

createTests({
name: "Map failure threshold exceeded percentage",
functionName: "map-failure-threshold-exceeded-percentage",
handler,
tests: (runner) => {
it("should return FAILURE_TOLERANCE_EXCEEDED when failure percentage exceeds threshold", async () => {
const execution = await runner.run();
const result = execution.getResult() as any;

expect(result.completionReason).toBe("FAILURE_TOLERANCE_EXCEEDED");
expect(result.successCount).toBe(2); // Items 4 and 5 succeed
expect(result.failureCount).toBe(3); // Items 1, 2, 3 fail (60% > 50% threshold)
expect(result.totalCount).toBe(5);

// Verify individual operation statuses
[
{ name: "process-0", status: OperationStatus.FAILED },
{ name: "process-1", status: OperationStatus.FAILED },
{ name: "process-2", status: OperationStatus.FAILED },
{ name: "process-3", status: OperationStatus.SUCCEEDED },
{ name: "process-4", status: OperationStatus.SUCCEEDED },
].forEach(({ name, status }) => {
expect(runner.getOperation(name)?.getStatus()).toBe(status);
});
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
DurableContext,
withDurableExecution,
createRetryStrategy,
} from "@aws/durable-execution-sdk-js";
import { ExampleConfig } from "../../../types";

export const config: ExampleConfig = {
name: "Map failure threshold exceeded percentage",
description:
"Map operation where failure percentage exceeds tolerance threshold",
};

export const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
const items = [1, 2, 3, 4, 5];

const result = await context.map(
"failure-threshold-items",
items,
async (ctx: DurableContext, item: number, index: number) => {
return await ctx.step(
`process-${index}`,
async () => {
if (item <= 3) {
throw new Error(`Item ${item} failed`);
}
return item * 2;
},
{ retryStrategy: createRetryStrategy({ maxAttempts: 2 }) },
);
},
{
completionConfig: {
toleratedFailurePercentage: 50, // Allow 50% failures, but we'll have 60% (3/5)
},
},
);

await context.wait({ seconds: 1 });

return {
completionReason: result.completionReason,
successCount: result.successCount,
failureCount: result.failureCount,
totalCount: result.totalCount,
};
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { handler } from "./map-min-successful";
import { createTests } from "../../../utils/test-helper";
import { OperationStatus } from "@aws/durable-execution-sdk-js-testing";

createTests({
name: "Map minSuccessful",
functionName: "map-min-successful",
handler,
tests: (runner) => {
it("should complete early when minSuccessful is reached", async () => {
const execution = await runner.run();
const result = execution.getResult() as any;

// Assert overall results
expect(result.successCount).toBe(2);
expect(result.completionReason).toBe("MIN_SUCCESSFUL_REACHED");
expect(result.results).toHaveLength(2);
expect(result.totalCount).toBe(5);

// Get the map operation from history to verify individual item results
// Get the map operation result
const mapResult = runner.getOperation("min-successful-items");

// Get individual map item operations
const item0 = runner.getOperation("process-0");
const item1 = runner.getOperation("process-1");
const item2 = runner.getOperation("process-2");
const item3 = runner.getOperation("process-3");
const item4 = runner.getOperation("process-4");

// First two items should succeed (items 1 and 2 process fastest due to timeout)
expect(item0?.getStatus()).toBe(OperationStatus.SUCCEEDED);
expect(item1?.getStatus()).toBe(OperationStatus.SUCCEEDED);

// Remaining items should be in STARTED state (not completed)
expect(item2?.getStatus()).toBe(OperationStatus.STARTED);
expect(item3?.getStatus()).toBe(OperationStatus.STARTED);
expect(item4?.getStatus()).toBe(OperationStatus.STARTED);

// Verify the results array matches
expect(result.results).toEqual(["Item 1 processed", "Item 2 processed"]);
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
DurableContext,
withDurableExecution,
} from "@aws/durable-execution-sdk-js";
import { ExampleConfig } from "../../../types";
import { log } from "../../../utils/logger";

export const config: ExampleConfig = {
name: "Map minSuccessful",
description: "Map operation with minSuccessful completion config",
};

export const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
const items = [1, 2, 3, 4, 5];

log(`Processing ${items.length} items with minSuccessful: 2`);

const results = await context.map(
"min-successful-items",
items,
async (ctx, item, index) => {
return await ctx.step(`process-${index}`, async () => {
// Simulate processing time
await new Promise((resolve) => setTimeout(resolve, 100 * item));
return `Item ${item} processed`;
});
},
{
completionConfig: {
minSuccessful: 2,
},
},
);

await context.wait({ seconds: 1 });

log(`Completed with ${results.successCount} successes`);
log(`Completion reason: ${results.completionReason}`);

return {
successCount: results.successCount,
totalCount: results.totalCount,
completionReason: results.completionReason,
results: results.getResults(),
};
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { handler } from "./map-tolerated-failure-count";
import { createTests } from "../../../utils/test-helper";
import { OperationStatus } from "@aws/durable-execution-sdk-js-testing";

createTests({
name: "Map toleratedFailureCount",
functionName: "map-tolerated-failure-count",
handler,
tests: (runner) => {
it("should complete when failure tolerance is reached", async () => {
const execution = await runner.run();
const result = execution.getResult() as any;

// Assert overall results
expect(result.failureCount).toBe(2);
expect(result.successCount).toBe(3);
expect(result.completionReason).toBe("ALL_COMPLETED");
expect(result.hasFailure).toBe(true);
expect(result.totalCount).toBe(5);

// Verify individual operation statuses
[
{ name: "process-0", status: OperationStatus.SUCCEEDED },
{ name: "process-1", status: OperationStatus.FAILED },
{ name: "process-2", status: OperationStatus.SUCCEEDED },
{ name: "process-3", status: OperationStatus.FAILED },
{ name: "process-4", status: OperationStatus.SUCCEEDED },
].forEach(({ name, status }) => {
expect(runner.getOperation(name)?.getStatus()).toBe(status);
});
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
DurableContext,
withDurableExecution,
retryPresets,
} from "@aws/durable-execution-sdk-js";
import { ExampleConfig } from "../../../types";
import { log } from "../../../utils/logger";

export const config: ExampleConfig = {
name: "Map toleratedFailureCount",
description: "Map operation with toleratedFailureCount completion config",
};

export const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
const items = [1, 2, 3, 4, 5];

log(`Processing ${items.length} items with toleratedFailureCount: 2`);

const results = await context.map(
"failure-count-items",
items,
async (ctx, item, index) => {
return await ctx.step(
`process-${index}`,
async () => {
// Items 2 and 4 will fail
if (item === 2 || item === 4) {
throw new Error(`Processing failed for item ${item}`);
}
return `Item ${item} processed`;
},
{ retryStrategy: retryPresets.noRetry },
);
},
{
completionConfig: {
toleratedFailureCount: 2,
},
},
);

await context.wait({ seconds: 1 });

log(`Completed with ${results.failureCount} failures`);
log(`Completion reason: ${results.completionReason}`);

return {
successCount: results.successCount,
failureCount: results.failureCount,
totalCount: results.totalCount,
completionReason: results.completionReason,
hasFailure: results.hasFailure,
};
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { handler } from "./map-tolerated-failure-percentage";
import { createTests } from "../../../utils/test-helper";
import { OperationStatus } from "@aws/durable-execution-sdk-js-testing";

createTests({
name: "Map toleratedFailurePercentage",
functionName: "map-tolerated-failure-percentage",
handler,
tests: (runner) => {
it("should complete with acceptable failure percentage", async () => {
const execution = await runner.run();
const result = execution.getResult() as any;

// Assert overall results
expect(result.failureCount).toBe(3);
expect(result.successCount).toBe(7);
expect(result.failurePercentage).toBe(30);
expect(result.completionReason).toBe("ALL_COMPLETED");
expect(result.totalCount).toBe(10);

// Verify individual operation statuses (items 3, 6, 9 fail; others succeed)
[
{ name: "process-0", status: OperationStatus.SUCCEEDED },
{ name: "process-1", status: OperationStatus.SUCCEEDED },
{ name: "process-2", status: OperationStatus.FAILED },
{ name: "process-3", status: OperationStatus.SUCCEEDED },
{ name: "process-4", status: OperationStatus.SUCCEEDED },
{ name: "process-5", status: OperationStatus.FAILED },
].forEach(({ name, status }) => {
expect(runner.getOperation(name)?.getStatus()).toBe(status);
});
});
},
});
Loading
Loading