-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathcommands.test.ts
More file actions
224 lines (190 loc) · 8.09 KB
/
Copy pathcommands.test.ts
File metadata and controls
224 lines (190 loc) · 8.09 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
import * as BSON from 'bson';
import { expect } from 'chai';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { readInt32LE } from '../../../src/bson';
import { DocumentSequence, OpMsgRequest, OpReply } from '../../mongodb';
// Helper to decode UTF-8 string from Uint8Array
function utf8Slice(buffer: Uint8Array, start: number, end: number): string {
return BSON.ByteUtils.toUTF8(buffer, start, end, false);
}
describe('commands', function () {
describe('OpMsgRequest', function () {
describe('#toBin', function () {
/**
* Note that #toBin returns an array of buffers, in this case we are interested in
* the buffer at index 3 of the array, which is a single buffer of all the
* document sequence sections.
*/
context('when the command has document sequences', function () {
context('when there is one document sequence', function () {
const command = {
test: 1,
field: new DocumentSequence('field', [{ test: 1 }])
};
const msg = new OpMsgRequest('admin', command, {});
const buffers = msg.toBin();
it('keeps the first section as type 0', function () {
// The type byte for the first section is at index 1.
expect(buffers[1][0]).to.equal(0);
});
it('does not serialize the document sequences in the first section', function () {
// Buffer at index 2 is the type 0 section - one document.
expect(BSON.deserialize(buffers[2])).to.deep.equal({ test: 1, $db: 'admin' });
});
it('removes the document sequence fields from the command', function () {
expect(command).to.not.haveOwnProperty('field');
});
it('sets the document sequence section type to 1', function () {
// First byte is a one byte type.
expect(buffers[3][0]).to.equal(1);
});
it('sets the length of the document sequence', function () {
// Bytes starting at index 1 is a 4 byte length.
expect(readInt32LE(buffers[3], 1)).to.equal(25);
});
it('sets the name of the first field to be replaced', function () {
// Bytes starting at index 5 is the field name.
expect(utf8Slice(buffers[3], 5, 10)).to.equal('field');
});
});
context('when there are multiple document sequences', function () {
const command = {
test: 1,
fieldOne: new DocumentSequence('fieldOne', [{ test: 1 }]),
fieldTwo: new DocumentSequence('fieldTwo', [{ test: 1 }])
};
const msg = new OpMsgRequest('admin', command, {});
const buffers = msg.toBin();
it('keeps the first section as type 0', function () {
// The type byte for the first section is at index 1.
expect(buffers[1][0]).to.equal(0);
});
it('does not serialize the document sequences in the first section', function () {
// Buffer at index 2 is the type 0 section - one document.
expect(BSON.deserialize(buffers[2])).to.deep.equal({ test: 1, $db: 'admin' });
});
it('removes the document sequence fields from the command', function () {
expect(command).to.not.haveOwnProperty('fieldOne');
expect(command).to.not.haveOwnProperty('fieldTwo');
});
it('sets the document sequence sections first type to 1', function () {
// First byte is a one byte type.
expect(buffers[3][0]).to.equal(1);
});
it('sets the length of the first document sequence', function () {
// Bytes starting at index 1 is a 4 byte length.
expect(readInt32LE(buffers[3], 1)).to.equal(28);
});
it('sets the name of the first field to be replaced', function () {
// Bytes starting at index 5 is the field name.
expect(utf8Slice(buffers[3], 5, 13)).to.equal('fieldOne');
});
it('sets the document sequence sections second type to 1', function () {
// First byte is a one byte type.
expect(buffers[3][29]).to.equal(1);
});
it('sets the length of the second document sequence', function () {
// Bytes starting at index 1 is a 4 byte length.
expect(readInt32LE(buffers[3], 30)).to.equal(28);
});
it('sets the name of the second field to be replaced', function () {
// Bytes starting at index 33 is the field name.
expect(utf8Slice(buffers[3], 34, 42)).to.equal('fieldTwo');
});
});
});
});
});
describe('Response', function () {
describe('#parse', function () {
context('when the message body is invalid', function () {
context('when the buffer is empty', function () {
const message = Buffer.from([]);
const header = {
length: 0,
requestId: 0,
responseTo: 0,
opCode: 0
};
const body = Buffer.from([]);
it('throws an exception', function () {
const response = new OpReply(message, header, body);
expect(() => response.parse()).to.throw(/outside buffer bounds/);
});
});
context('when numReturned is invalid', function () {
const message = Buffer.from([]);
const header = {
length: 0,
requestId: 0,
responseTo: 0,
opCode: 0
};
const body = Buffer.alloc(5 * 4);
body.writeInt32LE(-1, 16);
it('throws an exception', function () {
const response = new OpReply(message, header, body);
expect(() => response.parse()).to.throw(/Invalid array length/i);
});
});
});
});
describe('#constructor', function () {
context('when the message body is invalid', function () {
const message = Buffer.from([]);
const header = {
length: 0,
requestId: 0,
responseTo: 0,
opCode: 0
};
const body = Buffer.from([]);
it('does not throw an exception', function () {
let error;
try {
new OpReply(message, header, body);
} catch (err) {
error = err;
}
expect(error).to.be.undefined;
});
it('initializes the sections to an empty array', function () {
const response = new OpReply(message, header, body);
expect(response.sections).to.be.empty;
});
it('does not set the responseFlags', function () {
const response = new OpReply(message, header, body);
expect(response.responseFlags).to.be.undefined;
});
it('does not set the cursorNotFound flag', function () {
const response = new OpReply(message, header, body);
expect(response.cursorNotFound).to.be.undefined;
});
it('does not set the cursorId', function () {
const response = new OpReply(message, header, body);
expect(response.cursorId).to.be.undefined;
});
it('does not set startingFrom', function () {
const response = new OpReply(message, header, body);
expect(response.startingFrom).to.be.undefined;
});
it('does not set numberReturned', function () {
const response = new OpReply(message, header, body);
expect(response.numberReturned).to.be.undefined;
});
it('does not set queryFailure', function () {
const response = new OpReply(message, header, body);
expect(response.queryFailure).to.be.undefined;
});
it('does not set shardConfigStale', function () {
const response = new OpReply(message, header, body);
expect(response.shardConfigStale).to.be.undefined;
});
it('does not set awaitCapable', function () {
const response = new OpReply(message, header, body);
expect(response.awaitCapable).to.be.undefined;
});
});
});
});
});