Skip to content

Commit

Permalink
Better tests for CF Workers, Bun and Azure Functions & Update Azure e…
Browse files Browse the repository at this point in the history
…xample & Fix `event.request` ref issue in CFW (#1101)
  • Loading branch information
ardatan authored Jan 23, 2024
1 parent a5fd965 commit bf0c9ab
Show file tree
Hide file tree
Showing 19 changed files with 132 additions and 120 deletions.
5 changes: 5 additions & 0 deletions .changeset/cool-pillows-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@whatwg-node/server": patch
---

Access the property in the given server context object correctly
3 changes: 0 additions & 3 deletions .vscode/settings.json

This file was deleted.

10 changes: 10 additions & 0 deletions e2e/azure-function/.funcignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.js.map
*.ts
.git*
.vscode
local.settings.json
test
getting_started.md
node_modules/@types/
node_modules/azure-functions-core-tools/
node_modules/typescript/
3 changes: 3 additions & 0 deletions e2e/azure-function/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["ms-azuretools.vscode-azurefunctions"]
}
15 changes: 15 additions & 0 deletions e2e/azure-function/host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}
12 changes: 8 additions & 4 deletions e2e/azure-function/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
"name": "@e2e/azure-function",
"version": "0.0.0",
"private": true,
"main": "dist/functions/index.js",
"scripts": {
"build": "node scripts/bundle.js",
"e2e": "ts-node -r tsconfig-paths/register scripts/e2e.ts"
"build": "rm -rf dist/ && node scripts/bundle.js",
"e2e": "ts-node -r tsconfig-paths/register scripts/e2e.ts",
"prestart": "npm run build",
"start": "func start"
},
"dependencies": {
"@azure/functions": "4.1.0",
"@e2e/shared-scripts": "0.0.0"
"@azure/functions": "^4.1.0",
"@e2e/shared-scripts": "0.0.0",
"tslib": "^2.6.2"
},
"devDependencies": {
"@pulumi/azure-native": "2.26.0",
Expand Down
56 changes: 5 additions & 51 deletions e2e/azure-function/scripts/bundle.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,22 @@
const { build } = require('esbuild');
const { writeFileSync } = require('fs');
const { join } = require('path');
const packageJson = require('../package.json');

const projectRoot = join(__dirname, '..');

async function main() {
await build({
entryPoints: [join(projectRoot, './src/index.ts')],
outfile: join(projectRoot, 'dist/WhatWGNode/index.js'),
entryPoints: [join(projectRoot, './src/functions/index.ts')],
outfile: join(projectRoot, 'dist/functions/index.js'),
format: 'cjs',
minify: false,
bundle: true,
platform: 'node',
target: 'node14',
target: 'node20',
external: ['@azure/functions-core'],
});

writeFileSync(
join(projectRoot, './dist/package.json'),
JSON.stringify({
name: 'whatwg-node-test-function',
version: '0.0.1',
}),
);

writeFileSync(
join(projectRoot, './dist/host.json'),
JSON.stringify({
version: '2.0',
logging: {
applicationInsights: {
samplingSettings: {
isEnabled: true,
excludedTypes: 'Request',
},
},
},
extensionBundle: {
id: 'Microsoft.Azure.Functions.ExtensionBundle',
version: '[2.*, 3.0.0)',
},
}),
);

writeFileSync(
join(projectRoot, './dist/WhatWGNode/function.json'),
JSON.stringify({
bindings: [
{
authLevel: 'anonymous',
type: 'httpTrigger',
direction: 'in',
name: 'req',
methods: ['get', 'post'],
route: '{*segments}',
},
{
type: 'http',
direction: 'out',
name: 'res',
},
],
}),
);

console.info(`Azure Function build done!`);
}

Expand Down
4 changes: 2 additions & 2 deletions e2e/azure-function/scripts/createAzureFunctionDeployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export function createAzureFunctionDeployment(): DeploymentConfiguration<{
resourceGroupName: resourceGroup.name,
accountName: storageAccount.name,
containerName: codeContainer.name,
source: new pulumi.asset.FileArchive(join(__dirname, '..', 'dist')),
source: new pulumi.asset.FileArchive(join(__dirname, '..')),
},
{
deleteBeforeReplace: true,
Expand Down Expand Up @@ -156,7 +156,7 @@ export function createAzureFunctionDeployment(): DeploymentConfiguration<{
],
http20Enabled: true,
httpLoggingEnabled: true,
linuxFxVersion: 'node|16',
linuxFxVersion: 'node|20',
},
},
{
Expand Down
16 changes: 16 additions & 0 deletions e2e/azure-function/src/functions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { app, InvocationContext } from '@azure/functions';
import { createTestServerAdapter } from '@e2e/shared-server';

const handler = createTestServerAdapter<InvocationContext>();

declare global {
interface ReadableStream {
[Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>;
}
}

app.http('whatwgnode', {
methods: ['GET', 'POST'],
authLevel: 'anonymous',
handler,
});
36 changes: 0 additions & 36 deletions e2e/azure-function/src/index.ts

This file was deleted.

4 changes: 2 additions & 2 deletions e2e/azure-function/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
"target": "es2016",
"module": "commonjs",
"moduleResolution": "node",
"lib": ["dom"],
"lib": ["dom", "esnext"],
"declaration": true,
"sourceMap": true,
"resolveJsonModule": true
},
"include": ["./**/*.ts"]
"files": ["src/functions/index.ts", "scripts/e2e.ts"]
}
27 changes: 24 additions & 3 deletions e2e/bun/tests/bun.spec.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,50 @@
import { createServer } from 'node:http';
import { describe, expect, it } from 'bun:test';
import { afterEach, describe, expect, it } from 'bun:test';
import { createServerAdapter } from '@whatwg-node/server';
import { assertDeployedEndpoint } from '../../shared-scripts/src/index';
import { createTestServerAdapter } from '../../shared-server/src/index';

