162 lines
9.1 KiB
JavaScript
162 lines
9.1 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* 宿舍通知(DormitoryNotice)后端 P8 端到端冒烟脚本。
|
||
*
|
||
* 背景:/api/* 受基础平台 CAS/SSO 过滤器保护,无登录态会返回 302→SSO,脚本无法自证身份。
|
||
* 因此用「浏览器登录后复制 Cookie」的方式携带会话:
|
||
* 1) 起后端:先跑 sql/dormnotice_init.sql 建库+字典,再 `java -jar target/dormnotice.jar`
|
||
* (或 IDEA 运行 DormNoticeApplication,端口 8105)。
|
||
* 2) 浏览器用教师账号 lijing 走 SSO 登录前端(http://localhost:8078 或后端直连),
|
||
* 打开 DevTools→Network→任一 /api 请求,复制其「Cookie」请求头整串值。
|
||
* 3) 同样用学生账号(kegemo / csxs1)登录,复制一份学生 Cookie。
|
||
* 4) 运行:
|
||
* TEACHER_COOKIE='<粘贴教师Cookie>' STUDENT_COOKIE='<粘贴学生Cookie>' \
|
||
* node smoke/smoke.mjs
|
||
* 可选:BASE(默认 http://localhost:8105)。
|
||
*
|
||
* 只读为主;Create 冒烟会创建一条草稿再删除,不留脏数据。退出码非 0 表示有必过项失败。
|
||
*/
|
||
|
||
const BASE = (process.env.BASE || "http://localhost:8105").replace(/\/+$/, "");
|
||
const TEACHER = process.env.TEACHER_COOKIE || "";
|
||
const STU = process.env.STUDENT_COOKIE || "";
|
||
// 写链路默认执行(用一次性数据并自清理);设 WRITE=0 跳过所有增删改,仅跑只读冒烟。
|
||
const WRITE = (process.env.WRITE || "1") !== "0";
|
||
|
||
const results = [];
|
||
let failed = 0;
|
||
|
||
function hdr(cookie, extra = {}) {
|
||
const h = { Accept: "application/json", ...extra };
|
||
if (cookie) h.Cookie = cookie;
|
||
return h;
|
||
}
|
||
|
||
async function req(label, method, path, { cookie = TEACHER, body, form, json = true, must = true } = {}) {
|
||
const url = BASE + path;
|
||
const headers = hdr(cookie);
|
||
let payload;
|
||
if (body !== undefined) { headers["Content-Type"] = "application/json;charset=UTF-8"; payload = JSON.stringify(body); }
|
||
else if (form !== undefined) { headers["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-8"; payload = new URLSearchParams(form).toString(); }
|
||
let status, data, raw;
|
||
try {
|
||
const resp = await fetch(url, { method, headers, body: payload, redirect: "manual" });
|
||
status = resp.status;
|
||
raw = await resp.text();
|
||
if (json) { try { data = JSON.parse(raw); } catch { data = null; } }
|
||
} catch (e) {
|
||
push(label, false, `网络错误 ${e.message}`, must);
|
||
return null;
|
||
}
|
||
if (status === 302 || status === 301) {
|
||
push(label, false, `${status}→SSO 重定向:Cookie 无效/未登录`, must);
|
||
return null;
|
||
}
|
||
push(label, status === 200 && (!json || (data && data.code === 0)), `HTTP ${status} code=${data && data.code} msg=${data && data.msg}`, must);
|
||
return data ? data.data : raw;
|
||
}
|
||
|
||
function push(name, ok, detail, must) {
|
||
results.push({ name, ok, detail, must });
|
||
if (!ok && must) failed++;
|
||
console.log(`${ok ? "PASS" : "FAIL"} ${name} — ${detail}`);
|
||
}
|
||
|
||
async function main() {
|
||
console.log(`\n=== DormNotice 冒烟 @ ${BASE} ===`);
|
||
if (!TEACHER) { console.log("⚠ 未提供 TEACHER_COOKIE,鉴权类接口会 302。请先按脚本头注释获取。"); }
|
||
|
||
// 0. 免鉴权健康(actuator 返回 {status:UP},非 {code} 信封,只验 HTTP 200)
|
||
await req("actuator/health", "GET", "/actuator/health", { must: false, json: false });
|
||
|
||
// 1. 平台代理
|
||
await req("base/GetUserInfo", "GET", "/api/base/GetUserInfo");
|
||
await req("base/GetUserMod", "GET", "/api/base/GetUserMod");
|
||
await req("base/GetDormTree", "GET", "/api/base/GetDormTree", { must: false });
|
||
await req("base/SetRole(parent)", "POST", "/api/base/SetRole", { cookie: STU || TEACHER, body: { as: "parent" }, must: false });
|
||
|
||
// 2. 字典(B2:type 参数化 + 无“文字重复”)
|
||
const types = await req("dict/List?noticeType", "GET", "/api/dict/List?type=noticeType");
|
||
const depts = await req("dict/ManageList?dept", "GET", "/api/dict/ManageList?type=dept");
|
||
await req("dict/Sync", "GET", "/api/dict/Sync", { must: false });
|
||
// 断言无逗号重复名
|
||
if (Array.isArray(types)) {
|
||
const dup = types.some(t => String(t.v || t.k || "").includes(","));
|
||
push("字典名无“X,X”重复", !dup, dup ? "存在含逗号字典名,检查 B1 是否真后端未装" : "OK", true);
|
||
}
|
||
|
||
// 3. 宿舍域
|
||
const buildings = await req("dorm/BuildingList", "GET", "/api/dorm/BuildingList", { must: false });
|
||
await req("dorm/RoomList", "GET", "/api/dorm/RoomList", { must: false });
|
||
await req("dorm/PendingStudents", "GET", "/api/dorm/PendingStudents", { must: false });
|
||
|
||
// 4. 通知:FormTemplates / SendList / Create(草稿)→Detail→Delete
|
||
await req("notice/FormTemplates", "GET", "/api/notice/FormTemplates");
|
||
await req("notice/SendList", "GET", "/api/notice/SendList?pageIndex=1&pageSize=10");
|
||
await req("notice/Statis", "GET", "/api/notice/Statis", { must: false });
|
||
|
||
const deptCode = Array.isArray(depts) && depts[0] ? depts[0].CodeID : "D002";
|
||
const receiveList = Array.isArray(buildings) && buildings[0] ? String(buildings[0].buildingId * 10000) : "ALL";
|
||
// 4b. 通知写链路:Create(草稿) → Edit 改标题 → GetDetail 校验 → Delete 自清理
|
||
if (WRITE && TEACHER) {
|
||
const nid = await req("notice/Create(draft)", "POST", "/api/notice/Create", {
|
||
body: { title: "【冒烟】通知A", content: "<p>smoke</p>", noticeType: "其他",
|
||
level: "normal", receiptMode: "none", deptId: deptCode, receiveType: "room",
|
||
receiveList, draft: true },
|
||
});
|
||
if (nid) {
|
||
await req("notice/Edit(改标题)", "POST", "/api/notice/Edit", { body: { id: nid, title: "【冒烟】通知B", receiptMode: "none" } });
|
||
const d = await req("notice/GetDetail(校验)", "GET", `/api/notice/GetDetail?serid=${nid}`);
|
||
push("notice/Edit 生效(标题→B)", !!(d && /通知B/.test(String(d.title || ""))), `title=${d && d.title}`, false);
|
||
await req("notice/Delete(cleanup)", "POST", "/api/notice/Delete", { body: [nid] });
|
||
}
|
||
}
|
||
|
||
// 5. 家长端(学生 Cookie)
|
||
await req("notice/List(parent)", "GET", "/api/notice/List?pageIndex=1&pageSize=20", { cookie: STU, must: false });
|
||
await req("notice/MyNotices(parent)", "GET", "/api/notice/MyNotices", { cookie: STU, must: false });
|
||
|
||
// 6. 写链路自清理(用一次性数据,跑完即删;设 WRITE=0 跳过)
|
||
if (WRITE && TEACHER) {
|
||
// 6.1 字典:Add(noticeType) → ManageList 查得 → Delete
|
||
const tn = "冒烟类型" + Date.now();
|
||
await req("dict/Add(noticeType)", "POST", "/api/dict/Add", { form: { name: tn, type: "noticeType" } });
|
||
const ml = await req("dict/ManageList(noticeType)", "GET", "/api/dict/ManageList?type=noticeType", { must: false });
|
||
const dictItem = Array.isArray(ml) ? ml.find(x => x.Name === tn) : null;
|
||
push("dict/Add→ManageList 可查得", !!dictItem, dictItem ? "SerID=" + dictItem.SerID : "未找到新增项", false);
|
||
if (dictItem) await req("dict/Delete(cleanup)", "POST", "/api/dict/Delete", { form: { serid: dictItem.SerID, type: "noticeType" } });
|
||
|
||
// 6.2 联系组:AddGroup → GetGroup 查得 → DelGroup
|
||
const gn = "冒烟组" + Date.now();
|
||
await req("contacts/AddGroup", "POST", "/api/contacts/AddGroup", { form: { name: gn } });
|
||
const gp = await req("contacts/GetGroup", "GET", "/api/contacts/GetGroup?page=1&count=200", { must: false });
|
||
const grp = gp && Array.isArray(gp.list) ? gp.list.find(x => x.GroupName === gn) : null;
|
||
push("contacts/AddGroup→GetGroup 可查得", !!grp, grp ? "serid=" + grp.serid : "未找到", false);
|
||
if (grp) await req("contacts/DelGroup(cleanup)", "POST", "/api/contacts/DelGroup", { form: { ids: grp.serid } });
|
||
|
||
// 6.3 宿舍:AddBuilding → BuildingList 查得 → AddRoom → RoomList 查得 → DelRoom → DelBuilding
|
||
const bn = "冒烟楼" + Date.now();
|
||
await req("dorm/AddBuilding", "POST", "/api/dorm/AddBuilding", { form: { buildingName: bn, gender: "混合", floorCount: 1, roomsPerFloor: 1, bedsPerRoom: 2 } });
|
||
const bl = await req("dorm/BuildingList", "GET", "/api/dorm/BuildingList", { must: false });
|
||
const b = Array.isArray(bl) ? bl.find(x => x.buildingName === bn) : null;
|
||
push("dorm/AddBuilding→BuildingList 可查得", !!b, b ? "buildingId=" + b.buildingId : "未找到", false);
|
||
if (b) {
|
||
await req("dorm/AddRoom", "POST", "/api/dorm/AddRoom", { form: { buildingId: b.buildingId, floor: 1, roomNo: "901", bedCount: 2 } });
|
||
const rl = await req("dorm/RoomList", "GET", "/api/dorm/RoomList?buildingId=" + b.buildingId, { must: false });
|
||
const room = Array.isArray(rl) ? rl.find(x => x.roomNo === "901") : null;
|
||
push("dorm/AddRoom→RoomList 可查得", !!room, room ? "roomId=" + room.roomId : "未找到", false);
|
||
if (room) await req("dorm/DelRoom(cleanup)", "POST", "/api/dorm/DelRoom", { form: { roomId: room.roomId } });
|
||
await req("dorm/DelBuilding(cleanup)", "POST", "/api/dorm/DelBuilding", { form: { buildingId: b.buildingId } });
|
||
}
|
||
} else {
|
||
console.log("SKIP 写链路(WRITE=0 或缺 TEACHER_COOKIE)");
|
||
}
|
||
|
||
console.log("\n=== 汇总 ===");
|
||
const pass = results.filter(r => r.ok).length;
|
||
console.log(`${pass}/${results.length} 通过;必过失败 ${failed} 项`);
|
||
process.exit(failed ? 1 : 0);
|
||
}
|
||
|
||
main().catch(e => { console.error("脚本异常:", e); process.exit(2); });
|