Files
memind/server/portal-plaza-discovery-routes.test.mjs

203 lines
6.1 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { attachPortalPlazaDiscoveryRoutes } from './portal-plaza-discovery-routes.mjs';
function createRouterRecorder() {
const routes = new Map();
return {
routes,
get(path, handler) {
routes.set(path, handler);
},
};
}
function createResponseRecorder() {
return {
statusCode: 200,
body: undefined,
status(code) {
this.statusCode = code;
return this;
},
json(body) {
this.body = body;
return this;
},
};
}
function createRouteDependencies(overrides = {}) {
const calls = {
data: [],
errors: [],
routeErrors: [],
};
return {
calls,
dependencies: {
sendData(res, req, data) {
calls.data.push({ res, req, data });
return res.json({ data });
},
sendError(res, req, status, code, message) {
calls.errors.push({ res, req, status, code, message });
return res.status(status).json({ error: { code, message } });
},
handleRouteError(res, req, error) {
calls.routeErrors.push({ res, req, error });
return res.status(418).json({ mapped: error.message });
},
...overrides,
},
};
}
test('portal Plaza discovery module preserves route inventory and order', () => {
const api = createRouterRecorder();
attachPortalPlazaDiscoveryRoutes(api);
assert.deepEqual(
[...api.routes.keys()],
['/plaza/v1/categories', '/plaza/v1/seo/sitemap'],
);
});
test('GET /plaza/v1/categories preserves Plaza unavailable response', async () => {
const api = createRouterRecorder();
const { calls, dependencies } = createRouteDependencies();
attachPortalPlazaDiscoveryRoutes(api, dependencies);
const req = {};
const res = createResponseRecorder();
await api.routes.get('/plaza/v1/categories')(req, res);
assert.equal(res.statusCode, 503);
assert.deepEqual(res.body, {
error: { code: 'plaza_unavailable', message: 'Plaza 未启用' },
});
assert.deepEqual(calls.errors[0], {
res,
req,
status: 503,
code: 'plaza_unavailable',
message: 'Plaza 未启用',
});
});
test('GET /plaza/v1/categories preserves successful response envelope', async () => {
const api = createRouterRecorder();
const categories = [{ slug: 'technology', name: '科技' }];
const { calls, dependencies } = createRouteDependencies({
getPlazaPosts: () => ({
async listCategories() {
return categories;
},
}),
});
attachPortalPlazaDiscoveryRoutes(api, dependencies);
const req = {};
const res = createResponseRecorder();
await api.routes.get('/plaza/v1/categories')(req, res);
assert.deepEqual(calls.data[0], {
res,
req,
data: { categories },
});
assert.deepEqual(res.body, { data: { categories } });
});
test('GET /plaza/v1/categories preserves route error mapping', async () => {
const api = createRouterRecorder();
const failure = new Error('category query failed');
const { calls, dependencies } = createRouteDependencies({
getPlazaPosts: () => ({
async listCategories() {
throw failure;
},
}),
});
attachPortalPlazaDiscoveryRoutes(api, dependencies);
const req = {};
const res = createResponseRecorder();
await api.routes.get('/plaza/v1/categories')(req, res);
assert.deepEqual(calls.routeErrors[0], { res, req, error: failure });
assert.equal(res.statusCode, 418);
assert.deepEqual(res.body, { mapped: 'category query failed' });
});
test('GET /plaza/v1/seo/sitemap preserves Plaza and SEO availability gates', async () => {
const plazaUnavailableApi = createRouterRecorder();
const plazaUnavailable = createRouteDependencies();
attachPortalPlazaDiscoveryRoutes(plazaUnavailableApi, plazaUnavailable.dependencies);
const plazaUnavailableRes = createResponseRecorder();
await plazaUnavailableApi.routes.get('/plaza/v1/seo/sitemap')(
{ query: {} },
plazaUnavailableRes,
);
assert.equal(plazaUnavailableRes.statusCode, 503);
assert.equal(plazaUnavailable.calls.errors[0].message, 'Plaza 未启用');
const seoUnavailableApi = createRouterRecorder();
const seoUnavailable = createRouteDependencies({
getPlazaPosts: () => ({}),
});
attachPortalPlazaDiscoveryRoutes(seoUnavailableApi, seoUnavailable.dependencies);
const seoUnavailableRes = createResponseRecorder();
await seoUnavailableApi.routes.get('/plaza/v1/seo/sitemap')(
{ query: {} },
seoUnavailableRes,
);
assert.equal(seoUnavailableRes.statusCode, 503);
assert.equal(seoUnavailable.calls.errors[0].message, 'Plaza SEO 未启用');
});
test('GET /plaza/v1/seo/sitemap preserves query forwarding and response envelope', async () => {
const api = createRouterRecorder();
const sitemap = { categories: [], posts: [{ id: 'post-1' }], users: [] };
let receivedLimits = null;
const { calls, dependencies } = createRouteDependencies({
getPlazaPosts: () => ({}),
getPlazaSeo: () => ({
async listSitemapData(limits) {
receivedLimits = limits;
return sitemap;
},
}),
});
attachPortalPlazaDiscoveryRoutes(api, dependencies);
const req = { query: { post_limit: '25', user_limit: '10' } };
const res = createResponseRecorder();
await api.routes.get('/plaza/v1/seo/sitemap')(req, res);
assert.deepEqual(receivedLimits, { postLimit: '25', userLimit: '10' });
assert.deepEqual(calls.data[0], { res, req, data: sitemap });
assert.deepEqual(res.body, { data: sitemap });
});
test('GET /plaza/v1/seo/sitemap preserves route error mapping', async () => {
const api = createRouterRecorder();
const failure = new Error('sitemap query failed');
const { calls, dependencies } = createRouteDependencies({
getPlazaPosts: () => ({}),
getPlazaSeo: () => ({
async listSitemapData() {
throw failure;
},
}),
});
attachPortalPlazaDiscoveryRoutes(api, dependencies);
const req = { query: {} };
const res = createResponseRecorder();
await api.routes.get('/plaza/v1/seo/sitemap')(req, res);
assert.deepEqual(calls.routeErrors[0], { res, req, error: failure });
assert.equal(res.statusCode, 418);
assert.deepEqual(res.body, { mapped: 'sitemap query failed' });
});