// @vitest-environment node import { createHash } from "node:crypto"; import { readFile } from "node:fs/promises"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { GO_ENGINE_VERSION, GO_FLAVOUR_VERSION, GoEngineAdapter, } from "../../src/regex/execution/adapters/go/GoEngineAdapter"; import { GoWasmBridge } from "../../src/regex/execution/adapters/go/GoWasmBridge"; import type { RegexExecutionRequest, RegexReplacementRequest, } from "../../src/regex/model/match"; const pack = path.resolve("public/engines/go"); let adapter: GoEngineAdapter; function request( pattern: string, subject: string, overrides: Partial = {}, ): RegexExecutionRequest { return { flavour: "go", flavourVersion: GO_FLAVOUR_VERSION, pattern, flags: ["g"], subject, captureMetadata: [], scanAll: false, maximumMatches: 100, maximumCaptureRows: 1_000, ...overrides, }; } function replacement( pattern: string, subject: string, replacementText: string, overrides: Partial = {}, ): RegexReplacementRequest { return { ...request(pattern, subject), replacement: replacementText, maximumOutputBytes: 1_024, ...overrides, }; } beforeAll(async () => { const wasm = await WebAssembly.compile( await readFile(path.join(pack, "go-regex.wasm")), ); const bridge = new GoWasmBridge( pathToFileURL(path.join(pack, "wasm_exec.mjs")), wasm, ); adapter = new GoEngineAdapter(bridge, "Node.js conformance"); await adapter.load(); }, 30_000); afterAll(() => { adapter?.terminate(); }); describe("Go 1.26.5 standard-library regexp conformance", () => { it("loads the exact runtime and bridge self-test identity", async () => { await expect(adapter.load()).resolves.toEqual( expect.objectContaining({ flavour: "go", engineName: "Go standard-library regexp", engineVersion: GO_ENGINE_VERSION, runtimeVersion: expect.stringContaining("go1.26.5 js/wasm"), offsetUnit: "utf8-byte", }), ); }); it("returns native UTF-8 byte offsets and native named captures", async () => { const result = await adapter.execute( request("(?P😀+)([A-Z]+)?", "x😀😀AB 😀"), ); expect(result.accepted).toBe(true); expect( result.matches.map(({ nativeRange, range }) => ({ nativeRange, range })), ).toEqual([ { nativeRange: { start: 1, end: 11, unit: "utf8-byte" }, range: { startUtf16: 1, endUtf16: 7 }, }, { nativeRange: { start: 12, end: 16, unit: "utf8-byte" }, range: { startUtf16: 8, endUtf16: 10 }, }, ]); expect(result.matches[0]?.captures).toEqual([ expect.objectContaining({ groupNumber: 1, groupName: "emoji", value: "😀😀", nativeRange: { start: 1, end: 9, unit: "utf8-byte" }, }), expect.objectContaining({ groupNumber: 2, value: "AB" }), ]); expect(result.matches[1]?.captures[1]).toEqual({ groupNumber: 2, status: "did-not-participate", }); }); it("uses Go's native empty-match suppression and RE2 boundary", async () => { const empty = await adapter.execute(request("a*", "baaab")); expect(empty.matches.map(({ nativeRange }) => nativeRange)).toEqual([ { start: 0, end: 0, unit: "utf8-byte" }, { start: 1, end: 4, unit: "utf8-byte" }, { start: 5, end: 5, unit: "utf8-byte" }, ]); const unsupported = await adapter.execute(request("(?<=a)b", "ab")); expect(unsupported.accepted).toBe(false); expect(unsupported.diagnostics[0]).toEqual( expect.objectContaining({ code: "compile-error" }), ); }); it("maps i, m, s and U to native Go inline flags", async () => { const cases = [ ["a", "A", ["i"]], ["^b$", "a\nb\nc", ["m"]], ["a.b", "a\nb", ["s"]], ["a+?", "aaa", ["U"]], ] as const; for (const [pattern, subject, flags] of cases) { const result = await adapter.execute( request(pattern, subject, { flags: [...flags] }), ); expect(result.matches, `${flags.join("")}: ${pattern}`).toHaveLength(1); } const ungreedy = await adapter.execute( request("a+", "aaa", { flags: ["U"] }), ); expect(ungreedy.matches[0]?.value).toBe("a"); }); it("uses native ExpandString replacement name and longest-name rules", async () => { const result = await adapter.replace( replacement("(?P[a-z]+)-(\\d+)", "a-1 b-22", "${word}[$2]/$1/$$"), ); expect(result.execution.accepted).toBe(true); expect(result.output).toBe("a[1]/a/$ b[22]/b/$"); const longest = await adapter.replace(replacement("(a)", "a", "$1x|${1}x")); expect(longest.output).toBe("|ax"); }); it("bounds capture amplification and preserves the untouched partial suffix", async () => { const bounded = await adapter.replace( replacement("(a+)", "a".repeat(256 * 1024), "$1".repeat(4_096), { maximumOutputBytes: 5, }), ); expect(bounded.execution.accepted).toBe(true); expect(bounded.output).toBe("aaaaa"); expect(bounded.outputBytes).toBe(5); expect(bounded.outputTruncated).toBe(true); const partial = await adapter.replace( replacement("a", "aaa", "x", { maximumMatches: 1 }), ); expect(partial.execution.matches).toHaveLength(1); expect(partial.execution.truncated).toBe(true); expect(partial.output).toBe("xaa"); expect(partial.outputTruncated).toBe(false); }); it("enforces match/capture/output bounds without splitting UTF-8", async () => { const limited = await adapter.execute( request("a", "aaa", { maximumMatches: 1 }), ); expect(limited.matches).toHaveLength(1); expect(limited.truncated).toBe(true); const rows = await adapter.execute( request("(a)(b)", "ab", { maximumCaptureRows: 1 }), ); expect(rows.matches).toHaveLength(0); expect(rows.truncated).toBe(true); const output = await adapter.replace( replacement(".", "abc", "😀", { maximumOutputBytes: 5 }), ); expect(output.output).toBe("😀"); expect(output.outputBytes).toBe(4); expect(output.outputTruncated).toBe(true); }); it("rejects lone UTF-16 surrogates before entering Go", async () => { await expect(adapter.execute(request(".", "\ud800"))).rejects.toThrow( /unpaired UTF-16 surrogate/u, ); }); it("accepts control-heavy subjects within the decoded 16 MiB contract", async () => { const subject = "\u0000".repeat(3 * 1024 * 1024); const result = await adapter.execute( request("^\\x00+$", subject, { flags: [], maximumMatches: 1, maximumCaptureRows: 1, }), ); expect(result.accepted).toBe(true); expect(result.matches[0]?.nativeRange).toEqual({ start: 0, end: subject.length, unit: "utf8-byte", }); }); it("ships verified metadata, licence and checksums", async () => { const metadata = JSON.parse( await readFile(path.join(pack, "engine-metadata.json"), "utf8"), ) as { engineVersion: string; source: { tag: string; commitSha1: string; license: string }; toolchain: { archiveSha256: string }; }; expect(metadata).toEqual( expect.objectContaining({ engineVersion: "1.26.5", source: expect.objectContaining({ tag: "go1.26.5", commitSha1: "c19862e5f8415b4f24b189d065ed739517c548ba", license: "BSD-3-Clause", }), toolchain: expect.objectContaining({ archiveSha256: "5c2c3b16caefa1d968a94c1daca04a7ca301a496d9b086e17ad77bb81393f053", }), }), ); const checksums = await readFile(path.join(pack, "SHA256SUMS"), "utf8"); for (const line of checksums.trim().split("\n")) { const [expected, name] = line.split(/ {2}/u); const value = await readFile(path.join(pack, name ?? "")); expect(createHash("sha256").update(value).digest("hex"), name).toBe( expected, ); } expect(await readFile(path.join(pack, "LICENSE-Go.txt"), "utf8")).toContain( "Redistribution and use", ); }); });