import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { expect, test } from "bun:test"; import { executeHwpodNodeOpsPlan } from "../../tools/src/hwpod-node-lib.ts"; import { createProjectManagementApp } from "./server.ts"; import { createMdtodoSourceAdapter, normalizeProjectSourceInput } from "./source-adapter.ts"; import { createProjectManagementStore } from "./store.ts"; test("project management source adapter types HWPOD transport failures", async () => { const normalized = normalizeProjectSourceInput({ sourceId: "transport-hwpod-mdtodo", sourceKind: "hwpod-workspace", projectId: "project_transport", hwpodId: "transport-hwpod", nodeId: "transport-node", workspaceRootRef: "C:/Work/Transport", mdtodoRootRef: "docs/MDTODO" }); expect(normalized.ok).toBe(true); const adapter = createMdtodoSourceAdapter({ env: { HWLAB_PROJECT_MANAGEMENT_HWPOD_NODE_OPS_URL: "http://hwpod.invalid/node-ops" }, fetchImpl: async () => { throw new TypeError("connection refused with internal endpoint"); } }); let caught; try { await adapter.readFile(normalized.source, "MDTODO.md"); } catch (error) { caught = error; } expect(caught).toMatchObject({ code: "hwpod_transport_unavailable", statusCode: 502, sourceKind: "hwpod-workspace", message: "HWPOD node-ops transport is unavailable" }); expect(String(caught?.message)).not.toContain("internal endpoint"); }); test("project management app exposes MDTODO read model and Workbench link writes", async () => { const root = await mkdtemp(join(tmpdir(), "hwlab-project-management-")); try { await writeFile(join(root, "MDTODO.md"), "# Demo\n\n## R1 Launch project nav\n\n## R2 Wire read model [completed]\n", "utf8"); const app = createProjectManagementApp({ env: {}, store: createProjectManagementStore({ kind: "memory" }), sourceRoot: root, sourceId: "test-mdtodo", projectId: "project_test" }); const ready = await app.fetch(new Request("http://service/health/ready")); expect(ready.status).toBe(200); const navigation = await json(app, "/v1/project-management/navigation"); expect(navigation.navigation.id).toBe("project-management"); const sources = await json(app, "/v1/project-management/mdtodo/sources"); expect(sources.sources[0].sourceId).toBe("test-mdtodo"); const files = await json(app, "/v1/project-management/mdtodo/files?sourceId=test-mdtodo"); expect(files.files[0].taskCount).toBe(2); const tasks = await json(app, "/v1/project-management/mdtodo/tasks?sourceId=test-mdtodo"); expect(tasks.tasks.map((task) => task.taskId)).toEqual(["R1", "R2"]); expect(tasks.tasks.map((task) => task.status)).toEqual(["open", "completed"]); expect(tasks.tasks.every((task) => task.projectId === "project_test")).toBe(true); expect(tasks.page).toMatchObject({ offset: 0, limit: 200, total: 2, hasMore: false }); const taskWindow = await json(app, "/v1/project-management/mdtodo/tasks?sourceId=test-mdtodo&limit=1&offset=1"); expect(taskWindow.tasks.map((task) => task.taskId)).toEqual(["R2"]); expect(taskWindow.page).toMatchObject({ offset: 1, limit: 1, total: 2, hasMore: false }); const projectActivity = await json(app, "/v1/project-management/mdtodo/tasks?projectId=project_test&limit=1"); expect(projectActivity.tasks).toHaveLength(1); expect(projectActivity.tasks[0].projectId).toBe("project_test"); expect(projectActivity.page).toMatchObject({ offset: 0, limit: 1, total: 2, hasMore: true }); const links = await json(app, "/v1/project-management/workbench-links?projectId=project_test"); expect(links.links).toEqual([]); const linkResponse = await app.fetch(new Request("http://service/v1/project-management/workbench-links", { method: "POST", headers: { "content-type": "application/json", "x-hwlab-actor-id": "usr_test" }, body: JSON.stringify({ projectId: "project_test", taskRef: tasks.tasks[0].taskRef, sessionId: "ses_project_test", role: "launch", link: { workbenchUrl: "/workbench/sessions/ses_project_test", launchContext: { projectId: "project_test", taskRef: tasks.tasks[0].taskRef, title: tasks.tasks[0].title } } }) })); expect(linkResponse.status).toBe(201); const createdLink = await linkResponse.json(); expect(createdLink.link.sessionId).toBe("ses_project_test"); const linkedTasks = await json(app, "/v1/project-management/mdtodo/tasks?sourceId=test-mdtodo"); expect(linkedTasks.tasks[0].linkCount).toBe(1); const taskLinks = await json(app, `/v1/project-management/workbench-links?taskRef=${encodeURIComponent(tasks.tasks[0].taskRef)}`); expect(taskLinks.links[0].sessionId).toBe("ses_project_test"); const rejected = await app.fetch(new Request("http://service/v1/project-management/mdtodo/tasks", { method: "PUT" })); expect(rejected.status).toBe(405); await app.close(); } finally { await rm(root, { recursive: true, force: true }); } }); test("project management task list defaults to a bounded window", async () => { const root = await mkdtemp(join(tmpdir(), "hwlab-project-management-task-window-")); try { const body = ["# Demo", "", ...Array.from({ length: 5 }, (_, index) => `## R${index + 1} Task ${index + 1}`)].join("\n"); await writeFile(join(root, "MDTODO.md"), `${body}\n`, "utf8"); const app = createProjectManagementApp({ env: { HWLAB_PROJECT_MANAGEMENT_MDTODO_TASK_DEFAULT_LIMIT: "2", HWLAB_PROJECT_MANAGEMENT_MDTODO_TASK_MAX_LIMIT: "3" }, store: createProjectManagementStore({ kind: "memory" }), sourceRoot: root, sourceId: "bounded-mdtodo", projectId: "project_test" }); const ready = await app.fetch(new Request("http://service/health/ready")); expect(ready.status).toBe(200); const defaultWindow = await json(app, "/v1/project-management/mdtodo/tasks?sourceId=bounded-mdtodo"); expect(defaultWindow.tasks.map((task) => task.taskId)).toEqual(["R1", "R2"]); expect(defaultWindow.page).toMatchObject({ offset: 0, limit: 2, maxLimit: 3, total: 5, hasMore: true }); const cappedWindow = await json(app, "/v1/project-management/mdtodo/tasks?sourceId=bounded-mdtodo&limit=99"); expect(cappedWindow.tasks.map((task) => task.taskId)).toEqual(["R1", "R2", "R3"]); expect(cappedWindow.page).toMatchObject({ offset: 0, limit: 3, maxLimit: 3, total: 5, hasMore: true }); await app.close(); } finally { await rm(root, { recursive: true, force: true }); } }); test("project management task list restores an exact task beyond the first bounded window", async () => { const root = await mkdtemp(join(tmpdir(), "hwlab-project-management-deep-task-")); try { const body = ["# Demo", "", ...Array.from({ length: 205 }, (_, index) => `## R${index + 1} Task ${index + 1}`)].join("\n"); await writeFile(join(root, "MDTODO.md"), `${body}\n`, "utf8"); const app = createProjectManagementApp({ env: {}, store: createProjectManagementStore({ kind: "memory" }), sourceRoot: root, sourceId: "deep-mdtodo", projectId: "project_test" }); expect((await app.fetch(new Request("http://service/health/ready"))).status).toBe(200); const firstWindow = await json(app, "/v1/project-management/mdtodo/tasks?sourceId=deep-mdtodo&limit=100"); expect(firstWindow.tasks).toHaveLength(100); expect(firstWindow.page).toMatchObject({ offset: 0, limit: 100, total: 205, hasMore: true }); const taskRef = `mdtodo:deep-mdtodo:${firstWindow.tasks[0].fileRef}:R205`; const exact = await json(app, `/v1/project-management/mdtodo/tasks?sourceId=deep-mdtodo&taskRef=${encodeURIComponent(taskRef)}&limit=1`); expect(exact.tasks).toHaveLength(1); expect(exact.tasks[0]).toMatchObject({ taskRef, taskId: "R205", title: "Task 205" }); expect(exact.page).toMatchObject({ offset: 0, limit: 1, total: 1, hasMore: false }); await app.close(); } finally { await rm(root, { recursive: true, force: true }); } }); test("project management app configures HWPOD source and writes MDTODO content", async () => { const root = await mkdtemp(join(tmpdir(), "hwlab-project-management-hwpod-")); const defaultRoot = join(root, "default"); const hwpodRoot = join(root, "hwpod-workspace"); const mdtodoDir = join(hwpodRoot, "docs", "MDTODO"); const samplePath = join(mdtodoDir, "hwlab-v03-mdtodo-web-sample.md"); const reportDir = join(mdtodoDir, "20260110_LOG"); const reportPath = join(reportDir, "R1.1_log_implementation.md"); const store = createProjectManagementStore({ kind: "memory" }); try { await mkdir(defaultRoot, { recursive: true }); await mkdir(mdtodoDir, { recursive: true }); await mkdir(reportDir, { recursive: true }); await writeFile(join(defaultRoot, "MDTODO.md"), "# Default\n\n## R1 Default\n", "utf8"); await writeFile(samplePath, "# Sample\n\n## R1 Root\n\nSee [R1.1](./20260110_LOG/R1.1_log_implementation.md).\n\n### R1.1 Child [in_progress]\n", "utf8"); await writeFile(reportPath, "# R1.1 implementation report\n\nReport body.", "utf8"); const app = createProjectManagementApp({ env: {}, store, sourceRoot: defaultRoot, sourceId: "default-mdtodo", projectId: "project_test", hwpodNodeOpsHandler: (plan) => executeHwpodNodeOpsPlan(plan) }); const createResponse = await app.fetch(jsonRequest("/v1/project-management/mdtodo/sources", { method: "POST", body: { sourceId: "hwpod-mdtodo", sourceKind: "hwpod-workspace", displayName: "D601 F103 MDTODO", projectId: "project_test", hwpodId: "d601-f103-v2", nodeId: "node-d601-f103-v2", workspaceRootRef: hwpodRoot, mdtodoRootRef: "docs/MDTODO/" } })); expect(createResponse.status).toBe(201); const created = await createResponse.json(); expect(created.source.sourceKind).toBe("hwpod-workspace"); const probe = await post(app, "/v1/project-management/mdtodo/sources/hwpod-mdtodo/probe", {}); expect(probe.probe.status).toBe("ok"); const reindex = await post(app, "/v1/project-management/mdtodo/sources/hwpod-mdtodo/reindex", {}); expect(reindex.projection.documentCount).toBe(1); expect(reindex.projection.taskCount).toBe(2); const files = await json(app, "/v1/project-management/mdtodo/files?sourceId=hwpod-mdtodo"); expect(files.files).toHaveLength(1); expect(files.files[0].relativePath).toBe("hwlab-v03-mdtodo-web-sample.md"); const fileRef = files.files[0].fileRef; const content = await json(app, `/v1/project-management/mdtodo/files/${fileRef}/content?sourceId=hwpod-mdtodo`); expect(content.file.content).toContain("## R1 Root"); const rootTaskRef = `mdtodo:hwpod-mdtodo:${fileRef}:R1`; const detail = await json(app, `/v1/project-management/mdtodo/task-detail?taskRef=${encodeURIComponent(rootTaskRef)}`); expect(detail.detail.body).toContain("See [R1.1]"); expect(detail.detail.links[0]).toMatchObject({ kind: "markdown-report", relativePath: "20260110_LOG/R1.1_log_implementation.md" }); const report = await json(app, `/v1/project-management/mdtodo/report-preview?taskRef=${encodeURIComponent(rootTaskRef)}&linkId=${encodeURIComponent(detail.detail.links[0].linkId)}`); expect(report.report.content).toContain("Report body."); const launchContext = await json(app, `/v1/project-management/mdtodo/launch-context?taskRef=${encodeURIComponent(rootTaskRef)}`); expect(launchContext.executionContext).toMatchObject({ sourceId: "hwpod-mdtodo", sourceKind: "hwpod-workspace", projectId: "project_test", taskRef: rootTaskRef, fileRef, relativePath: "hwlab-v03-mdtodo-web-sample.md", taskId: "R1", hwpodId: "d601-f103-v2", nodeId: "node-d601-f103-v2", mdtodoRootRef: "docs/MDTODO", valuesRedacted: true }); expect(launchContext.executionContext.hwpodWorkspaceArgs).toContain("--hwpod-id d601-f103-v2"); expect(launchContext.executionContext.hwpodWorkspaceArgs).toContain("--workspace-path"); expect(launchContext.executionContext.workspaceRootHash).toMatch(/^[0-9a-f]{64}$/u); expect(launchContext.executionContext.contextFingerprint).toMatch(/^ctx_[0-9a-f]{24}$/u); const linkWithContext = await app.fetch(jsonRequest("/v1/project-management/workbench-links", { method: "POST", body: { projectId: "project_test", taskRef: rootTaskRef, sessionId: "ses_hwpod_launch", role: "launch", link: { workbenchUrl: "/workbench/sessions/ses_hwpod_launch", launchContext: launchContext.launchContext } } })); expect(linkWithContext.status).toBe(201); const savedLink = await linkWithContext.json(); expect(savedLink.link.link.launchContext).toMatchObject({ sourceId: "hwpod-mdtodo", hwpodId: "d601-f103-v2", mdtodoRootRef: "docs/MDTODO", contextFingerprint: launchContext.executionContext.contextFingerprint }); const nextContent = "# Sample\n\n## R1 Edited root\n\n### R1.1 Child [completed]\n"; const write = await app.fetch(jsonRequest(`/v1/project-management/mdtodo/files/${fileRef}/content?sourceId=hwpod-mdtodo`, { method: "PATCH", body: { sourceId: "hwpod-mdtodo", expectedFingerprint: content.file.fingerprint, content: nextContent } })); expect(write.status).toBe(200); const written = await write.json(); expect(written.file.fingerprint).not.toBe(content.file.fingerprint); expect(await readFile(samplePath, "utf8")).toBe(nextContent); const tasks = await json(app, "/v1/project-management/mdtodo/tasks?sourceId=hwpod-mdtodo"); expect(tasks.tasks.map((task) => [task.taskId, task.title, task.status])).toEqual([ ["R1", "Edited root", "open"], ["R1.1", "Child", "completed"] ]); const updateTask = await app.fetch(jsonRequest(`/v1/project-management/mdtodo/tasks/${encodeURIComponent(rootTaskRef)}`, { method: "PATCH", body: { expectedFingerprint: written.file.fingerprint, title: "Root from task API", status: "in_progress", body: "Task API body." } })); expect(updateTask.status).toBe(200); const updatedTask = await updateTask.json(); expect(updatedTask.task.title).toBe("Root from task API"); expect(updatedTask.task.status).toBe("in_progress"); expect(await readFile(samplePath, "utf8")).toContain("## R1 Root from task API [in_progress]\n\nTask API body."); const addSubtask = await app.fetch(jsonRequest("/v1/project-management/mdtodo/tasks", { method: "POST", body: { parentTaskRef: rootTaskRef, expectedFingerprint: updatedTask.file.fingerprint, title: "Added child", body: "Child body." } })); expect(addSubtask.status).toBe(201); const added = await addSubtask.json(); expect(added.changedTaskId).toBe("R1.2"); expect(added.task.taskRef).toBe(`mdtodo:hwpod-mdtodo:${fileRef}:R1.2`); const continueTask = await app.fetch(jsonRequest("/v1/project-management/mdtodo/tasks", { method: "POST", body: { afterTaskRef: `mdtodo:hwpod-mdtodo:${fileRef}:R1.1`, expectedFingerprint: added.file.fingerprint, title: "Continued child" } })); expect(continueTask.status).toBe(201); const continued = await continueTask.json(); expect(continued.changedTaskId).toBe("R1.3"); const deleteTask = await app.fetch(jsonRequest(`/v1/project-management/mdtodo/tasks/${encodeURIComponent(added.task.taskRef)}`, { method: "DELETE", body: { expectedFingerprint: continued.file.fingerprint } })); expect(deleteTask.status).toBe(200); const deleted = await deleteTask.json(); expect(deleted.affectedTaskIds).toEqual(["R1.2"]); expect(await readFile(samplePath, "utf8")).not.toContain("R1.2 Added child"); const conflict = await app.fetch(jsonRequest(`/v1/project-management/mdtodo/files/${fileRef}/content?sourceId=hwpod-mdtodo`, { method: "PATCH", body: { sourceId: "hwpod-mdtodo", expectedFingerprint: content.file.fingerprint, content: nextContent } })); expect(conflict.status).toBe(409); const rejected = await app.fetch(jsonRequest("/v1/project-management/mdtodo/sources", { method: "POST", body: { sourceKind: "hwpod-workspace", hwpodId: "d601-f103-v2", nodeId: "node-d601-f103-v2", workspaceRootRef: hwpodRoot, mdtodoRootRef: "../secret" } })); expect(rejected.status).toBe(400); expect((await store.listAuditEvents()).some((event) => event.action === "mdtodo.file.write")).toBe(true); await app.close(); } finally { await rm(root, { recursive: true, force: true }); } }); test("project management app bootstraps HWPOD sources from a mounted JSON file", async () => { const root = await mkdtemp(join(tmpdir(), "hwlab-project-management-bootstrap-")); const defaultRoot = join(root, "default"); const hwpodRoot = join(root, "hwpod-workspace"); const mdtodoDir = join(hwpodRoot, "docs", "MDTODO"); const configPath = join(root, "sources.json"); try { await mkdir(defaultRoot, { recursive: true }); await mkdir(mdtodoDir, { recursive: true }); await writeFile(join(defaultRoot, "MDTODO.md"), "# Default\n\n## R1 Default\n", "utf8"); await writeFile(join(mdtodoDir, "bootstrap.md"), "# Bootstrap\n\n## R1 From mounted source\n", "utf8"); await writeFile(configPath, JSON.stringify({ sources: [{ sourceId: "constart-71freq-mdtodo", sourceKind: "hwpod-workspace", displayName: "71-FREQ MDTODO", projectId: "project_constart_71freq", hwpodId: "constart-71freq-c", nodeId: "node-d601-f103-v2", workspaceRootRef: hwpodRoot, mdtodoRootRef: "docs/MDTODO" }] }), "utf8"); const app = createProjectManagementApp({ env: { HWLAB_PROJECT_MANAGEMENT_BOOTSTRAP_SOURCES_PATH: configPath }, store: createProjectManagementStore({ kind: "memory" }), sourceRoot: defaultRoot, sourceId: "default-mdtodo", projectId: "project_test", hwpodNodeOpsHandler: (plan) => executeHwpodNodeOpsPlan(plan) }); const ready = await app.fetch(new Request("http://service/health/ready")); expect(ready.status).toBe(200); const sources = await json(app, "/v1/project-management/mdtodo/sources"); expect(sources.sources.map((source) => source.sourceId).sort()).toEqual(["constart-71freq-mdtodo", "default-mdtodo"]); const files = await json(app, "/v1/project-management/mdtodo/files?sourceId=constart-71freq-mdtodo"); expect(files.files[0].relativePath).toBe("bootstrap.md"); await app.close(); } finally { await rm(root, { recursive: true, force: true }); } }); test("project management task detail falls back to the last HWPOD projection when the node is offline", async () => { const root = await mkdtemp(join(tmpdir(), "hwlab-project-management-hwpod-partial-")); const defaultRoot = join(root, "default"); const configPath = join(root, "sources.json"); let hwpodState = "ready"; try { await mkdir(defaultRoot, { recursive: true }); await writeFile(join(defaultRoot, "MDTODO.md"), "# Default\n\n## R1 Default\n", "utf8"); await writeFile(configPath, JSON.stringify({ sources: [{ sourceId: "offline-hwpod-mdtodo", sourceKind: "hwpod-workspace", displayName: "Offline HWPOD MDTODO", projectId: "project_hwpod_partial", hwpodId: "offline-hwpod", nodeId: "node-offline", workspaceRootRef: "F:/Work/Offline", mdtodoRootRef: "docs/MDTODO" }] }), "utf8"); const app = createProjectManagementApp({ env: { HWLAB_PROJECT_MANAGEMENT_BOOTSTRAP_SOURCES_PATH: configPath }, store: createProjectManagementStore({ kind: "memory" }), sourceRoot: defaultRoot, sourceId: "default-mdtodo", projectId: "project_test", hwpodNodeOpsHandler: (plan) => { if (hwpodState === "bug") throw new Error("programmer bug with internal detail"); if (hwpodState === "offline") { const blocker = { code: "hwpod_node_offline", summary: "node-offline is not connected" }; return { ok: false, status: "blocked", blocker, results: [{ opId: "op_01", op: plan.intent, ok: false, status: "blocked", blocker }] }; } const output = plan.intent === "workspace.ls" ? { entries: [{ name: "offline.md", type: "file" }] } : { content: "# Offline\n\n## R1 Projected task [in_progress]\n\nProjected body. See [report](./reports/projected.md).\n" }; return { ok: true, status: "completed", results: [{ opId: "op_01", op: plan.intent, ok: true, status: "completed", output }] }; } }); expect((await app.fetch(new Request("http://service/health/ready"))).status).toBe(200); const files = await json(app, "/v1/project-management/mdtodo/files?sourceId=offline-hwpod-mdtodo"); const tasks = await json(app, `/v1/project-management/mdtodo/tasks?sourceId=offline-hwpod-mdtodo&fileRef=${encodeURIComponent(files.files[0].fileRef)}`); hwpodState = "offline"; const response = await app.fetch(new Request(`http://service/v1/project-management/mdtodo/task-detail?taskRef=${encodeURIComponent(tasks.tasks[0].taskRef)}`)); expect(response.status).toBe(200); const payload = await response.json(); expect(payload.status).toBe("partial"); expect(payload.task).toMatchObject({ taskId: "R1", title: "Projected task" }); expect(payload.task.bodyPreview).toContain("Projected body."); expect(payload.detail).toBeNull(); expect(payload.file).toMatchObject({ fileRef: files.files[0].fileRef, taskCount: 1 }); expect(payload.detailAuthority).toMatchObject({ state: "unavailable", authority: "project-management-projection", sourceKind: "hwpod-workspace", code: "hwpod_node_offline", retryable: true, valuesRedacted: true }); expect(JSON.stringify(payload)).not.toContain("node-offline is not connected"); const projectedLink = tasks.tasks[0].links[0]; const reportResponse = await app.fetch(new Request(`http://service/v1/project-management/mdtodo/report-preview?taskRef=${encodeURIComponent(tasks.tasks[0].taskRef)}&linkId=${encodeURIComponent(projectedLink.linkId)}`)); expect(reportResponse.status).toBe(503); const reportPayload = await reportResponse.json(); expect(reportPayload).toMatchObject({ error: { code: "hwpod_node_offline", message: "HWPOD 源当前不可读;报告预览需等待 Node 恢复。", status: 503 } }); expect(JSON.stringify(reportPayload)).not.toContain("node-offline is not connected"); hwpodState = "bug"; const programmerError = await app.fetch(new Request(`http://service/v1/project-management/mdtodo/task-detail?taskRef=${encodeURIComponent(tasks.tasks[0].taskRef)}`)); expect(programmerError.status).toBe(500); expect(await programmerError.json()).toMatchObject({ error: { code: "project_management_error", status: 500 } }); await app.close(); } finally { await rm(root, { recursive: true, force: true }); } }); test("project management HWPOD source adapter authenticates node-ops requests from configured env", async () => { const root = await mkdtemp(join(tmpdir(), "hwlab-project-management-hwpod-auth-")); const defaultRoot = join(root, "default"); const seen = []; try { await mkdir(defaultRoot, { recursive: true }); await writeFile(join(defaultRoot, "MDTODO.md"), "# Default\n\n## R1 Default\n", "utf8"); const app = createProjectManagementApp({ env: { HWLAB_PROJECT_MANAGEMENT_HWPOD_NODE_OPS_URL: "http://cloud-api.test/v1/hwpod-node-ops", HWLAB_PROJECT_MANAGEMENT_HWPOD_NODE_OPS_API_KEY: "hwl_live_project_management_test.redacted" }, store: createProjectManagementStore({ kind: "memory" }), sourceRoot: defaultRoot, sourceId: "default-mdtodo", projectId: "project_test", fetchImpl: async (url, init) => { const headers = new Headers(init.headers); seen.push({ url: String(url), authorization: headers.get("authorization"), sourceServiceId: headers.get("x-source-service-id") }); return new Response(JSON.stringify({ ok: true, status: "completed", results: [{ opId: "op_01", op: "workspace.ls", ok: true, status: "completed", output: { entries: [] } }] }), { status: 200, headers: { "content-type": "application/json" } }); } }); const createResponse = await app.fetch(jsonRequest("/v1/project-management/mdtodo/sources", { method: "POST", body: { sourceId: "hwpod-mdtodo", sourceKind: "hwpod-workspace", displayName: "D601 F103 MDTODO", projectId: "project_test", hwpodId: "d601-f103-v2", nodeId: "node-d601-f103-v2", workspaceRootRef: "F:/Work/ConStart", mdtodoRootRef: "docs/MDTODO/" } })); expect(createResponse.status).toBe(201); const probe = await post(app, "/v1/project-management/mdtodo/sources/hwpod-mdtodo/probe", {}); expect(probe.probe.status).toBe("ok"); expect(seen[0]).toEqual({ url: "http://cloud-api.test/v1/hwpod-node-ops", authorization: "Bearer hwl_live_project_management_test.redacted", sourceServiceId: "hwlab-project-management" }); await app.close(); } finally { await rm(root, { recursive: true, force: true }); } }); async function json(app, path) { const response = await app.fetch(new Request(`http://service${path}`, { headers: { "x-hwlab-actor-id": "usr_test" } })); expect(response.status).toBe(200); return response.json(); } async function post(app, path, body) { const response = await app.fetch(jsonRequest(path, { method: "POST", body })); expect(response.status).toBe(200); return response.json(); } function jsonRequest(path, options = {}) { return new Request(`http://service${path}`, { method: options.method ?? "GET", headers: { "content-type": "application/json", "x-hwlab-actor-id": "usr_test" }, body: options.body === undefined ? undefined : JSON.stringify(options.body) }); }