/ node_modules / pg-protocol / dist / inbound-parser.test.js
inbound-parser.test.js
  1  "use strict";
  2  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  3      function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  4      return new (P || (P = Promise))(function (resolve, reject) {
  5          function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  6          function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  7          function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  8          step((generator = generator.apply(thisArg, _arguments || [])).next());
  9      });
 10  };
 11  var __importDefault = (this && this.__importDefault) || function (mod) {
 12      return (mod && mod.__esModule) ? mod : { "default": mod };
 13  };
 14  Object.defineProperty(exports, "__esModule", { value: true });
 15  const test_buffers_1 = __importDefault(require("./testing/test-buffers"));
 16  const buffer_list_1 = __importDefault(require("./testing/buffer-list"));
 17  const _1 = require(".");
 18  const assert_1 = __importDefault(require("assert"));
 19  const stream_1 = require("stream");
 20  const authOkBuffer = test_buffers_1.default.authenticationOk();
 21  const paramStatusBuffer = test_buffers_1.default.parameterStatus('client_encoding', 'UTF8');
 22  const readyForQueryBuffer = test_buffers_1.default.readyForQuery();
 23  const backendKeyDataBuffer = test_buffers_1.default.backendKeyData(1, 2);
 24  const commandCompleteBuffer = test_buffers_1.default.commandComplete('SELECT 3');
 25  const parseCompleteBuffer = test_buffers_1.default.parseComplete();
 26  const bindCompleteBuffer = test_buffers_1.default.bindComplete();
 27  const portalSuspendedBuffer = test_buffers_1.default.portalSuspended();
 28  const row1 = {
 29      name: 'id',
 30      tableID: 1,
 31      attributeNumber: 2,
 32      dataTypeID: 3,
 33      dataTypeSize: 4,
 34      typeModifier: 5,
 35      formatCode: 0,
 36  };
 37  const oneRowDescBuff = test_buffers_1.default.rowDescription([row1]);
 38  row1.name = 'bang';
 39  const twoRowBuf = test_buffers_1.default.rowDescription([
 40      row1,
 41      {
 42          name: 'whoah',
 43          tableID: 10,
 44          attributeNumber: 11,
 45          dataTypeID: 12,
 46          dataTypeSize: 13,
 47          typeModifier: 14,
 48          formatCode: 0,
 49      },
 50  ]);
 51  const rowWithBigOids = {
 52      name: 'bigoid',
 53      tableID: 3000000001,
 54      attributeNumber: 2,
 55      dataTypeID: 3000000003,
 56      dataTypeSize: 4,
 57      typeModifier: 5,
 58      formatCode: 0,
 59  };
 60  const bigOidDescBuff = test_buffers_1.default.rowDescription([rowWithBigOids]);
 61  const emptyRowFieldBuf = test_buffers_1.default.dataRow([]);
 62  const oneFieldBuf = test_buffers_1.default.dataRow(['test']);
 63  const expectedAuthenticationOkayMessage = {
 64      name: 'authenticationOk',
 65      length: 8,
 66  };
 67  const expectedParameterStatusMessage = {
 68      name: 'parameterStatus',
 69      parameterName: 'client_encoding',
 70      parameterValue: 'UTF8',
 71      length: 25,
 72  };
 73  const expectedBackendKeyDataMessage = {
 74      name: 'backendKeyData',
 75      processID: 1,
 76      secretKey: 2,
 77  };
 78  const expectedReadyForQueryMessage = {
 79      name: 'readyForQuery',
 80      length: 5,
 81      status: 'I',
 82  };
 83  const expectedCommandCompleteMessage = {
 84      name: 'commandComplete',
 85      length: 13,
 86      text: 'SELECT 3',
 87  };
 88  const emptyRowDescriptionBuffer = new buffer_list_1.default()
 89      .addInt16(0) // number of fields
 90      .join(true, 'T');
 91  const expectedEmptyRowDescriptionMessage = {
 92      name: 'rowDescription',
 93      length: 6,
 94      fieldCount: 0,
 95      fields: [],
 96  };
 97  const expectedOneRowMessage = {
 98      name: 'rowDescription',
 99      length: 27,
100      fieldCount: 1,
101      fields: [
102          {
103              name: 'id',
104              tableID: 1,
105              columnID: 2,
106              dataTypeID: 3,
107              dataTypeSize: 4,
108              dataTypeModifier: 5,
109              format: 'text',
110          },
111      ],
112  };
113  const expectedTwoRowMessage = {
114      name: 'rowDescription',
115      length: 53,
116      fieldCount: 2,
117      fields: [
118          {
119              name: 'bang',
120              tableID: 1,
121              columnID: 2,
122              dataTypeID: 3,
123              dataTypeSize: 4,
124              dataTypeModifier: 5,
125              format: 'text',
126          },
127          {
128              name: 'whoah',
129              tableID: 10,
130              columnID: 11,
131              dataTypeID: 12,
132              dataTypeSize: 13,
133              dataTypeModifier: 14,
134              format: 'text',
135          },
136      ],
137  };
138  const expectedBigOidMessage = {
139      name: 'rowDescription',
140      length: 31,
141      fieldCount: 1,
142      fields: [
143          {
144              name: 'bigoid',
145              tableID: 3000000001,
146              columnID: 2,
147              dataTypeID: 3000000003,
148              dataTypeSize: 4,
149              dataTypeModifier: 5,
150              format: 'text',
151          },
152      ],
153  };
154  const emptyParameterDescriptionBuffer = new buffer_list_1.default()
155      .addInt16(0) // number of parameters
156      .join(true, 't');
157  const oneParameterDescBuf = test_buffers_1.default.parameterDescription([1111]);
158  const twoParameterDescBuf = test_buffers_1.default.parameterDescription([2222, 3333]);
159  const expectedEmptyParameterDescriptionMessage = {
160      name: 'parameterDescription',
161      length: 6,
162      parameterCount: 0,
163      dataTypeIDs: [],
164  };
165  const expectedOneParameterMessage = {
166      name: 'parameterDescription',
167      length: 10,
168      parameterCount: 1,
169      dataTypeIDs: [1111],
170  };
171  const expectedTwoParameterMessage = {
172      name: 'parameterDescription',
173      length: 14,
174      parameterCount: 2,
175      dataTypeIDs: [2222, 3333],
176  };
177  const testForMessage = function (buffer, expectedMessage) {
178      it('receives and parses ' + expectedMessage.name, () => __awaiter(this, void 0, void 0, function* () {
179          const messages = yield parseBuffers([buffer]);
180          const [lastMessage] = messages;
181          for (const key in expectedMessage) {
182              assert_1.default.deepEqual(lastMessage[key], expectedMessage[key]);
183          }
184      }));
185  };
186  const plainPasswordBuffer = test_buffers_1.default.authenticationCleartextPassword();
187  const md5PasswordBuffer = test_buffers_1.default.authenticationMD5Password();
188  const SASLBuffer = test_buffers_1.default.authenticationSASL();
189  const SASLContinueBuffer = test_buffers_1.default.authenticationSASLContinue();
190  const SASLFinalBuffer = test_buffers_1.default.authenticationSASLFinal();
191  const expectedPlainPasswordMessage = {
192      name: 'authenticationCleartextPassword',
193  };
194  const expectedMD5PasswordMessage = {
195      name: 'authenticationMD5Password',
196      salt: Buffer.from([1, 2, 3, 4]),
197  };
198  const expectedSASLMessage = {
199      name: 'authenticationSASL',
200      mechanisms: ['SCRAM-SHA-256'],
201  };
202  const expectedSASLContinueMessage = {
203      name: 'authenticationSASLContinue',
204      data: 'data',
205  };
206  const expectedSASLFinalMessage = {
207      name: 'authenticationSASLFinal',
208      data: 'data',
209  };
210  const notificationResponseBuffer = test_buffers_1.default.notification(4, 'hi', 'boom');
211  const expectedNotificationResponseMessage = {
212      name: 'notification',
213      processId: 4,
214      channel: 'hi',
215      payload: 'boom',
216  };
217  const parseBuffers = (buffers) => __awaiter(void 0, void 0, void 0, function* () {
218      const stream = new stream_1.PassThrough();
219      for (const buffer of buffers) {
220          stream.write(buffer);
221      }
222      stream.end();
223      const msgs = [];
224      yield (0, _1.parse)(stream, (msg) => msgs.push(msg));
225      return msgs;
226  });
227  describe('PgPacketStream', function () {
228      testForMessage(authOkBuffer, expectedAuthenticationOkayMessage);
229      testForMessage(plainPasswordBuffer, expectedPlainPasswordMessage);
230      testForMessage(md5PasswordBuffer, expectedMD5PasswordMessage);
231      testForMessage(SASLBuffer, expectedSASLMessage);
232      testForMessage(SASLContinueBuffer, expectedSASLContinueMessage);
233      // this exercises a found bug in the parser:
234      // https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084
235      // and adds a test which is deterministic, rather than relying on network packet chunking
236      const extendedSASLContinueBuffer = Buffer.concat([SASLContinueBuffer, Buffer.from([1, 2, 3, 4])]);
237      testForMessage(extendedSASLContinueBuffer, expectedSASLContinueMessage);
238      testForMessage(SASLFinalBuffer, expectedSASLFinalMessage);
239      // this exercises a found bug in the parser:
240      // https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084
241      // and adds a test which is deterministic, rather than relying on network packet chunking
242      const extendedSASLFinalBuffer = Buffer.concat([SASLFinalBuffer, Buffer.from([1, 2, 4, 5])]);
243      testForMessage(extendedSASLFinalBuffer, expectedSASLFinalMessage);
244      testForMessage(paramStatusBuffer, expectedParameterStatusMessage);
245      testForMessage(backendKeyDataBuffer, expectedBackendKeyDataMessage);
246      testForMessage(readyForQueryBuffer, expectedReadyForQueryMessage);
247      testForMessage(commandCompleteBuffer, expectedCommandCompleteMessage);
248      testForMessage(notificationResponseBuffer, expectedNotificationResponseMessage);
249      testForMessage(test_buffers_1.default.emptyQuery(), {
250          name: 'emptyQuery',
251          length: 4,
252      });
253      testForMessage(Buffer.from([0x6e, 0, 0, 0, 4]), {
254          name: 'noData',
255      });
256      describe('rowDescription messages', function () {
257          testForMessage(emptyRowDescriptionBuffer, expectedEmptyRowDescriptionMessage);
258          testForMessage(oneRowDescBuff, expectedOneRowMessage);
259          testForMessage(twoRowBuf, expectedTwoRowMessage);
260          testForMessage(bigOidDescBuff, expectedBigOidMessage);
261      });
262      describe('parameterDescription messages', function () {
263          testForMessage(emptyParameterDescriptionBuffer, expectedEmptyParameterDescriptionMessage);
264          testForMessage(oneParameterDescBuf, expectedOneParameterMessage);
265          testForMessage(twoParameterDescBuf, expectedTwoParameterMessage);
266      });
267      describe('parsing rows', function () {
268          describe('parsing empty row', function () {
269              testForMessage(emptyRowFieldBuf, {
270                  name: 'dataRow',
271                  fieldCount: 0,
272              });
273          });
274          describe('parsing data row with fields', function () {
275              testForMessage(oneFieldBuf, {
276                  name: 'dataRow',
277                  fieldCount: 1,
278                  fields: ['test'],
279              });
280          });
281      });
282      describe('notice message', function () {
283          // this uses the same logic as error message
284          const buff = test_buffers_1.default.notice([{ type: 'C', value: 'code' }]);
285          testForMessage(buff, {
286              name: 'notice',
287              code: 'code',
288          });
289      });
290      testForMessage(test_buffers_1.default.error([]), {
291          name: 'error',
292      });
293      describe('with all the fields', function () {
294          const buffer = test_buffers_1.default.error([
295              {
296                  type: 'S',
297                  value: 'ERROR',
298              },
299              {
300                  type: 'C',
301                  value: 'code',
302              },
303              {
304                  type: 'M',
305                  value: 'message',
306              },
307              {
308                  type: 'D',
309                  value: 'details',
310              },
311              {
312                  type: 'H',
313                  value: 'hint',
314              },
315              {
316                  type: 'P',
317                  value: '100',
318              },
319              {
320                  type: 'p',
321                  value: '101',
322              },
323              {
324                  type: 'q',
325                  value: 'query',
326              },
327              {
328                  type: 'W',
329                  value: 'where',
330              },
331              {
332                  type: 'F',
333                  value: 'file',
334              },
335              {
336                  type: 'L',
337                  value: 'line',
338              },
339              {
340                  type: 'R',
341                  value: 'routine',
342              },
343              {
344                  type: 'Z',
345                  value: 'alsdkf',
346              },
347          ]);
348          testForMessage(buffer, {
349              name: 'error',
350              severity: 'ERROR',
351              code: 'code',
352              message: 'message',
353              detail: 'details',
354              hint: 'hint',
355              position: '100',
356              internalPosition: '101',
357              internalQuery: 'query',
358              where: 'where',
359              file: 'file',
360              line: 'line',
361              routine: 'routine',
362          });
363      });
364      testForMessage(parseCompleteBuffer, {
365          name: 'parseComplete',
366      });
367      testForMessage(bindCompleteBuffer, {
368          name: 'bindComplete',
369      });
370      testForMessage(bindCompleteBuffer, {
371          name: 'bindComplete',
372      });
373      testForMessage(test_buffers_1.default.closeComplete(), {
374          name: 'closeComplete',
375      });
376      describe('parses portal suspended message', function () {
377          testForMessage(portalSuspendedBuffer, {
378              name: 'portalSuspended',
379          });
380      });
381      describe('parses replication start message', function () {
382          testForMessage(Buffer.from([0x57, 0x00, 0x00, 0x00, 0x04]), {
383              name: 'replicationStart',
384              length: 4,
385          });
386      });
387      describe('copy', () => {
388          testForMessage(test_buffers_1.default.copyIn(0), {
389              name: 'copyInResponse',
390              length: 7,
391              binary: false,
392              columnTypes: [],
393          });
394          testForMessage(test_buffers_1.default.copyIn(2), {
395              name: 'copyInResponse',
396              length: 11,
397              binary: false,
398              columnTypes: [0, 1],
399          });
400          testForMessage(test_buffers_1.default.copyOut(0), {
401              name: 'copyOutResponse',
402              length: 7,
403              binary: false,
404              columnTypes: [],
405          });
406          testForMessage(test_buffers_1.default.copyOut(3), {
407              name: 'copyOutResponse',
408              length: 13,
409              binary: false,
410              columnTypes: [0, 1, 2],
411          });
412          testForMessage(test_buffers_1.default.copyDone(), {
413              name: 'copyDone',
414              length: 4,
415          });
416          testForMessage(test_buffers_1.default.copyData(Buffer.from([5, 6, 7])), {
417              name: 'copyData',
418              length: 7,
419              chunk: Buffer.from([5, 6, 7]),
420          });
421      });
422      // since the data message on a stream can randomly divide the incomming
423      // tcp packets anywhere, we need to make sure we can parse every single
424      // split on a tcp message
425      describe('split buffer, single message parsing', function () {
426          const fullBuffer = test_buffers_1.default.dataRow([null, 'bang', 'zug zug', null, '!']);
427          it('parses when full buffer comes in', function () {
428              return __awaiter(this, void 0, void 0, function* () {
429                  const messages = yield parseBuffers([fullBuffer]);
430                  const message = messages[0];
431                  assert_1.default.equal(message.fields.length, 5);
432                  assert_1.default.equal(message.fields[0], null);
433                  assert_1.default.equal(message.fields[1], 'bang');
434                  assert_1.default.equal(message.fields[2], 'zug zug');
435                  assert_1.default.equal(message.fields[3], null);
436                  assert_1.default.equal(message.fields[4], '!');
437              });
438          });
439          const testMessageReceivedAfterSplitAt = function (split) {
440              return __awaiter(this, void 0, void 0, function* () {
441                  const firstBuffer = Buffer.alloc(fullBuffer.length - split);
442                  const secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length);
443                  fullBuffer.copy(firstBuffer, 0, 0);
444                  fullBuffer.copy(secondBuffer, 0, firstBuffer.length);
445                  const messages = yield parseBuffers([firstBuffer, secondBuffer]);
446                  const message = messages[0];
447                  assert_1.default.equal(message.fields.length, 5);
448                  assert_1.default.equal(message.fields[0], null);
449                  assert_1.default.equal(message.fields[1], 'bang');
450                  assert_1.default.equal(message.fields[2], 'zug zug');
451                  assert_1.default.equal(message.fields[3], null);
452                  assert_1.default.equal(message.fields[4], '!');
453              });
454          };
455          it('parses when split in the middle', function () {
456              return testMessageReceivedAfterSplitAt(6);
457          });
458          it('parses when split at end', function () {
459              return testMessageReceivedAfterSplitAt(2);
460          });
461          it('parses when split at beginning', function () {
462              return Promise.all([
463                  testMessageReceivedAfterSplitAt(fullBuffer.length - 2),
464                  testMessageReceivedAfterSplitAt(fullBuffer.length - 1),
465                  testMessageReceivedAfterSplitAt(fullBuffer.length - 5),
466              ]);
467          });
468      });
469      describe('split buffer, multiple message parsing', function () {
470          const dataRowBuffer = test_buffers_1.default.dataRow(['!']);
471          const readyForQueryBuffer = test_buffers_1.default.readyForQuery();
472          const fullBuffer = Buffer.alloc(dataRowBuffer.length + readyForQueryBuffer.length);
473          dataRowBuffer.copy(fullBuffer, 0, 0);
474          readyForQueryBuffer.copy(fullBuffer, dataRowBuffer.length, 0);
475          const verifyMessages = function (messages) {
476              assert_1.default.strictEqual(messages.length, 2);
477              assert_1.default.deepEqual(messages[0], {
478                  name: 'dataRow',
479                  fieldCount: 1,
480                  length: 11,
481                  fields: ['!'],
482              });
483              assert_1.default.equal(messages[0].fields[0], '!');
484              assert_1.default.deepEqual(messages[1], {
485                  name: 'readyForQuery',
486                  length: 5,
487                  status: 'I',
488              });
489          };
490          // sanity check
491          it('receives both messages when packet is not split', function () {
492              return __awaiter(this, void 0, void 0, function* () {
493                  const messages = yield parseBuffers([fullBuffer]);
494                  verifyMessages(messages);
495              });
496          });
497          const splitAndVerifyTwoMessages = function (split) {
498              return __awaiter(this, void 0, void 0, function* () {
499                  const firstBuffer = Buffer.alloc(fullBuffer.length - split);
500                  const secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length);
501                  fullBuffer.copy(firstBuffer, 0, 0);
502                  fullBuffer.copy(secondBuffer, 0, firstBuffer.length);
503                  const messages = yield parseBuffers([firstBuffer, secondBuffer]);
504                  verifyMessages(messages);
505              });
506          };
507          describe('receives both messages when packet is split', function () {
508              it('in the middle', function () {
509                  return splitAndVerifyTwoMessages(11);
510              });
511              it('at the front', function () {
512                  return Promise.all([
513                      splitAndVerifyTwoMessages(fullBuffer.length - 1),
514                      splitAndVerifyTwoMessages(fullBuffer.length - 4),
515                      splitAndVerifyTwoMessages(fullBuffer.length - 6),
516                  ]);
517              });
518              it('at the end', function () {
519                  return Promise.all([splitAndVerifyTwoMessages(8), splitAndVerifyTwoMessages(1)]);
520              });
521          });
522      });
523  });
524  //# sourceMappingURL=inbound-parser.test.js.map