Skip to content

Commit

Permalink
test: heartbeat duration
Browse files Browse the repository at this point in the history
  • Loading branch information
nbbeeken committed Jan 24, 2024
1 parent 0ba5e40 commit 5f1947b
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 51 deletions.
2 changes: 1 addition & 1 deletion src/cmap/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ export async function makeSocket(options: MakeConnectionOptions): Promise<Stream
const connectEvent = useTLS ? 'secureConnect' : 'connect';
socket
.once(connectEvent, () => resolve(socket))
.once('error', error => reject(error))
.once('error', error => reject(connectionFailureError('error', error)))
.once('timeout', () => reject(connectionFailureError('timeout')))
.once('close', () => reject(connectionFailureError('close')));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ describe('Connection', function () {
it('should execute a command against a server', {
metadata: { requires: { apiVersion: false, topology: '!load-balanced' } },
test: async function () {
const connectOptions: Partial<ConnectionOptions> = {
const connectOptions: ConnectionOptions = {
connectionType: Connection,
...this.configuration.options,
metadata: makeClientMetadata({ driverInfo: {} })
};

let conn;
try {
conn = await promisify(connect)(connectOptions as any as ConnectionOptions);
conn = await connect(connectOptions);
const hello = await conn?.command(ns('admin.$cmd'), { [LEGACY_HELLO_COMMAND]: 1 });
expect(hello).to.have.property('ok', 1);
} finally {
Expand All @@ -55,7 +55,7 @@ describe('Connection', function () {
it('should emit command monitoring events', {
metadata: { requires: { apiVersion: false, topology: '!load-balanced' } },
test: async function () {
const connectOptions: Partial<ConnectionOptions> = {
const connectOptions: ConnectionOptions = {
connectionType: Connection,
...this.configuration.options,
monitorCommands: true,
Expand All @@ -64,7 +64,7 @@ describe('Connection', function () {

let conn;
try {
conn = await promisify(connect)(connectOptions as any as ConnectionOptions);
conn = await connect(connectOptions);

const events: any[] = [];
conn.on('commandStarted', event => events.push(event));
Expand Down
70 changes: 24 additions & 46 deletions test/unit/cmap/connect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('Connect Tests', function () {

afterEach(() => mock.cleanup());

it('should auth against a non-arbiter', function (done) {
it('should auth against a non-arbiter', async function () {
const whatHappened = {};

test.server.setMessageHandler(request => {
Expand All @@ -71,19 +71,13 @@ describe('Connect Tests', function () {
}
});

connect(test.connectOptions, err => {
try {
expect(whatHappened).to.have.property(LEGACY_HELLO_COMMAND, true);
expect(whatHappened).to.have.property('saslStart', true);
} catch (_err) {
err = _err;
}
await connect(test.connectOptions);

done(err);
});
expect(whatHappened).to.have.property(LEGACY_HELLO_COMMAND, true);
expect(whatHappened).to.have.property('saslStart', true);
});

it('should not auth against an arbiter', function (done) {
it('should not auth against an arbiter', async function () {
const whatHappened = {};
test.server.setMessageHandler(request => {
const doc = request.document;
Expand All @@ -102,16 +96,10 @@ describe('Connect Tests', function () {
}
});

connect(test.connectOptions, err => {
try {
expect(whatHappened).to.have.property(LEGACY_HELLO_COMMAND, true);
expect(whatHappened).to.not.have.property('saslStart');
} catch (_err) {
err = _err;
}
await connect(test.connectOptions);

done(err);
});
expect(whatHappened).to.have.property(LEGACY_HELLO_COMMAND, true);
expect(whatHappened).to.not.have.property('saslStart');
});
});

Expand All @@ -133,10 +121,7 @@ describe('Connect Tests', function () {
socketTimeoutMS: 15000
};

connection = await promisify<Connection>(callback =>
//@ts-expect-error: Callbacks do not have mutual exclusion for error/result existence
connect(connectOptions, callback)
)();
connection = await connect(connectOptions);
});

afterEach(async () => {
Expand Down Expand Up @@ -166,19 +151,13 @@ describe('Connect Tests', function () {
});
});

const error = await promisify<Connection>(callback =>
connect(
{
...connectOptions,
// Ensure these timeouts do not fire first
socketTimeoutMS: 5000,
connectTimeoutMS: 5000,
cancellationToken
},
//@ts-expect-error: Callbacks do not have mutual exclusion for error/result existence
callback
)
)().catch(error => error);
const error = await connect({
...connectOptions,
// Ensure these timeouts do not fire first
socketTimeoutMS: 5000,
connectTimeoutMS: 5000,
cancellationToken
}).catch(error => error);

expect(error, error.stack).to.match(/connection establishment was cancelled/);
});
Expand All @@ -189,21 +168,20 @@ describe('Connect Tests', function () {
// set no response handler for mock server, effectively black hole requests
server.setMessageHandler(() => null);

const error = await promisify<Connection>(callback =>
//@ts-expect-error: Callbacks do not have mutual exclusion for error/result existence
connect({ ...connectOptions, connectTimeoutMS: 5 }, callback)
)().catch(error => error);
const error = await connect({ ...connectOptions, connectTimeoutMS: 5 }).catch(
error => error
);

expect(error).to.match(/timed out/);
});
});
});

it('should emit `MongoNetworkError` for network errors', function (done) {
connect({ hostAddress: new HostAddress('non-existent:27018') }, err => {
expect(err).to.be.instanceOf(MongoNetworkError);
done();
});
it('should emit `MongoNetworkError` for network errors', async function () {
const error = await connect({
hostAddress: new HostAddress('non-existent:27018')
}).catch(e => e);
expect(error).to.be.instanceOf(MongoNetworkError);
});

context('prepareHandshakeDocument', () => {
Expand Down
31 changes: 31 additions & 0 deletions test/unit/sdam/monitor.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as net from 'node:net';

import { expect } from 'chai';
import { coerce } from 'semver';
import * as sinon from 'sinon';
import { setTimeout } from 'timers';

import { MongoClient } from '../../mongodb';
import {
isHello,
LEGACY_HELLO_COMMAND,
Expand Down Expand Up @@ -579,4 +582,32 @@ describe('monitoring', function () {
});
});
});

describe('Heartbeat duration', function () {
let client: MongoClient;
let serverHeartbeatFailed;

beforeEach(async function () {
// Artificially make creating a connection take 200ms
sinon.stub(net, 'createConnection').callsFake(function () {
const socket = new net.Socket();
setTimeout(() => socket.emit('connect'), 80);
socket.on('data', () => socket.destroy(new Error('I am not real!')));
return socket;
});

client = new MongoClient(`mongodb://localhost:1`, { serverSelectionTimeoutMS: 200 });
client.on('serverHeartbeatFailed', ev => (serverHeartbeatFailed = ev));
});

afterEach(function () {
sinon.restore();
});

it('includes only the time to perform handshake', async function () {
const maybeError = await client.connect().catch(e => e);
expect(maybeError).to.be.instanceOf(Error);
expect(serverHeartbeatFailed).to.have.property('duration').that.is.lessThan(20); // way less than 80ms
});
});
});

0 comments on commit 5f1947b

Please sign in to comment.