mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-10-30 11:35:13 +08:00
update 2026-09-20 01:22:33
This commit is contained in:
1 parent
42e0cfbec4
commit
deb27aee9d
864 files changed
+180566
-42
No files matched your search
@@ -0,0 +1,186 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { existsSync, readFileSync, statSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { test } from "node:test";
|
||||
|
||||
/* The patch ships as a plain deferred script, not a module. Evaluate it with
|
||||
an injected `module` to reach its parsing exports; `document` stays
|
||||
undefined so the DOM bootstrap is skipped. */
|
||||
const source = await readFile(
|
||||
new URL("../src/resource/patches/admin-status-logs.js", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const mod = { exports: {} };
|
||||
new Function("module", "exports", "document", "window", source)(
|
||||
mod,
|
||||
mod.exports,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
const { parseSyslogLine, parseDmesgLine, evaluate, PARSE_GATE } = mod.exports;
|
||||
|
||||
test("classic logread format (23.05 syslog, 24.10 syslog-wrapper) parses", () => {
|
||||
const r = parseSyslogLine(
|
||||
"Sat Jul 26 03:14:15 2026 daemon.info dnsmasq-dhcp[1]: DHCPACK(br-lan) 192.168.1.140",
|
||||
);
|
||||
assert.deepEqual(
|
||||
{ time: r.time, fac: r.fac, sev: r.sev, sevTok: r.sevTok, tag: r.tag },
|
||||
{
|
||||
time: "Sat Jul 26 03:14:15 2026",
|
||||
fac: "daemon",
|
||||
sev: "info",
|
||||
sevTok: "daemon.info",
|
||||
tag: "dnsmasq-dhcp[1]",
|
||||
},
|
||||
);
|
||||
assert.equal(r.msg, "DHCPACK(br-lan) 192.168.1.140");
|
||||
|
||||
// ctime() pads single-digit days with a space — kept verbatim.
|
||||
const padded = parseSyslogLine(
|
||||
"Sun Jul 6 09:05:07 2026 daemon.warn odhcpd[2029]: No default route present",
|
||||
);
|
||||
assert.equal(padded.time, "Sun Jul 6 09:05:07 2026");
|
||||
|
||||
// Kernel lines routed through logread; hostapd logs without a pid.
|
||||
assert.equal(
|
||||
parseSyslogLine(
|
||||
"Sat Jul 26 03:14:16 2026 kern.info kernel: [73412.882110] br-lan: port 3(lan3) entered forwarding state",
|
||||
).tag,
|
||||
"kernel",
|
||||
);
|
||||
assert.equal(
|
||||
parseSyslogLine(
|
||||
"Sat Jul 26 03:14:17 2026 daemon.notice hostapd: wlan1: AP-STA-CONNECTED 3c:22:fb:8a:12:9d",
|
||||
).tag,
|
||||
"hostapd",
|
||||
);
|
||||
});
|
||||
|
||||
test("master RPC bracket format parses across browser locales", () => {
|
||||
// The datestr renders verbatim, brackets and timezone included — the
|
||||
// stamp is UTC-formatted upstream and hiding the zone read as local time.
|
||||
const en = parseSyslogLine(
|
||||
"[Jul 24, 2026, 10:59:50 PM UTC] daemon.info: dnsmasq-dhcp[1]: DHCPREQUEST(br-lan) 192.168.1.140",
|
||||
);
|
||||
assert.equal(en.time, "[Jul 24, 2026, 10:59:50 PM UTC]");
|
||||
assert.equal(en.sevTok, "daemon.info:");
|
||||
|
||||
assert.equal(
|
||||
parseSyslogLine(
|
||||
"[Jul 24, 2026, 10:59:50 PM Coordinated Universal Time] daemon.warn: odhcpd[2029]: No default route present",
|
||||
).time,
|
||||
"[Jul 24, 2026, 10:59:50 PM Coordinated Universal Time]",
|
||||
);
|
||||
|
||||
// Non-en datestrs parse fine and display untouched — never guessed at.
|
||||
const zh = parseSyslogLine(
|
||||
"[2026年7月24日 GMT 22:59:50] daemon.err: odhcpd[2029]: Failed to send RA",
|
||||
);
|
||||
assert.equal(zh.sev, "err");
|
||||
assert.equal(zh.time, "[2026年7月24日 GMT 22:59:50]");
|
||||
});
|
||||
|
||||
test("rendered tokens rejoin into the source line byte for byte", () => {
|
||||
const lines = [
|
||||
"[Jul 24, 2026, 10:59:50 PM UTC] daemon.info: dnsmasq-dhcp[1]: DHCPREQUEST(br-lan) 192.168.1.140",
|
||||
"[Jul 25, 2026, 4:30:55 PM UTC] kern.info: [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x51af8014]",
|
||||
"Sun Jul 6 09:05:07 2026 daemon.warn odhcpd[2029]: No default route present",
|
||||
"Sat Jul 26 03:14:16 2026 kern.info kernel: [73412.882110] br-lan: port 3(lan3) entered forwarding state",
|
||||
];
|
||||
for (const line of lines) {
|
||||
const p = parseSyslogLine(line);
|
||||
const parts = [p.time, p.sevTok];
|
||||
if (p.tag) parts.push(p.tag + ":");
|
||||
parts.push(p.msg);
|
||||
assert.equal(parts.join(" "), line, line);
|
||||
}
|
||||
const dm = "[ 12.345678] br-lan: port 3(lan3) entered forwarding state";
|
||||
const d = parseDmesgLine(dm);
|
||||
assert.equal([d.time, d.msg].join(" "), dm);
|
||||
});
|
||||
|
||||
test("severity comes from the field, never from message content", () => {
|
||||
const r = parseSyslogLine(
|
||||
"[Jul 24, 2026, 11:00:00 PM UTC] daemon.info: logger: user said daemon.err: fake",
|
||||
);
|
||||
assert.equal(r.sev, "info");
|
||||
assert.equal(r.msg, "user said daemon.err: fake");
|
||||
});
|
||||
|
||||
test("unknown facility/severity and foreign formats are rejected", () => {
|
||||
assert.equal(
|
||||
parseSyslogLine("[Jul 24, 2026, 11:00:00 PM UTC] bogus.info: x: y"),
|
||||
null,
|
||||
);
|
||||
assert.equal(
|
||||
parseSyslogLine("[Jul 24, 2026, 11:00:00 PM UTC] daemon.bogus: x: y"),
|
||||
null,
|
||||
);
|
||||
// syslog-ng's classic template has no facility.severity field at all.
|
||||
assert.equal(
|
||||
parseSyslogLine("Jul 26 10:00:00 OpenWrt dnsmasq[123]: query answered"),
|
||||
null,
|
||||
);
|
||||
assert.equal(parseSyslogLine(""), null);
|
||||
});
|
||||
|
||||
test("dmesg lines: uptime stamp only, message verbatim, no severity", () => {
|
||||
// Kernel lines have no uniform tag grammar (upstream renders them as plain
|
||||
// text for the same reason) — a partial split styled only some lines and
|
||||
// ate only their colons, so no tag is ever extracted and the message keeps
|
||||
// its punctuation untouched.
|
||||
const r = parseDmesgLine(
|
||||
"[ 12.345678] br-lan: port 3(lan3) entered forwarding state",
|
||||
);
|
||||
assert.equal(r.time, "[ 12.345678]");
|
||||
assert.equal(r.tag, "");
|
||||
assert.equal(r.sev, "");
|
||||
assert.equal(r.msg, "br-lan: port 3(lan3) entered forwarding state");
|
||||
|
||||
const pci = parseDmesgLine(
|
||||
"[75821.004518] mt7921e 0000:01:00.0: Message 00000010 timeout",
|
||||
);
|
||||
assert.equal(pci.tag, "");
|
||||
assert.equal(pci.msg, "mt7921e 0000:01:00.0: Message 00000010 timeout");
|
||||
|
||||
assert.equal(parseDmesgLine("no bracket prefix here"), null);
|
||||
});
|
||||
|
||||
test("parse gate: mixed content falls back, empty value stays calm", () => {
|
||||
const good = "Sat Jul 26 03:14:15 2026 daemon.info dnsmasq[1]: ok";
|
||||
const bad = "Jul 26 10:00:00 OpenWrt dnsmasq[123]: syslog-ng style";
|
||||
|
||||
const healthy = evaluate(
|
||||
[good, good, good, good, bad].join("\n"),
|
||||
parseSyslogLine,
|
||||
);
|
||||
assert.ok(healthy.ratio >= PARSE_GATE, "4/5 parsed must stay enhanced");
|
||||
|
||||
const foreign = evaluate([bad, bad, bad].join("\n"), parseSyslogLine);
|
||||
assert.ok(foreign.ratio < PARSE_GATE, "foreign log must fall back");
|
||||
|
||||
const empty = evaluate("", parseSyslogLine);
|
||||
assert.equal(empty.rows.length, 0);
|
||||
assert.equal(empty.ratio, 1, "empty log must not trip the gate");
|
||||
});
|
||||
|
||||
test("built log payloads land in their homes", () => {
|
||||
const out = resolve(import.meta.dirname, "../../htdocs/luci-static/aurora");
|
||||
// The JS ships on-demand under one name — every supported release
|
||||
// (23.05/24.10/master) mounts both log pages under admin/status/logs/*.
|
||||
const js = resolve(out, "patches/admin-status-logs.js");
|
||||
assert.ok(existsSync(js), `${js} missing — run pnpm build first`);
|
||||
assert.ok(statSync(js).size <= 6_000, `${js} exceeds 6 KB`);
|
||||
// The CSS is core-page styling and lives in the main bundle, where the
|
||||
// Tailwind token pipeline (radius-base chain, app shadows) applies — a
|
||||
// stray patches/ copy would mean the component migration regressed.
|
||||
const main = readFileSync(resolve(out, "main.css"), "utf8");
|
||||
assert.ok(main.includes(".syslog-view"), "main.css lost the log viewer");
|
||||
assert.ok(
|
||||
main.includes("calc(var(--radius-base)"),
|
||||
"radius utilities no longer resolve through the reactive base chain",
|
||||
);
|
||||
assert.ok(!existsSync(resolve(out, "patches/admin-status-logs.css")));
|
||||
assert.ok(!existsSync(resolve(out, "patches/_log-shared.css")));
|
||||
});
|
||||
Reference in new issue
Block a user