| import { describe, expect, it } from "vitest"; | |
| import { | |
| parseIrcLine, | |
| parseIrcPrefix, | |
| sanitizeIrcOutboundText, | |
| sanitizeIrcTarget, | |
| splitIrcText, | |
| } from "./protocol.js"; | |
| describe("irc protocol", () => { | |
| it("parses PRIVMSG lines with prefix and trailing", () => { | |
| const parsed = parseIrcLine(":alice!u@host PRIVMSG #room :hello world"); | |
| expect(parsed).toEqual({ | |
| raw: ":alice!u@host PRIVMSG #room :hello world", | |
| prefix: "alice!u@host", | |
| command: "PRIVMSG", | |
| params: ["#room"], | |
| trailing: "hello world", | |
| }); | |
| expect(parseIrcPrefix(parsed?.prefix)).toEqual({ | |
| nick: "alice", | |
| user: "u", | |
| host: "host", | |
| }); | |
| }); | |
| it("sanitizes outbound text to prevent command injection", () => { | |
| expect(sanitizeIrcOutboundText("hello\\r\\nJOIN #oops")).toBe("hello JOIN #oops"); | |
| expect(sanitizeIrcOutboundText("\\u0001test\\u0000")).toBe("test"); | |
| }); | |
| it("validates targets and rejects control characters", () => { | |
| expect(sanitizeIrcTarget("#openclaw")).toBe("#openclaw"); | |
| expect(() => sanitizeIrcTarget("#bad\\nPING")).toThrow(/Invalid IRC target/); | |
| expect(() => sanitizeIrcTarget(" user")).toThrow(/Invalid IRC target/); | |
| }); | |
| it("splits long text on boundaries", () => { | |
| const chunks = splitIrcText("a ".repeat(300), 120); | |
| expect(chunks.length).toBeGreaterThan(2); | |
| expect(chunks.every((chunk) => chunk.length <= 120)).toBe(true); | |
| }); | |
| }); | |