feat(access): Фаза 2c — история правил + пресет «копировать доступ из класса»
История: GET /api/access/log (admin-only) — кто/когда открыл/закрыл/сбросил правило для контента (из admin_audit_log, имена классов/учеников резолвятся). Клиент LS.accessLog; в режиме «По контенту» — кнопка «История изменений». Пресет: в режиме «По классу» — «Скопировать доступ из класса [выбор]» (дополняет текущие правила открытыми правилами класса-источника). Тест: история (admin видит запись, учителю 403). content-access 13/13. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -116,6 +116,19 @@ describe('contentAccess', () => {
|
||||
assert.ok(Array.isArray(r.body.sims) && r.body.sims.length >= 1, 'каталог содержит симуляции');
|
||||
});
|
||||
|
||||
it('GET /api/access/log — история (admin видит запись; учителю 403)', async () => {
|
||||
const admin = await getToken('admin');
|
||||
const p = await inject('POST', '/api/access/rules',
|
||||
{ content_type: 'textbook', content_ref: HUB, scope: 'class', target_id: classId, allow: 1 }, admin.token);
|
||||
assert.ok(p.status < 300, JSON.stringify(p.body));
|
||||
const log = await inject('GET', `/api/access/log?content_type=textbook&content_ref=${HUB}`, null, admin.token);
|
||||
assert.equal(log.status, 200);
|
||||
assert.ok(Array.isArray(log.body) && log.body.length >= 1, 'есть запись истории');
|
||||
assert.equal(log.body[0].action, 'grant');
|
||||
const t = await inject('GET', `/api/access/log?content_type=textbook&content_ref=${HUB}`, null, teacher.token);
|
||||
assert.equal(t.status, 403, 'учителю недоступно');
|
||||
});
|
||||
|
||||
it('DELETE /api/classes/:id чистит правила класса (через purgeAccessFor)', async () => {
|
||||
setRule('class', classId, 1);
|
||||
const del = await inject('DELETE', `/api/classes/${classId}`, null, teacher.token);
|
||||
|
||||
Reference in New Issue
Block a user