Changeset 0.37.10 (#222)
Co-authored-by: gobha <jasafpro@gmail.com> Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
@@ -7,9 +7,12 @@
|
||||
// policy exists but the frontend doesn't
|
||||
// check it.
|
||||
//
|
||||
// v0.22.5: Updated for server-rendered Go
|
||||
// templates. HTML is now in server/pages/
|
||||
// templates/ — not in src/index.html.
|
||||
// v0.22.5: Updated for server-rendered Go templates.
|
||||
// v0.37.5: Settings surface moved to Preact — legacy SPA tests
|
||||
// replaced with component source audits.
|
||||
// v0.37.10: Legacy SPA source audit removed (ui-core.js, app.js,
|
||||
// pages.js, settings-handlers.js, ui-admin.js all deleted).
|
||||
// Policy gating now verified via Preact surfaces + admin templates.
|
||||
//
|
||||
// Run: node --test src/js/__tests__/policy-gating.test.js
|
||||
// ==========================================
|
||||
@@ -36,94 +39,6 @@ function readAllTemplates() {
|
||||
return files.join('\n');
|
||||
}
|
||||
|
||||
// ── Source code audits ───────────────────────
|
||||
// These tests read the actual source files and verify that required
|
||||
// wiring exists. If someone removes a policy check, CI breaks.
|
||||
|
||||
describe('Policy wiring audit — source code', () => {
|
||||
// Read all UI files (ui-core.js + ui-settings.js + ui-admin.js replace old ui.js)
|
||||
const uiSrc = ['ui-core.js', 'ui-settings.js', 'ui-admin.js', 'ui-format.js']
|
||||
.map(f => fs.readFileSync(path.join(SRC, f), 'utf-8')).join('\n');
|
||||
// Read all app-side files (app.js + extracted handler files replace old monolith app.js)
|
||||
const appSrc = ['app.js', 'settings-handlers.js', 'admin-handlers.js', 'chat.js', 'tokens.js', 'notes.js']
|
||||
.map(f => fs.readFileSync(path.join(SRC, f), 'utf-8')).join('\n');
|
||||
// Pages.js — server-rendered page handlers (v0.22.5+)
|
||||
const pagesSrc = fs.readFileSync(path.join(SRC, 'pages.js'), 'utf-8');
|
||||
const templateSrc = readAllTemplates();
|
||||
|
||||
// ── allow_user_byok ──
|
||||
|
||||
it('UI has checkUserProvidersAllowed function', () => {
|
||||
assert.ok(uiSrc.includes('checkUserProvidersAllowed'),
|
||||
'MISSING: checkUserProvidersAllowed — provider tab will show when policy is off');
|
||||
});
|
||||
|
||||
it('checkUserProvidersAllowed reads App.policies.allow_user_byok', () => {
|
||||
assert.ok(uiSrc.includes('allow_user_byok'),
|
||||
'MISSING: allow_user_byok check in UI');
|
||||
});
|
||||
|
||||
// ── allow_user_personas ──
|
||||
|
||||
it('UI has checkUserPersonasAllowed function', () => {
|
||||
assert.ok(uiSrc.includes('checkUserPersonasAllowed'),
|
||||
'MISSING: checkUserPersonasAllowed — persona button will show when policy is off');
|
||||
});
|
||||
|
||||
it('checkUserPersonasAllowed reads App.policies.allow_user_personas', () => {
|
||||
assert.ok(uiSrc.includes('allow_user_personas'),
|
||||
'MISSING: allow_user_personas check in UI');
|
||||
});
|
||||
|
||||
it('admin settings template has user-personas toggle', () => {
|
||||
assert.ok(templateSrc.includes('settUserPersonas'),
|
||||
'MISSING: settUserPersonas toggle in admin settings template');
|
||||
});
|
||||
|
||||
it('Pages.saveSettings writes allow_user_personas', () => {
|
||||
assert.ok(pagesSrc.includes('allow_user_personas'),
|
||||
'MISSING: allow_user_personas in Pages.saveSettings');
|
||||
});
|
||||
|
||||
it('admin settings save writes allow_user_personas (SPA bridge)', () => {
|
||||
assert.ok(appSrc.includes('allow_user_personas'),
|
||||
'MISSING: handleSaveAdminSettings must write allow_user_personas');
|
||||
});
|
||||
|
||||
// ── Settings live-apply ──
|
||||
|
||||
it('admin save calls initBanners for policy refresh', () => {
|
||||
assert.ok(appSrc.includes('await initBanners()'),
|
||||
'MISSING: initBanners call after admin save — policies won\'t refresh');
|
||||
});
|
||||
|
||||
it('admin save calls checkUserProvidersAllowed', () => {
|
||||
// Find in handleSaveAdminSettings
|
||||
const saveFunc = appSrc.slice(appSrc.indexOf('handleSaveAdminSettings'));
|
||||
assert.ok(saveFunc.includes('checkUserProvidersAllowed'),
|
||||
'MISSING: checkUserProvidersAllowed call after admin save');
|
||||
});
|
||||
|
||||
it('admin save calls checkUserPersonasAllowed', () => {
|
||||
const saveFunc = appSrc.slice(appSrc.indexOf('handleSaveAdminSettings'));
|
||||
assert.ok(saveFunc.includes('checkUserPersonasAllowed'),
|
||||
'MISSING: checkUserPersonasAllowed call after admin save');
|
||||
});
|
||||
|
||||
it('admin save calls fetchModels to refresh model list', () => {
|
||||
const saveFunc = appSrc.slice(appSrc.indexOf('handleSaveAdminSettings'));
|
||||
assert.ok(saveFunc.includes('fetchModels'),
|
||||
'MISSING: fetchModels call after admin save — model list stale after settings change');
|
||||
});
|
||||
|
||||
// ── Models tab calls preset check ──
|
||||
|
||||
it('models tab switch calls checkUserPersonasAllowed', () => {
|
||||
assert.ok(uiSrc.includes('checkUserPersonasAllowed'),
|
||||
'MISSING: checkUserPersonasAllowed call on models tab switch');
|
||||
});
|
||||
});
|
||||
|
||||
// ── Policy evaluation logic ──────────────────
|
||||
|
||||
describe('Policy evaluation logic', () => {
|
||||
@@ -158,7 +73,6 @@ describe('Policy evaluation logic', () => {
|
||||
});
|
||||
|
||||
// ── Team member dropdown logic ───────────────
|
||||
// Bug: Empty dropdown because resp.data used instead of resp.users
|
||||
|
||||
describe('Team member dropdown population', () => {
|
||||
function getAvailableUsers(usersResp, membersResp) {
|
||||
@@ -221,72 +135,37 @@ describe('Team member dropdown population', () => {
|
||||
|
||||
// ── Admin settings field mapping ─────────────
|
||||
// v0.22.5: Server-rendered admin settings template uses new element IDs.
|
||||
// Pages.saveSettings() in pages.js is the primary handler.
|
||||
// v0.37.10: pages.js deleted — admin settings save now handled by
|
||||
// Preact admin surface. Template elements still required.
|
||||
|
||||
describe('Admin settings field mapping (server templates)', () => {
|
||||
// New element IDs used by server-rendered admin/settings.html + pages.js
|
||||
const settingsFieldMap = {
|
||||
'settRegEnabled': 'allow_registration',
|
||||
'settRegDefaultState': 'default_user_active',
|
||||
'settUserBYOK': 'allow_user_byok',
|
||||
'settUserPersonas': 'allow_user_personas',
|
||||
'settBannerEnabled': 'banner',
|
||||
};
|
||||
|
||||
const pagesSrc = fs.readFileSync(path.join(SRC, 'pages.js'), 'utf-8');
|
||||
const templateSrc = readAllTemplates();
|
||||
|
||||
for (const [elementId, settingKey] of Object.entries(settingsFieldMap)) {
|
||||
it(`template has element #${elementId}`, () => {
|
||||
assert.ok(templateSrc.includes(`id="${elementId}"`),
|
||||
`MISSING: #${elementId} in server templates — admin settings incomplete`);
|
||||
});
|
||||
|
||||
it(`Pages.saveSettings writes setting "${settingKey}"`, () => {
|
||||
assert.ok(pagesSrc.includes(settingKey),
|
||||
`MISSING: "${settingKey}" in Pages.saveSettings`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ── SPA bridge field mapping (backward compat) ──
|
||||
// The SPA chat surface still loads settings-handlers.js + ui-admin.js.
|
||||
// These use legacy element IDs for handleSaveAdminSettings().
|
||||
// Verified at the JS level (elements are SPA-modal DOM, not templates).
|
||||
|
||||
describe('SPA bridge — admin settings handler references policy keys', () => {
|
||||
const appSrc = ['settings-handlers.js', 'admin-handlers.js']
|
||||
.map(f => fs.readFileSync(path.join(SRC, f), 'utf-8')).join('\n');
|
||||
|
||||
const requiredPolicies = [
|
||||
'allow_registration',
|
||||
'default_user_active',
|
||||
'allow_user_byok',
|
||||
'allow_user_personas',
|
||||
const requiredElements = [
|
||||
'settRegEnabled',
|
||||
'settRegDefaultState',
|
||||
'settUserBYOK',
|
||||
'settUserPersonas',
|
||||
'settBannerEnabled',
|
||||
];
|
||||
|
||||
for (const key of requiredPolicies) {
|
||||
it(`SPA bridge writes policy "${key}"`, () => {
|
||||
assert.ok(appSrc.includes(key),
|
||||
`MISSING: "${key}" in SPA bridge handler — policy not saved`);
|
||||
for (const id of requiredElements) {
|
||||
it(`template has element #${id}`, () => {
|
||||
assert.ok(templateSrc.includes(`id="${id}"`),
|
||||
`MISSING: #${id} in server templates — admin settings incomplete`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ── HTML element existence checks ────────────
|
||||
// v0.22.5: Elements now live in server templates, not index.html.
|
||||
// v0.37.5: Settings surface elements moved to Preact components
|
||||
// (src/js/sw/surfaces/settings/). Only admin template elements
|
||||
// are checked here. Settings surface policy gating is handled by
|
||||
// the Preact SettingsSurface component (nav gating + section routing).
|
||||
// ── Critical HTML elements in templates ───────
|
||||
|
||||
describe('Critical HTML elements exist in server templates', () => {
|
||||
const templateSrc = readAllTemplates();
|
||||
|
||||
const requiredElements = [
|
||||
// Admin settings — policy toggles (still in Go templates)
|
||||
'settUserBYOK', // Admin toggle for BYOK (was adminUserProvidersToggle)
|
||||
'settUserPersonas', // Admin toggle for presets (was adminUserPresetsToggle)
|
||||
'settUserBYOK', // Admin toggle for BYOK
|
||||
'settUserPersonas', // Admin toggle for personas
|
||||
];
|
||||
|
||||
for (const id of requiredElements) {
|
||||
@@ -297,6 +176,34 @@ describe('Critical HTML elements exist in server templates', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// ── Chat surface template ────────────────────
|
||||
// v0.37.10: Chat surface is now Preact-rendered. Verify the mount
|
||||
// point exists and the old SPA scaffold is gone.
|
||||
|
||||
describe('Chat surface template (v0.37.10)', () => {
|
||||
const templateSrc = readAllTemplates();
|
||||
|
||||
it('chat-mount div exists', () => {
|
||||
assert.ok(templateSrc.includes('id="chat-mount"'),
|
||||
'MISSING: #chat-mount — Preact chat surface cannot render');
|
||||
});
|
||||
|
||||
it('old appContainer div is gone', () => {
|
||||
assert.ok(!templateSrc.includes('id="appContainer"'),
|
||||
'STALE: #appContainer still in templates — old SPA scaffold not removed');
|
||||
});
|
||||
|
||||
it('chat template loads Preact surface module', () => {
|
||||
assert.ok(templateSrc.includes('sw/surfaces/chat/index.js'),
|
||||
'MISSING: chat surface module import in template');
|
||||
});
|
||||
|
||||
it('chat template loads SDK boot', () => {
|
||||
assert.ok(templateSrc.includes('sw/sdk/index.js'),
|
||||
'MISSING: SDK boot import in chat template');
|
||||
});
|
||||
});
|
||||
|
||||
// ── Preact surface policy gating ─────────────
|
||||
// v0.37.5: Settings surface elements are now rendered by Preact
|
||||
// components. Verify the components handle policy gating.
|
||||
@@ -326,20 +233,26 @@ describe('Settings Preact surface handles policy gating', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── SPA bridge — dynamic DOM elements ────────
|
||||
// adminMemberUser is created by ui-admin.js loadMemberUserDropdown()
|
||||
// which runs inside the SPA chat surface. Verify the JS function exists.
|
||||
// ── Admin Preact surface ─────────────────────
|
||||
// v0.37.6: Admin surface handles settings save via Preact.
|
||||
// Verify the admin surface has policy key handling.
|
||||
|
||||
describe('SPA bridge — dynamic element creators', () => {
|
||||
const uiAdminSrc = fs.readFileSync(path.join(SRC, 'ui-admin.js'), 'utf-8');
|
||||
describe('Admin Preact surface handles settings', () => {
|
||||
const SURFACES = path.join(SRC, 'sw', 'surfaces', 'admin');
|
||||
const settingsPath = path.join(SURFACES, 'settings.js');
|
||||
|
||||
it('ui-admin.js has loadMemberUserDropdown', () => {
|
||||
assert.ok(uiAdminSrc.includes('loadMemberUserDropdown'),
|
||||
'MISSING: loadMemberUserDropdown — team member add will break');
|
||||
});
|
||||
// Only test if the admin settings component exists
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
const settingsSrc = fs.readFileSync(settingsPath, 'utf-8');
|
||||
|
||||
it('loadMemberUserDropdown references adminMemberUser', () => {
|
||||
assert.ok(uiAdminSrc.includes('adminMemberUser'),
|
||||
'MISSING: adminMemberUser reference in loadMemberUserDropdown');
|
||||
});
|
||||
it('admin settings component references allow_user_byok', () => {
|
||||
assert.ok(settingsSrc.includes('allow_user_byok'),
|
||||
'MISSING: allow_user_byok in admin settings component');
|
||||
});
|
||||
|
||||
it('admin settings component references allow_user_personas', () => {
|
||||
assert.ok(settingsSrc.includes('allow_user_personas'),
|
||||
'MISSING: allow_user_personas in admin settings component');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user