Skip to content

Commit 20937c8

Browse files
committed
chore: add tests
Signed-off-by: dhmlau <dhmlau@ca.ibm.com>
1 parent 8568a4a commit 20937c8

8 files changed

Lines changed: 2835 additions & 0 deletions

File tree

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
// Copyright IBM Corp. and LoopBack contributors 2026. All Rights Reserved.
2+
// Node module: @loopback/http-server
3+
// This file is licensed under the MIT License.
4+
// License text available at https://opensource.org/licenses/MIT
5+
6+
import {expect} from '@loopback/testlab';
7+
import {IncomingMessage, ServerResponse} from 'http';
8+
import {HttpOptions, HttpServer} from '../../';
9+
10+
describe('HttpServer (unit)', () => {
11+
let server: HttpServer | undefined;
12+
13+
afterEach(async () => {
14+
if (server) {
15+
await server.stop();
16+
server = undefined;
17+
}
18+
});
19+
20+
describe('constructor', () => {
21+
it('creates HTTP server with default options', () => {
22+
server = new HttpServer(dummyRequestHandler);
23+
expect(server).to.be.instanceOf(HttpServer);
24+
expect(server.protocol).to.equal('http');
25+
expect(server.serverOptions.port).to.equal(0);
26+
});
27+
28+
it('creates HTTP server with custom port', () => {
29+
server = new HttpServer(dummyRequestHandler, {port: 3000});
30+
expect(server.serverOptions.port).to.equal(3000);
31+
});
32+
33+
it('creates HTTP server with custom host', () => {
34+
server = new HttpServer(dummyRequestHandler, {host: 'localhost'});
35+
expect(server.serverOptions.host).to.equal('localhost');
36+
});
37+
38+
it('defaults to http protocol when not specified', () => {
39+
server = new HttpServer(dummyRequestHandler, {});
40+
expect(server.protocol).to.equal('http');
41+
});
42+
43+
it('applies keepAliveTimeout option', () => {
44+
server = new HttpServer(dummyRequestHandler, {
45+
keepAliveTimeout: 5000,
46+
});
47+
expect(server.server.keepAliveTimeout).to.equal(5000);
48+
});
49+
50+
it('applies headersTimeout option', () => {
51+
server = new HttpServer(dummyRequestHandler, {
52+
headersTimeout: 6000,
53+
});
54+
expect(server.server.headersTimeout).to.equal(6000);
55+
});
56+
57+
it('applies maxConnections option', () => {
58+
server = new HttpServer(dummyRequestHandler, {
59+
maxConnections: 100,
60+
});
61+
expect(server.server.maxConnections).to.equal(100);
62+
});
63+
64+
it('applies maxHeadersCount option', () => {
65+
server = new HttpServer(dummyRequestHandler, {
66+
maxHeadersCount: 50,
67+
});
68+
expect(server.server.maxHeadersCount).to.equal(50);
69+
});
70+
71+
it('applies timeout option', () => {
72+
server = new HttpServer(dummyRequestHandler, {
73+
timeout: 10000,
74+
});
75+
expect(server.server.timeout).to.equal(10000);
76+
});
77+
78+
it('applies multiple server properties', () => {
79+
server = new HttpServer(dummyRequestHandler, {
80+
keepAliveTimeout: 1000,
81+
headersTimeout: 2000,
82+
maxConnections: 10,
83+
maxHeadersCount: 20,
84+
timeout: 3000,
85+
});
86+
expect(server.server.keepAliveTimeout).to.equal(1000);
87+
expect(server.server.headersTimeout).to.equal(2000);
88+
expect(server.server.maxConnections).to.equal(10);
89+
expect(server.server.maxHeadersCount).to.equal(20);
90+
expect(server.server.timeout).to.equal(3000);
91+
});
92+
93+
it('sets up graceful stop with gracePeriodForClose', () => {
94+
server = new HttpServer(dummyRequestHandler, {
95+
gracePeriodForClose: 5000,
96+
});
97+
expect(server.serverOptions.gracePeriodForClose).to.equal(5000);
98+
});
99+
100+
it('handles gracePeriodForClose set to 0', () => {
101+
server = new HttpServer(dummyRequestHandler, {
102+
gracePeriodForClose: 0,
103+
});
104+
expect(server.serverOptions.gracePeriodForClose).to.equal(0);
105+
});
106+
107+
it('does not set up stoppable when gracePeriodForClose is undefined', () => {
108+
server = new HttpServer(dummyRequestHandler, {});
109+
expect(server.serverOptions.gracePeriodForClose).to.be.undefined();
110+
});
111+
});
112+
113+
describe('properties before start', () => {
114+
it('returns protocol before start', () => {
115+
server = new HttpServer(dummyRequestHandler);
116+
expect(server.protocol).to.equal('http');
117+
});
118+
119+
it('returns port before start', () => {
120+
server = new HttpServer(dummyRequestHandler, {port: 3000});
121+
expect(server.port).to.equal(3000);
122+
});
123+
124+
it('returns host before start', () => {
125+
server = new HttpServer(dummyRequestHandler, {host: 'localhost'});
126+
expect(server.host).to.equal('localhost');
127+
});
128+
129+
it('returns undefined host when not specified', () => {
130+
server = new HttpServer(dummyRequestHandler);
131+
expect(server.host).to.be.undefined();
132+
});
133+
134+
it('returns listening as false before start', () => {
135+
server = new HttpServer(dummyRequestHandler);
136+
expect(server.listening).to.be.false();
137+
});
138+
139+
it('returns undefined address before start', () => {
140+
server = new HttpServer(dummyRequestHandler);
141+
expect(server.address).to.be.undefined();
142+
});
143+
144+
it('exposes server instance before start', () => {
145+
server = new HttpServer(dummyRequestHandler);
146+
expect(server.server).to.be.ok();
147+
});
148+
});
149+
150+
describe('properties after start', () => {
151+
it('returns listening as true after start', async () => {
152+
server = new HttpServer(dummyRequestHandler);
153+
await server.start();
154+
expect(server.listening).to.be.true();
155+
});
156+
157+
it('returns actual port after start', async () => {
158+
server = new HttpServer(dummyRequestHandler, {port: 0});
159+
await server.start();
160+
expect(server.port).to.be.greaterThan(0);
161+
});
162+
163+
it('returns address after start', async () => {
164+
server = new HttpServer(dummyRequestHandler);
165+
await server.start();
166+
expect(server.address).to.be.ok();
167+
});
168+
169+
it('returns url after start', async () => {
170+
server = new HttpServer(dummyRequestHandler);
171+
await server.start();
172+
expect(server.url).to.match(/^http:\/\//);
173+
});
174+
});
175+
176+
describe('properties after stop', () => {
177+
it('returns listening as false after stop', async () => {
178+
server = new HttpServer(dummyRequestHandler);
179+
await server.start();
180+
await server.stop();
181+
expect(server.listening).to.be.false();
182+
});
183+
184+
it('returns undefined address after stop', async () => {
185+
server = new HttpServer(dummyRequestHandler);
186+
await server.start();
187+
await server.stop();
188+
expect(server.address).to.be.undefined();
189+
});
190+
});
191+
192+
describe('start and stop', () => {
193+
it('can stop server before starting', async () => {
194+
server = new HttpServer(dummyRequestHandler);
195+
await server.stop();
196+
expect(server.listening).to.be.false();
197+
});
198+
199+
it('can start and stop server multiple times', async () => {
200+
server = new HttpServer(dummyRequestHandler);
201+
202+
await server.start();
203+
expect(server.listening).to.be.true();
204+
await server.stop();
205+
expect(server.listening).to.be.false();
206+
207+
await server.start();
208+
expect(server.listening).to.be.true();
209+
await server.stop();
210+
expect(server.listening).to.be.false();
211+
});
212+
213+
it('assigns different ports on each start', async () => {
214+
server = new HttpServer(dummyRequestHandler, {port: 0});
215+
216+
await server.start();
217+
const port1 = server.port;
218+
await server.stop();
219+
220+
await server.start();
221+
const port2 = server.port;
222+
await server.stop();
223+
224+
expect(port1).to.not.equal(port2);
225+
});
226+
});
227+
228+
describe('URL generation', () => {
229+
it('generates URL with IPv4 address', async () => {
230+
server = new HttpServer(dummyRequestHandler, {host: '127.0.0.1'});
231+
await server.start();
232+
expect(server.url).to.match(/^http:\/\/127\.0\.0\.1:\d+$/);
233+
});
234+
235+
it('converts 0.0.0.0 to 127.0.0.1 in URL', async () => {
236+
server = new HttpServer(dummyRequestHandler, {host: '0.0.0.0'});
237+
await server.start();
238+
expect(server.url).to.match(/^http:\/\/127\.0\.0\.1:\d+$/);
239+
});
240+
});
241+
242+
describe('server options', () => {
243+
it('merges provided options with defaults', () => {
244+
const options: HttpOptions = {
245+
port: 3000,
246+
host: 'localhost',
247+
};
248+
server = new HttpServer(dummyRequestHandler, options);
249+
expect(server.serverOptions).to.containDeep(options);
250+
});
251+
252+
it('preserves all provided options', () => {
253+
const options: HttpOptions = {
254+
port: 3000,
255+
host: 'localhost',
256+
keepAliveTimeout: 5000,
257+
headersTimeout: 6000,
258+
};
259+
server = new HttpServer(dummyRequestHandler, options);
260+
expect(server.serverOptions.port).to.equal(3000);
261+
expect(server.serverOptions.host).to.equal('localhost');
262+
expect(server.serverOptions.keepAliveTimeout).to.equal(5000);
263+
expect(server.serverOptions.headersTimeout).to.equal(6000);
264+
});
265+
});
266+
267+
describe('request handler', () => {
268+
it('uses provided request handler', () => {
269+
const customHandler = (req: IncomingMessage, res: ServerResponse) => {
270+
res.end();
271+
};
272+
server = new HttpServer(customHandler);
273+
expect(server).to.be.ok();
274+
});
275+
});
276+
277+
describe('edge cases', () => {
278+
it('handles undefined serverOptions', () => {
279+
server = new HttpServer(dummyRequestHandler, undefined);
280+
expect(server.protocol).to.equal('http');
281+
expect(server.serverOptions.port).to.equal(0);
282+
});
283+
284+
it('handles empty serverOptions object', () => {
285+
server = new HttpServer(dummyRequestHandler, {});
286+
expect(server.protocol).to.equal('http');
287+
expect(server.serverOptions.port).to.equal(0);
288+
});
289+
290+
it('handles port 0 for automatic port assignment', async () => {
291+
server = new HttpServer(dummyRequestHandler, {port: 0});
292+
await server.start();
293+
expect(server.port).to.be.greaterThan(0);
294+
});
295+
});
296+
297+
describe('protocol handling', () => {
298+
it('sets protocol to http by default', () => {
299+
server = new HttpServer(dummyRequestHandler);
300+
expect(server.protocol).to.equal('http');
301+
});
302+
303+
it('sets protocol to http when explicitly specified', () => {
304+
server = new HttpServer(dummyRequestHandler, {protocol: 'http'});
305+
expect(server.protocol).to.equal('http');
306+
});
307+
});
308+
309+
describe('server instance', () => {
310+
it('exposes underlying http.Server instance', () => {
311+
server = new HttpServer(dummyRequestHandler);
312+
expect(server.server).to.have.property('listen');
313+
expect(server.server).to.have.property('close');
314+
});
315+
});
316+
317+
function dummyRequestHandler(
318+
req: IncomingMessage,
319+
res: ServerResponse,
320+
): void {
321+
res.end('OK');
322+
}
323+
});
324+
325+
// Made with Bob

0 commit comments

Comments
 (0)