fix: 消除并发测试中的 tool 导出竞争和 SQLite 目录碰撞
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import Database from "bun:sqlite";
|
import Database from "bun:sqlite";
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
import { mkdirSync, rmSync } from "node:fs";
|
import { mkdirSync, rmSync } from "node:fs";
|
||||||
import { rm } from "node:fs/promises";
|
import { rm } from "node:fs/promises";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
@@ -105,7 +106,7 @@ export function createTestDatabase(prefix: string, migrations: MigrationRecord[]
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function makeTempDir(prefix: string): string {
|
export function makeTempDir(prefix: string): string {
|
||||||
const dir = join(tmpdir(), `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
const dir = join(tmpdir(), `${prefix}-${randomUUID()}`);
|
||||||
mkdirSync(dir, { recursive: true });
|
mkdirSync(dir, { recursive: true });
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,8 @@
|
|||||||
import { describe, expect, mock, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
|
|
||||||
import { createNoopLogger } from "../../../src/server/logger";
|
import { createNoopLogger } from "../../../src/server/logger";
|
||||||
import { createMigratedTestDatabase } from "../../helpers";
|
import { createMigratedTestDatabase } from "../../helpers";
|
||||||
|
import "../mocks/ai";
|
||||||
void mock.module("ai", () => ({
|
|
||||||
createProviderRegistry: (providers: Record<string, { languageModel: (modelId: string) => unknown }>) => ({
|
|
||||||
languageModel: (id: string) => {
|
|
||||||
const [providerId, modelId] = id.split(":");
|
|
||||||
const provider = providers[providerId ?? ""];
|
|
||||||
if (!provider || !modelId) throw new Error(`No such provider: ${id}`);
|
|
||||||
return provider.languageModel(modelId);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
generateText: () => Promise.resolve({ text: "Hi" }),
|
|
||||||
}));
|
|
||||||
|
|
||||||
async function withProviderServer(
|
async function withProviderServer(
|
||||||
modelsResponse: Response,
|
modelsResponse: Response,
|
||||||
|
|||||||
36
tests/server/mocks/ai.ts
Normal file
36
tests/server/mocks/ai.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { mock } from "bun:test";
|
||||||
|
|
||||||
|
void mock.module("ai", () => ({
|
||||||
|
createAgentUIStreamResponse: (opts: {
|
||||||
|
agent: unknown;
|
||||||
|
messages: unknown[];
|
||||||
|
onFinish:
|
||||||
|
| ((event: { finishReason?: string; responseMessage: { parts?: Array<{ text: string; type: string }> } }) => void)
|
||||||
|
| undefined;
|
||||||
|
}) => {
|
||||||
|
if (opts.onFinish) {
|
||||||
|
opts.onFinish({
|
||||||
|
responseMessage: {
|
||||||
|
parts: [{ text: "test reply from AI", type: "text" }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve(
|
||||||
|
new Response(
|
||||||
|
'data: {"type":"start-step"}\n\ndata: {"type":"text-start","id":"txt-1"}\n\ndata: {"type":"text-delta","id":"txt-1","delta":"test reply from AI"}\n\ndata: {"type":"text-end","id":"txt-1"}\n\ndata: {"type":"finish-step"}\n\ndata: {"type":"finish"}\n\n',
|
||||||
|
{
|
||||||
|
headers: { "Content-Type": "text/event-stream" },
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
createProviderRegistry: () => ({
|
||||||
|
languageModel: () => ({}),
|
||||||
|
}),
|
||||||
|
generateText: () => Promise.resolve({ text: "AI\u603B\u7ED3\u6807\u9898", usage: {} }),
|
||||||
|
stepCountIs: () => () => true,
|
||||||
|
tool: () => ({ execute: async () => await Promise.resolve({}) }),
|
||||||
|
ToolLoopAgent: function M() {
|
||||||
|
// no-op: createAgentUIStreamResponse handles streaming
|
||||||
|
},
|
||||||
|
}));
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type Database from "bun:sqlite";
|
import type Database from "bun:sqlite";
|
||||||
|
|
||||||
import { describe, expect, mock, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
|
|
||||||
import type { Conversation, Message, RuntimeMode } from "../../../src/shared/api";
|
import type { Conversation, Message, RuntimeMode } from "../../../src/shared/api";
|
||||||
|
|
||||||
@@ -9,45 +9,11 @@ import { createProject } from "../../../src/server/db/projects";
|
|||||||
import { createProvider } from "../../../src/server/db/providers";
|
import { createProvider } from "../../../src/server/db/providers";
|
||||||
import { createNoopLogger } from "../../../src/server/logger";
|
import { createNoopLogger } from "../../../src/server/logger";
|
||||||
import { createMigratedMemoryTestDatabase } from "../../helpers";
|
import { createMigratedMemoryTestDatabase } from "../../helpers";
|
||||||
|
import "../mocks/ai";
|
||||||
|
|
||||||
const MODE: RuntimeMode = "test";
|
const MODE: RuntimeMode = "test";
|
||||||
const LOG = createNoopLogger();
|
const LOG = createNoopLogger();
|
||||||
|
|
||||||
void mock.module("ai", () => ({
|
|
||||||
createAgentUIStreamResponse: (opts: {
|
|
||||||
agent: unknown;
|
|
||||||
messages: unknown[];
|
|
||||||
onFinish:
|
|
||||||
| ((event: { finishReason?: string; responseMessage: { parts?: Array<{ text: string; type: string }> } }) => void)
|
|
||||||
| undefined;
|
|
||||||
}) => {
|
|
||||||
if (opts.onFinish) {
|
|
||||||
opts.onFinish({
|
|
||||||
responseMessage: {
|
|
||||||
parts: [{ text: "test reply from AI", type: "text" }],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Promise.resolve(
|
|
||||||
new Response(
|
|
||||||
'data: {"type":"start-step"}\n\ndata: {"type":"text-start","id":"txt-1"}\n\ndata: {"type":"text-delta","id":"txt-1","delta":"test reply from AI"}\n\ndata: {"type":"text-end","id":"txt-1"}\n\ndata: {"type":"finish-step"}\n\ndata: {"type":"finish"}\n\n',
|
|
||||||
{
|
|
||||||
headers: { "Content-Type": "text/event-stream" },
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
createProviderRegistry: () => ({
|
|
||||||
languageModel: () => ({}),
|
|
||||||
}),
|
|
||||||
generateText: () => Promise.resolve({ text: "AI总结标题", usage: {} }),
|
|
||||||
stepCountIs: () => () => true,
|
|
||||||
tool: () => ({ execute: async () => await Promise.resolve({}) }),
|
|
||||||
ToolLoopAgent: function M() {
|
|
||||||
// no-op: createAgentUIStreamResponse handles streaming
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
async function createConversationViaHandler(req: Request, db: Database): Promise<Response> {
|
async function createConversationViaHandler(req: Request, db: Database): Promise<Response> {
|
||||||
const { handleCreateConversation: h } = await import("../../../src/server/routes/chat/create");
|
const { handleCreateConversation: h } = await import("../../../src/server/routes/chat/create");
|
||||||
return h(req, db, MODE, LOG);
|
return h(req, db, MODE, LOG);
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import type Database from "bun:sqlite";
|
import type Database from "bun:sqlite";
|
||||||
|
|
||||||
import { describe, expect, mock, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
|
|
||||||
import type { Model, RuntimeMode } from "../../../src/shared/api";
|
import type { Model, RuntimeMode } from "../../../src/shared/api";
|
||||||
|
|
||||||
import { createNoopLogger } from "../../../src/server/logger";
|
import { createNoopLogger } from "../../../src/server/logger";
|
||||||
import { createMigratedMemoryTestDatabase } from "../../helpers";
|
import { createMigratedMemoryTestDatabase } from "../../helpers";
|
||||||
|
import "../mocks/ai";
|
||||||
|
|
||||||
const MODE: RuntimeMode = "test";
|
const MODE: RuntimeMode = "test";
|
||||||
const LOG = createNoopLogger();
|
const LOG = createNoopLogger();
|
||||||
@@ -49,13 +50,6 @@ async function listModelsViaHandler(req: Request, db: Database): Promise<Respons
|
|||||||
import { createModel } from "../../../src/server/db/models";
|
import { createModel } from "../../../src/server/db/models";
|
||||||
import { createProvider } from "../../../src/server/db/providers";
|
import { createProvider } from "../../../src/server/db/providers";
|
||||||
|
|
||||||
void mock.module("ai", () => ({
|
|
||||||
createProviderRegistry: () => ({
|
|
||||||
languageModel: () => ({}),
|
|
||||||
}),
|
|
||||||
generateText: () => Promise.resolve({ text: "Hi" }),
|
|
||||||
}));
|
|
||||||
|
|
||||||
function seedProvider(db: Database, name?: string): string {
|
function seedProvider(db: Database, name?: string): string {
|
||||||
const result = createProvider(
|
const result = createProvider(
|
||||||
db,
|
db,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type Database from "bun:sqlite";
|
import type Database from "bun:sqlite";
|
||||||
|
|
||||||
import { describe, expect, mock, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
|
|
||||||
import type { Provider, ProviderOption, RuntimeMode } from "../../../src/shared/api";
|
import type { Provider, ProviderOption, RuntimeMode } from "../../../src/shared/api";
|
||||||
|
|
||||||
@@ -8,16 +8,11 @@ import { createModel } from "../../../src/server/db/models";
|
|||||||
import { createProvider } from "../../../src/server/db/providers";
|
import { createProvider } from "../../../src/server/db/providers";
|
||||||
import { createNoopLogger } from "../../../src/server/logger";
|
import { createNoopLogger } from "../../../src/server/logger";
|
||||||
import { createMigratedMemoryTestDatabase } from "../../helpers";
|
import { createMigratedMemoryTestDatabase } from "../../helpers";
|
||||||
|
import "../mocks/ai";
|
||||||
|
|
||||||
const MODE: RuntimeMode = "test";
|
const MODE: RuntimeMode = "test";
|
||||||
const LOG = createNoopLogger();
|
const LOG = createNoopLogger();
|
||||||
|
|
||||||
void mock.module("ai", () => ({
|
|
||||||
createProviderRegistry: () => ({
|
|
||||||
languageModel: () => ({}),
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
async function createProviderViaHandler(req: Request, db: Database): Promise<Response> {
|
async function createProviderViaHandler(req: Request, db: Database): Promise<Response> {
|
||||||
const { handleCreateProvider: h } = await import("../../../src/server/routes/providers/create");
|
const { handleCreateProvider: h } = await import("../../../src/server/routes/providers/create");
|
||||||
return h(req, db, MODE, LOG);
|
return h(req, db, MODE, LOG);
|
||||||
|
|||||||
Reference in New Issue
Block a user