mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
fix(knowledge): 修正文档上传接口请求的字段名为 docIds
- 将请求体中的 dataSource.fileIds 改为扁平结构的 docIds 字段 - 移除嵌套的 dataSource 对象,显式指定 sourceType 字段 - 更新相关单元测试以匹配新的请求参数格式和字段名称 - 在知识库删除测试中增加了导入结果和最终状态的断言,确保导入流程完整 - 新增校验导入文件在知识库文档列表中正确显示 - 调整测试中对请求体结构的断言逻辑以适配改动
This commit is contained in:
@@ -149,10 +149,10 @@ export default defineCommand({
|
||||
endpoint: ragEndpoint(workspaceId, RAG_PATHS.indexJobCreate),
|
||||
request: {
|
||||
indexId: flags.indexId,
|
||||
// Gotcha (live-verified): job/create requires the nested dataSource shape;
|
||||
// the public docs' flat documentIds body returns Index.InvalidParameter.
|
||||
// Omitting sourceType would import the entire data center.
|
||||
dataSource: { sourceType: "DATA_CENTER_FILE", fileIds: ["<fileId>"] },
|
||||
// Live-verified: the field name is docIds (not documentIds as in the
|
||||
// public docs); omitting sourceType would import the entire data center.
|
||||
sourceType: "DATA_CENTER_FILE",
|
||||
docIds: ["<fileId>"],
|
||||
} as unknown,
|
||||
});
|
||||
}
|
||||
@@ -259,11 +259,10 @@ export default defineCommand({
|
||||
method: "POST",
|
||||
body: {
|
||||
indexId: flags.indexId,
|
||||
// Live-verified shape: nested dataSource (the docs' flat documentIds is rejected)
|
||||
dataSource: {
|
||||
sourceType: "DATA_CENTER_FILE",
|
||||
fileIds: uploaded.map((item) => item.fileId),
|
||||
},
|
||||
// Live-verified: the field name is docIds (not documentIds as in the
|
||||
// public docs); omitting sourceType would import the entire data center.
|
||||
sourceType: "DATA_CENTER_FILE",
|
||||
docIds: uploaded.map((item) => item.fileId),
|
||||
},
|
||||
});
|
||||
ingestionId = job.data?.ingestionId;
|
||||
|
||||
@@ -113,7 +113,7 @@ describe("e2e: knowledge doc upload", () => {
|
||||
expect(data.skipped).toEqual([]);
|
||||
});
|
||||
|
||||
test("--dry-run 带 --index-id 输出 4 步且 job 请求含显式 sourceType", async () => {
|
||||
test("--dry-run 带 --index-id 输出 4 步且 job 请求含扁平 docIds", async () => {
|
||||
const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_DOC_UPLOAD_ROUTES, [
|
||||
"knowledge",
|
||||
"doc",
|
||||
@@ -135,13 +135,15 @@ describe("e2e: knowledge doc upload", () => {
|
||||
expect(data.steps).toHaveLength(4);
|
||||
const jobRequest = data.steps[3]!.request as {
|
||||
indexId?: string;
|
||||
dataSource?: { sourceType?: string; fileIds?: string[] };
|
||||
sourceType?: string;
|
||||
docIds?: string[];
|
||||
};
|
||||
// Live-verified gotcha: job/create requires the nested dataSource shape, and
|
||||
// omitting sourceType would import the entire data center
|
||||
expect(jobRequest.dataSource?.sourceType).toBe("DATA_CENTER_FILE");
|
||||
// Live-verified: the field name is docIds (not documentIds as in the
|
||||
// public docs); omitting sourceType would import the entire data center.
|
||||
expect(jobRequest.sourceType).toBe("DATA_CENTER_FILE");
|
||||
expect(jobRequest.docIds).toEqual(["<fileId>"]);
|
||||
expect(jobRequest.indexId).toBe("idx_test");
|
||||
expect(jobRequest).not.toHaveProperty("documentIds");
|
||||
expect(jobRequest).not.toHaveProperty("dataSource");
|
||||
});
|
||||
|
||||
test("--file <dir> dry-run 展开目录且 skipped 包含不支持的文件", async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// (import orchestration) → delete → list to verify removal.
|
||||
import { mkdtempSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { basename, join } from "node:path";
|
||||
import { describe, expect, test } from "vite-plus/test";
|
||||
import { isKbAdminE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts";
|
||||
import { deleteKbWithRetry } from "./journeys/journey-helpers.ts";
|
||||
@@ -132,7 +132,9 @@ describe.skipIf(!isKbAdminE2EReady())("e2e: knowledge kb 写链路 (live, 自清
|
||||
|
||||
// 2.7) Live coverage of the upload → import orchestration (doc upload --index-id --wait):
|
||||
// the journeys always upload bare files and import via kb create, so this is the
|
||||
// only place the createImportJob step runs live
|
||||
// only place the createImportJob step runs live. Using --output json (not --quiet)
|
||||
// so we can assert final_status and ingestion_id — a regression in the job/create
|
||||
// request body (e.g. wrong field name) is caught by the server returning HTTP 400.
|
||||
const importFilePath = join(fixtureDir, `chain-import-${Date.now()}.md`);
|
||||
writeFileSync(importFilePath, "# kb chain e2e import fixture\n");
|
||||
const importUploadRun = await runCommandE2e(KNOWLEDGE_KB_DELETE_ROUTES, [
|
||||
@@ -146,13 +148,47 @@ describe.skipIf(!isKbAdminE2EReady())("e2e: knowledge kb 写链路 (live, 自清
|
||||
"--wait",
|
||||
"--workspace-id",
|
||||
workspaceId,
|
||||
"--quiet",
|
||||
"--output",
|
||||
"json",
|
||||
]);
|
||||
expect(importUploadRun.exitCode, importUploadRun.stderr).toBe(0);
|
||||
const importedFileId = importUploadRun.stdout.trim();
|
||||
const importData = parseStdoutJson<{
|
||||
files: Array<{ fileId: string }>;
|
||||
ingestion_id?: string;
|
||||
final_status?: string;
|
||||
}>(importUploadRun.stdout);
|
||||
// These assertions verify the full pipeline completed: ingestion_id proves the
|
||||
// job was created, and final_status COMPLETED proves parsing finished successfully.
|
||||
expect(importData.ingestion_id).toBeTruthy();
|
||||
expect(importData.final_status).toBe("COMPLETED");
|
||||
const importedFileId = importData.files?.[0]?.fileId;
|
||||
expect(importedFileId).toMatch(/^file_/);
|
||||
fixtureFileIds.push(importedFileId);
|
||||
|
||||
// 2.8) Verify the imported file is actually visible in the KB — final_status
|
||||
// COMPLETED only proves the job finished; an independent doc list query
|
||||
// confirms the file was registered as a document in the index
|
||||
const importedFileName = basename(importFilePath);
|
||||
const docListRun = await runCommandE2e(KNOWLEDGE_KB_DELETE_ROUTES, [
|
||||
"knowledge",
|
||||
"doc",
|
||||
"list",
|
||||
"--index-id",
|
||||
indexId,
|
||||
"--workspace-id",
|
||||
workspaceId,
|
||||
"--output",
|
||||
"json",
|
||||
]);
|
||||
expect(docListRun.exitCode, docListRun.stderr).toBe(0);
|
||||
const docListData = parseStdoutJson<{
|
||||
data?: { rows?: Array<{ doc_name?: string; status?: string }> };
|
||||
}>(docListRun.stdout);
|
||||
const importedDoc = docListData.data?.rows?.find((row) =>
|
||||
row.doc_name?.includes(importedFileName),
|
||||
);
|
||||
expect(importedDoc, `expected doc list to contain file "${importedFileName}"`).toBeTruthy();
|
||||
|
||||
// 3) Delete the base (--yes non-interactive; deleteKbWithRetry retries on
|
||||
// IndexStatusError: readiness can lag briefly even after the import completes)
|
||||
const deleteRun = await deleteKbWithRetry(
|
||||
|
||||
Reference in New Issue
Block a user