169 lines
8.8 KiB
JavaScript
169 lines
8.8 KiB
JavaScript
import test from "node:test";
|
||
import assert from "node:assert/strict";
|
||
import { readFileSync } from "node:fs";
|
||
import { parseDocx, details } from "../server/parser.js";
|
||
import { parseWeeksDocx } from "../server/calendar.js";
|
||
import { mergeWeekTables } from "../server/source.js";
|
||
import {
|
||
peopleAndRooms,
|
||
buildIndex,
|
||
occurrence,
|
||
search,
|
||
dayOf,
|
||
freeRooms,
|
||
examsOf,
|
||
isPhysicalRoom,
|
||
} from "../server/directory.js";
|
||
|
||
const fixture = (name) =>
|
||
readFileSync(new URL(`./fixtures/${name}.docx`, import.meta.url));
|
||
const calendar = {
|
||
...mergeWeekTables([parseWeeksDocx(fixture("weeks"))]),
|
||
bells: [
|
||
{ pair: 1, start: "08:30", end: "09:55", halves: [] },
|
||
{ pair: 2, start: "10:05", end: "11:30", halves: [] },
|
||
{ pair: 3, start: "11:45", end: "13:10", halves: [] },
|
||
],
|
||
source: {},
|
||
};
|
||
const doc = (id, schedule) => ({ id, file: { id, name: `${id}.docx` }, schedule });
|
||
const fef = doc("fef", parseDocx(fixture("fef-23-ba"), { name: "23-ЛГ_23-БА.docx", faculty: "ФЭФ" }));
|
||
const rooms = (raw, room = "") => peopleAndRooms({ raw, room, teacher: "" }, "2026");
|
||
|
||
test("Аудитории: реальные формулировки из Word", () => {
|
||
assert.deepEqual(rooms("Охрана труда (л.з.) Лемачко С.Н. ауд.205;6,8н.- Горунова С.А.", "205").rooms, ["205"]);
|
||
const moved = rooms("История (л.) Сумко Е.В. ауд. 432 14.10. -ауд. 333", "432");
|
||
assert.deepEqual(moved.rooms, ["432"]);
|
||
assert.deepEqual(moved.overrides, [{ date: "2026-10-14", room: "333" }]);
|
||
assert.deepEqual(rooms("Материаловедение (л.) Фруцкий В.А. ауд. 106н 17.10. -210 мк").overrides, [
|
||
{ date: "2026-10-17", room: "210МК" },
|
||
]);
|
||
// A typo glues the room to the date: "22417.09." is room 224 and a move on 17 September.
|
||
const glued = rooms("Информационные технологии (л.) Моняк Т.М. ауд. 22417.09.- ауд.332", "22417");
|
||
assert.deepEqual([glued.rooms, glued.overrides[0].room], [["224"], "332"]);
|
||
assert.deepEqual(rooms("Единый день информирования 10.09;15.10;19.11;17.12", "12").rooms, []);
|
||
assert.deepEqual(rooms("Информационный час ауд. В0-05", "05").rooms, ["В0-05"]);
|
||
assert.deepEqual(rooms("Физкультура ауд. 526н325н502н").rooms, ["526Н", "325Н", "502Н"]);
|
||
assert.deepEqual(rooms("Предмет (л.) Иванов И.И. ауд.129, Петров П.П. ауд. 130").teachers, [
|
||
"Иванов И.И.",
|
||
"Петров П.П.",
|
||
]);
|
||
assert.equal(rooms("Линейная алгебра (л.)\nЗабелендик О.Н.\n200 (ДО)", "200 (ДО)").distance, true);
|
||
assert.equal(rooms("Сети (л.) Лукьянов А.О. 200ДО").rooms.length, 0);
|
||
assert.deepEqual(rooms("История (л) Емельянчик О.А. 21/28 (дискозал)").rooms, ["21/28"]);
|
||
assert.ok(isPhysicalRoom("207Г") && isPhysicalRoom("А1-15") && !isPhysicalRoom("3"));
|
||
});
|
||
|
||
test("Занятость по дате учитывает недели, цвет строки и семестр", () => {
|
||
const idx = buildIndex([fef]);
|
||
const find = (subject, day) =>
|
||
idx.entries.find((e) => e.lesson.subject === subject && e.lesson.day === day && e.lesson.groups.includes("23-БА"));
|
||
// Friday 16 October 2026 is week 7: "1-4,6-11н." runs, "12-15н." does not.
|
||
assert.equal(occurrence(find("PR-менеджмент", 5), "2026-10-16", calendar), "yes");
|
||
const later = idx.entries.find((e) => e.lesson.condition === "12-15н." && e.lesson.day === 5);
|
||
assert.equal(occurrence(later, "2026-10-16", calendar), "no");
|
||
assert.equal(occurrence(later, "2026-11-20", calendar), "yes");
|
||
// An autumn schedule does not run in March.
|
||
assert.equal(occurrence(find("PR-менеджмент", 5), "2027-03-05", calendar), "no");
|
||
// A spring document without a spring week table: nothing is guessed.
|
||
const spring = doc("spring", { ...fef.schedule, period: "Весенний семестр" });
|
||
const entry = { ...find("PR-менеджмент", 5), doc: spring };
|
||
assert.equal(occurrence(entry, "2027-03-05", calendar), "unknown");
|
||
});
|
||
|
||
test("Поиск, день преподавателя и свободные аудитории", () => {
|
||
const idx = buildIndex([fef]);
|
||
idx.docs = [fef];
|
||
idx.total = 1;
|
||
idx.failed = [];
|
||
idx.year = "2026–2027";
|
||
assert.equal(search(idx, "селез").teachers[0].name, "Селезнева А.А.");
|
||
assert.equal(search(idx, "4").teachers.length, 0);
|
||
const day = dayOf(idx, calendar, { teacher: "Селезнева А.А." }, "2026-10-16");
|
||
assert.deepEqual(day.lessons.map((l) => [l.start, l.subject, l.rooms.join()]), [
|
||
["08:30", "Инновационный менеджмент", "333"],
|
||
]);
|
||
const room = dayOf(idx, calendar, { room: "129" }, "2026-10-16");
|
||
assert.ok(room.lessons.some((l) => l.subject === "PR-менеджмент"));
|
||
const free = freeRooms(idx, calendar, "2026-10-16", 2);
|
||
const freeList = free.free.flatMap((g) => g.rooms);
|
||
assert.ok(!freeList.includes("129"), "PR-менеджмент занимает 129 на 2-й паре");
|
||
assert.ok(freeList.includes("333"), "333 занята только на 1-й паре");
|
||
assert.equal(free.coverage.documents, 1);
|
||
assert.equal(freeRooms(idx, calendar, "2026-10-16", 9), null);
|
||
});
|
||
|
||
test("Экзамены, зачёты и консультации распознаются и собираются по группе", () => {
|
||
assert.deepEqual(
|
||
["Экзамен. Финансы организаций Иванов И.И. ауд.129", "Финансы (экз.) Иванов И.И.", "Зачет: Физическая культура Петров П.П.", "Консультация Финансы Иванов И.И."]
|
||
.map((raw) => [details(raw).type, details(raw).subject]),
|
||
[
|
||
["Экзамен", "Финансы организаций"],
|
||
["Экзамен", "Финансы"],
|
||
["Зачёт", "Физическая культура"],
|
||
["Консультация", "Финансы"],
|
||
],
|
||
);
|
||
assert.equal(details("Психология профессионального консультирования (л.) Сидоров С.С.").type, "Лекция");
|
||
const exam = (date, start, type, subject) => ({
|
||
id: date + start,
|
||
day: 1,
|
||
date,
|
||
start,
|
||
end: null,
|
||
subject,
|
||
teacher: "Иванов И.И.",
|
||
room: "129",
|
||
type,
|
||
condition: "",
|
||
groups: ["23-БА"],
|
||
subgroups: [],
|
||
variant: "all",
|
||
raw: `${type} ${subject} Иванов И.И. ауд.129`,
|
||
});
|
||
const session = doc("session", {
|
||
lessons: [
|
||
exam("2027-01-14", "09:00", "Экзамен", "Финансы"),
|
||
exam("2027-01-13", "15:00", "Консультация", "Финансы"),
|
||
exam("2027-01-14", "09:00", "Экзамен", "Финансы"),
|
||
{ ...exam("2027-01-20", "09:00", "Экзамен", "Маркетинг"), groups: ["23-ЛГ"] },
|
||
],
|
||
source: { faculty: "ФЭФ" },
|
||
period: "Экзаменационная сессия",
|
||
});
|
||
const idx = buildIndex([fef, session]);
|
||
const list = examsOf(idx, "23-БА", "ФЭФ");
|
||
assert.deepEqual(list.map((e) => [e.date, e.type, e.subject, e.rooms.join()]), [
|
||
["2027-01-13", "Консультация", "Финансы", "129"],
|
||
["2027-01-14", "Экзамен", "Финансы", "129"],
|
||
]);
|
||
assert.equal(examsOf(idx, "23-БА", "ФИТ").length, 0);
|
||
});
|
||
|
||
test("Поиск группы: любое написание, без учёта фильтров, с фасетами каталога", async () => {
|
||
const { findGroups, groupKey } = await import("../server/directory.js");
|
||
const fit = parseDocx(fixture("fit-daily"), { name: "24-ИТ.docx" });
|
||
const docs = [
|
||
{ id: "fef", file: { id: "fef", name: "23-ЛГ_23-БА.docx", faculty: "ФЭФ", course: "4 курс", form: "Дневная форма" }, schedule: fef.schedule },
|
||
{ id: "fit", file: { id: "fit", name: "24-ИТ.docx", faculty: "ФИТ", course: "3 курс", form: "Дневная форма" }, schedule: fit },
|
||
];
|
||
const idx = { docs, year: "2026–2027" };
|
||
assert.equal(groupKey("23 ба"), groupKey("23-БА"));
|
||
for (const q of ["23-БА", "23 ба", "23ба"])
|
||
assert.deepEqual(findGroups(idx, { q }).results.map((r) => r.group), ["23-БА"]);
|
||
// The query ignores the faculty filter: a student must find their group anywhere.
|
||
assert.deepEqual(
|
||
findGroups(idx, { q: "24-ит", faculty: "ФЭФ" }).results.map((r) => r.group),
|
||
["24-ИТ-1", "24-ИТ-2"],
|
||
);
|
||
const browse = findGroups(idx, { faculty: "ФИТ" });
|
||
assert.deepEqual(browse.results.map((r) => [r.group, r.fileId]), [
|
||
["24-ИТ-1", "fit"],
|
||
["24-ИТ-2", "fit"],
|
||
]);
|
||
assert.deepEqual(browse.facets.faculties.map((f) => f.name), ["ФИТ", "ФЭФ"]);
|
||
assert.deepEqual(browse.facets.courses.map((c) => c.name), ["3 курс"]);
|
||
assert.equal(findGroups(idx, {}).results.length, 0);
|
||
assert.equal(findGroups(idx, {}).total, 4);
|
||
});
|