|
@@ -0,0 +1,101 @@
|
|
|
|
|
+import { after, before, beforeEach, test } from 'node:test';
|
|
|
|
|
+import assert from 'node:assert/strict';
|
|
|
|
|
+import { readFileSync } from 'node:fs';
|
|
|
|
|
+import vm from 'node:vm';
|
|
|
|
|
+import { PGlite } from '@electric-sql/pglite';
|
|
|
|
|
+
|
|
|
|
|
+const db = new PGlite();
|
|
|
|
|
+const source = readFileSync(new URL('../deploy-admin-functions.mjs', import.meta.url), 'utf8');
|
|
|
|
|
+const gateway = vm.createContext({});
|
|
|
|
|
+vm.runInContext(source.match(/const appGatewayCode = String.raw`([\s\S]*?)\n`;/)[1].replace(/const DEFAULT_COMPANY_ID = '[^']+';/, "const DEFAULT_COMPANY_ID = 'co';"), gateway);
|
|
|
|
|
+gateway.Psql = {
|
|
|
|
|
+ one: async (sql, args) => (await db.query(sql, args)).rows[0],
|
|
|
|
|
+ query: async (sql, args) => (await db.query(sql, args)).rows,
|
|
|
|
|
+};
|
|
|
|
|
+gateway.appOverviewToday = () => '2026-10-30';
|
|
|
|
|
+const coach = { get: key => key === 'legacyUserId' ? 300 : undefined };
|
|
|
|
|
+const page = (input = {}) => gateway.coachStudentPage({ scope: 'current-scheduled', ...input }, coach);
|
|
|
|
|
+const ids = result => Array.from(result.rows, row => row.UserID);
|
|
|
|
|
+
|
|
|
|
|
+before(async () => {
|
|
|
|
|
+ await db.exec(`
|
|
|
|
|
+ CREATE TABLE "_User"("objectId" text,"company" text,"legacyUserId" int,"legacyGroupId" int,"legacyUserData" jsonb,
|
|
|
|
|
+ "isDisabled" boolean,"isDeleted" boolean,"username" text,"email" text,"mobile" text,"phone" text,"avatar" text,
|
|
|
|
|
+ "nickname" text,"nickName" text,"realName" text,"createdAt" timestamptz);
|
|
|
|
|
+ CREATE TABLE "CourseAppointment"("company" text,"id" int,"szyh" text,"pl" text,"dszt" text,"bxrq" text,"yysj" text,"isDeleted" boolean);
|
|
|
|
|
+ CREATE TABLE "CommonModel"("company" text,"modelId" int,"itemId" int,"status" int,"updatedAt" timestamptz,"isDeleted" boolean);
|
|
|
|
|
+ `);
|
|
|
|
|
+});
|
|
|
|
|
+beforeEach(() => db.exec('TRUNCATE "_User","CourseAppointment","CommonModel"'));
|
|
|
|
|
+after(() => db.close());
|
|
|
|
|
+
|
|
|
|
|
+async function user(id, { disabled = false, deleted = false, group = 1, company = 'co' } = {}) {
|
|
|
|
|
+ await db.query('INSERT INTO "_User"("objectId","company","legacyUserId","legacyGroupId","isDisabled","isDeleted","username","legacyUserData") VALUES($1,$2,$3,$4,$5,$6,$7,$8)',
|
|
|
|
|
+ ['u' + id, company, id, group, disabled, deleted, 'student-' + id, { GroupID: 1 }]);
|
|
|
|
|
+}
|
|
|
|
|
+async function appointment(id, student, { date = '2026-10-31', yysj = '', status = '0', commonStatus = 99, teacher = '300', company = 'co', deleted = false, commonDeleted = false } = {}) {
|
|
|
|
|
+ await db.query('INSERT INTO "CourseAppointment" VALUES($1,$2,$3,$4,$5,$6,$7,$8)', [company, id, String(student), teacher, status, date, yysj, deleted]);
|
|
|
|
|
+ await db.query('INSERT INTO "CommonModel" VALUES($1,54,$2,$3,$4,$5)', [company, id, commonStatus, '2026-10-30T00:00:00Z', commonDeleted]);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+test('仅列出正常学员账号且在当前老师名下仍有今天或未来排课,先筛选再分页', async () => {
|
|
|
|
|
+ for (let id = 1; id <= 13; id++) await user(id, { disabled: id === 2, deleted: id === 3, group: id === 4 ? 3 : 1 });
|
|
|
|
|
+ await appointment(1, 1);
|
|
|
|
|
+ await appointment(2, 2);
|
|
|
|
|
+ await appointment(3, 3);
|
|
|
|
|
+ await appointment(4, 4);
|
|
|
|
|
+ await appointment(5, 5, { date: '2026-10-29' });
|
|
|
|
|
+ await appointment(6, 6, { status: '30' });
|
|
|
|
|
+ await appointment(7, 7, { commonStatus: -2 });
|
|
|
|
|
+ await appointment(8, 8, { teacher: '301' });
|
|
|
|
|
+ await appointment(9, 9, { company: 'other' });
|
|
|
|
|
+ await appointment(10, 10, { date: '2026-10-30', status: '10' });
|
|
|
|
|
+ await appointment(11, 11, { deleted: true });
|
|
|
|
|
+ await appointment(12, 12, { commonDeleted: true });
|
|
|
|
|
+ await appointment(13, 13, { status: '-1' });
|
|
|
|
|
+ await appointment(14, 999); // No matching account.
|
|
|
|
|
+ await appointment(15, 1); // Multiple lessons must not duplicate a student.
|
|
|
|
|
+ const first = await page({ psize: 1 });
|
|
|
|
|
+ const second = await page({ psize: 1, cpage: 2 });
|
|
|
|
|
+ assert.equal(first.page.itemCount, 2);
|
|
|
|
|
+ assert.equal(first.page.pageCount, 2);
|
|
|
|
|
+ assert.deepEqual([...ids(first), ...ids(second)].sort((a, b) => a - b), [1, 10]);
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+test('日期兼容紧凑日期、斜线日期及预约时间回退,空日期不进入列表', async () => {
|
|
|
|
|
+ for (let id = 1; id <= 5; id++) await user(id);
|
|
|
|
|
+ await appointment(1, 1, { date: '20261030' });
|
|
|
|
|
+ await appointment(2, 2, { date: '', yysj: '2026-10-30 09:00:00' });
|
|
|
|
|
+ await appointment(3, 3, { date: '2026/10/31' });
|
|
|
|
|
+ await appointment(4, 4, { date: '', yysj: '' });
|
|
|
|
|
+ await appointment(5, 5, { date: '20261029' });
|
|
|
|
|
+ assert.deepEqual(ids(await page()).sort((a, b) => a - b), [1, 2, 3]);
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+test('恢复有效学员时将其放在首屏,停用或无排课的旧选择不能绕过筛选', async () => {
|
|
|
|
|
+ for (let id = 1; id <= 4; id++) { await user(id, { disabled: id === 4 }); await appointment(id, id); }
|
|
|
|
|
+ await user(5);
|
|
|
|
|
+ const selected = await page({ selectedStudentId: 3, psize: 2 });
|
|
|
|
|
+ assert.equal(ids(selected)[0], 3);
|
|
|
|
|
+ const next = await page({ selectedStudentId: 3, psize: 2, cpage: 2 });
|
|
|
|
|
+ assert.equal(new Set([...ids(selected), ...ids(next)]).size, 3);
|
|
|
|
|
+ for (const selectedStudentId of [4, 5]) {
|
|
|
|
|
+ const result = await page({ selectedStudentId });
|
|
|
|
|
+ assert.equal(result.page.itemCount, 3);
|
|
|
|
|
+ assert.ok(!ids(result).includes(selectedStudentId));
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+test('筛选固定使用登录老师和当前帐套,请求参数不能指定其他老师', async () => {
|
|
|
|
|
+ await user(1); await user(2); await user(3, { company: 'other' });
|
|
|
|
|
+ await appointment(1, 1); await appointment(2, 2, { teacher: '301' }); await appointment(3, 3);
|
|
|
|
|
+ assert.deepEqual(ids(await page({ uid: 301, coachId: 301, company: 'other' })), [1]);
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+test('未请求新筛选的我的学员接口仍可列出历史学员', async () => {
|
|
|
|
|
+ await user(1); await user(2, { disabled: true });
|
|
|
|
|
+ await appointment(1, 1, { date: '2020-01-01', status: '30' });
|
|
|
|
|
+ await appointment(2, 2);
|
|
|
|
|
+ assert.deepEqual(ids(await page()), []);
|
|
|
|
|
+ assert.deepEqual(ids(await page({ scope: '' })).sort((a, b) => a - b), [1, 2]);
|
|
|
|
|
+});
|