Skip to content

Commit 89cb3ee

Browse files
add e2e tests/ github actions added
1 parent ee3ab98 commit 89cb3ee

31 files changed

+2541
-2223
lines changed

.github/workflows/npm-publish.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,18 @@ on:
55
types: [published]
66

77
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v3
12+
- uses: actions/setup-node@v3
13+
with:
14+
node-version: 21
15+
cache: 'npm'
16+
- run: npm test
17+
818
publish-npm:
19+
needs: [test]
920
runs-on: ubuntu-latest
1021
steps:
1122
- uses: actions/checkout@v3

.github/workflows/npm-test.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Test hook
2+
3+
on:
4+
push:
5+
branches: [ "master" ]
6+
pull_request:
7+
branches: [ "master" ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v3
14+
- uses: actions/setup-node@v3
15+
with:
16+
node-version: 21
17+
- run: npm test

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"keywords": ["web", "web-server", "http", "http-server", "oop"],
77
"scripts": {
88
"prepareToPublish": "node dist/prepareToPublish.js",
9-
"test": "node --test --experimental-test-coverage --test-reporter=spec"
9+
"test": "node --test"
1010
},
1111
"author": {
1212
"name": "volatilization",

src/js/server/ClusteredServer.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@ module.exports = class ClusteredServer {
1616
}
1717

1818
} else {
19-
await this.#origin.start();
19+
return new ClusteredServer(await this.#origin.start(), this.#cluster, this.#options);
2020
}
21-
22-
return this;
2321
}
2422

2523
options() {

src/js/server/LoggedServer.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,22 @@ module.exports = class LoggedServer {
88
}
99

1010
async start() {
11-
await this.#origin.start();
11+
const server = await this.#origin.start();
1212

1313
this.#logger.debug(`HttpServer is running at port: ${this.#origin.options().port}`);
1414

15-
return this;
15+
return new LoggedServer(server, this.#logger);
1616
}
1717

18+
async stop() {
19+
const server = await this.#origin.stop();
20+
21+
this.#logger.debug(`HttpServer at port: ${this.#origin.options().port} is stopped`);
22+
23+
return new LoggedServer(server, this.#logger);
24+
}
25+
26+
1827
options() {
1928
return this.#origin.options();
2029
}

src/js/server/Server.js

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,74 @@ module.exports = class HttpServer {
44
#response;
55
#endpoints;
66
#options;
7+
#server;
78

8-
constructor(http, request, response, endpoints, options) {
9+
constructor(http, request, response, endpoints, options, server) {
910
this.#http = http;
1011
this.#request = request;
1112
this.#response = response;
1213
this.#endpoints = endpoints;
1314
this.#options = options;
15+
this.#server = server;
1416
}
1517

1618
start() {
17-
return new Promise(resolve => {
18-
this.#http
19-
.createServer(async (requestStream, responseStream) => {
20-
try {
21-
return await (this.#response
22-
.copy(responseStream, await this.#endpoints
23-
.handle(await (this.#request
24-
.copy(requestStream))
25-
.flush())))
26-
.flush();
19+
const server = this.#http.createServer(async (requestStream, responseStream) => {
20+
try {
21+
return await (this.#response
22+
.copy(responseStream, await this.#endpoints
23+
.handle(await (this.#request
24+
.copy(requestStream))
25+
.flush())))
26+
.flush();
27+
28+
} catch (e) {
29+
if (e.cause === 'INVALID_REQUEST') {
30+
return await (this.#response
31+
.copy(responseStream, {
32+
statusCode: 400,
33+
body: e.message
34+
}))
35+
.flush();
36+
}
37+
38+
return await (this.#response
39+
.copy(responseStream, {
40+
statusCode: 500,
41+
body: 'Unexpected server error.'
42+
}))
43+
.flush();
44+
}
45+
});
2746

28-
} catch (e) {
29-
if (e.cause === 'INVALID_REQUEST') {
30-
return this.#response
31-
.copy(responseStream, {
32-
statusCode: 400,
33-
body: e.message
34-
})
35-
.flush();
36-
}
47+
return new Promise(resolve => {
48+
server.listen(
49+
this.#options,
50+
() => resolve(new HttpServer(
51+
this.#http,
52+
this.#request,
53+
this.#response,
54+
this.#endpoints,
55+
this.#options,
56+
server))
57+
);
58+
});
59+
}
3760

38-
return this.#response
39-
.copy(responseStream, {
40-
statusCode: 500,
41-
body: 'Unexpected server error.'
42-
})
43-
.flush();
44-
}
45-
})
46-
.listen(
47-
{port: this.#options.port},
48-
() => resolve(this)
49-
);
61+
stop() {
62+
return new Promise(resolve => {
63+
this.#server.close(
64+
() => resolve(new HttpServer(
65+
this.#http,
66+
this.#request,
67+
this.#response,
68+
this.#endpoints,
69+
this.#options))
70+
);
5071
});
5172
}
5273

5374
options() {
5475
return this.#options;
5576
}
56-
}
77+
};

src/test/e2e/json.server.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/* node:coverage disable */
2+
3+
const {
4+
Server,
5+
InputRequest,
6+
JsonInputRequest,
7+
OutputResponse,
8+
JsonOutputResponse,
9+
Endpoints
10+
} = require('../../js').server;
11+
const {describe, it, before, after} = require('node:test');
12+
const assert = require('node:assert');
13+
const http = require('node:http');
14+
15+
const testBody = {value: 'value', queryValue: 'otherQueryValue'};
16+
17+
const serverConfig = new Server(
18+
http,
19+
new JsonInputRequest(new InputRequest()),
20+
new JsonOutputResponse(new OutputResponse()),
21+
new Endpoints([
22+
{
23+
route() {
24+
return {
25+
method: 'GET',
26+
path: '/test'
27+
};
28+
},
29+
handle(request) {
30+
return {
31+
statusCode: 200,
32+
body: JSON.stringify({queryKey: request.query().get('queryKey')})
33+
};
34+
}
35+
},
36+
{
37+
route() {
38+
return {
39+
method: 'POST',
40+
path: '/test'
41+
};
42+
},
43+
handle(request) {
44+
return {
45+
statusCode: 201,
46+
body: JSON.stringify(request.body())
47+
};
48+
}
49+
},
50+
]),
51+
{port: 8081}
52+
);
53+
54+
55+
describe('JSON server', async () => {
56+
let serverInstance;
57+
before(async () => {
58+
serverInstance = await serverConfig.start();
59+
});
60+
after(async () => await serverInstance.stop());
61+
62+
await it('should be started', async () => {
63+
await assert.doesNotReject(() => fetch('http://localhost:8081'),
64+
{message: 'fetch failed'});
65+
});
66+
67+
await it('should return 501', async () => {
68+
const response = await fetch('http://localhost:8081/test0',
69+
{method: 'GET'});
70+
const body = await (await response.blob()).text();
71+
72+
assert.strictEqual(response.status, 501);
73+
assert.strictEqual(response.headers.get('Content-Type').includes('application/json'), true);
74+
assert.strictEqual(body, 'There are no handler for request.');
75+
});
76+
77+
await it('should return 200 and queryValue in body', async () => {
78+
const response = await fetch('http://localhost:8081/test?queryKey=queryValue',
79+
{method: 'GET'});
80+
const body = await response.json();
81+
82+
assert.strictEqual(response.status, 200);
83+
assert.strictEqual(response.headers.get('Content-Type').includes('application/json'), true);
84+
assert.deepStrictEqual(body, {queryKey: 'queryValue'});
85+
});
86+
87+
await it('should return 400, cause content-type is not application/json', async () => {
88+
const response = await fetch('http://localhost:8081/test',
89+
{
90+
method: 'POST',
91+
headers: {'content-type': 'application/notJson'}
92+
});
93+
const body = await (await response.blob()).text();
94+
95+
assert.strictEqual(response.status, 400);
96+
assert.strictEqual(response.headers.get('Content-Type').includes('application/json'), true);
97+
assert.strictEqual(body, 'Wrong content-type. Only application/json accepted.');
98+
});
99+
100+
await it('should return 400, cause content-type is not set', async () => {
101+
const response = await fetch('http://localhost:8081/test',
102+
{method: 'POST'});
103+
const body = await (await response.blob()).text();
104+
105+
assert.strictEqual(response.status, 400);
106+
assert.strictEqual(response.headers.get('Content-Type').includes('application/json'), true);
107+
assert.strictEqual(body, 'Wrong content-type. Only application/json accepted.');
108+
});
109+
110+
await it('should return 201 and test body', async () => {
111+
const response = await fetch('http://localhost:8081/test?queryKey=queryValue',
112+
{
113+
method: 'POST',
114+
body: JSON.stringify(testBody),
115+
headers: {'content-type': 'application/json'}
116+
});
117+
const body = await response.json();
118+
119+
assert.strictEqual(response.status, 201);
120+
assert.strictEqual(response.headers.get('Content-Type').includes('application/json'), true);
121+
assert.deepStrictEqual(body, testBody);
122+
});
123+
});

0 commit comments

Comments
 (0)