Skip to content

Commit 5bd0a19

Browse files
authored
stop vitest pool workers breaking when containers are present (#10162)
* add vitest fixture * changeset * add override to disable containers in vitest * fix pnpm lock * fixup
1 parent 390b7fb commit 5bd0a19

File tree

19 files changed

+216
-16
lines changed

19 files changed

+216
-16
lines changed

.changeset/evil-loops-eat.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@cloudflare/vitest-pool-workers": patch
3+
---
4+
5+
fix: stop containers breaking vitest-pool-workers
6+
7+
Testing interactions with containers is still currently unsupported.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM node:22-alpine
2+
3+
WORKDIR /usr/src/app
4+
RUN echo '{"name": "simple-node-app", "version": "1.0.0", "dependencies": {"ws": "^8.0.0"}}' > package.json
5+
RUN npm install
6+
7+
COPY ./container/simple-node-app.js app.js
8+
EXPOSE 8787
9+
CMD ["node", "app.js"]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Apps with no-op containers
2+
3+
Currently, `vitest-pool-workers` does not support testing containers yet. It should still let you test your application as long as you do not test with any code paths that interact with containers.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const { createServer } = require("http");
2+
3+
// Create HTTP server
4+
const server = createServer(function (req, res) {
5+
res.writeHead(200, { "Content-Type": "text/plain" });
6+
res.write("Hello World! Have an env var! " + process.env.MESSAGE);
7+
res.end();
8+
});
9+
10+
server.listen(8787, function () {
11+
console.log("Server listening on port 8080");
12+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Container, getContainer, getRandom } from "@cloudflare/containers"; // in a real Worker
2+
3+
export class MyContainer extends Container {
4+
defaultPort = 8787; // The default port for the container to listen on
5+
sleepAfter = "3m"; // Sleep the container if no requests are made in this timeframe
6+
7+
envVars = {
8+
MESSAGE: "I was passed in via the container class!",
9+
};
10+
11+
override onStart() {
12+
console.log("Container successfully started");
13+
}
14+
15+
override onStop() {
16+
console.log("Container successfully shut down");
17+
}
18+
19+
override onError(error: unknown) {
20+
console.log("Container error:", error);
21+
}
22+
}
23+
24+
export default {
25+
async fetch(
26+
request: Request,
27+
env: { MY_CONTAINER: DurableObjectNamespace<MyContainer> }
28+
): Promise<Response> {
29+
const pathname = new URL(request.url).pathname;
30+
// If you want to route requests to a specific container,
31+
// pass a unique container identifier to .get()
32+
33+
if (pathname.startsWith("/container")) {
34+
const containerInstance = getContainer(env.MY_CONTAINER, pathname);
35+
return containerInstance.fetch(request);
36+
}
37+
38+
if (pathname.startsWith("/error")) {
39+
const containerInstance = getContainer(env.MY_CONTAINER, "error-test");
40+
return containerInstance.fetch(request);
41+
}
42+
43+
if (pathname.startsWith("/lb")) {
44+
const containerInstance = await getRandom(env.MY_CONTAINER, 3);
45+
return containerInstance.fetch(request);
46+
}
47+
48+
if (pathname.startsWith("/singleton")) {
49+
// getContainer will return a specific instance if no second argument is provided
50+
return getContainer(env.MY_CONTAINER).fetch(request);
51+
}
52+
53+
return new Response(
54+
"Call /container to start a container with a 10s timeout.\nCall /error to start a container that errors\nCall /lb to test load balancing"
55+
);
56+
},
57+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../tsconfig.workerd.json",
3+
"include": ["./**/*.ts"]
4+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { env, runDurableObjectAlarm, SELF } from "cloudflare:test";
2+
import { expect, it, vi } from "vitest";
3+
4+
it("dispatches fetch event", { timeout: 10_000 }, async () => {
5+
// requests to code paths that do not interact with a container should work fine
6+
const res = await SELF.fetch("http://example.com/");
7+
expect(await res.text()).toMatchInlineSnapshot(`
8+
"Call /container to start a container with a 10s timeout.
9+
Call /error to start a container that errors
10+
Call /lb to test load balancing"
11+
`);
12+
// however if you attempt to start a container, you should expect an error
13+
await expect(() =>
14+
SELF.fetch("http://example.com/container/hello")
15+
).rejects.toThrow();
16+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { env } from "cloudflare:test";
2+
import { expect, it } from "vitest";
3+
4+
it("dispatches fetch event", { timeout: 10000 }, async () => {
5+
const id = env.MY_CONTAINER.idFromName("helloagain");
6+
const stub = env.MY_CONTAINER.get(id);
7+
// the DO constructor will now throw
8+
await expect(() => stub.fetch("http://example.com/")).rejects.toThrow();
9+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
declare module "cloudflare:test" {
2+
// Controls the type of `import("cloudflare:test").env`
3+
interface ProvidedEnv extends Env {
4+
MY_CONTAINER: DurableObjectNamespace<MyContainer>;
5+
}
6+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../tsconfig.workerd-test.json",
3+
"include": ["./**/*.ts"]
4+
}

0 commit comments

Comments
 (0)