Skip to content

Commit 63efff4

Browse files
committed
test(microsoft-foundry): cover shared deployment filter for picker + persistence
Adds focused unit tests for the new partitionFoundryDeployments helper and the picker/persistence contract returned by selectFoundryDeployment: - mixed GPT+Claude resource: only GPT deployments persisted, Claude surfaced via the unsupported-deployment note - all-Anthropic resource: actionable error path - all-GPT resource: unchanged supported list with no warning note
1 parent dfa3a05 commit 63efff4

1 file changed

Lines changed: 100 additions & 1 deletion

File tree

extensions/microsoft-foundry/index.test.ts

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@ import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api";
33
import { beforeEach, describe, expect, it, vi } from "vitest";
44
import { getAccessTokenResultAsync } from "./cli.js";
55
import plugin from "./index.js";
6-
import { buildFoundryConnectionTest, isValidTenantIdentifier } from "./onboard.js";
6+
import {
7+
buildFoundryConnectionTest,
8+
isValidTenantIdentifier,
9+
selectFoundryDeployment,
10+
} from "./onboard.js";
711
import { resetFoundryRuntimeAuthCaches } from "./runtime.js";
812
import {
913
buildFoundryAuthResult,
1014
isAnthropicFoundryDeployment,
1115
normalizeFoundryEndpoint,
16+
partitionFoundryDeployments,
1217
requiresFoundryMaxCompletionTokens,
1318
supportsFoundryImageInput,
1419
usesFoundryResponsesByDefault,
@@ -723,6 +728,100 @@ describe("microsoft-foundry plugin", () => {
723728
});
724729
});
725730

731+
describe("partitionFoundryDeployments", () => {
732+
it("keeps GPT deployments and skips Claude in mixed resources", () => {
733+
const { supported, anthropic } = partitionFoundryDeployments([
734+
{ name: "prod-gpt", modelName: "gpt-5.4" },
735+
{ name: "prod-claude", modelName: "claude-opus-4-6" },
736+
{ name: "prod-mini", modelName: "gpt-4o-mini" },
737+
]);
738+
expect(supported.map((d) => d.name)).toEqual(["prod-gpt", "prod-mini"]);
739+
expect(anthropic.map((d) => d.name)).toEqual(["prod-claude"]);
740+
});
741+
742+
it("returns empty supported when only Anthropic deployments exist", () => {
743+
const { supported, anthropic } = partitionFoundryDeployments([
744+
{ name: "only-claude", modelName: "claude-3.5-sonnet" },
745+
]);
746+
expect(supported).toEqual([]);
747+
expect(anthropic.map((d) => d.name)).toEqual(["only-claude"]);
748+
});
749+
750+
it("is a no-op for all-OpenAI resources", () => {
751+
const deployments = [
752+
{ name: "prod-gpt", modelName: "gpt-5.4" },
753+
{ name: "prod-mini", modelName: "gpt-4o-mini" },
754+
];
755+
const { supported, anthropic } = partitionFoundryDeployments(deployments);
756+
expect(supported).toEqual(deployments);
757+
expect(anthropic).toEqual([]);
758+
});
759+
});
760+
761+
describe("selectFoundryDeployment", () => {
762+
function makeCtx(overrides: { selectValue?: string } = {}) {
763+
const noteCalls: Array<{ message: string; title: string }> = [];
764+
const selectCalls: Array<{ options: Array<{ value: string }> }> = [];
765+
const ctx = {
766+
prompter: {
767+
note: vi.fn(async (message: string, title: string) => {
768+
noteCalls.push({ message, title });
769+
}),
770+
select: vi.fn(async (params: { options: Array<{ value: string }> }) => {
771+
selectCalls.push({ options: params.options });
772+
return overrides.selectValue ?? params.options[0]?.value;
773+
}),
774+
},
775+
} as never;
776+
return { ctx, noteCalls, selectCalls };
777+
}
778+
779+
const fakeResource = {
780+
id: "/sub/x/rg/y/account/z",
781+
accountName: "foundry-resource",
782+
kind: "AIServices" as const,
783+
resourceGroup: "rg",
784+
endpoint: "https://example.services.ai.azure.com",
785+
projects: [],
786+
};
787+
788+
it("persists only supported deployments for mixed GPT+Claude resources", async () => {
789+
const { ctx, selectCalls, noteCalls } = makeCtx({ selectValue: "prod-gpt" });
790+
const result = await selectFoundryDeployment(ctx, fakeResource, [
791+
{ name: "prod-gpt", modelName: "gpt-5.4", state: "Succeeded" },
792+
{ name: "prod-claude", modelName: "claude-opus-4-6", state: "Succeeded" },
793+
{ name: "prod-mini", modelName: "gpt-4o-mini", state: "Succeeded" },
794+
]);
795+
796+
expect(result.supported.map((d) => d.name)).toEqual(["prod-gpt", "prod-mini"]);
797+
expect(result.selected.name).toBe("prod-gpt");
798+
expect(selectCalls[0]?.options.map((o) => o.value)).toEqual(["prod-gpt", "prod-mini"]);
799+
expect(noteCalls.some((n) => n.title === "Unsupported Deployments")).toBe(true);
800+
});
801+
802+
it("throws an actionable error when only Anthropic deployments exist", async () => {
803+
const { ctx, noteCalls } = makeCtx();
804+
await expect(
805+
selectFoundryDeployment(ctx, fakeResource, [
806+
{ name: "only-claude", modelName: "claude-3.5-sonnet", state: "Succeeded" },
807+
]),
808+
).rejects.toThrow(/Only Anthropic deployments/);
809+
expect(noteCalls.some((n) => n.title === "Unsupported Deployments")).toBe(true);
810+
});
811+
812+
it("leaves all-OpenAI resources unchanged", async () => {
813+
const { ctx, selectCalls, noteCalls } = makeCtx({ selectValue: "prod-mini" });
814+
const result = await selectFoundryDeployment(ctx, fakeResource, [
815+
{ name: "prod-gpt", modelName: "gpt-5.4", state: "Succeeded" },
816+
{ name: "prod-mini", modelName: "gpt-4o-mini", state: "Succeeded" },
817+
]);
818+
expect(result.supported.map((d) => d.name)).toEqual(["prod-gpt", "prod-mini"]);
819+
expect(result.selected.name).toBe("prod-mini");
820+
expect(selectCalls).toHaveLength(1);
821+
expect(noteCalls.some((n) => n.title === "Unsupported Deployments")).toBe(false);
822+
});
823+
});
824+
726825
describe("isAnthropicFoundryDeployment", () => {
727826
it.each(["claude-opus-4-6", "Claude-Sonnet-4", "claude-3.5-haiku", "CLAUDE-instant"])(
728827
"detects Anthropic model: %s",

0 commit comments

Comments
 (0)