Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export enum OrchestrationStatus {
RUNNING = pb.OrchestrationStatus.ORCHESTRATION_STATUS_RUNNING,
COMPLETED = pb.OrchestrationStatus.ORCHESTRATION_STATUS_COMPLETED,
FAILED = pb.OrchestrationStatus.ORCHESTRATION_STATUS_FAILED,
CANCELED = pb.OrchestrationStatus.ORCHESTRATION_STATUS_CANCELED,
TERMINATED = pb.OrchestrationStatus.ORCHESTRATION_STATUS_TERMINATED,
CONTINUED_AS_NEW = pb.OrchestrationStatus.ORCHESTRATION_STATUS_CONTINUED_AS_NEW,
PENDING = pb.OrchestrationStatus.ORCHESTRATION_STATUS_PENDING,
Expand Down
3 changes: 3 additions & 0 deletions packages/durabletask-js/src/testing/in-memory-backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ export class InMemoryOrchestrationBackend {
return ClientOrchestrationStatus.FAILED;
case pb.OrchestrationStatus.ORCHESTRATION_STATUS_TERMINATED:
return ClientOrchestrationStatus.TERMINATED;
case pb.OrchestrationStatus.ORCHESTRATION_STATUS_CANCELED:
return ClientOrchestrationStatus.CANCELED;
case pb.OrchestrationStatus.ORCHESTRATION_STATUS_SUSPENDED:
return ClientOrchestrationStatus.SUSPENDED;
case pb.OrchestrationStatus.ORCHESTRATION_STATUS_CONTINUED_AS_NEW:
Expand All @@ -511,6 +513,7 @@ export class InMemoryOrchestrationBackend {
return (
status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_COMPLETED ||
status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_FAILED ||
status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_CANCELED ||
status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_TERMINATED
);
}
Expand Down
1 change: 1 addition & 0 deletions packages/durabletask-js/src/testing/test-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export class TestOrchestrationClient {
return (
status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_COMPLETED ||
status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_FAILED ||
status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_CANCELED ||
status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_TERMINATED
);
}
Expand Down
24 changes: 24 additions & 0 deletions packages/durabletask-js/test/in-memory-backend.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,30 @@ describe("In-Memory Backend", () => {
expect(state?.serializedOutput).toEqual(JSON.stringify(42));
});

describe("toClientStatus", () => {
it("should map CANCELED proto status to CANCELED client status", () => {
const { OrchestrationStatus: pbStatus } = require("../src/proto/orchestrator_service_pb");
expect(backend.toClientStatus(pbStatus.ORCHESTRATION_STATUS_CANCELED)).toBe(OrchestrationStatus.CANCELED);
});
Comment on lines +606 to +609

it("should map all known proto statuses without throwing", () => {
const { OrchestrationStatus: pbStatus } = require("../src/proto/orchestrator_service_pb");
const protoStatuses = [
pbStatus.ORCHESTRATION_STATUS_RUNNING,
pbStatus.ORCHESTRATION_STATUS_COMPLETED,
pbStatus.ORCHESTRATION_STATUS_CONTINUED_AS_NEW,
pbStatus.ORCHESTRATION_STATUS_FAILED,
pbStatus.ORCHESTRATION_STATUS_CANCELED,
pbStatus.ORCHESTRATION_STATUS_TERMINATED,
pbStatus.ORCHESTRATION_STATUS_PENDING,
pbStatus.ORCHESTRATION_STATUS_SUSPENDED,
];
Comment on lines +611 to +622
for (const status of protoStatuses) {
expect(() => backend.toClientStatus(status)).not.toThrow();
}
});
});

it("waitForState with zero timeout should wait indefinitely until state matches", async () => {
const orchestrator: TOrchestrator = async (_: OrchestrationContext) => "done";

Expand Down
86 changes: 86 additions & 0 deletions packages/durabletask-js/test/orchestration-status.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import * as pb from "../src/proto/orchestrator_service_pb";
import { OrchestrationStatus, fromProtobuf, toProtobuf } from "../src/orchestration/enum/orchestration-status.enum";

/**
* Exhaustive mapping between client OrchestrationStatus and proto OrchestrationStatus.
* Every entry in the proto enum (except unspecified/unknown sentinel values) must have
* a corresponding client enum member.
*/
const STATUS_PAIRS: [OrchestrationStatus, pb.OrchestrationStatus][] = [
[OrchestrationStatus.RUNNING, pb.OrchestrationStatus.ORCHESTRATION_STATUS_RUNNING],
[OrchestrationStatus.COMPLETED, pb.OrchestrationStatus.ORCHESTRATION_STATUS_COMPLETED],
[OrchestrationStatus.CONTINUED_AS_NEW, pb.OrchestrationStatus.ORCHESTRATION_STATUS_CONTINUED_AS_NEW],
[OrchestrationStatus.FAILED, pb.OrchestrationStatus.ORCHESTRATION_STATUS_FAILED],
[OrchestrationStatus.CANCELED, pb.OrchestrationStatus.ORCHESTRATION_STATUS_CANCELED],
[OrchestrationStatus.TERMINATED, pb.OrchestrationStatus.ORCHESTRATION_STATUS_TERMINATED],
[OrchestrationStatus.PENDING, pb.OrchestrationStatus.ORCHESTRATION_STATUS_PENDING],
[OrchestrationStatus.SUSPENDED, pb.OrchestrationStatus.ORCHESTRATION_STATUS_SUSPENDED],
];

describe("OrchestrationStatus enum", () => {
describe("fromProtobuf", () => {
it.each(STATUS_PAIRS)(
"should convert client %s from proto %s",
(clientStatus, protoStatus) => {
expect(fromProtobuf(protoStatus)).toBe(clientStatus);
},
);

it("should throw on unknown proto value", () => {
expect(() => fromProtobuf(999 as pb.OrchestrationStatus)).toThrow(
"Unknown protobuf OrchestrationStatus value: 999",
);
});
});

describe("toProtobuf", () => {
it.each(STATUS_PAIRS)(
"should convert client %s to proto %s",
(clientStatus, protoStatus) => {
expect(toProtobuf(clientStatus)).toBe(protoStatus);
},
);

it("should throw on unknown client value", () => {
expect(() => toProtobuf(999 as OrchestrationStatus)).toThrow(
"Unknown OrchestrationStatus value: 999",
);
});
});

describe("round-trip", () => {
it.each(STATUS_PAIRS)(
"should round-trip client %s through proto and back",
(clientStatus) => {
expect(fromProtobuf(toProtobuf(clientStatus))).toBe(clientStatus);
},
);
});

describe("completeness", () => {
it("should cover all non-zero proto OrchestrationStatus values", () => {
// Get all numeric values from the proto enum (protobuf JS enums are plain objects)
const protoValues = new Set<number>();
Comment on lines +64 to +66
for (const [key, value] of Object.entries(pb.OrchestrationStatus)) {
if (typeof value === "number" && key.startsWith("ORCHESTRATION_STATUS_")) {
protoValues.add(value);
}
}

const coveredProtoValues = new Set(STATUS_PAIRS.map(([, proto]) => proto as number));

for (const protoValue of protoValues) {
expect(coveredProtoValues).toContain(protoValue);
}
});

it("should have matching numeric values between client and proto enums", () => {
for (const [clientStatus, protoStatus] of STATUS_PAIRS) {
expect(clientStatus as number).toBe(protoStatus as number);
}
});
});
});
Loading