describe('Bun', () => {
let stopServer: () => void;
afterEach(() => stopServer?.());
it('works', async () => {
const server = Bun.serve({
fetch: createTestServerAdapter(),
port: 3000,
});
stopServer = () => server.stop(true);
try {
await assertDeployedEndpoint(`http://localhost:3000/graphql`);
} catch (e) {
expect(e).toBeUndefined();
}
server.stop(true);
});
it('should have unique contexts for each request', async () => {
const contexts = new Set();
const adapter = createServerAdapter((_, ctx) => {
contexts.add(ctx);
return new Response(null, {
status: 204,
});
});
const server = Bun.serve({
fetch: adapter,
port: 3000,
});
stopServer = () => server.stop(true);
for (let i = 0; i < 10; i++) {
await fetch(`http://localhost:3000/graphql`);
}
expect(contexts.size).toBe(10);
});
it('works with Node compat mode', async () => {
const server = createServer(createTestServerAdapter());
stopServer = () => server.close();
await new Promise<void>(resolve => server.listen(3000, resolve));
try {
await assertDeployedEndpoint(`http://localhost:3000/graphql`);
} catch (e) {
expect(e).toBeUndefined();
}
return new Promise(resolve => server.close(resolve));
});
});
7 changes: 3 additions & 4 deletions e2e/cloudflare-modules/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { createTestServerAdapter } from '@e2e/shared-server';

const app = createTestServerAdapter();

export default {
async fetch(request: Request, env: Record<string, string>, ctx: any): Promise<Response> {
const app = createTestServerAdapter();
return app.handle(request, env, ctx);
},
fetch: app,
};
2 changes: 1 addition & 1 deletion e2e/cloudflare-modules/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"sourceMap": true,
"resolveJsonModule": true
},
"include": ["./**/*.ts"]
"files": ["src/index.ts", "scripts/e2e.ts"]
}
12 changes: 10 additions & 2 deletions e2e/cloudflare-workers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { createTestServerAdapter } from '@e2e/shared-server';
import { createServerAdapter, type FetchEvent } from '@whatwg-node/server';

