Files
omniai-server/scripts/projectRouteContract.test.js
T
2026-06-02 13:14:10 +08:00

124 lines
3.8 KiB
JavaScript

const assert = require("node:assert/strict");
const { createRequire } = require("node:module");
const nodeRequire = createRequire(__filename);
const contextPath = "../src/routes/context.js";
const projectRoutesPath = "../src/routes/projects.js";
const ossClientPath = "../src/ossClient.js";
function passThrough(_req, _res, next) {
next();
}
function createMockResponse() {
const res = {};
res.status = (statusCode) => {
res.statusCode = statusCode;
return res;
};
res.json = (body) => {
res.body = body;
return res;
};
return res;
}
function loadProjectRouter(pool, ossClient) {
const express = nodeRequire("express");
const router = express.Router();
const contextResolvedPath = nodeRequire.resolve(contextPath);
const projectsResolvedPath = nodeRequire.resolve(projectRoutesPath);
const ossResolvedPath = nodeRequire.resolve(ossClientPath);
const originalContextModule = nodeRequire.cache[contextResolvedPath];
const originalProjectsModule = nodeRequire.cache[projectsResolvedPath];
const originalOssModule = nodeRequire.cache[ossResolvedPath];
delete nodeRequire.cache[projectsResolvedPath];
nodeRequire.cache[contextResolvedPath] = {
id: contextResolvedPath,
filename: contextResolvedPath,
loaded: true,
exports: {
requireAuth: passThrough,
pool,
withTransaction: async (fn) => fn(pool),
computeNextRevision: () => ({ nextRevision: 1 }),
normalizeRevisionValue: (value) => Number(value || 0),
shouldRejectStaleRevision: () => false,
formatGenerationTaskRow: (row) => row,
normalizeGenerationTaskPayload: (body) => ({ value: body }),
normalizeProjectOssKey: (value) => ({ value }),
buildOssPublicUrl: (key) => `https://cdn.example/${key}`,
requireOwnedProject: async () => true,
upsertGenerationTask: async () => ({}),
},
};
nodeRequire.cache[ossResolvedPath] = {
id: ossResolvedPath,
filename: ossResolvedPath,
loaded: true,
exports: ossClient,
};
nodeRequire(projectRoutesPath).registerProjectRoutes(router);
return {
router,
restore() {
if (originalContextModule) nodeRequire.cache[contextResolvedPath] = originalContextModule;
else delete nodeRequire.cache[contextResolvedPath];
if (originalProjectsModule) nodeRequire.cache[projectsResolvedPath] = originalProjectsModule;
else delete nodeRequire.cache[projectsResolvedPath];
if (originalOssModule) nodeRequire.cache[ossResolvedPath] = originalOssModule;
else delete nodeRequire.cache[ossResolvedPath];
},
};
}
function getRouteHandler(router, method, routePath) {
const layer = router.stack.find(
(candidate) => candidate.route?.path === routePath && candidate.route.methods[method.toLowerCase()],
);
const handler = layer?.route?.stack.at(-1)?.handle;
if (!handler) throw new Error(`Route not found: ${method.toUpperCase()} ${routePath}`);
return handler;
}
async function main() {
const missing = new Error("OSS GET failed (404): NoSuchKey");
missing.status = 404;
missing.code = "oss_no_such_key";
const { router, restore } = loadProjectRouter(
{ query: async () => ({ rows: [{ oss_key: "users/1/projects/missing/current/project.json" }] }) },
{
isOssConfigured: () => true,
getObject: async () => {
throw missing;
},
putObject: async () => ({}),
},
);
try {
const handler = getRouteHandler(router, "get", "/projects/:id/content");
const res = createMockResponse();
await handler(
{ params: { id: "workflow-missing" }, query: { resolveMedia: "1" }, user: { id: 1 } },
res,
);
assert.equal(res.statusCode, 404);
assert.equal(res.body.code, "project_content_missing");
} finally {
restore();
}
console.log("project route contract tests passed");
}
main().catch((error) => {
console.error(error);
process.exit(1);
});