refactor: align provider and task contracts
This commit is contained in:
@@ -143,7 +143,7 @@ test("admin image model products publish capability parameters and require a res
|
||||
const login = await fetch(`${base}/api/v1/admin/auth/login`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email: "admin@example.com", password: "change-me-now" }) });
|
||||
const loginPayload = await login.json() as { data: { accessToken: string } };
|
||||
const headers = { authorization: `Bearer ${loginPayload.data.accessToken}`, "content-type": "application/json" };
|
||||
const created = await fetch(`${base}/api/v1/admin/model-products`, { method: "POST", headers: { ...headers, "idempotency-key": "model-capability-create" }, body: JSON.stringify({ id: "openai-image-product", publicModelId: "gpt-image-2", name: "OpenAI 图片模型", capabilities: ["image"], channelGroupId: "image-default", basePrice: 20, resolutions: [{ id: "1K", label: "1K", width: 1024, height: 1024, priceMultiplier: 1, ratios: ["1:1", "16:9"] }] }) });
|
||||
const created = await fetch(`${base}/api/v1/admin/model-products`, { method: "POST", headers: { ...headers, "idempotency-key": "model-capability-create" }, body: JSON.stringify({ id: "openai-image-product", publicModelId: "gpt-image-2", name: "OpenAI 图片模型", capabilities: ["image"], enabled: false, resolutions: [{ id: "1K", label: "1K", width: 1024, height: 1024, ratios: ["1:1", "16:9"] }] }) });
|
||||
assert.equal(created.status, 201);
|
||||
const createdPayload = await created.json() as { data: { item: { capabilitiesSchema?: Array<{ capability: string; parameters?: Record<string, { enum?: string[] }> }> } } };
|
||||
const imageSchema = createdPayload.data.item.capabilitiesSchema?.find((item) => item.capability === "image");
|
||||
@@ -152,69 +152,78 @@ test("admin image model products publish capability parameters and require a res
|
||||
assert.deepEqual(imageSchema.parameters?.resolution.enum, ["1K", "2K", "4K"]);
|
||||
assert.deepEqual(imageSchema.parameters?.input_fidelity.enum, ["low", "high"]);
|
||||
|
||||
const invalid = await fetch(`${base}/api/v1/admin/model-products`, { method: "POST", headers: { ...headers, "idempotency-key": "model-capability-invalid" }, body: JSON.stringify({ id: "openai-image-invalid", publicModelId: "gpt-image-invalid", name: "无分辨率图片模型", capabilities: ["image"], channelGroupId: "image-default", basePrice: 20, resolutions: [] }) });
|
||||
const invalid = await fetch(`${base}/api/v1/admin/model-products`, { method: "POST", headers: { ...headers, "idempotency-key": "model-capability-invalid" }, body: JSON.stringify({ id: "openai-image-invalid", publicModelId: "gpt-image-invalid", name: "无分辨率图片模型", capabilities: ["image"], enabled: true, resolutions: [] }) });
|
||||
assert.equal(invalid.status, 422);
|
||||
} finally {
|
||||
await new Promise<void>((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
||||
}
|
||||
});
|
||||
|
||||
test("admin channel groups bind model products separately from request channels", async () => {
|
||||
test("admin channel groups keep model routes separate from public model records", async () => {
|
||||
const { server, base, store } = await start();
|
||||
try {
|
||||
const login = await fetch(`${base}/api/v1/admin/auth/login`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email: "admin@example.com", password: "change-me-now" }) });
|
||||
const loginPayload = await login.json() as { data: { accessToken: string } };
|
||||
const headers = { authorization: `Bearer ${loginPayload.data.accessToken}`, "content-type": "application/json" };
|
||||
const firstProduct = await fetch(`${base}/api/v1/admin/model-products`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-product-first" }, body: JSON.stringify({ id: "group-basic-image", publicModelId: "group-basic-image-v1", name: "分组基础图片", capabilities: ["image"], enabled: false, basePrice: 10, resolutions: [{ id: "1K", label: "1K", width: 1024, height: 1024, priceMultiplier: 1, ratios: ["1:1"] }] }) });
|
||||
const firstProduct = await fetch(`${base}/api/v1/admin/model-products`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-product-first" }, body: JSON.stringify({ id: "group-basic-image", publicModelId: "group-basic-image-v1", name: "分组基础图片", capabilities: ["image"], enabled: false, resolutions: [{ id: "1K", label: "1K", width: 1024, height: 1024, ratios: ["1:1"] }] }) });
|
||||
assert.equal(firstProduct.status, 201);
|
||||
const secondProduct = await fetch(`${base}/api/v1/admin/model-products`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-product-second" }, body: JSON.stringify({ id: "group-advanced-image", publicModelId: "group-advanced-image-v1", name: "分组高级图片", capabilities: ["image"], enabled: false, basePrice: 25, resolutions: [{ id: "2K", label: "2K", width: 2048, height: 2048, priceMultiplier: 1.5, ratios: ["1:1"] }] }) });
|
||||
const secondProduct = await fetch(`${base}/api/v1/admin/model-products`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-product-second" }, body: JSON.stringify({ id: "group-advanced-image", publicModelId: "group-advanced-image-v1", name: "分组高级图片", capabilities: ["image"], enabled: false, resolutions: [{ id: "2K", label: "2K", width: 2048, height: 2048, ratios: ["1:1"] }] }) });
|
||||
assert.equal(secondProduct.status, 201);
|
||||
const channel = store.channels.find((item) => item.id === "channel-a")!;
|
||||
const channelUpdate = await fetch(`${base}/api/v1/admin/channels/channel-a`, { method: "PATCH", headers: { ...headers, "If-Match": String(channel.version), "idempotency-key": "channel-group-product-models" }, body: JSON.stringify({ models: [...channel.models, { requestModelId: "group-basic-image-v1", source: "manual", enabled: true }, { requestModelId: "group-advanced-image-v1", source: "manual", enabled: true }] }) });
|
||||
assert.equal(channelUpdate.status, 200);
|
||||
const created = await fetch(`${base}/api/v1/admin/channel-groups`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-product-bind" }, body: JSON.stringify({ id: "image-product-group", name: "图片产品组", capabilities: ["image"], productIds: ["group-basic-image"], channelIds: ["channel-a"], retryBudget: 2 }) });
|
||||
assert.equal(created.status, 201);
|
||||
const createdPayload = await created.json() as { data: { item: { productIds?: string[] } } };
|
||||
assert.deepEqual(createdPayload.data.item.productIds, ["group-basic-image"]);
|
||||
assert.equal(store.products.find((product) => product.id === "group-basic-image")?.channelGroupId, "image-product-group");
|
||||
assert.equal(created.status, 422);
|
||||
const createdWithRoutes = await fetch(`${base}/api/v1/admin/channel-groups`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-product-bind-routes" }, body: JSON.stringify({ id: "image-product-group", name: "图片产品组", capabilities: ["image"], routes: [{ publicModelId: "group-basic-image-v1", channels: [{ channelId: "channel-a", priorityTier: 1, orderWithinTier: 1, retryBudget: 1 }], strategy: "strict", retryBudget: 2, version: 1 }] }) });
|
||||
assert.equal(createdWithRoutes.status, 201);
|
||||
const createdPayload = await createdWithRoutes.json() as { data: { item: { routes: Array<{ publicModelId: string }> } } };
|
||||
assert.deepEqual(createdPayload.data.item.routes.map((route) => route.publicModelId), ["group-basic-image-v1"]);
|
||||
assert.equal(store.products.find((product) => product.id === "group-basic-image")?.enabled, false);
|
||||
|
||||
const listed = await fetch(`${base}/api/v1/admin/channel-groups`, { headers });
|
||||
assert.equal(listed.status, 200);
|
||||
const listedPayload = await listed.json() as { data: { items: Array<{ id: string; productIds?: string[]; products?: Array<{ productId: string }> }> } };
|
||||
const listedPayload = await listed.json() as { data: { items: Array<{ id: string; routes?: Array<{ publicModelId: string }>; products?: Array<{ productId: string }> }> } };
|
||||
const listedGroup = listedPayload.data.items.find((item) => item.id === "image-product-group");
|
||||
assert.deepEqual(listedGroup?.productIds, ["group-basic-image"]);
|
||||
assert.deepEqual(listedGroup?.routes?.map((route) => route.publicModelId), ["group-basic-image-v1"]);
|
||||
assert.deepEqual(listedGroup?.products?.map((product) => product.productId), ["group-basic-image"]);
|
||||
|
||||
const edited = await fetch(`${base}/api/v1/admin/channel-groups/image-product-group`, { method: "PATCH", headers: { ...headers, "If-Match": "1", "idempotency-key": "channel-group-product-rebind" }, body: JSON.stringify({ productIds: ["group-advanced-image"], capabilities: ["image"], channelIds: ["channel-a"], retryBudget: 2, enabled: true }) });
|
||||
assert.equal(edited.status, 200);
|
||||
assert.equal(store.products.find((product) => product.id === "group-basic-image")?.channelGroupId, undefined);
|
||||
assert.equal(edited.status, 422);
|
||||
const editedWithRoutes = await fetch(`${base}/api/v1/admin/channel-groups/image-product-group`, { method: "PATCH", headers: { ...headers, "If-Match": "1", "idempotency-key": "channel-group-product-rebind-routes" }, body: JSON.stringify({ routes: [{ publicModelId: "group-advanced-image-v1", channels: [{ channelId: "channel-a", priorityTier: 1, orderWithinTier: 1, retryBudget: 1 }], strategy: "strict", retryBudget: 2, version: 1 }], capabilities: ["image"], enabled: true }) });
|
||||
assert.equal(editedWithRoutes.status, 200);
|
||||
assert.deepEqual(store.channelGroups.find((group) => group.id === "image-product-group")?.routes.map((route) => route.publicModelId), ["group-advanced-image-v1"]);
|
||||
assert.equal(store.products.find((product) => product.id === "group-basic-image")?.enabled, false);
|
||||
assert.equal(store.products.find((product) => product.id === "group-advanced-image")?.channelGroupId, "image-product-group");
|
||||
} finally {
|
||||
await new Promise<void>((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
||||
}
|
||||
});
|
||||
|
||||
test("admin channel groups reject incompatible or silently migrated model products", async () => {
|
||||
test("admin channel groups reject incompatible routes and legacy model binding fields", async () => {
|
||||
const { server, base, store } = await start();
|
||||
try {
|
||||
const login = await fetch(`${base}/api/v1/admin/auth/login`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email: "admin@example.com", password: "change-me-now" }) });
|
||||
const loginPayload = await login.json() as { data: { accessToken: string } };
|
||||
const headers = { authorization: `Bearer ${loginPayload.data.accessToken}`, "content-type": "application/json" };
|
||||
const imageGroup = await fetch(`${base}/api/v1/admin/channel-groups`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-capability-image" }, body: JSON.stringify({ id: "capability-image-group", name: "图片能力组", capabilities: ["image"], productIds: [], channelIds: ["channel-a"], retryBudget: 1 }) });
|
||||
assert.equal(imageGroup.status, 201);
|
||||
const product = await fetch(`${base}/api/v1/admin/model-products`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-capability-product" }, body: JSON.stringify({ id: "capability-image-product", publicModelId: "capability-image-v1", name: "能力校验图片产品", capabilities: ["image"], channelGroupId: "capability-image-group", basePrice: 5, resolutions: [{ id: "1K", label: "1K", width: 1024, height: 1024, priceMultiplier: 1, ratios: ["1:1"] }] }) });
|
||||
const channel = store.channels.find((item) => item.id === "channel-a")!;
|
||||
const channelUpdate = await fetch(`${base}/api/v1/admin/channels/channel-a`, { method: "PATCH", headers: { ...headers, "If-Match": String(channel.version), "idempotency-key": "channel-group-capability-model" }, body: JSON.stringify({ models: [...channel.models, { requestModelId: "capability-image-v1", source: "manual", enabled: true }] }) });
|
||||
assert.equal(channelUpdate.status, 200);
|
||||
const product = await fetch(`${base}/api/v1/admin/model-products`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-capability-product" }, body: JSON.stringify({ id: "capability-image-product", publicModelId: "capability-image-v1", name: "能力校验图片产品", capabilities: ["image"], enabled: false, resolutions: [{ id: "1K", label: "1K", width: 1024, height: 1024, ratios: ["1:1"] }] }) });
|
||||
assert.equal(product.status, 201);
|
||||
const textGroup = await fetch(`${base}/api/v1/admin/channel-groups`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-capability-text" }, body: JSON.stringify({ id: "capability-text-group", name: "文字能力组", capabilities: ["text"], productIds: [], channelIds: ["channel-a"], retryBudget: 1 }) });
|
||||
const imageGroup = await fetch(`${base}/api/v1/admin/channel-groups`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-capability-image" }, body: JSON.stringify({ id: "capability-image-group", name: "图片能力组", capabilities: ["image"], routes: [{ publicModelId: "capability-image-v1", channels: [{ channelId: "channel-a", priorityTier: 1, orderWithinTier: 1, retryBudget: 1 }], strategy: "strict", retryBudget: 1, version: 1 }] }) });
|
||||
assert.equal(imageGroup.status, 201);
|
||||
const textGroup = await fetch(`${base}/api/v1/admin/channel-groups`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-capability-text" }, body: JSON.stringify({ id: "capability-text-group", name: "文字能力组", capabilities: ["text"], routes: [] }) });
|
||||
assert.equal(textGroup.status, 201);
|
||||
|
||||
const incompatibleMode = await fetch(`${base}/api/v1/admin/channel-groups/capability-image-group`, { method: "PATCH", headers: { ...headers, "If-Match": "1", "idempotency-key": "channel-group-capability-change" }, body: JSON.stringify({ capabilities: ["text"] }) });
|
||||
assert.equal(incompatibleMode.status, 422);
|
||||
assert.deepEqual(store.channelGroups.find((group) => group.id === "capability-image-group")?.capabilities, ["image"]);
|
||||
assert.equal(store.products.find((item) => item.id === "capability-image-product")?.channelGroupId, "capability-image-group");
|
||||
assert.deepEqual(store.channelGroups.find((group) => group.id === "capability-image-group")?.routes.map((route) => route.publicModelId), ["capability-image-v1"]);
|
||||
|
||||
const incompatibleProduct = await fetch(`${base}/api/v1/admin/model-products/capability-image-product`, { method: "PATCH", headers: { ...headers, "If-Match": "1", "idempotency-key": "channel-group-capability-product-change" }, body: JSON.stringify({ channelGroupId: "capability-text-group" }) });
|
||||
assert.equal(incompatibleProduct.status, 422);
|
||||
assert.equal(store.products.find((item) => item.id === "capability-image-product")?.channelGroupId, "capability-image-group");
|
||||
assert.equal(store.products.find((item) => item.id === "capability-image-product")?.enabled, false);
|
||||
|
||||
const duplicateBinding = await fetch(`${base}/api/v1/admin/channel-groups`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-ownership-conflict" }, body: JSON.stringify({ id: "capability-other-group", name: "重复绑定组", capabilities: ["image"], productIds: ["capability-image-product"], channelIds: ["channel-a"], retryBudget: 1 }) });
|
||||
const duplicateBinding = await fetch(`${base}/api/v1/admin/channel-groups`, { method: "POST", headers: { ...headers, "idempotency-key": "channel-group-ownership-conflict" }, body: JSON.stringify({ id: "capability-other-group", name: "重复绑定组", capabilities: ["image"], routes: [{ publicModelId: "capability-image-v1", channels: [{ channelId: "channel-a", priorityTier: 1, orderWithinTier: 1, retryBudget: 1 }], strategy: "strict", retryBudget: 1, version: 1 }] }) });
|
||||
assert.equal(duplicateBinding.status, 409);
|
||||
assert.equal(store.channelGroups.some((group) => group.id === "capability-other-group"), false);
|
||||
} finally {
|
||||
@@ -251,7 +260,7 @@ test("message provider and template writes survive a file-store restart", async
|
||||
}
|
||||
});
|
||||
|
||||
test("admin unknown-task reconciliation stores real image metadata and clamps the charge", async () => {
|
||||
test("admin unknown-task reconciliation rejects overcharge and stores real image metadata", async () => {
|
||||
const { store, server, base } = await start();
|
||||
const taskId = "reconcile-image-task";
|
||||
const ownerId = "reconcile-user";
|
||||
@@ -267,13 +276,14 @@ test("admin unknown-task reconciliation stores real image metadata and clamps th
|
||||
status: "unknown",
|
||||
estimatedCost: 10,
|
||||
reservedCost: 10,
|
||||
count: 2,
|
||||
pricingSnapshot: { basePrice: 5, multiplier: 1, unitVersion: 1 },
|
||||
count: 1,
|
||||
pricingSnapshot: { ruleId: "price-basic-image", ruleVersion: 1, publicModelId: "basic-image-v1", resolutionId: "low", unitPrice: 10, quantity: 1, billingUnit: "output", multiplier: 1, balanceUnitVersion: 1 },
|
||||
modelSnapshot: { publicModelId: "basic-image-v1", modelVersion: 1, name: "基础模型", tier: "basic", capabilities: ["image"], resolutionId: "low", resolution: { id: "low", label: "低", width: 1024, height: 1024, ratios: ["1:1"] } },
|
||||
routeSnapshot: { groupId: "image-default", groupVersion: 1, publicModelId: "basic-image-v1", routeVersion: 1, orderedCandidates: [{ channelId: "channel-a", priorityTier: 1, orderWithinTier: 1, retryBudget: 0, requestModelId: "basic-image-v1" }], strategy: "strict", totalRetryBudget: 0 },
|
||||
planSnapshot: { queuePriority: 0, maxConcurrent: 1 },
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
eventSequence: 0,
|
||||
channelGroupId: "image-default",
|
||||
routeSnapshotVersion: 1,
|
||||
attempts: [{ id: "reconcile-attempt", channelId: "channel-a", sequence: 1, status: "unknown", startedAt: now, reconciliationStatus: "pending" }],
|
||||
outputs: [],
|
||||
inputSnapshot: { references: [], params: { resolution: "2K" } },
|
||||
@@ -287,11 +297,15 @@ test("admin unknown-task reconciliation stores real image metadata and clamps th
|
||||
assert.equal(store.tasks.get(taskId)?.status, "unknown");
|
||||
assert.equal(store.balances.get(ownerId)?.reserved, 10);
|
||||
const form = new FormData(); form.set("outcome", "success"); form.set("chargedAmount", "99"); form.append("file", new Blob([Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M/wHwAF/gL+XwAAAABJRU5ErkJggg==", "base64")], { type: "image/png" }), "result.png");
|
||||
const overcharged = await fetch(`${base}/api/v1/admin/tasks/${taskId}/reconcile`, { method: "POST", headers: { authorization: `Bearer ${loginPayload.data.accessToken}`, "idempotency-key": "reconcile-image-overcharge", "if-match": "0" }, body: form });
|
||||
assert.equal(overcharged.status, 422);
|
||||
assert.equal(store.tasks.get(taskId)?.status, "unknown");
|
||||
form.set("chargedAmount", "10");
|
||||
const response = await fetch(`${base}/api/v1/admin/tasks/${taskId}/reconcile`, { method: "POST", headers: { authorization: `Bearer ${loginPayload.data.accessToken}`, "idempotency-key": "reconcile-image-success", "if-match": "0" }, body: form });
|
||||
assert.equal(response.status, 200);
|
||||
const payload = await response.json() as { data: { item: GenerationTask } };
|
||||
const settledEntries = store.ledger.filter((entry) => entry.type === "settle" && entry.referenceId === taskId).length;
|
||||
const repeatedForm = new FormData(); repeatedForm.set("outcome", "success"); repeatedForm.set("chargedAmount", "99"); repeatedForm.append("file", new Blob([Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M/wHwAF/gL+XwAAAABJRU5ErkJggg==", "base64")], { type: "image/png" }), "result.png");
|
||||
const repeatedForm = new FormData(); repeatedForm.set("outcome", "success"); repeatedForm.set("chargedAmount", "10"); repeatedForm.append("file", new Blob([Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M/wHwAF/gL+XwAAAABJRU5ErkJggg==", "base64")], { type: "image/png" }), "result.png");
|
||||
const repeated = await fetch(`${base}/api/v1/admin/tasks/${taskId}/reconcile`, { method: "POST", headers: { authorization: `Bearer ${loginPayload.data.accessToken}`, "idempotency-key": "reconcile-image-success", "if-match": "0" }, body: repeatedForm });
|
||||
assert.equal(repeated.status, 200);
|
||||
assert.equal(store.ledger.filter((entry) => entry.type === "settle" && entry.referenceId === taskId).length, settledEntries);
|
||||
@@ -306,7 +320,7 @@ test("admin unknown-task reconciliation stores real image metadata and clamps th
|
||||
assert.equal(output.metadata?.format, "png");
|
||||
assert.equal(output.metadata?.source, "base64");
|
||||
assert.equal(output.metadata?.resolution, "2K");
|
||||
assert.equal(output.unitPriceSnapshot, 5);
|
||||
assert.equal(output.unitPriceSnapshot, 10);
|
||||
assert.equal(output.chargedAmount, 10);
|
||||
assert.equal(store.balances.get(ownerId)?.reserved, 0);
|
||||
assert.equal(store.ledger.some((entry) => entry.type === "settle" && entry.referenceId === taskId), true);
|
||||
@@ -333,8 +347,10 @@ test("admin unknown-task failure reconciliation releases the full reservation",
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
eventSequence: 0,
|
||||
channelGroupId: "image-default",
|
||||
routeSnapshotVersion: 1,
|
||||
pricingSnapshot: { ruleId: "price-basic-image", ruleVersion: 1, publicModelId: "basic-image-v1", resolutionId: "low", unitPrice: 10, quantity: 1, billingUnit: "output", multiplier: 1, balanceUnitVersion: 1 },
|
||||
modelSnapshot: { publicModelId: "basic-image-v1", modelVersion: 1, name: "基础模型", tier: "basic", capabilities: ["image"], resolutionId: "low", resolution: { id: "low", label: "低", width: 1024, height: 1024, ratios: ["1:1"] } },
|
||||
routeSnapshot: { groupId: "image-default", groupVersion: 1, publicModelId: "basic-image-v1", routeVersion: 1, orderedCandidates: [{ channelId: "channel-a", priorityTier: 1, orderWithinTier: 1, retryBudget: 0, requestModelId: "basic-image-v1" }], strategy: "strict", totalRetryBudget: 0 },
|
||||
planSnapshot: { queuePriority: 0, maxConcurrent: 1 },
|
||||
attempts: [{ id: "reconcile-failure-attempt", channelId: "channel-a", sequence: 1, status: "unknown", startedAt: now, reconciliationStatus: "pending" }],
|
||||
outputs: [],
|
||||
} satisfies GenerationTask);
|
||||
@@ -566,12 +582,13 @@ test("task route snapshots follow channel-group priority order", async () => {
|
||||
store.balances.set(user.registered.userId, { available: 100, reserved: 0 });
|
||||
const group = store.channelGroups.find((item) => item.id === "image-default");
|
||||
assert.ok(group);
|
||||
group.channelIds = ["channel-c", "channel-a", "channel-d", "channel-b"];
|
||||
group.channelPriorities = { "channel-c": 0, "channel-a": 1, "channel-d": 2, "channel-b": 3 };
|
||||
const route = group.routes.find((item) => item.publicModelId === "basic-image-v1");
|
||||
assert.ok(route);
|
||||
route.channels = ["channel-c", "channel-a", "channel-d", "channel-b"].map((channelId, index) => ({ channelId, priorityTier: 1, orderWithinTier: index + 1, retryBudget: 0 }));
|
||||
const response = await fetch(`${base}/api/v1/tasks/image`, { method: "POST", headers: { authorization: `Bearer ${user.payload.data.accessToken!}`, "content-type": "application/json", "idempotency-key": "channel-order-task" }, body: JSON.stringify({ modelProductId: "basic-image-v1", prompt: "route snapshot" }) });
|
||||
assert.equal(response.status, 202);
|
||||
const task = [...store.tasks.values()].find((item) => item.ownerId === user.registered.userId);
|
||||
assert.deepEqual(task?.routeSnapshot?.channelIds, ["channel-c", "channel-a", "channel-d", "channel-b"]);
|
||||
assert.deepEqual(task?.routeSnapshot?.orderedCandidates.map((candidate) => candidate.channelId), ["channel-c", "channel-a", "channel-d", "channel-b"]);
|
||||
} finally {
|
||||
await new Promise<void>((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
||||
}
|
||||
@@ -586,7 +603,7 @@ test("documented admin singleton and detail method aliases remain callable", asy
|
||||
assert.equal((await fetch(`${base}/api/v1/admin/billing/units`, { headers: headers("alias-units-get") })).status, 200);
|
||||
assert.equal((await fetch(`${base}/api/v1/admin/billing/units`, { method: "POST", headers: headers("alias-units-post"), body: JSON.stringify({ name: "金币" }) })).status, 201);
|
||||
assert.equal((await fetch(`${base}/api/v1/admin/billing/conversion-rules`, { method: "POST", headers: headers("alias-conversion-post"), body: JSON.stringify({ conversionRate: 1, rounding: "round" }) })).status, 201);
|
||||
assert.equal((await fetch(`${base}/api/v1/admin/pricing-rules`, { method: "POST", headers: headers("alias-pricing-post"), body: JSON.stringify({ modelProductId: "basic-image", basePrice: 10 }) })).status, 201);
|
||||
assert.equal((await fetch(`${base}/api/v1/admin/pricing-rules`, { method: "POST", headers: headers("alias-pricing-post"), body: JSON.stringify({ publicModelId: "basic-image-v1", resolutionId: "low", billingUnit: "output", tiers: [{ minQuantity: 1, unitPrice: 10 }], multiplier: 1, enabled: false }) })).status, 201);
|
||||
assert.equal((await fetch(`${base}/api/v1/admin/storage-policies`, { method: "POST", headers: headers("alias-storage-post"), body: JSON.stringify({ retentionDays: 30 }) })).status, 201);
|
||||
assert.equal((await fetch(`${base}/api/v1/admin/recharge-orders/unknown-order`, { headers: headers("alias-order-detail") })).status, 404);
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user