const app = createTestServerAdapter();
const app = createServerAdapter<FetchEvent>(async (request, ctx) =>
Response.json({
url: request.url,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
reqText: request.method === 'POST' ? await request.text() : '',
reqExistsInCtx: ctx.request === request,
}),
);

self.addEventListener('fetch', app);
2 changes: 1 addition & 1 deletion e2e/cloudflare-workers/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"sourceMap": true,
"resolveJsonModule": true
},
"include": ["./**/*.ts"]
"files": ["src/index.ts", "scripts/e2e.ts"]
}
14 changes: 14 additions & 0 deletions packages/server/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface ServerAdapterObject<TServerContext> extends EventListenerObject
handleUWS: UWSHandler;

handle(req: NodeRequest, res: NodeResponse, ...ctx: Partial<TServerContext>[]): Promise<void>;
handle(requestLike: RequestLike, ...ctx: Partial<TServerContext>[]): Promise<Response> | Response;
handle(request: Request, ...ctx: Partial<TServerContext>[]): Promise<Response> | Response;
handle(fetchEvent: FetchEvent & Partial<TServerContext>, ...ctx: Partial<TServerContext>[]): void;
handle(res: UWSResponse, req: UWSRequest, ...ctx: Partial<TServerContext>[]): Promise<void>;
Expand All @@ -70,6 +71,19 @@ export interface ServerAdapterObject<TServerContext> extends EventListenerObject
): Promise<Response> | Response;
}

export interface RequestLike {
url: string;
method: string;
headers: HeadersLike;
body?: AsyncIterable<Uint8Array> | null;
}

export interface HeadersLike extends Iterable<[string, string]> {
get(name: string): string | null | undefined;
set(name: string, value: string): void;
has(name: string): boolean;
}

export type ServerAdapter<
TServerContext,
TBaseObject extends ServerAdapterBaseObject<TServerContext>,
Expand Down
21 changes: 12 additions & 9 deletions packages/server/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,24 +462,27 @@ export function isolateObject<TIsolatedObject extends object>(
const extraProps: Partial<TIsolatedObject> = {};
const deletedProps = new Set<string | symbol>();
return new Proxy(originalCtx, {
get(originalCtx, prop, receiver) {
get(originalCtx, prop) {
if (waitUntilPromises != null && prop === 'waitUntil') {
return function waitUntil(promise: Promise<unknown>) {
waitUntilPromises.push(promise.catch(err => console.error(err)));
};
}
if (prop in extraProps) {
return Reflect.get(extraProps, prop, receiver);
const extraPropVal = (extraProps as any)[prop];
if (extraPropVal != null) {
if (typeof extraPropVal === 'function') {
return extraPropVal.bind(extraProps);
}
return extraPropVal;
}
if (deletedProps.has(prop)) {
return undefined;
}
if (prop in originalCtx) {
return Reflect.get(originalCtx, prop, receiver);
}
return (originalCtx as any)[prop];
},
set(_originalCtx, prop, value, receiver) {
return Reflect.set(extraProps, prop, value, receiver);
set(_originalCtx, prop, value) {
(extraProps as any)[prop] = value;
return true;
},
has(originalCtx, prop) {
if (waitUntilPromises != null && prop === 'waitUntil') {
Expand All @@ -491,7 +494,7 @@ export function isolateObject<TIsolatedObject extends object>(
if (prop in extraProps) {
return true;
}
return Reflect.has(originalCtx, prop);
return prop in originalCtx;
},
defineProperty(_originalCtx, prop, descriptor) {
return Reflect.defineProperty(extraProps, prop, descriptor);
Expand Down
Loading

0 comments on commit bf0c9ab

Please sign in to comment.