Skip to content

Commit

Permalink
test: don't mock client under test (#442)
Browse files Browse the repository at this point in the history
* test: don't mock client under test
* test: check that the http client handles multiple requests
  • Loading branch information
wkillerud authored Nov 12, 2024
1 parent 0df7f44 commit aab67bc
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 47 deletions.
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,30 @@
"undici": "6.20.1"
},
"devDependencies": {
"@podium/eslint-config": "1.0.0",
"@podium/semantic-release-config": "2.0.0",
"@podium/test-utils": "3.1.0-next.5",
"@podium/typescript-config": "1.0.0",
"@semantic-release/changelog": "6.0.3",
"@semantic-release/git": "10.0.1",
"@semantic-release/github": "10.0.6",
"@semantic-release/npm": "12.0.1",
"@semantic-release/release-notes-generator": "13.0.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.1.3",
"express": "4.21.1",
"@podium/eslint-config": "1.0.0",
"@podium/semantic-release-config": "2.0.0",
"@podium/typescript-config": "1.0.0",
"@sinonjs/fake-timers": "11.3.1",
"@types/readable-stream": "4.0.18",
"benchmark": "2.1.4",
"eslint": "9.6.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.1.3",
"express": "4.21.1",
"get-stream": "9.0.1",
"http-proxy": "1.18.1",
"is-stream": "4.0.1",
"npm-run-all2": "6.2.3",
"prettier": "3.3.2",
"semantic-release": "24.1.2",
"tap": "18.7.2",
"typescript": "5.6.3"
"typescript": "5.6.3",
"why-is-node-running": "3.2.1"
}
}
32 changes: 32 additions & 0 deletions tests/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,35 @@ tap.test(
await Promise.all([serverA.close(), serverB.close()]);
},
);

/**
* timeouts
*/

tap.test('client.fetch() - should handle unreachable podlet', async (t) => {
const server = new PodletServer({
name: 'aa',
});
const podlet = await server.listen();

const client = new Client({ name: 'podiumClient' });

const up = client.register(podlet.options);
const down = client.register({
name: 'consents',
uri: 'https://localhost:8128/manifest.json',
});

await client.refreshManifests();

const incoming = new HttpIncoming({ headers });

try {
await Promise.all([up.fetch(incoming), down.fetch(incoming)]);
} catch (e) {
t.fail(e);
}

await server.close();
t.pass();
});
130 changes: 90 additions & 40 deletions tests/http.test.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,94 @@
import { test } from 'node:test';
import { rejects } from 'node:assert';
import tap from 'tap';
import express from 'express';
import HTTP from '../lib/http.js';

test('should abort the request if it takes longer than the timeout', async () => {
// Mock the undici.Client's request method
const mockRequestFn = async (url, { signal }) => {
return new Promise((resolve, reject) => {
// Simulate a delay longer than the timeout
tap.test(
'should abort the request if it takes longer than the timeout',
(t) => {
const app = express();
app.get('/', (req, res) => {
setTimeout(() => {
if (signal.aborted) {
const abortError = new Error(
'Request aborted due to timeout',
);
abortError.name = 'AbortError';
reject(abortError);
} else {
resolve({
statusCode: 200,
headers: {},
body: 'OK',
});
}
}, 2000); // 2 seconds delay
res.send('OK');
}, 2000); // longer than the default request timeout
});
};

// @ts-ignore
const http = new HTTP(mockRequestFn);
const url = 'https://example.com/test';
const options = {
method: /** @type {'GET'} */ ('GET'),
timeout: 1000, // 1 second timeout
};

// Assert that the request is rejected with an AbortError
await rejects(
http.request(url, options),
(/** @type {Error} */ err) =>
err.name === 'AbortError' &&
err.message === 'Request aborted due to timeout',
'Expected request to be aborted due to timeout',
);
});

const server = app.listen(
{
host: '0.0.0.0',
port: 0,
},
() => {
const url = `http://${server.address().address}:${server.address().port}/`;
const options = {
method: /** @type {'GET'} */ ('GET'),
};

t.rejects(
async () => {
const http = new HTTP();
await http.request(url, options);
},
{
name: 'AbortError',
message: 'This operation was aborted',
},
).finally(() => {
server.close();
t.end();
});
},
);
},
);

