This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/surfaces/icd-test-runner/js/crud/personas.js
2026-03-14 19:36:33 +00:00

248 lines
11 KiB
JavaScript

/**
* ICD Test Runner — CRUD: Personas
* Admin persona lifecycle (create → list → update → tool grants →
* KB bindings → delete), personal persona (policy-gated), team persona
* (fixture team), persona groups CRUD.
*/
(function () {
'use strict';
var T = window.ICD;
if (!T) return;
if (!T.crud) T.crud = {};
T.crud.personas = async function (testTag) {
// ── Personas CRUD (admin + personal + team + tool grants + groups) ──
var personaId = '';
var personalPersonaId = '';
var teamPersonaId = '';
// Admin persona CRUD
await T.test('crud', 'personas', 'POST /admin/personas (create global)', async function () {
var d = await T.apiPost('/admin/personas', {
name: 'ICD Test Persona', base_model_id: 'test-model',
system_prompt: 'You are a test persona.', description: 'Created by ICD test runner'
});
T.assert(d.id, 'persona should have id');
T.assert(d.scope === 'global', 'admin persona scope should be global');
T.assert(d.handle, 'handle should be auto-generated');
personaId = d.id;
});
if (personaId) {
await T.test('crud', 'personas', 'GET /admin/personas (list contains new)', async function () {
var d = await T.apiGet('/admin/personas');
T.assertHasKey(d, 'data', '/admin/personas');
var found = d.data.some(function (p) { return p.id === personaId; });
T.assert(found, 'created persona should appear in admin list');
});
await T.test('crud', 'personas', 'PUT /admin/personas/:id (update)', async function () {
var d = await T.apiPut('/admin/personas/' + personaId, { name: 'ICD Updated Persona' });
T.assert(d.message === 'persona updated', 'expected update confirmation');
});
// Tool grants
await T.test('crud', 'personas', 'GET /admin/personas/:id/tool-grants (empty)', async function () {
var d = await T.apiGet('/admin/personas/' + personaId + '/tool-grants');
T.assertHasKey(d, 'data', 'tool-grants');
T.assert(Array.isArray(d.data), 'grants should be array');
T.assert(d.data.length === 0, 'initial grants should be empty');
});
await T.test('crud', 'personas', 'PUT /admin/personas/:id/tool-grants (set)', async function () {
var d = await T.apiPut('/admin/personas/' + personaId + '/tool-grants', {
tool_names: ['web_search', 'calculator']
});
T.assert(d.status === 'ok', 'expected ok');
});
await T.test('crud', 'personas', 'GET /admin/personas/:id/tool-grants (verify)', async function () {
var d = await T.apiGet('/admin/personas/' + personaId + '/tool-grants');
T.assert(d.data.length === 2, 'should have 2 grants, got ' + d.data.length);
});
await T.test('crud', 'personas', 'PUT /admin/personas/:id/tool-grants (clear)', async function () {
await T.apiPut('/admin/personas/' + personaId + '/tool-grants', { tool_names: [] });
var d = await T.apiGet('/admin/personas/' + personaId + '/tool-grants');
T.assert(d.data.length === 0, 'grants should be empty after clear');
});
// KB bindings
await T.test('crud', 'personas', 'GET /admin/personas/:id/knowledge-bases (empty)', async function () {
var d = await T.apiGet('/admin/personas/' + personaId + '/knowledge-bases');
T.assertHasKey(d, 'data', 'persona-kbs');
T.assert(Array.isArray(d.data), 'kbs should be array');
});
await T.test('crud', 'personas', 'DELETE /admin/personas/:id', async function () {
await T.apiDelete('/admin/personas/' + personaId);
});
await T.test('crud', 'personas', 'GET /admin/personas (deleted gone)', async function () {
var d = await T.apiGet('/admin/personas');
var found = d.data.some(function (p) { return p.id === personaId; });
T.assert(!found, 'deleted persona should not appear in list');
});
}
// Personal persona (policy-gated)
await T.test('crud', 'personas', 'POST /personas (create personal)', async function () {
// Ensure policy allows personal personas
try { await T.apiPut('/admin/settings/allow_user_personas', { value: 'true' }); } catch (e) { /* may already be set */ }
var d = await T.apiPost('/personas', {
name: 'ICD Personal Bot', base_model_id: 'test-model',
system_prompt: 'Personal test.'
});
T.assert(d.id, 'personal persona should have id');
T.assert(d.scope === 'personal', 'scope should be personal');
personalPersonaId = d.id;
});
if (personalPersonaId) {
await T.test('crud', 'personas', 'PUT /personas/:id (update personal)', async function () {
var d = await T.apiPut('/personas/' + personalPersonaId, { description: 'updated desc' });
T.assert(d.message === 'persona updated', 'expected update confirmation');
});
await T.test('crud', 'personas', 'GET /personas (list contains personal)', async function () {
var d = await T.apiGet('/personas');
T.assertHasKey(d, 'data', '/personas');
var found = d.data.some(function (p) { return p.id === personalPersonaId; });
T.assert(found, 'personal persona should appear in user list');
});
await T.test('crud', 'personas', 'DELETE /personas/:id (delete personal)', async function () {
await T.apiDelete('/personas/' + personalPersonaId);
});
}
// Team persona (uses fixture team)
if (T.fixtures.team) {
var tId = T.fixtures.team.id;
await T.test('crud', 'personas', 'POST /teams/:teamId/personas (create)', async function () {
var d = await T.apiPost('/teams/' + tId + '/personas', {
name: 'ICD Team Bot', base_model_id: 'test-model',
system_prompt: 'Team test.'
});
T.assert(d.id, 'team persona should have id');
T.assert(d.scope === 'team', 'scope should be team');
teamPersonaId = d.id;
});
if (teamPersonaId) {
await T.test('crud', 'personas', 'PUT /teams/:teamId/personas/:id (update)', async function () {
var d = await T.apiPut('/teams/' + tId + '/personas/' + teamPersonaId, { name: 'ICD Team Bot v2' });
T.assert(d.message === 'persona updated', 'expected update confirmation');
});
await T.test('crud', 'personas', 'GET /teams/:teamId/personas/:id/tool-grants', async function () {
var d = await T.apiGet('/teams/' + tId + '/personas/' + teamPersonaId + '/tool-grants');
T.assertHasKey(d, 'data', 'team-tool-grants');
T.assert(Array.isArray(d.data), 'grants should be array');
});
await T.test('crud', 'personas', 'PUT /teams/:teamId/personas/:id/tool-grants (set)', async function () {
await T.apiPut('/teams/' + tId + '/personas/' + teamPersonaId + '/tool-grants', {
tool_names: ['kb_search']
});
var d = await T.apiGet('/teams/' + tId + '/personas/' + teamPersonaId + '/tool-grants');
T.assert(d.data.length === 1, 'team persona should have 1 grant');
});
await T.test('crud', 'personas', 'GET /teams/:teamId/personas/:id/knowledge-bases', async function () {
var d = await T.apiGet('/teams/' + tId + '/personas/' + teamPersonaId + '/knowledge-bases');
T.assertHasKey(d, 'data', 'team-persona-kbs');
});
await T.test('crud', 'personas', 'DELETE /teams/:teamId/personas/:id', async function () {
await T.apiDelete('/teams/' + tId + '/personas/' + teamPersonaId);
});
await T.test('crud', 'personas', 'GET /teams/:teamId/personas (deleted gone)', async function () {
var d = await T.apiGet('/teams/' + tId + '/personas');
var found = d.data.some(function (p) { return p.id === teamPersonaId; });
T.assert(!found, 'deleted team persona should not appear in list');
});
}
}
// Persona Groups CRUD
var pgId = '';
var pgMemberId = '';
await T.test('crud', 'personas', 'POST /persona-groups (create)', async function () {
var d = await T.apiPost('/persona-groups', { name: 'ICD Test Group', description: 'Test' });
T.assert(d.id, 'group should have id');
T.assert(d.scope === 'personal', 'group scope should be personal');
pgId = d.id;
});
if (pgId) {
await T.test('crud', 'personas', 'GET /persona-groups/:id (read)', async function () {
var d = await T.apiGet('/persona-groups/' + pgId);
T.assert(d.id === pgId, 'group id should match');
T.assert(d.name === 'ICD Test Group', 'group name should match');
T.assert(Array.isArray(d.members), 'members should be array');
});
await T.test('crud', 'personas', 'PUT /persona-groups/:id (update)', async function () {
var d = await T.apiPut('/persona-groups/' + pgId, { name: 'ICD Updated Group' });
T.assert(d.ok === true, 'expected ok');
});
await T.test('crud', 'personas', 'GET /persona-groups (list)', async function () {
var d = await T.apiGet('/persona-groups');
T.assertHasKey(d, 'data', '/persona-groups');
var found = d.data.some(function (g) { return g.id === pgId; });
T.assert(found, 'created group should appear in list');
});
// Add a member (need a persona — create a temporary one)
var tmpPersonaId = '';
await T.test('crud', 'personas', 'POST /admin/personas (for group member)', async function () {
var d = await T.apiPost('/admin/personas', {
name: 'Group Member Bot', base_model_id: 'test-model'
});
tmpPersonaId = d.id;
});
if (tmpPersonaId) {
await T.test('crud', 'personas', 'POST /persona-groups/:id/members (add)', async function () {
var d = await T.apiPost('/persona-groups/' + pgId + '/members', {
persona_id: tmpPersonaId, is_leader: true
});
T.assert(d.ok === true, 'expected ok');
});
await T.test('crud', 'personas', 'GET /persona-groups/:id (with member)', async function () {
var d = await T.apiGet('/persona-groups/' + pgId);
T.assert(d.members.length === 1, 'should have 1 member');
T.assert(d.members[0].is_leader === true, 'member should be leader');
pgMemberId = d.members[0].id;
});
if (pgMemberId) {
await T.test('crud', 'personas', 'DELETE /persona-groups/:id/members/:mid', async function () {
await T.apiDelete('/persona-groups/' + pgId + '/members/' + pgMemberId);
});
await T.test('crud', 'personas', 'GET /persona-groups/:id (member removed)', async function () {
var d = await T.apiGet('/persona-groups/' + pgId);
T.assert(d.members.length === 0, 'should have 0 members after removal');
});
}
// Cleanup temp persona
try { await T.apiDelete('/admin/personas/' + tmpPersonaId); } catch (e) { /* ok */ }
}
await T.test('crud', 'personas', 'DELETE /persona-groups/:id', async function () {
await T.apiDelete('/persona-groups/' + pgId);
});
}
};
})();