Files
memind/server/portal-template-catalog-routes.test.mjs
T
john fde6503bdf
Memind CI / Test, build, and release guards (push) Has been cancelled
feat(mindspace): add SEO/GEO delivery, page template catalog, and admin hooks
Enable optional SEO/GEO injection and discovery routes for confirmed public pages while keeping private pages noindex. Add premium page template skills, portal catalog API, template shop UI, and Baidu push gated by memind_adm config.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 08:06:12 +08:00

103 lines
3.2 KiB
JavaScript

import assert from 'node:assert/strict';
import express from 'express';
import test from 'node:test';
import { attachPortalTemplateCatalogRoutes } from './portal-template-catalog-routes.mjs';
function createApp({ catalog, enabled = true }) {
const app = express();
app.use(express.json());
app.use((req, _res, next) => {
req.currentUser = { id: 'user-1' };
next();
});
attachPortalTemplateCatalogRoutes(app, {
getTemplateCatalog: () => catalog,
ensureMindSpaceEnabled: (res) => {
if (!enabled) {
res.status(503).json({ error: { code: 'disabled', message: 'disabled' } });
return false;
}
return true;
},
});
return app;
}
async function request(app, method, url, body) {
const server = app.listen(0);
try {
const { port } = server.address();
const response = await fetch(`http://127.0.0.1:${port}${url}`, {
method,
headers: body ? { 'Content-Type': 'application/json' } : undefined,
body: body ? JSON.stringify(body) : undefined,
});
const json = await response.json();
return { status: response.status, json };
} finally {
await new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())));
}
}
test('GET /mindspace/v1/template-catalog returns catalog items', async () => {
const app = createApp({
catalog: {
async listCatalogForUser(userId) {
assert.equal(userId, 'user-1');
return [{ skillName: 'page-template-travel', owned: false, priceCents: 990 }];
},
},
});
const { status, json } = await request(app, 'GET', '/mindspace/v1/template-catalog');
assert.equal(status, 200);
assert.equal(json.data.items[0].skillName, 'page-template-travel');
});
test('GET template preview returns html', async () => {
const app = createApp({
catalog: {
async getTemplatePreviewHtml(skillName) {
assert.equal(skillName, 'page-template-travel');
return { ok: true, html: '<!DOCTYPE html><title>preview</title>' };
},
},
});
const server = app.listen(0);
try {
const { port } = server.address();
const response = await fetch(`http://127.0.0.1:${port}/mindspace/v1/template-catalog/page-template-travel/preview`);
const html = await response.text();
assert.equal(response.status, 200);
assert.match(html, /preview/);
assert.match(response.headers.get('content-type') ?? '', /text\/html/);
} finally {
await new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())));
}
});
test('POST purchase returns 402 on insufficient balance', async () => {
const app = createApp({
catalog: {
async purchaseWithBalance(userId, skillName) {
assert.equal(userId, 'user-1');
assert.equal(skillName, 'page-template-travel');
return {
ok: false,
code: 'INSUFFICIENT_BALANCE',
message: '余额不足',
balanceCents: 100,
minRechargeCents: 500,
suggestedTiers: [500],
};
},
},
});
const { status, json } = await request(
app,
'POST',
'/mindspace/v1/template-catalog/page-template-travel/purchase',
);
assert.equal(status, 402);
assert.equal(json.error.code, 'INSUFFICIENT_BALANCE');
});