forked from mongodb/node-mongodb-native
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconvert_socket_errors.test.ts
More file actions
130 lines (110 loc) · 4.55 KB
/
convert_socket_errors.test.ts
File metadata and controls
130 lines (110 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { Duplex } from 'node:stream';
import { expect } from 'chai';
import * as sinon from 'sinon';
import { type Collection, type Document, type MongoClient, MongoNetworkError } from '../../../src';
import { Connection } from '../../../src/cmap/connection';
import { ns } from '../../../src/utils';
import { clearFailPoint, configureFailPoint } from '../../tools/utils';
import { filterForCommands } from '../shared';
describe('Socket Errors', () => {
describe('when a socket emits an error', () => {
it('command throws a MongoNetworkError', async () => {
const socket = new Duplex();
// @ts-expect-error: not a real socket
const connection = new Connection(socket, {});
const testError = new Error('blah');
socket.emit('error', testError);
const commandRes = connection.command(ns('a.b'), { ping: 1 }, {});
const error = await commandRes.catch(error => error);
expect(error).to.be.instanceOf(MongoNetworkError);
expect(error.cause).to.equal(testError);
});
});
describe('when the sized message stream emits an error', () => {
it('command throws the same error', async () => {
const socket = new Duplex();
// @ts-expect-error: not a real socket
const connection = new Connection(socket, {});
const testError = new Error('blah');
// @ts-expect-error: private field
connection.messageStream.emit('error', testError);
const commandRes = connection.command(ns('a.b'), { ping: 1 }, {});
const error = await commandRes.catch(error => error);
expect(error).to.equal(testError);
});
});
describe('when destroyed by failpoint', () => {
let client: MongoClient;
let collection: Collection<Document>;
const metadata: MongoDBMetadataUI = { requires: { mongodb: '>=4.4' } };
beforeEach(async function () {
if (!this.configuration.filters.NodeVersionFilter.filter({ metadata })) {
return;
}
await configureFailPoint(this.configuration, {
configureFailPoint: 'failCommand',
mode: 'alwaysOn',
data: {
appName: 'failInserts2',
failCommands: ['insert'],
closeConnection: true
}
});
client = this.configuration.newClient({}, { appName: 'failInserts2' });
await client.connect();
const db = client.db('closeConn');
collection = db.collection('closeConn');
});
afterEach(async function () {
sinon.restore();
await clearFailPoint(this.configuration);
await client.close();
});
it('throws a MongoNetworkError', metadata, async () => {
const error = await collection.insertOne({ name: 'test' }).catch(error => error);
expect(error, error.stack).to.be.instanceOf(MongoNetworkError);
});
});
describe('when an error is thrown writing data to a socket', () => {
let client: MongoClient;
let collection: Collection<Document>;
beforeEach(async function () {
client = this.configuration.newClient({ monitorCommands: true });
await client.connect();
const db = client.db('closeConn');
collection = db.collection('closeConn');
await collection.deleteMany({});
for (const [, server] of client.topology.s.servers) {
//@ts-expect-error: private property
for (const connection of server.pool.connections) {
//@ts-expect-error: private property
const socket = connection.socket;
const stub = sinon.stub(socket, 'write').callsFake(function () {
stub.restore();
throw new Error('This socket has been ended by the other party');
});
}
}
});
afterEach(async function () {
sinon.restore();
await client.close();
});
it('retries and succeeds', async () => {
const commandSucceededEvents: string[] = [];
const commandFailedEvents: string[] = [];
const commandStartedEvents: string[] = [];
client.on('commandStarted', filterForCommands('find', commandStartedEvents));
client.on('commandSucceeded', filterForCommands('find', commandSucceededEvents));
client.on('commandFailed', filterForCommands('find', commandFailedEvents));
// call find, fail once, succeed on retry
const item = await collection.findOne({});
// check that we didn't find anything, as expected
expect(item).to.be.null;
// check that we have the expected command monitoring events
expect(commandStartedEvents).to.have.length(2);
expect(commandFailedEvents).to.have.length(1);
expect(commandSucceededEvents).to.have.length(1);
});
});
});