production runtime and mock removal

This commit is contained in:
inman
2026-08-17 23:44:14 +08:00
parent 2ef3da7af5
commit 6480e503eb
55 changed files with 581 additions and 540 deletions

View File

@@ -4,8 +4,7 @@ import {
bailianStatus,
buildBailianImagePayload,
buildBailianVideoPayload,
deriveBailianNativeBaseUrl,
shouldMockBailian
deriveBailianNativeBaseUrl
} from "@/lib/bailian/client";
afterEach(() => {
@@ -13,7 +12,6 @@ afterEach(() => {
delete process.env.BAILIAN_VIDEO_MODEL;
delete process.env.BAILIAN_API_KEY;
delete process.env.DASHSCOPE_API_KEY;
delete process.env.BAILIAN_MOCK;
});
describe("Bailian client", () => {
@@ -22,12 +20,6 @@ describe("Bailian client", () => {
.toBe("https://workspace.cn-beijing.maas.aliyuncs.com");
});
it("does not silently mock when the API key is missing", () => {
expect(shouldMockBailian()).toBe(false);
process.env.BAILIAN_MOCK = "true";
expect(shouldMockBailian()).toBe(true);
});
it("builds Wan 2.7 text-to-image and reference-image payloads", () => {
const text = buildBailianImagePayload("image.generate", { prompt: "海边酒店", width: 2048, height: 2048 });
expect(text).toMatchObject({ model: "wan2.7-image-pro", parameters: { size: "2048*2048", thinking_mode: true, watermark: false } });

View File

@@ -9,7 +9,7 @@ describe("Go billing core compatibility fixture", () => {
statuses: { insufficientBalance: 402, idempotencyConflict: 409 },
idempotencyKeys: { charge: "job-charge:{jobId}", refund: "job-refund:{jobId}" },
quote: { quantityRounding: "ceil", tiedWinner: "ambiguity_error", tierStandardFactors: "multiply", tierMarkupMultipliers: "maximum" },
exemptions: { billingDisabled: true, mockProvider: true, platformSuperAdminQuota: true }
exemptions: { billingDisabled: true, platformSuperAdminQuota: true }
});
});
});

View File

@@ -29,7 +29,6 @@ describe("jobs v1 cross-language contract", () => {
runtimeDir = await mkdtemp(join(tmpdir(), "zhinian-jobs-contract-"));
process.env.ZHINIAN_RUNTIME_DIR = runtimeDir;
process.env.ZHINIAN_DATA_BACKEND = "local";
process.env.JIMENG_VISUAL_MOCK = "true";
const request = new Request("http://local.test/api/v1/jobs", {
headers: { "Idempotency-Key": "header-key" },
});

View File

@@ -1,3 +1,3 @@
import { readFile } from "node:fs/promises";
import { describe, expect, it } from "vitest";
describe("Go provider adapter contract",()=>{it("freezes bounded adapters and safe errors",async()=>{const c=JSON.parse(await readFile(new URL("../contracts/providers/http-v1.json",import.meta.url),"utf8"));expect(Object.keys(c.providers)).toEqual(["volcengine-visual","evolink","bailian","seedance","mock"]);expect(c.errors).toEqual({generic:true,secretSafe:true});expect(c.liveCallsInTests).toBe(false);});});
describe("Go provider adapter contract",()=>{it("freezes bounded adapters and safe errors",async()=>{const c=JSON.parse(await readFile(new URL("../contracts/providers/http-v1.json",import.meta.url),"utf8"));expect(Object.keys(c.providers)).toEqual(["volcengine-visual","evolink","bailian","seedance"]);expect(c.errors).toEqual({generic:true,secretSafe:true});expect(c.liveCallsInTests).toBe(false);});});

View File

@@ -1,7 +1,7 @@
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
authenticatePublicApiRequest,
publicApiOwnerId,
@@ -22,9 +22,8 @@ const envNames = [
"ZHINIAN_RUNTIME_DIR",
"ZHINIAN_DATA_BACKEND",
"ZHINIAN_API_KEYS",
"JIMENG_VISUAL_MOCK",
"IMAGE_GENERATE_ENGINE",
"EVOLINK_MOCK",
"ZHINIAN_WORKER_POLL_INTERVAL_MS",
"EVOLINK_API_KEY",
"VOLCENGINE_ACCESS_KEY_ID",
"VOLCENGINE_SECRET_ACCESS_KEY",
@@ -42,23 +41,36 @@ describe("task management and public API helpers", () => {
for (const name of envNames) previousEnv.set(name, process.env[name]);
process.env.ZHINIAN_RUNTIME_DIR = runtimeDir;
process.env.ZHINIAN_DATA_BACKEND = "local";
process.env.ZHINIAN_WORKER_POLL_INTERVAL_MS = "1";
process.env.ZHINIAN_API_KEYS = "agent-a:secret-a,agent-b:secret-b";
process.env.JIMENG_VISUAL_MOCK = "true";
process.env.VOLCENGINE_ACCESS_KEY_ID = "test-access";
process.env.VOLCENGINE_SECRET_ACCESS_KEY = "test-secret";
delete process.env.IMAGE_GENERATE_ENGINE;
delete process.env.EVOLINK_MOCK;
delete process.env.EVOLINK_API_KEY;
delete process.env.VOLCENGINE_ACCESS_KEY_ID;
delete process.env.VOLCENGINE_SECRET_ACCESS_KEY;
delete process.env.ALI_OSS_ENDPOINT;
delete process.env.ALI_OSS_BUCKET;
delete process.env.ALI_OSS_ACCESS_KEY_ID;
delete process.env.ALI_OSS_ACCESS_KEY_SECRET;
delete process.env.ALI_OSS_PUBLIC_BASE_URL;
vi.stubGlobal("fetch", vi.fn(async (input: RequestInfo | URL) => {
const target = String(input);
if (target.includes("CVSync2AsyncSubmitTask")) {
return new Response(JSON.stringify({ code: 10000, data: { task_id: "visual-task-1" } }), { status: 200, headers: { "Content-Type": "application/json" } });
}
if (target.includes("CVSync2AsyncGetResult")) {
return new Response(JSON.stringify({ code: 10000, data: { status: "done", image_urls: ["https://cdn.test/result.png"] } }), { status: 200, headers: { "Content-Type": "application/json" } });
}
if (target === "https://cdn.test/result.png") {
return new Response(new Uint8Array([137, 80, 78, 71]), { status: 200, headers: { "Content-Type": "image/png" } });
}
throw new Error(`unexpected test fetch: ${target}`);
}));
});
afterEach(async () => {
for (const name of envNames) restoreEnv(name, previousEnv.get(name));
previousEnv.clear();
vi.unstubAllGlobals();
await rm(runtimeDir, { force: true, recursive: true });
});
@@ -139,7 +151,6 @@ describe("task management and public API helpers", () => {
it("passes EvoLink quality through public image jobs", async () => {
process.env.IMAGE_GENERATE_ENGINE = "evolink";
process.env.EVOLINK_MOCK = "true";
process.env.EVOLINK_API_KEY = "test-key";
const result = await createPublicGenerationJob({
client: { id: "agent-a", key: "secret-a" },
@@ -151,7 +162,7 @@ describe("task management and public API helpers", () => {
quality: "high"
}
});
expect(result.job.provider).toBe("mock");
expect(result.job.provider).toBe("evolink");
expect(result.job.requestPayload.providerPayload).toMatchObject({
model: "gpt-image-2",
quality: "high"
@@ -160,11 +171,11 @@ describe("task management and public API helpers", () => {
it("allows image jobs to override the default generation engine", async () => {
process.env.IMAGE_GENERATE_ENGINE = "evolink";
process.env.EVOLINK_MOCK = "true";
process.env.EVOLINK_API_KEY = "test-key";
const jimengJob = await submitImageJob({
ownerId: DEFAULT_OWNER_ID,
externalClientId: "agent-test",
capability: "image.generate",
engine: "jimeng",
prompt: "即梦模板",
@@ -178,6 +189,7 @@ describe("task management and public API helpers", () => {
const image2Job = await submitImageJob({
ownerId: DEFAULT_OWNER_ID,
externalClientId: "agent-test",
capability: "image.generate",
engine: "evolink",
prompt: "Image2 模板",
@@ -194,7 +206,7 @@ describe("task management and public API helpers", () => {
await Promise.all(Array.from({ length: 6 }, (_, index) => createGenerationJob({
ownerId: DEFAULT_OWNER_ID,
capability: "image.generate",
provider: "mock",
provider: "volcengine-visual",
reqKey: "jimeng_seedream46_cvtob",
status: "queued",
prompt: `job ${index}`,
@@ -214,7 +226,7 @@ describe("task management and public API helpers", () => {
expect(await claimGenerationJobs({ workerId: "worker-c", limit: 1 })).toHaveLength(0);
});
it("processes a mock queued job to a terminal result through the worker tick", async () => {
it("processes a real-provider queued job to a terminal result through the worker tick", async () => {
const job = await createPublicGenerationJob({
client: { id: "agent-a", key: "secret-a" },
request: new Request("http://local.test/api/v1/jobs"),
@@ -225,13 +237,22 @@ describe("task management and public API helpers", () => {
}
});
expect(job.job.status).toBe("queued");
const tick = await runWorkerTick({
let tick = await runWorkerTick({
workerId: "test-worker",
origin: "http://local.test",
limit: 1
});
expect(tick.claimed).toBe(1);
const stored = await getGenerationJob(job.job.id);
let stored = await getGenerationJob(job.job.id);
for (let attempt = 0; attempt < 3 && stored?.status !== "succeeded"; attempt += 1) {
await new Promise((resolve) => setTimeout(resolve, 5));
tick = await runWorkerTick({
workerId: "test-worker",
origin: "http://local.test",
limit: 1
});
stored = await getGenerationJob(job.job.id);
}
expect(stored?.status).toBe("succeeded");
expect(stored?.completedAt).toBeTruthy();
expect(stored?.outputAssetIds.length).toBe(1);