Files
psu-schedule/test/parser.test.js
2026-10-16 22:08:45 +03:00

143 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { parseDocx, parseWord } from "../server/parser.js";
import { decodeListing, fileMetadata } from "../server/source.js";
const fixture = (name) =>
readFileSync(new URL(`./fixtures/${name}.docx`, import.meta.url));
const fef = () => parseDocx(fixture("fef-23-ba"), { name: "23-ЛГ_23-БА.docx" });
test("ФЭФ: 23-БА не получает дисциплины 23-ЛГ", () => {
const s = fef(),
ba = s.lessons.filter((l) => l.groups.includes("23-БА"));
assert.deepEqual(s.groups, ["23-ЛГ", "23-БА"]);
assert.equal(ba.length, 33);
assert.ok(ba.some((l) => l.subject === "Бизнес-анализ"));
assert.ok(
!ba.some((l) =>
/логистик|Экспедирование|Управление запасами/i.test(l.subject),
),
);
assert.ok(
s.lessons
.filter((l) => l.groups.includes("23-ЛГ"))
.some((l) => /Финансовая логистика/.test(l.subject)),
);
});
test("ФЭФ: общая ячейка принадлежит обеим группам", () => {
const shared = fef().lessons.filter((l) =>
l.subject.startsWith("Единый день информирования"),
);
assert.equal(shared.length, 1);
assert.deepEqual(shared[0].groups, ["23-ЛГ", "23-БА"]);
assert.match(shared[0].raw, /15 октября/);
assert.equal(shared[0].start, "13:40");
});
test("ФЭФ: варианты недель, цвет, преподаватель и аудитория сохранены", () => {
const s = fef();
const lessons = s.lessons.filter(
(l) =>
l.day === 4 &&
l.groups.includes("23-БА") &&
l.subject === "Бизнес-анализ",
);
assert.deepEqual(
lessons.map((l) => [l.condition, l.variant, l.room, l.teacher]),
[
["7,9,11н.", "upper", "430", "Войтенок Ж.Е."],
["4-12н", "lower", "430", "Войтенок Ж.Е."],
],
);
assert.equal(lessons[1].sourceColor, "CCFFCC");
assert.equal(lessons[0].start, "10:05");
assert.equal(lessons[0].end, "11:30");
const plan = s.lessons.find(
(l) =>
l.subject === "Бизнес-планирование" && l.start === "11:45" && l.day === 4,
);
assert.equal(plan.condition, "1-8,10-17");
assert.ok(s.warnings.length);
});
test("ФЭФ: кураторский час в 16.50 не становится невозможной датой", () => {
const l = fef().lessons.find(
(l) =>
l.groups.includes("23-БА") &&
l.day === 1 &&
l.subject.includes("кураторский"),
);
assert.equal(l.start, "16:50");
assert.equal(l.end, null);
assert.equal(l.date, null);
assert.equal(l.condition, "");
for (const lesson of fef().lessons)
if (lesson.date)
assert.ok(Number.isFinite(new Date(lesson.date).getTime()));
});
test("ФЭФ: объединённое занятие не дублируется на зелёной строке", () => {
const s = fef();
const l = s.lessons.filter(
(l) =>
l.day === 2 &&
l.groups.includes("23-БА") &&
l.subject === "Управление проектами" &&
l.start === "15:20",
);
assert.equal(l.length, 1);
assert.equal(l[0].variant, "all");
});
test("Дневные таблицы четырёх факультетов распознаются со своими группами", () => {
for (const [name, groups] of [
["fit-daily", ["24-ИТ-1", "24-ИТ-2"]],
["fkne-daily", ["24-КБ", "24-КФ"]],
["isf-daily", ["26-ПГС-1", "26-ПГС-2"]],
["gf-daily", ["25-РГФ-1", "25-РГФ-2"]],
]) {
const s = parseDocx(fixture(name), { name });
assert.deepEqual(s.groups, groups);
assert.ok(s.lessons.length > 20);
assert.ok(
s.lessons.every(
(l) => l.day >= 1 && l.day <= 7 && /^\d{2}:\d{2}$/.test(l.start),
),
);
assert.ok(
s.lessons.every((l) => l.groups.every((g) => groups.includes(g))),
);
}
});
test("Сессии: отдельные колонки времени, аудитории и преподавателя", () => {
const s = parseDocx(fixture("fit"), { name: "26-ИТзс.docx" }),
l = s.lessons[0];
assert.deepEqual(s.groups, ["26-ИТзс"]);
assert.equal(l.date, "2026-09-17");
assert.equal(l.start, "11:00");
assert.equal(l.end, "11:30");
assert.equal(l.teacher, "Петрович О.Н.");
assert.equal(l.room, "220Д");
assert.deepEqual(l.subgroups, []);
assert.match(l.raw, /Петрович/);
assert.equal(s.period, "Установочная сессия");
const f = parseDocx(fixture("fkne"), {});
assert.deepEqual(f.groups, ["26-ЭЭТзс"]);
assert.ok(f.lessons.every((l) => l.date));
});
test("Импорт отклоняет повреждённые и посторонние файлы", async () => {
assert.throws(() => parseDocx(Buffer.from("broken")), /повреждён/);
await assert.rejects(
() => parseWord(Buffer.from("not a word file")),
/настоящий файл Word/,
);
});
test("DAV: метаданные вложенных документов и только доверенный каталог", () => {
const xml = `<d:multistatus xmlns:d="DAV:"><d:response><d:href>/public.php/dav/files/no5zKbMrns6j5jQ/2026-2027/</d:href><d:propstat><d:prop><d:resourcetype><d:collection/></d:resourcetype></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response><d:response><d:href>https://evil.example/file.docx</d:href></d:response></d:multistatus>`;
const entries = decodeListing(xml);
assert.equal(entries.length, 1);
assert.equal(entries[0].folder, true);
const metadata = JSON.parse(
readFileSync(new URL("./fixtures/fef-23-ba.docx.json", import.meta.url)),
);
const file = fileMetadata(metadata);
assert.equal(file.faculty, "Финансово-экономический факультет");
assert.equal(file.course, "4 курс");
assert.equal(file.name, "23-ЛГ_23-БА.docx");
});