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/teams.js
2026-03-14 19:36:33 +00:00

282 lines
13 KiB
JavaScript

/**
* ICD Test Runner — CRUD: Teams
* Admin team lifecycle (create → read → update → members → team-scoped
* endpoints → team tasks → groups → grants → permissions → audit →
* usage → assignments → member remove → delete).
*/
(function () {
'use strict';
var T = window.ICD;
if (!T) return;
if (!T.crud) T.crud = {};
T.crud.teams = async function (testTag) {
// ── Teams CRUD (admin — create team, exercise team-scoped endpoints) ──
if (T.user.role === 'admin') {
var teamId = null;
await T.test('crud', 'teams', 'POST /admin/teams (create)', async function () {
var d = await T.apiPost('/admin/teams', { name: testTag + '-team', description: 'ICD test team' });
T.assertShape(d, T.S.team, 'team');
teamId = d.id;
T.registerCleanup(function () { if (teamId) return T.safeDelete('/admin/teams/' + teamId); });
});
if (teamId) {
await T.test('crud', 'teams', 'GET /admin/teams/:id (read)', async function () {
var d = await T.apiGet('/admin/teams/' + teamId);
T.assertShape(d, T.S.team, 'team');
T.assert(d.id === teamId, 'id mismatch');
});
await T.test('crud', 'teams', 'PUT /admin/teams/:id (update)', async function () {
var d = await T.apiPut('/admin/teams/' + teamId, { name: testTag + '-team-updated' });
T.assert(typeof d === 'object', 'expected object');
});
// Add current user as team admin
await T.test('crud', 'teams', 'POST /admin/teams/:id/members (add self)', async function () {
var d = await T.apiPost('/admin/teams/' + teamId + '/members', { user_id: T.user.id, role: 'admin' });
T.assert(typeof d === 'object', 'expected object');
});
await T.test('crud', 'teams', 'GET /admin/teams/:id/members', async function () {
var d = await T.apiGet('/admin/teams/' + teamId + '/members');
T.assertHasKey(d, 'data', '/team-members');
T.assert(Array.isArray(d.data), 'data should be array');
T.assert(d.data.length >= 1, 'expected at least 1 member');
});
// Team-scoped endpoints (via team admin routes)
await T.test('crud', 'teams', 'GET /teams/:teamId/members (team route)', async function () {
var d = await T.apiGet('/teams/' + teamId + '/members');
T.assertHasKey(d, 'data', '/team-members');
});
// Member update + remove (need a second user — use admin self-membership)
await T.test('crud', 'teams', 'PUT /admin/teams/:id/members/:mid (update role)', async function () {
// Get own membership ID
var d = await T.apiGet('/admin/teams/' + teamId + '/members');
var me = d.data.find(function (m) { return m.user_id === T.user.id; });
T.assert(me, 'should find self in members');
// Update role to member then back to admin
var r = await T.apiPut('/admin/teams/' + teamId + '/members/' + me.id, { role: 'member' });
T.assert(typeof r === 'object', 'expected object');
// Restore admin role
await T.apiPut('/admin/teams/' + teamId + '/members/' + me.id, { role: 'admin' });
});
// Team groups listing
await T.test('crud', 'teams', 'GET /teams/:teamId/groups', async function () {
var d = await T.apiGet('/teams/' + teamId + '/groups');
T.assertHasKey(d, 'data', '/team-groups');
T.assert(Array.isArray(d.data), 'data should be array');
});
await T.test('crud', 'teams', 'GET /teams/:teamId/personas', async function () {
var d = await T.apiGet('/teams/' + teamId + '/personas');
T.assertHasKey(d, 'data', '/team-personas');
T.assert(Array.isArray(d.data), 'team personas should be array');
});
await T.test('crud', 'teams', 'GET /teams/:teamId/providers', async function () {
var d = await T.apiGet('/teams/' + teamId + '/providers');
T.assertHasKey(d, 'data', '/team-providers');
T.assert(Array.isArray(d.data), 'data should be array');
T.assertHasKey(d, 'allow_team_providers', '/team-providers');
});
await T.test('crud', 'teams', 'GET /teams/:teamId/models', async function () {
var d = await T.apiGet('/teams/' + teamId + '/models');
T.assertHasKey(d, 'models', '/team-models');
});
// ── Team Tasks CRUD ──
var teamTaskId = null;
await T.test('crud', 'teams', 'POST /teams/:teamId/tasks (create team task)', async function () {
var d = await T.apiPost('/teams/' + teamId + '/tasks', {
name: testTag + '-team-task',
task_type: 'prompt',
schedule: '@daily',
user_prompt: 'Team task test',
model_id: 'test-model'
});
T.assertShape(d, T.S.taskFull, 'team task');
T.assert(d.scope === 'team', 'team task scope should be team');
T.assert(d.team_id === teamId, 'team_id should match');
teamTaskId = d.id;
T.registerCleanup(function () { if (teamTaskId) return T.safeDelete('/teams/' + teamId + '/tasks/' + teamTaskId); });
});
await T.test('crud', 'teams', 'GET /teams/:teamId/tasks (list team tasks)', async function () {
var d = await T.apiGet('/teams/' + teamId + '/tasks');
T.assertHasKey(d, 'data', '/team-tasks');
T.assert(Array.isArray(d.data), 'data should be array');
if (teamTaskId) {
T.assert(d.data.length >= 1, 'should have at least 1 team task');
var found = d.data.some(function (t) { return t.id === teamTaskId; });
T.assert(found, 'created team task should be in list');
}
});
if (teamTaskId) {
await T.test('crud', 'teams', 'PUT /teams/:teamId/tasks/:id (update team task)', async function () {
var d = await T.apiPut('/teams/' + teamId + '/tasks/' + teamTaskId, {
name: testTag + '-team-task-updated'
});
T.assert(d.name === testTag + '-team-task-updated', 'name not updated');
});
await T.test('crud', 'teams', 'GET /teams/:teamId/tasks/:id/runs (team task runs)', async function () {
var d = await T.apiGet('/teams/' + teamId + '/tasks/' + teamTaskId + '/runs');
T.assertHasKey(d, 'data', '/team-task-runs');
T.assert(Array.isArray(d.data), 'data should be array');
});
await T.test('crud', 'teams', 'POST /teams/:teamId/tasks/:id/run (team task run)', async function () {
var d = await T.apiPost('/teams/' + teamId + '/tasks/' + teamTaskId + '/run', {});
T.assertHasKey(d, 'scheduled', '/team-run');
T.assert(d.scheduled === true, 'should be scheduled');
});
await T.test('crud', 'teams', 'POST /teams/:teamId/tasks/:id/kill (team task kill)', async function () {
var token = await T.getAuthToken();
var d = await T.authFetch(token, 'POST', '/teams/' + teamId + '/tasks/' + teamTaskId + '/kill');
T.assert(d._status === 200 || d._status === 404,
'expected 200 or 404, got ' + d._status);
});
await T.test('crud', 'teams', 'DELETE /teams/:teamId/tasks/:id (delete team task)', async function () {
await T.safeDelete('/teams/' + teamId + '/tasks/' + teamTaskId);
teamTaskId = null;
});
}
await T.test('crud', 'teams', 'GET /teams/:teamId/roles', async function () {
var d = await T.apiGet('/teams/' + teamId + '/roles');
T.assertHasKey(d, 'data', '/team-roles');
T.assert(typeof d.data === 'object', 'data should be object (role overrides map)');
});
await T.test('crud', 'teams', 'GET /teams/:teamId/audit', async function () {
var d = await T.apiGet('/teams/' + teamId + '/audit');
T.assertHasKey(d, 'data', '/team-audit');
T.assert(Array.isArray(d.data), 'data should be array');
T.assertHasKey(d, 'total', '/team-audit pagination');
T.assertHasKey(d, 'page', '/team-audit pagination');
});
await T.test('crud', 'teams', 'GET /teams/:teamId/audit/actions', async function () {
var d = await T.apiGet('/teams/' + teamId + '/audit/actions');
T.assertHasKey(d, 'actions', '/team-audit-actions');
T.assert(Array.isArray(d.actions), 'actions should be array');
});
await T.test('crud', 'teams', 'GET /teams/:teamId/usage', async function () {
var d = await T.apiGet('/teams/' + teamId + '/usage');
T.assertHasKey(d, 'totals', '/team-usage');
T.assertHasKey(d, 'results', '/team-usage');
});
await T.test('crud', 'teams', 'GET /teams/:teamId/assignments', async function () {
var d = await T.apiGet('/teams/' + teamId + '/assignments');
T.assert(typeof d === 'object', 'expected object');
});
// Admin groups CRUD within team context
var groupId = null;
await T.test('crud', 'teams', 'POST /admin/groups (create team group)', async function () {
var d = await T.apiPost('/admin/groups', { name: testTag + '-group', scope: 'team', team_id: teamId });
T.assertShape(d, T.S.group, 'group');
groupId = d.id;
T.registerCleanup(function () { if (groupId) return T.safeDelete('/admin/groups/' + groupId); });
});
if (groupId) {
await T.test('crud', 'teams', 'GET /admin/groups/:id', async function () {
var d = await T.apiGet('/admin/groups/' + groupId);
T.assertShape(d, T.S.group, 'group');
});
await T.test('crud', 'teams', 'PUT /admin/groups/:id (set permissions)', async function () {
var d = await T.apiPut('/admin/groups/' + groupId, {
permissions: ['model.use', 'kb.read', 'channel.create']
});
T.assert(typeof d === 'object', 'expected object');
});
await T.test('crud', 'teams', 'POST /admin/groups/:id/members (add self)', async function () {
var d = await T.apiPost('/admin/groups/' + groupId + '/members', { user_id: T.user.id });
T.assert(typeof d === 'object', 'expected object');
});
await T.test('crud', 'teams', 'GET /admin/groups/:id/members', async function () {
var d = await T.apiGet('/admin/groups/' + groupId + '/members');
T.assertHasKey(d, 'data', '/group-members');
T.assert(Array.isArray(d.data), 'data should be array');
});
await T.test('crud', 'teams', 'DELETE /admin/groups/:id/members/:uid', async function () {
await T.safeDelete('/admin/groups/' + groupId + '/members/' + T.user.id);
});
await T.test('crud', 'teams', 'DELETE /admin/groups/:id', async function () {
await T.safeDelete('/admin/groups/' + groupId);
groupId = null;
});
}
// Grants
await T.test('crud', 'teams', 'GET /admin/grants/persona/:id (404 = no grant)', async function () {
// No persona to test, just verify the endpoint responds
try {
var d = await T.apiGet('/admin/grants/persona/00000000-0000-0000-0000-000000000000');
T.assert(typeof d === 'object', 'expected object');
} catch (e) {
if (e.message && e.message.indexOf('404') !== -1) return; // expected
throw e;
}
});
// User permissions
await T.test('crud', 'teams', 'GET /admin/users/:id/permissions', async function () {
var d = await T.apiGet('/admin/users/' + T.user.id + '/permissions');
T.assertHasKey(d, 'permissions', '/user-permissions');
T.assert(Array.isArray(d.permissions), 'permissions should be array');
T.assertHasKey(d, 'user_id', '/user-permissions');
T.assertHasKey(d, 'groups', '/user-permissions contributing groups');
T.assert(Array.isArray(d.groups), 'groups should be array');
});
// List all permissions
await T.test('crud', 'teams', 'GET /admin/permissions', async function () {
var d = await T.apiGet('/admin/permissions');
T.assertHasKey(d, 'permissions', '/admin/permissions');
T.assert(Array.isArray(d.permissions), 'permissions should be array');
T.assert(d.permissions.length > 0, 'should have at least 1 permission');
});
// Member remove (remove self then re-add to keep admin for cleanup)
await T.test('crud', 'teams', 'DELETE /admin/teams/:id/members/:mid (remove)', async function () {
var d = await T.apiGet('/admin/teams/' + teamId + '/members');
var me = d.data.find(function (m) { return m.user_id === T.user.id; });
T.assert(me, 'should find self in members');
var r = await T.safeDelete('/admin/teams/' + teamId + '/members/' + me.id);
T.assert(typeof r === 'object' || r === undefined, 'expected object or void');
// Re-add self as admin for cleanup
await T.apiPost('/admin/teams/' + teamId + '/members', { user_id: T.user.id, role: 'admin' });
});
await T.test('crud', 'teams', 'DELETE /admin/teams/:id', async function () {
await T.safeDelete('/admin/teams/' + teamId);
teamId = null;
});
}
}
};
})();