aboutsummaryrefslogtreecommitdiffhomepage
path: root/worker/php/lib.test.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'worker/php/lib.test.mjs')
-rw-r--r--worker/php/lib.test.mjs150
1 files changed, 150 insertions, 0 deletions
diff --git a/worker/php/lib.test.mjs b/worker/php/lib.test.mjs
new file mode 100644
index 0000000..6613066
--- /dev/null
+++ b/worker/php/lib.test.mjs
@@ -0,0 +1,150 @@
+import { describe, expect, it } from "vitest";
+import { buildResult, createIOCallbacks, preprocessCode } from "./lib.mjs";
+
+describe("preprocessCode", () => {
+ it("removes <?php tag and prepends PRELUDE", () => {
+ const result = preprocessCode('<?php echo "hello";');
+ expect(result).toContain('echo "hello";');
+ expect(result).toContain("error_reporting");
+ expect(result).not.toContain("<?php");
+ });
+
+ it("removes <? short tag and prepends PRELUDE", () => {
+ const result = preprocessCode('<? echo "hello";');
+ expect(result).toContain('echo "hello";');
+ expect(result).toContain("error_reporting");
+ expect(result).not.toContain("<?");
+ });
+
+ it("prepends PRELUDE when no php tag present", () => {
+ const result = preprocessCode('echo "hello";');
+ expect(result).toContain('echo "hello";');
+ expect(result).toContain("error_reporting");
+ });
+
+ it("handles empty string", () => {
+ const result = preprocessCode("");
+ expect(result).toContain("error_reporting");
+ });
+
+ it("does not remove <?php when not at the start", () => {
+ const result = preprocessCode('echo "x"; <?php echo "y";');
+ expect(result).toContain("<?php");
+ });
+});
+
+describe("createIOCallbacks", () => {
+ describe("stdin", () => {
+ it("reads input byte by byte", () => {
+ const io = createIOCallbacks("AB");
+ expect(io.stdin()).toBe(65); // 'A'
+ expect(io.stdin()).toBe(66); // 'B'
+ });
+
+ it("returns null at EOF", () => {
+ const io = createIOCallbacks("A");
+ io.stdin(); // consume 'A'
+ expect(io.stdin()).toBeNull();
+ expect(io.stdin()).toBeNull();
+ });
+
+ it("returns null immediately for empty input", () => {
+ const io = createIOCallbacks("");
+ expect(io.stdin()).toBeNull();
+ });
+ });
+
+ describe("stdout", () => {
+ it("captures ASCII writes", () => {
+ const io = createIOCallbacks("");
+ io.stdout(72); // 'H'
+ io.stdout(105); // 'i'
+ expect(io.getStdout()).toBe("Hi");
+ });
+
+ it("ignores null (flush)", () => {
+ const io = createIOCallbacks("");
+ io.stdout(65);
+ io.stdout(null);
+ io.stdout(66);
+ expect(io.getStdout()).toBe("AB");
+ });
+
+ it("corrects negative asciiCode by adding 256", () => {
+ const io = createIOCallbacks("");
+ // -191 + 256 = 65 = 'A'
+ io.stdout(-191);
+ expect(io.getStdout()).toBe("A");
+ });
+
+ it("truncates output at 10KB buffer limit", () => {
+ const io = createIOCallbacks("");
+ const limit = 10 * 1024;
+ for (let i = 0; i < limit + 100; i++) {
+ io.stdout(65);
+ }
+ expect(io.getStdout().length).toBe(limit);
+ });
+ });
+
+ describe("stderr", () => {
+ it("captures ASCII writes", () => {
+ const io = createIOCallbacks("");
+ io.stderr(69); // 'E'
+ io.stderr(114); // 'r'
+ expect(io.getStderr()).toBe("Er");
+ });
+
+ it("ignores null (flush)", () => {
+ const io = createIOCallbacks("");
+ io.stderr(65);
+ io.stderr(null);
+ expect(io.getStderr()).toBe("A");
+ });
+
+ it("corrects negative asciiCode by adding 256", () => {
+ const io = createIOCallbacks("");
+ // -156 + 256 = 100 = 'd'
+ io.stderr(-156);
+ expect(io.getStderr()).toBe("d");
+ });
+
+ it("truncates output at 10KB buffer limit", () => {
+ const io = createIOCallbacks("");
+ const limit = 10 * 1024;
+ for (let i = 0; i < limit + 100; i++) {
+ io.stderr(65);
+ }
+ expect(io.getStderr().length).toBe(limit);
+ });
+ });
+});
+
+describe("buildResult", () => {
+ it("returns success when err is null and result is 0", () => {
+ const result = buildResult(null, 0, () => "out", () => "");
+ expect(result).toEqual({
+ status: "success",
+ stdout: "out",
+ stderr: "",
+ });
+ });
+
+ it("returns runtime_error when result is non-zero", () => {
+ const result = buildResult(null, 1, () => "out", () => "err");
+ expect(result).toEqual({
+ status: "runtime_error",
+ stdout: "out",
+ stderr: "err",
+ });
+ });
+
+ it("returns runtime_error with concatenated stderr when err is thrown", () => {
+ const err = new Error("fatal");
+ const result = buildResult(err, undefined, () => "out", () => "err");
+ expect(result.status).toBe("runtime_error");
+ expect(result.stdout).toBe("out");
+ expect(result.stderr).toContain("err");
+ expect(result.stderr).toContain("Error: fatal");
+ });
+});