248 lines
11 KiB
JavaScript
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);
|
|
});
|
|
}
|
|
|
|
};
|
|
})();
|