Skip to content

Commit

Permalink
Merge pull request #966 from thefrontside/tm/test-more-node-versions
Browse files Browse the repository at this point in the history
Run tests in CI for Node 16, 18, 20, 22
  • Loading branch information
taras authored Jan 22, 2025
2 parents 6f5b31f + 39013f7 commit e0dc265
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 169 deletions.
32 changes: 24 additions & 8 deletions .github/workflows/verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,18 @@ permissions:
contents: read

jobs:
test:
test-deno:
runs-on: ubuntu-latest

steps:
- name: checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: setup deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x

- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: 20.x
registry-url: https://registry.npmjs.com

- name: format
run: deno fmt --check

Expand All @@ -36,5 +30,27 @@ jobs:

- name: test
run: deno task test

test-node:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20, 22]

steps:
- name: checkout
uses: actions/checkout@v4

- name: setup deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.com

- name: test:node
run: deno task test:node
10 changes: 8 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
},
"lock": false,
"tasks": {
"test": "deno test --allow-run=deno",
"test:node": "deno task build:npm 0.0.0 && node test/main/node.mjs hello world",
"test": "deno test --allow-run=deno --allow-read --allow-env",
"test:node": "deno run -A tasks/built-test.ts",
"build:npm": "deno run -A tasks/build-npm.ts",
"bench": "deno run -A tasks/bench.ts",
"build:jsr": "deno run -A tasks/build-jsr.ts"
Expand Down Expand Up @@ -42,5 +42,11 @@
"dom",
"dom.iterable"
]
},
"imports": {
"@std/testing": "jsr:@std/testing@1",
"ts-expect": "npm:[email protected]",
"@std/expect": "jsr:@std/expect@1",
"tinyexec": "npm:[email protected]"
}
}
2 changes: 1 addition & 1 deletion lib/race.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function* race<T extends Operation<unknown>>(
// encapsulate the race in a hermetic scope.
let result = yield* trap(() =>
encapsulate(function* () {
for (let operation of operations.toReversed()) {
for (let operation of operations.slice().reverse()) {
tasks.push(
yield* spawn(function* () {
// let contestant = yield* useScope();
Expand Down
35 changes: 35 additions & 0 deletions tasks/built-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { build, emptyDir } from "jsr:@deno/[email protected]";

const outDir = "./build/test";

await emptyDir(outDir);

const entryPoints = [
"./lib/mod.ts",
];
for await (const entry of Deno.readDir("test")) {
if (entry.isFile) {
entryPoints.push(`./test/${entry.name}`);
}
}

await build({
entryPoints,
outDir,
shims: {
deno: true,
},
typeCheck: false,
compilerOptions: {
lib: ["ESNext", "DOM"],
target: "ES2020",
sourceMap: true,
},
importMap: "deno.json",
package: {
// package.json properties
name: "effection-tests",
version: "0.0.0",
sideEffects: false,
},
});
163 changes: 48 additions & 115 deletions test/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,166 +1,99 @@
import { $await, describe, expect, it, useCommand } from "./suite.ts";
import { type Operation, resource, run, sleep, spawn } from "../mod.ts";
import { describe, expect, it, x } from "./suite.ts";
import { each, run, type Stream } from "../mod.ts";

function* until(stream: Stream<string, void>, text: string) {
for (const line of yield* each(stream)) {
if (line.includes(text)) {
return;
}
yield* each.next();
}
}

describe("main", () => {
it("gracefully shuts down on SIGINT", async () => {
await run(function* () {
let daemon = yield* useCommand("deno", {
stdout: "piped",
args: ["run", "test/main/ok.daemon.ts"],
});
let stdout = yield* buffer(daemon.stdout);
yield* detect(stdout, "started");
let proc = yield* x("deno", ["run", "test/main/ok.daemon.ts"]);

daemon.kill("SIGINT");
yield* until(proc.lines, "started");

let status = yield* $await(daemon.status);
const { exitCode, stdout } = yield* proc.kill("SIGINT");

expect(status.code).toBe(130);
expect(stdout).toContain("gracefully stopped");

yield* detect(stdout, "gracefully stopped");
expect(exitCode).toBe(130);
});
});

it("gracefully shuts down on SIGTERM", async () => {
await run(function* () {
let daemon = yield* useCommand("deno", {
stdout: "piped",
args: ["run", "test/main/ok.daemon.ts"],
});
let stdout = yield* buffer(daemon.stdout);
yield* detect(stdout, "started");
let proc = yield* x("deno", ["run", "test/main/ok.daemon.ts"]);

daemon.kill("SIGTERM");
yield* until(proc.lines, "started");

let status = yield* $await(daemon.status);
const { exitCode, stdout } = yield* proc.kill("SIGTERM");

expect(status.code).toBe(143);
expect(stdout).toContain("gracefully stopped");

yield* detect(stdout, "gracefully stopped");
expect(exitCode).toBe(143);
});
});

it("exits gracefully on explicit exit()", async () => {
await run(function* () {
let cmd = yield* useCommand("deno", {
stdout: "piped",
args: ["run", "test/main/ok.exit.ts"],
});

let stdout = yield* buffer(cmd.stdout);
let proc = yield* x("deno", ["run", "test/main/ok.exit.ts"]);

yield* detect(stdout, "goodbye.\nOk, computer.");
yield* until(proc.lines, "goodbye.");
yield* until(proc.lines, "Ok, computer.");
});
});

it("exits gracefully with 0 on implicit exit", async () => {
await run(function* () {
let cmd = yield* useCommand("deno", {
stdout: "piped",
args: ["run", "test/main/ok.implicit.ts"],
});
let proc = yield* x("deno", ["run", "test/main/ok.implicit.ts"]);

yield* until(proc.lines, "goodbye.");

let stdout = yield* buffer(cmd.stdout);
let status = yield* $await(cmd.status);
const { exitCode } = yield* proc;

yield* detect(stdout, "goodbye.");
expect(status.code).toEqual(0);
expect(exitCode).toEqual(0);
});
});

it("exits gracefully on explicit exit failure exit()", async () => {
await run(function* () {
let cmd = yield* useCommand("deno", {
stdout: "piped",
stderr: "piped",
args: ["run", "test/main/fail.exit.ts"],
});
let stdout = yield* buffer(cmd.stdout);
let stderr = yield* buffer(cmd.stderr);
let status = yield* $await(cmd.status);

yield* detect(stdout, "graceful goodbye");
yield* detect(stderr, "It all went horribly wrong");
expect(status.code).toEqual(23);
let proc = yield* x("deno", ["run", "test/main/fail.exit.ts"]);

const { stderr, exitCode, stdout } = yield* proc;

expect(stdout).toContain("graceful goodbye");
expect(stderr).toContain("It all went horribly wrong");
expect(exitCode).toEqual(23);
});
});

it("error exits gracefully on unexpected errors", async () => {
await run(function* () {
let cmd = yield* useCommand("deno", {
stdout: "piped",
stderr: "piped",
args: ["run", "test/main/fail.unexpected.ts"],
});

let stdout = yield* buffer(cmd.stdout);
let stderr = yield* buffer(cmd.stderr);
let status = yield* $await(cmd.status);

yield* detect(stdout, "graceful goodbye");
yield* detect(stderr, "Error: moo");
expect(status.code).toEqual(1);
let proc = yield* x("deno", ["run", "test/main/fail.unexpected.ts"]);

const { stderr, stdout, exitCode } = yield* proc;

expect(stdout).toContain("graceful goodbye");
expect(stderr).toContain("Error: moo");
expect(exitCode).toEqual(1);
});
});

it("works even if suspend is the only operation", async () => {
await run(function* () {
let process = yield* useCommand("deno", {
stdout: "piped",
args: ["run", "test/main/just.suspend.ts"],
});
let stdout = yield* buffer(process.stdout);
yield* detect(stdout, "started");

process.kill("SIGINT");
let proc = yield* x("deno", ["run", "test/main/just.suspend.ts"]);

let status = yield* $await(process.status);
yield* until(proc.lines, "started");

expect(status.code).toBe(130);
const { exitCode, stdout } = yield* proc.kill("SIGINT");

yield* detect(stdout, "gracefully stopped");
expect(exitCode).toBe(130);
expect(stdout).toContain("gracefully stopped");
});
});
});

interface Buffer {
content: string;
}

function buffer(stream: ReadableStream<Uint8Array>): Operation<Buffer> {
return resource<{ content: string }>(function* (provide) {
let buff = { content: " " };
yield* spawn(function* () {
let decoder = new TextDecoder();
let reader = stream.getReader();

try {
let next = yield* $await(reader.read());
while (!next.done) {
buff.content += decoder.decode(next.value);
next = yield* $await(reader.read());
}
} finally {
yield* $await(reader.cancel());
}
});

yield* provide(buff);
});
}

function* detect(
buffer: Buffer,
text: string,
options: { timeout: number } = { timeout: 1000 },
): Operation<void> {
let start = new Date().getTime();

while ((new Date().getTime() - start) < options.timeout) {
if (buffer.content.includes(text)) {
return;
}
yield* sleep(10);
}
expect(buffer.content).toContain(text);
}
8 changes: 0 additions & 8 deletions test/main/node.mjs

This file was deleted.

Loading

0 comments on commit e0dc265

Please sign in to comment.