Skip to content

Commit f25c48e

Browse files
authored
Add tests for convertRequest (NodeJS sSDK runtime). (#711)
Co-authored-by: Alberto Pose <[email protected]>
1 parent d39d36e commit f25c48e

File tree

2 files changed

+138
-2
lines changed

2 files changed

+138
-2
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { mkdtemp } from "fs/promises";
7+
import { createServer, IncomingMessage, request, RequestOptions, Server, ServerResponse } from "http";
8+
import * as os from "os";
9+
import * as path from "path";
10+
import { Readable } from "stream";
11+
12+
import { convertRequest } from "./node";
13+
14+
let socketPath: string;
15+
let promiseResolve: ([req, res]: [IncomingMessage, ServerResponse]) => void;
16+
17+
let server: Server;
18+
beforeAll(async () => {
19+
server = createServer(function (req, res) {
20+
promiseResolve([req, res]);
21+
resToEnd = res;
22+
});
23+
// Create a temporary named pipe where to run the server and obtain a request
24+
socketPath = path.join(await mkdtemp(path.join(os.tmpdir(), "named-pipe-for-test-")), "server");
25+
// TODO Add support to Windows by using '\\\\?\\pipe'
26+
// See: https://nodejs.org/api/net.html#identifying-paths-for-ipc-connections
27+
server.listen(socketPath);
28+
});
29+
30+
let resToEnd: ServerResponse;
31+
32+
function getRequest(options: RequestOptions & { body?: String }): Promise<[IncomingMessage, ServerResponse]> {
33+
return new Promise((resolve, _) => {
34+
promiseResolve = resolve;
35+
request({
36+
socketPath,
37+
...options,
38+
}).end(Buffer.from(options.body || []));
39+
});
40+
}
41+
42+
afterAll(() => {
43+
server?.close();
44+
});
45+
46+
async function streamToString(stream: Readable) {
47+
const chunks = [];
48+
49+
for await (const chunk of stream) {
50+
chunks.push(Buffer.from(chunk));
51+
}
52+
53+
return Buffer.concat(chunks).toString("utf-8");
54+
}
55+
56+
describe("convertRequest", () => {
57+
afterEach(async () => {
58+
resToEnd?.end();
59+
});
60+
it("converts a simple GET / correctly", async () => {
61+
const [req, _] = await getRequest({
62+
host: "example.com",
63+
path: "/",
64+
});
65+
66+
const convertedReq = convertRequest(req);
67+
expect(convertedReq.hostname).toEqual("example.com");
68+
expect(convertedReq.method).toEqual("GET");
69+
expect(convertedReq.path).toEqual("/");
70+
expect(convertedReq.protocol).toEqual("http:");
71+
expect(convertedReq.query).toEqual({});
72+
expect(convertedReq.headers).toEqual({
73+
connection: "close",
74+
host: "example.com",
75+
});
76+
expect(await streamToString(convertedReq.body)).toEqual("");
77+
});
78+
it("converts a POST with query string correctly", async () => {
79+
const [req, _] = await getRequest({
80+
method: "POST",
81+
host: "example.com",
82+
path: "/some/endpoint?q=hello&a=world",
83+
body: "hello",
84+
});
85+
86+
const convertedReq = convertRequest(req);
87+
expect(convertedReq.hostname).toEqual("example.com");
88+
expect(convertedReq.method).toEqual("POST");
89+
expect(convertedReq.path).toEqual("/some/endpoint");
90+
expect(convertedReq.protocol).toEqual("http:");
91+
expect(convertedReq.query).toEqual({
92+
q: "hello",
93+
a: "world",
94+
});
95+
expect(convertedReq.headers).toEqual({
96+
connection: "close",
97+
host: "example.com",
98+
"content-length": "5",
99+
});
100+
expect(await streamToString(convertedReq.body)).toEqual("hello");
101+
});
102+
it("converts OPTIONS CORS requests", async () => {
103+
const [req, _] = await getRequest({
104+
method: "OPTIONS",
105+
host: "example.com",
106+
path: "/some/resource",
107+
headers: {
108+
"Access-Control-Request-Method": "DELETE",
109+
"Access-Control-Request-Headers": "origin, x-requested-with",
110+
Origin: "https://example.com",
111+
},
112+
});
113+
const convertedReq = convertRequest(req);
114+
expect(convertedReq.hostname).toEqual("example.com");
115+
expect(convertedReq.method).toEqual("OPTIONS");
116+
expect(convertedReq.path).toEqual("/some/resource");
117+
expect(convertedReq.protocol).toEqual("http:");
118+
expect(convertedReq.query).toEqual({});
119+
expect(convertedReq.headers).toEqual({
120+
"access-control-request-headers": "origin, x-requested-with",
121+
"access-control-request-method": "DELETE",
122+
origin: "https://example.com",
123+
connection: "close",
124+
host: "example.com",
125+
});
126+
expect(await streamToString(convertedReq.body)).toEqual("");
127+
});
128+
});
129+
130+
// TODO Implement writeResponse tests
131+
// describe("writeResponse", () => {
132+
// it("converts a simple GET / correctly", async () => {});
133+
// });

smithy-typescript-ssdk-libs/server-node/src/node.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ function convertQueryString(qs: URLSearchParams): QueryParameterBag {
1919

2020
export function convertRequest(req: IncomingMessage): HttpRequest {
2121
const url = new URL(req.url || "", `http://${req.headers.host}`);
22+
2223
return new HttpRequest({
24+
hostname: url.hostname,
2325
method: req.method,
24-
headers: convertHeaders(req.headers),
25-
query: convertQueryString(url.searchParams),
2626
path: url.pathname,
27+
protocol: url.protocol,
28+
query: convertQueryString(url.searchParams),
29+
headers: convertHeaders(req.headers),
2730
body: req,
2831
});
2932
}

0 commit comments

Comments
 (0)