import { test } from 'node:test'; import assert from 'node:assert/strict'; import { Hono } from 'hono'; // The dialog modules import supabase.js, which exits without these; dummies are // enough since this test only checks structure/mounting, never calls a handler. process.env.SUPABASE_URL ||= 'http://localhost'; process.env.SUPABASE_SERVICE_KEY ||= 'x'; process.env.JWT_SECRET ||= 'x'; process.env.SITE_DIR ||= '/tmp/nosite'; const { loadPlugins } = await import('../src/plugin-manager.js'); const m = await loadPlugins(['dialog']); test('dialog plugin loads with a valid manifest', () => { assert.deepEqual(m.names(), ['dialog']); assert.equal(m.has('dialog'), true); }); test('dialog: route tiers as in index.js', () => { const routes = m.plugins[0].routes; const pub = routes.filter((r) => r.public).map((r) => `${r.method || 'route'} ${r.path}`); const priv = routes.filter((r) => !r.public).map((r) => `${r.method || 'route'} ${r.path}`); assert.deepEqual(pub, [ 'get /comments', 'get /forums', 'get /forums/:slug', 'get /recent', 'get /thread', 'post /auth/login', ]); assert.deepEqual(priv, [ 'post /comments', 'delete /comments/:id', 'post /threads', 'route /mod', 'route /admin/forums', ]); }); test('dialog: widget login is rate-limited', () => { const loginRoute = m.plugins[0].routes.find((r) => r.path === '/auth/login'); assert.equal(loginRoute.public, true); assert.equal(Array.isArray(loginRoute.use) && loginRoute.use.length, 1); }); test('dialog: lifecycle hooks + stats present', () => { const p = m.plugins[0]; assert.equal(typeof p.onBoot, 'function'); assert.equal(typeof p.onPublish, 'function'); assert.equal(typeof p.stats, 'function'); }); test('dialog: mounts into the pipeline without throwing', () => { const app = new Hono(); const requireAdmin = async (c, next) => next(); assert.doesNotThrow(() => { m.mountPublic(app); app.use('/api/*', async (c, next) => next()); m.mountPrivate(app, '/api', requireAdmin); }); });