mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-04 10:54:05 +00:00
Extract shared normalization/coercion helpers into private @openclaw/normalization-core workspace package while preserving existing plugin SDK helper subpaths.\n\nAlso keeps direct normalization-core imports internal, wires UI/build/loader resolution, and replaces the slow PR network CodeQL lane with a fast added-line boundary scan while retaining full CodeQL for scheduled/manual runs.\n\nVerification: local moved tests, plugin SDK boundary tests, extension loader tests, agents-support shard, UI build/test, build artifacts, lint, workflow guards, autoreview, and GitHub CI passed on PR head 963d893715.
143 lines
5.5 KiB
TypeScript
143 lines
5.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
normalizeAtHashSlug,
|
|
normalizeHyphenSlug,
|
|
normalizeSortedUniqueStringEntries,
|
|
normalizeSortedUniqueTrimmedStringList,
|
|
normalizeStringEntries,
|
|
normalizeStringEntriesLower,
|
|
normalizeUniqueSingleOrTrimmedStringList,
|
|
normalizeUniqueStringEntries,
|
|
normalizeUniqueStringEntriesLower,
|
|
normalizeUniqueTrimmedStringList,
|
|
sortUniqueStrings,
|
|
uniqueStrings,
|
|
} from "./string-normalization.js";
|
|
|
|
describe("normalization-core/string-normalization", () => {
|
|
it("normalizes mixed allow-list entries", () => {
|
|
expect(normalizeStringEntries([" a ", 42, "", " ", "z"])).toEqual(["a", "42", "z"]);
|
|
expect(normalizeStringEntries([" ok ", null, { toString: () => " obj " }])).toEqual([
|
|
"ok",
|
|
"null",
|
|
"obj",
|
|
]);
|
|
expect(normalizeStringEntries(undefined)).toStrictEqual([]);
|
|
});
|
|
|
|
it("normalizes mixed allow-list entries to lowercase", () => {
|
|
expect(normalizeStringEntriesLower([" A ", "MiXeD", 7])).toEqual(["a", "mixed", "7"]);
|
|
});
|
|
|
|
it("sorts unique string values", () => {
|
|
expect(sortUniqueStrings(["b", "a", "b"])).toEqual(["a", "b"]);
|
|
});
|
|
|
|
it("deduplicates string values while preserving first-seen order", () => {
|
|
expect(uniqueStrings(["b", "a", "b", "c", "a"])).toEqual(["b", "a", "c"]);
|
|
});
|
|
|
|
it("normalizes unique string entries", () => {
|
|
expect(normalizeUniqueStringEntries([" b ", "a", "b", "", 4, "a"])).toEqual(["b", "a", "4"]);
|
|
});
|
|
|
|
it("normalizes unique lowercase string entries", () => {
|
|
expect(normalizeUniqueStringEntriesLower([" A ", "a", "MiXeD", "", 7])).toEqual([
|
|
"a",
|
|
"mixed",
|
|
"7",
|
|
]);
|
|
});
|
|
|
|
it("normalizes sorted unique string entries", () => {
|
|
expect(normalizeSortedUniqueStringEntries([" b ", "a", "b", "", 4])).toEqual(["4", "a", "b"]);
|
|
});
|
|
|
|
it("normalizes unique trimmed string lists", () => {
|
|
expect(normalizeUniqueTrimmedStringList([" b ", "a", "b", "", "a"])).toEqual(["b", "a"]);
|
|
expect(normalizeUniqueTrimmedStringList("b")).toEqual([]);
|
|
});
|
|
|
|
it("normalizes sorted unique trimmed string lists", () => {
|
|
expect(normalizeSortedUniqueTrimmedStringList([" b ", "a", "b", "", "a"])).toEqual(["a", "b"]);
|
|
expect(normalizeSortedUniqueTrimmedStringList(["z", 1, " a "] as unknown[])).toEqual([
|
|
"a",
|
|
"z",
|
|
]);
|
|
});
|
|
|
|
it("normalizes unique single-or-list string values", () => {
|
|
expect(normalizeUniqueSingleOrTrimmedStringList([" b ", "a", "b", "", "a"])).toEqual([
|
|
"b",
|
|
"a",
|
|
]);
|
|
expect(normalizeUniqueSingleOrTrimmedStringList(" b ")).toEqual(["b"]);
|
|
});
|
|
|
|
it("normalizes slug-like labels while preserving supported symbols", () => {
|
|
expect(normalizeHyphenSlug(" Team Room ")).toBe("team-room");
|
|
expect(normalizeHyphenSlug(" #My_Channel + Alerts ")).toBe("#my_channel-+-alerts");
|
|
expect(normalizeHyphenSlug("..foo---bar..")).toBe("foo-bar");
|
|
expect(normalizeHyphenSlug(undefined)).toBe("");
|
|
expect(normalizeHyphenSlug(null)).toBe("");
|
|
});
|
|
|
|
it("collapses repeated separators and trims leading/trailing punctuation", () => {
|
|
expect(normalizeHyphenSlug(" ...Hello / World--- ")).toBe("hello-world");
|
|
expect(normalizeHyphenSlug(" ###Team@@@Room### ")).toBe("###team@@@room###");
|
|
});
|
|
|
|
it("normalizes @/# prefixed slugs used by channel allowlists", () => {
|
|
expect(normalizeAtHashSlug(" #My_Channel + Alerts ")).toBe("my-channel-alerts");
|
|
expect(normalizeAtHashSlug("@@Room___Name")).toBe("room-name");
|
|
expect(normalizeAtHashSlug(undefined)).toBe("");
|
|
expect(normalizeAtHashSlug(null)).toBe("");
|
|
});
|
|
|
|
it("strips repeated prefixes and collapses separator-only results", () => {
|
|
expect(normalizeAtHashSlug("###__Room Name__")).toBe("room-name");
|
|
expect(normalizeAtHashSlug("@@@___")).toBe("");
|
|
});
|
|
|
|
it.each([
|
|
["技术讨论组", "技术讨论组"],
|
|
[" AI 助手群 ", "ai-助手群"],
|
|
["友達グループ", "友達グループ"],
|
|
["개발자 모임", "개발자-모임"],
|
|
["Team 技术讨论", "team-技术讨论"],
|
|
["#OpenClaw中文群", "#openclaw中文群"],
|
|
["Команда разработки", "команда-разработки"],
|
|
["فريق التطوير", "فريق-التطوير"],
|
|
])("preserves Unicode letters in normalizeHyphenSlug: %s", (input, expected) => {
|
|
expect(normalizeHyphenSlug(input)).toBe(expected);
|
|
});
|
|
|
|
it.each([
|
|
["Cafe\u0301 Team", "café-team"],
|
|
["हिन्दी चर्चा", "हिन्दी-चर्चा"],
|
|
["ห้อง แช็ต", "ห้อง-แช็ต"],
|
|
])("preserves combining marks in normalizeHyphenSlug: %s", (input, expected) => {
|
|
expect(normalizeHyphenSlug(input)).toBe(expected);
|
|
});
|
|
|
|
it.each([
|
|
["#技术频道", "技术频道"],
|
|
["@中文群组", "中文群组"],
|
|
["#日本語チャンネル", "日本語チャンネル"],
|
|
["#한국어채널", "한국어채널"],
|
|
["#Команда разработки", "команда-разработки"],
|
|
["@فريق التطوير", "فريق-التطوير"],
|
|
["#OpenClaw中文群", "openclaw中文群"],
|
|
])("preserves Unicode letters in normalizeAtHashSlug: %s", (input, expected) => {
|
|
expect(normalizeAtHashSlug(input)).toBe(expected);
|
|
});
|
|
|
|
it.each([
|
|
["#Cafe\u0301_Team", "café-team"],
|
|
["@हिन्दी चर्चा", "हिन्दी-चर्चा"],
|
|
["#ห้อง แช็ต", "ห้อง-แช็ต"],
|
|
])("preserves combining marks in normalizeAtHashSlug: %s", (input, expected) => {
|
|
expect(normalizeAtHashSlug(input)).toBe(expected);
|
|
});
|
|
});
|