9a145e5d62
Миграция 051: расширяет content_access.content_type на 'course'/'sim' (пересборка таблицы — SQLite не умеет ALTER CHECK) + мост «открыть все включённые симуляции всем существующим классам» → текущее поведение не меняется. GET /api/lab/sims теперь фильтрует список для НЕпривилегированных по allowedRefs(uid,'sim'); admin/ teacher видят все. Ролевой simulations.access остаётся «модуль вкл.» (добавочно). Тесты: lab-access (4/4, allowlist+класс+личное), lab-sims переведён на admin для проверки полного каталога (видимость ученика — в lab-access). /api/lab в харнессе. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59 lines
2.9 KiB
JavaScript
59 lines
2.9 KiB
JavaScript
'use strict';
|
|
/**
|
|
* Фаза 1 (добавочная модель): видимость симуляций по классам через content_access.
|
|
* Ролевой simulations.access решает «включён ли модуль» (тут не проверяется —
|
|
* это делает фронт/действия); GET /api/lab/sims фильтрует список так, что ученик
|
|
* видит только разрешённые его классу/лично симуляции, а admin/teacher — все.
|
|
*/
|
|
const { describe, it, before, after } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { db, getToken, inject, cleanup } = require('./setup');
|
|
|
|
after(() => cleanup());
|
|
|
|
describe('lab sim access (per-class)', () => {
|
|
let teacher, student, classId, simA, simB;
|
|
|
|
before(async () => {
|
|
teacher = await getToken('teacher');
|
|
student = await getToken('student');
|
|
const sims = db.prepare('SELECT id FROM lab_sims WHERE enabled = 1 ORDER BY id LIMIT 2').all();
|
|
assert.ok(sims.length >= 2, 'в seed есть хотя бы 2 симуляции');
|
|
simA = sims[0].id; simB = sims[1].id;
|
|
|
|
const r = await inject('POST', '/api/classes', { name: 'LabAcc Class' }, teacher.token);
|
|
assert.ok(r.status < 300, JSON.stringify(r.body));
|
|
classId = db.prepare('SELECT id FROM classes WHERE name = ?').get('LabAcc Class').id;
|
|
await inject('POST', `/api/classes/${classId}/members`, { user_id: student.userId }, teacher.token);
|
|
});
|
|
|
|
const ids = (body) => (body.sims || []).map(s => s.id);
|
|
const rule = (ref, scope, target) =>
|
|
db.prepare(`INSERT OR IGNORE INTO content_access (content_type,content_ref,scope,target_id,allow)
|
|
VALUES ('sim',?,?,?,1)`).run(ref, scope, target);
|
|
|
|
it('ученик без правил не видит симуляций (allowlist)', async () => {
|
|
const r = await inject('GET', '/api/lab/sims', null, student.token);
|
|
assert.equal(r.status, 200);
|
|
assert.equal(ids(r.body).length, 0);
|
|
});
|
|
|
|
it('teacher видит все симуляции (privileged, без фильтра)', async () => {
|
|
const r = await inject('GET', '/api/lab/sims', null, teacher.token);
|
|
assert.ok(ids(r.body).length >= 2);
|
|
});
|
|
|
|
it('симуляция, открытая классу, видна ученику (и только она)', async () => {
|
|
rule(simA, 'class', classId);
|
|
const got = ids((await inject('GET', '/api/lab/sims', null, student.token)).body);
|
|
assert.ok(got.includes(simA), 'видит открытую');
|
|
assert.ok(!got.includes(simB), 'не видит неоткрытую');
|
|
});
|
|
|
|
it('личное правило ученика добавляет симуляцию', async () => {
|
|
rule(simB, 'student', student.userId);
|
|
const got = ids((await inject('GET', '/api/lab/sims', null, student.token)).body);
|
|
assert.ok(got.includes(simA) && got.includes(simB));
|
|
});
|
|
});
|