/** * ICD Test Runner — CRUD: Admin * Admin user lifecycle (create → deactivate → login reject → activate → * login success → role change → password reset → vault reset → delete), * admin settings roundtrip, channel purge. */ (function () { 'use strict'; var T = window.ICD; if (!T) return; if (!T.crud) T.crud = {}; T.crud.admin = async function (testTag) { // ── Admin User Lifecycle (create → login fails → activate → login succeeds → cleanup) ── if (T.user.role === 'admin') { var testUserId = null; var testUsername = 'icdtest' + (Date.now() % 100000); var testPassword = 'IcdT3st!Pw' + (Date.now() % 10000); await T.test('crud', 'admin', 'POST /admin/users (create)', async function () { var d = await T.apiPost('/admin/users', { username: testUsername, password: testPassword, email: testUsername + '@example.com', role: 'user' }); T.assertShape(d, T.S.adminUser, 'admin-user'); testUserId = d.id; T.registerCleanup(function () { if (testUserId) return T.safeDelete('/admin/users/' + testUserId); }); }); if (testUserId) { // Users start active (CreateUser sets IsActive=true). // Deactivate first, then test inactive login rejection. await T.test('crud', 'admin', 'PUT /admin/users/:id/active (deactivate)', async function () { var d = await T.apiPut('/admin/users/' + testUserId + '/active', { is_active: false }); T.assert(typeof d === 'object', 'expected object'); }); await T.test('crud', 'admin', 'POST /auth/login (inactive user → reject)', async function () { var d = await T.publicPost('/auth/login', { login: testUsername, password: testPassword }); T.assert(d._status === 401 || d._status === 403, 'expected 401/403 for inactive user, got ' + d._status); }); await T.test('crud', 'admin', 'PUT /admin/users/:id/active (activate)', async function () { var d = await T.apiPut('/admin/users/' + testUserId + '/active', { is_active: true }); T.assert(typeof d === 'object', 'expected object'); }); var testUserToken = null; await T.test('crud', 'admin', 'POST /auth/login (active user → success)', async function () { var d = await T.publicPost('/auth/login', { login: testUsername, password: testPassword }); T.assert(d._status === 200, 'expected 200, got ' + d._status + (d.error ? ' (' + d.error + ')' : '')); T.assertHasKey(d, 'access_token', 'login'); T.assertHasKey(d, 'refresh_token', 'login'); T.assertHasKey(d, 'user', 'login'); T.assert(d.user.username === testUsername, 'username mismatch'); testUserToken = d.access_token; }); if (testUserToken) { await T.test('crud', 'admin', 'GET /profile (as test user)', async function () { var d = await T.authFetch(testUserToken, 'GET', '/profile'); T.assertStatus(d, 200, 'profile'); T.assert(d.username === testUsername, 'username mismatch'); }); await T.test('crud', 'admin', 'POST /auth/logout (test user)', async function () { var d = await T.publicPost('/auth/logout', { refresh_token: '' }); T.assert(d._status === 200 || d._status === 401 || d._status === 204, 'expected 200/204/401, got ' + d._status); }); } await T.test('crud', 'admin', 'PUT /admin/users/:id/role (set admin)', async function () { var d = await T.apiPut('/admin/users/' + testUserId + '/role', { role: 'admin' }); T.assert(typeof d === 'object', 'expected object'); }); await T.test('crud', 'admin', 'POST /admin/users/:id/reset-password', async function () { var d = await T.apiPost('/admin/users/' + testUserId + '/reset-password', { password: 'ResetPw!' + Date.now() }); T.assert(typeof d === 'object', 'expected object'); T.assert(!d.error, 'unexpected error: ' + (d.error || '')); }); await T.test('crud', 'admin', 'POST /admin/users/:id/vault/reset', async function () { var d = await T.apiPost('/admin/users/' + testUserId + '/vault/reset', {}); // May return 200 with message, or 400 if vault not configured T.assert(d._status === undefined || !d.error || d.error === 'vault not configured', 'unexpected error: ' + (d.error || '')); }); await T.test('crud', 'admin', 'DELETE /admin/users/:id', async function () { await T.safeDelete('/admin/users/' + testUserId); testUserId = null; }); } // ── Admin Settings Roundtrip ── await T.test('crud', 'admin', 'PUT /admin/settings/:key (policy roundtrip)', async function () { // Read current value var before = await T.apiGet('/admin/settings/allow_registration'); var originalVal = before.value; // Write a known value await T.apiPut('/admin/settings/allow_registration', { value: 'false' }); var after = await T.apiGet('/admin/settings/allow_registration'); T.assert(after.value === 'false', 'expected policy value "false"'); // Restore if (originalVal !== undefined) { await T.apiPut('/admin/settings/allow_registration', { value: String(originalVal) }); } else { await T.apiPut('/admin/settings/allow_registration', { value: 'true' }); } }); // ── Channel Purge ── await T.test('crud', 'admin', 'DELETE /admin/channels/:id/purge (lifecycle)', async function () { // Create a throwaway channel var ch = await T.apiPost('/channels', { title: 'icd-purge-test', type: 'direct' }); T.assert(ch.id, 'expected channel id'); // Archive it await T.apiPut('/channels/' + ch.id, { is_archived: true }); // Purge rejects non-archived channel attempts — ours is archived, so purge should succeed var del = await T.apiDelete('/admin/channels/' + ch.id + '/purge'); T.assert(del.ok === true, 'expected { ok: true }'); }); } }; })();