From d94cc787b238cd120783dc653a526049b629d36c Mon Sep 17 00:00:00 2001 From: Hong Jiarong Date: Mon, 13 Jul 2026 15:28:59 +0800 Subject: [PATCH] Revert "fix: import legacy projects without wrapper folder" This reverts commit 4f8df12fb0225e57fde03829a83e189f9091a0ef. --- hub/src/deployment/legacyProjectImport.ts | 5 +-- .../integration/legacy-project-import.test.ts | 34 ++----------------- 2 files changed, 5 insertions(+), 34 deletions(-) diff --git a/hub/src/deployment/legacyProjectImport.ts b/hub/src/deployment/legacyProjectImport.ts index 4b04972..f452666 100644 --- a/hub/src/deployment/legacyProjectImport.ts +++ b/hub/src/deployment/legacyProjectImport.ts @@ -81,7 +81,7 @@ export async function importLegacyProjects(input: { sourceRelativePath: entry.sourceRelativePath, }; await writeState(input.stateFile, { version: 1, projects }); - const folderId = await ensureFolderPath(input.prisma, input.organizationId, entry.folderPath); + const folderId = await ensureFolderPath(input.prisma, input.organizationId, ["旧教学资产", ...entry.folderPath]); input.onProgress?.(`import ${entry.legacyId}: ${entry.name}`); const created = await createProjectFromOrgAdmin(input.prisma, { organizationId: input.organizationId, @@ -123,7 +123,7 @@ async function ensureFolderPath( prisma: PrismaClient, organizationId: string, parts: readonly string[], -): Promise { +): Promise { let parentId: string | undefined; for (const name of parts) { const existing = await prisma.folder.findFirst({ @@ -141,6 +141,7 @@ async function ensureFolderPath( }); parentId = created.id; } + if (parentId === undefined) throw new Error("legacy import folder path is empty"); return parentId; } diff --git a/hub/test/integration/legacy-project-import.test.ts b/hub/test/integration/legacy-project-import.test.ts index 1279a4e..1973f8f 100644 --- a/hub/test/integration/legacy-project-import.test.ts +++ b/hub/test/integration/legacy-project-import.test.ts @@ -29,7 +29,7 @@ describe("legacy teaching-material project import", () => { afterAll(async () => prisma.$disconnect()); - it("imports each legacy project as an unbound resumable project under its original folder path", async () => { + it("imports each legacy project as an unbound resumable project under its old folder path", async () => { const sourceRoot = await temporaryRoot("cph-legacy-source-"); const workspaceRoot = await temporaryRoot("cph-legacy-target-"); const projectSource = join(sourceRoot, "物理", "M-243-牛顿力学"); @@ -70,7 +70,7 @@ describe("legacy teaching-material project import", () => { }); expect(project.name).toBe("牛顿力学"); expect(project.folder?.name).toBe("物理"); - expect(project.folder?.parent).toBeNull(); + expect(project.folder?.parent?.name).toBe("旧教学资产"); expect(project.groupBindings).toEqual([]); await expect(readFile(join(imported.workspaceDir, "chapters", "lesson.typ"), "utf8")) .resolves.toBe("= 牛顿第二定律\n"); @@ -116,36 +116,6 @@ describe("legacy teaching-material project import", () => { await expect(prisma.project.count({ where: { id: imported.projectId } })).resolves.toBe(1); }); - it("places a legacy project without a source folder in the system Inbox", async () => { - const sourceRoot = await temporaryRoot("cph-legacy-source-"); - const workspaceRoot = await temporaryRoot("cph-legacy-target-"); - const projectSource = join(sourceRoot, "top-level-project"); - await mkdir(join(projectSource, "workspace"), { recursive: true }); - await writeFile(join(projectSource, "workspace", "project.toml"), "title = \"顶层项目\"\n"); - await writeFile(join(projectSource, "project.json"), "{\"id\":\"top-level\"}\n"); - - const state = await importLegacyProjects({ - prisma, - organizationId: DEFAULT_ORG_ID, - actorFeishuOpenId: "ou_legacy_owner", - workspaceRoot, - sourceRoot, - stateFile: join(workspaceRoot, "state.json"), - projects: [{ - legacyId: "top-level", - name: "顶层项目", - folderPath: [], - sourceRelativePath: "top-level-project", - }], - }); - - const project = await prisma.project.findUniqueOrThrow({ - where: { id: state.projects["top-level"]?.projectId }, - include: { folder: true }, - }); - expect(project.folder).toMatchObject({ name: "Inbox", kind: "SYSTEM_INBOX" }); - }); - it("rejects symlinks instead of importing paths outside the staged project", async () => { const sourceRoot = await temporaryRoot("cph-legacy-symlink-"); const workspaceRoot = await temporaryRoot("cph-legacy-target-");