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' }); });