tap.test(
'should not timeout if there are multiple requests in flight and one of them fails',
(t) => {
const app = express();

app.get('/fast', (req, res) => {
res.send('OK');
});

app.get('/slow', (req, res) => {
setTimeout(() => {
res.send('OK');
}, 2000); // longer than the default request timeout
});

const server = app.listen(
{
host: '0.0.0.0',
port: 0,
},
() => {
const options = {
method: /** @type {'GET'} */ ('GET'),
};

t.rejects(
async () => {
const http = new HTTP();
await Promise.all([
http.request(
`http://${server.address().address}:${server.address().port}/slow`,
options,
),
http.request(
`http://${server.address().address}:${server.address().port}/fast`,
options,
),
]);
},
{
name: 'AbortError',
message: 'This operation was aborted',
},
).finally(() => {
server.close();
t.end();
});
},
);
},
);
40 changes: 40 additions & 0 deletions tests/resolver.content.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -682,3 +682,43 @@ tap.test(
t.end();
},
);

tap.test('resolver.content() - should handle unreachable podlet', async (t) => {
const server = new PodletServer({
name: 'aa',
});
const podlet = await server.listen();

const up = new HttpOutgoing(
{
uri: podlet.manifest,
name: 'up',
timeout: 1000,
maxAge: Infinity,
},
{},
new HttpIncoming({ headers }),
);
const down = new HttpOutgoing(
{
uri: 'https://localhost:8128/manifest.json',
name: 'down',
timeout: 1000,
maxAge: Infinity,
},
{},
new HttpIncoming({ headers }),
);

try {
await Promise.all([
new Content().resolve(up),
new Content().resolve(down),
]);
} catch (e) {
t.fail(e);
}

await server.close();
t.pass();
});
43 changes: 43 additions & 0 deletions tests/resolver.fallback.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,46 @@ tap.test(
t.end();
},
);

tap.test(
'resolver.fallback() - should handle unreachable podlet',
async (t) => {
const server = new PodletServer({
name: 'aa',
});
const podlet = await server.listen();

const up = new HttpOutgoing(
{
uri: podlet.manifest,
name: 'up',
timeout: 1000,
maxAge: Infinity,
},
{},
new HttpIncoming({ headers }),
);
const down = new HttpOutgoing(
{
uri: 'https://localhost:8128/manifest.json',
name: 'down',
timeout: 1000,
maxAge: Infinity,
},
{},
new HttpIncoming({ headers }),
);

try {
await Promise.all([
new Fallback().resolve(up),
new Fallback().resolve(down),
]);
} catch (e) {
t.fail(e);
}

await server.close();
t.pass();
},
);
43 changes: 43 additions & 0 deletions tests/resolver.manifest.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -529,3 +529,46 @@ tap.test(
t.end();
},
);

tap.test(
'resolver.manifest() - should handle unreachable podlet',
async (t) => {
const server = new PodletServer({
name: 'aa',
});
const podlet = await server.listen();

const up = new HttpOutgoing(
{
uri: podlet.manifest,
name: 'up',
timeout: 1000,
maxAge: Infinity,
},
{},
new HttpIncoming({ headers }),
);
const down = new HttpOutgoing(
{
uri: 'https://localhost:8128/manifest.json',
name: 'down',
timeout: 1000,
maxAge: Infinity,
},
{},
new HttpIncoming({ headers }),
);

try {
await Promise.all([
new Manifest().resolve(up),
new Manifest().resolve(down),
]);
} catch (e) {
t.fail(e);
}

await server.close();
t.pass();
},
);

0 comments on commit aab67bc

Please sign in to comment.