Squashed 'cms/core/' content from commit 59b9e20

git-subtree-dir: cms/core
git-subtree-split: 59b9e2075ac48cf017db38009d54dbe56a98fffc
This commit is contained in:
2026-06-30 01:46:20 +02:00
commit bba1df32cb
59 changed files with 7398 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Hono } from 'hono';
import { createManager } from '../src/plugin-manager.js';
// Tiny sub-app that answers GET / with a fixed text.
function mk(text) { const a = new Hono(); a.get('/', (c) => c.text(text)); return a; }
function fixture() {
const state = { booted: false, published: null, previewed: null };
const demo = {
name: 'demo',
routes: [
// sub-app routes
{ path: '/demo-pub', app: mk('pub'), public: true },
{ path: '/demo-priv', app: mk('priv') },
{ path: '/demo-adm', app: mk('adm'), admin: true },
// handler routes (method + handler)
{ method: 'get', path: '/h-pub', handler: (c) => c.text('hpub'), public: true },
{ method: 'post', path: '/h-priv', handler: (c) => c.text('hpriv') },
// per-route middleware (e.g. a rate limit)
{
method: 'post', path: '/h-limited', public: true,
use: [async (c, next) => (c.req.header('x-pass') ? next() : c.json({ e: 'limited' }, 429))],
handler: (c) => c.text('ok'),
},
],
onBoot: async () => { state.booted = true; },
onPublish: async (_ctx, p) => { state.published = p.path; },
onPreview: async (_ctx, p) => { state.previewed = p.path; },
stats: async () => ({ widgets: 3 }),
migrations: ['001_demo.sql'],
};
return { demo, state };
}
// Build the same pipeline as index.js: public → requireAuth → private(+admin).
function buildApp(m) {
const app = new Hono();
m.mountPublic(app);
app.use('/api/*', async (c, next) => (c.req.header('x-auth') ? next() : c.json({ error: 'no auth' }, 401)));
const requireAdmin = async (c, next) => (c.req.header('x-admin') ? next() : c.json({ error: 'no admin' }, 403));
m.mountPrivate(app, '/api', requireAdmin);
return app;
}
test('manager: names / has', () => {
const { demo } = fixture();
const m = createManager([demo]);
assert.deepEqual(m.names(), ['demo']);
assert.equal(m.has('demo'), true);
assert.equal(m.has('nope'), false);
});
test('manager: public routes reachable WITHOUT auth (sub-app + handler)', async () => {
const app = buildApp(createManager([fixture().demo]));
const sub = await app.request('/api/demo-pub');
assert.equal(sub.status, 200); assert.equal(await sub.text(), 'pub');
const h = await app.request('/api/h-pub');
assert.equal(h.status, 200); assert.equal(await h.text(), 'hpub');
});
test('manager: private routes require auth (sub-app + handler)', async () => {
const app = buildApp(createManager([fixture().demo]));
assert.equal((await app.request('/api/demo-priv')).status, 401);
assert.equal((await app.request('/api/h-priv', { method: 'POST' })).status, 401);
const ok = await app.request('/api/h-priv', { method: 'POST', headers: { 'x-auth': '1' } });
assert.equal(ok.status, 200); assert.equal(await ok.text(), 'hpriv');
});
test('manager: admin route guarded by requireAdmin', async () => {
const app = buildApp(createManager([fixture().demo]));
assert.equal((await app.request('/api/demo-adm', { headers: { 'x-auth': '1' } })).status, 403);
const ok = await app.request('/api/demo-adm', { headers: { 'x-auth': '1', 'x-admin': '1' } });
assert.equal(ok.status, 200); assert.equal(await ok.text(), 'adm');
});
test('manager: per-route use middleware runs (rate-limit style)', async () => {
const app = buildApp(createManager([fixture().demo]));
assert.equal((await app.request('/api/h-limited', { method: 'POST' })).status, 429);
const ok = await app.request('/api/h-limited', { method: 'POST', headers: { 'x-pass': '1' } });
assert.equal(ok.status, 200); assert.equal(await ok.text(), 'ok');
});
test('manager: lifecycle hooks run', async () => {
const { demo, state } = fixture();
const m = createManager([demo]);
await m.runBoot({});
await m.runPublish({}, { path: 'a.md' });
await m.runPreview({}, { path: 'b.md' });
assert.equal(state.booted, true);
assert.equal(state.published, 'a.md');
assert.equal(state.previewed, 'b.md');
});
test('manager: stats merged under plugin name', async () => {
const m = createManager([fixture().demo]);
assert.deepEqual(await m.collectStats({}), { demo: { widgets: 3 } });
});
test('manager: declared migrations surfaced', () => {
const m = createManager([fixture().demo]);
assert.deepEqual(m.migrations(), [{ plugin: 'demo', file: '001_demo.sql', url: null }]);
});
test('manager: empty plugin set is a no-op', async () => {
const m = createManager([]);
assert.deepEqual(m.names(), []);
assert.deepEqual(await m.collectStats({}), {});
assert.deepEqual(m.migrations(), []);
const app = buildApp(m);
assert.equal((await app.request('/api/anything', { headers: { 'x-auth': '1' } })).status, 404);
});