Skip to content

Commit aed1cf0

Browse files
fix(NODE-5839): support for multibyte code-points in stringifyWithMaxLen (#3979)
1 parent 8f7bb59 commit aed1cf0

File tree

3 files changed

+55
-15
lines changed

3 files changed

+55
-15
lines changed

Diff for: src/mongo_logger.ts

+23-4
Original file line numberDiff line numberDiff line change
@@ -420,10 +420,29 @@ export function stringifyWithMaxLen(
420420
): string {
421421
let strToTruncate = '';
422422

423-
try {
424-
strToTruncate = typeof value !== 'function' ? EJSON.stringify(value, options) : value.name;
425-
} catch (e) {
426-
strToTruncate = `Extended JSON serialization failed with: ${e.message}`;
423+
if (typeof value === 'string') {
424+
strToTruncate = value;
425+
} else if (typeof value === 'function') {
426+
strToTruncate = value.name;
427+
} else {
428+
try {
429+
strToTruncate = EJSON.stringify(value, options);
430+
} catch (e) {
431+
strToTruncate = `Extended JSON serialization failed with: ${e.message}`;
432+
}
433+
}
434+
435+
// handle truncation that occurs in the middle of multi-byte codepoints
436+
if (
437+
maxDocumentLength !== 0 &&
438+
strToTruncate.length > maxDocumentLength &&
439+
strToTruncate.charCodeAt(maxDocumentLength - 1) !==
440+
strToTruncate.codePointAt(maxDocumentLength - 1)
441+
) {
442+
maxDocumentLength--;
443+
if (maxDocumentLength === 0) {
444+
return '';
445+
}
427446
}
428447

429448
return maxDocumentLength !== 0 && strToTruncate.length > maxDocumentLength

Diff for: test/integration/command-logging-and-monitoring/command_logging_and_monitoring.prose.test.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ describe('Command Logging and Monitoring Prose Tests', function () {
156156
});
157157
});
158158

159-
context.skip('Truncation with multi-byte codepoints', function () {
159+
context('Truncation with multi-byte codepoints', function () {
160160
/*
161161
A specific test case is not provided here due to the allowed variations in truncation logic
162162
as well as varying extended JSON whitespace usage.
@@ -216,12 +216,12 @@ describe('Command Logging and Monitoring Prose Tests', function () {
216216
);
217217

218218
// multi-byte codepoint in middle of truncated string
219-
expect(insertManyCommandStarted?.command.charCodeAt(maxDocLength - 1)).to.equal(
220-
firstByteChar
221-
);
222-
expect(insertManyCommandStarted?.command.charCodeAt(maxDocLength - 1)).to.equal(
219+
expect(insertManyCommandStarted?.command.charCodeAt(maxDocLength - 2)).to.equal(
223220
secondByteChar
224221
);
222+
expect(insertManyCommandStarted?.command.charCodeAt(maxDocLength - 3)).to.equal(
223+
firstByteChar
224+
);
225225

226226
const insertManyCommandSucceeded = writable.buffer[1];
227227
expect(insertManyCommandSucceeded?.message).to.equal('Command succeeded');
@@ -230,5 +230,5 @@ describe('Command Logging and Monitoring Prose Tests', function () {
230230
maxDocLength + ELLIPSES_LENGTH
231231
);
232232
});
233-
}).skipReason = 'todo(NODE-5839)';
233+
});
234234
});

Diff for: test/unit/mongo_logger.test.ts

+26-5
Original file line numberDiff line numberDiff line change
@@ -1268,14 +1268,36 @@ describe('class MongoLogger', async function () {
12681268

12691269
context('when maxDocumentLength is non-zero', function () {
12701270
context('when document has length greater than maxDocumentLength', function () {
1271-
it('truncates ejson string to length of maxDocumentLength + 3', function () {
1272-
expect(stringifyWithMaxLen(largeDoc, DEFAULT_MAX_DOCUMENT_LENGTH)).to.have.lengthOf(
1273-
DEFAULT_MAX_DOCUMENT_LENGTH + 3
1274-
);
1271+
context('when truncation does not occur mid-multibyte codepoint', function () {
1272+
it('truncates ejson string to length of maxDocumentLength + 3', function () {
1273+
expect(stringifyWithMaxLen(largeDoc, DEFAULT_MAX_DOCUMENT_LENGTH)).to.have.lengthOf(
1274+
DEFAULT_MAX_DOCUMENT_LENGTH + 3
1275+
);
1276+
});
12751277
});
1278+
12761279
it('ends with "..."', function () {
12771280
expect(stringifyWithMaxLen(largeDoc, DEFAULT_MAX_DOCUMENT_LENGTH)).to.match(/^.*\.\.\.$/);
12781281
});
1282+
1283+
context('when truncation occurs mid-multibyte codepoint', function () {
1284+
const multiByteCodePoint = '\ud83d\ude0d'; // heart eyes emoji
1285+
context('when maxDocumentLength = 1 but greater than 0', function () {
1286+
it('should return an empty string', function () {
1287+
expect(stringifyWithMaxLen(multiByteCodePoint, 1, { relaxed: true })).to.equal('');
1288+
});
1289+
});
1290+
1291+
context('when maxDocumentLength > 1', function () {
1292+
it('should round down maxDocLength to previous codepoint', function () {
1293+
const randomStringMinusACodePoint = `random ${multiByteCodePoint}random random${multiByteCodePoint}`;
1294+
const randomString = `${randomStringMinusACodePoint}${multiByteCodePoint}`;
1295+
expect(
1296+
stringifyWithMaxLen(randomString, randomString.length - 1, { relaxed: true })
1297+
).to.equal(`${randomStringMinusACodePoint}...`);
1298+
});
1299+
});
1300+
});
12791301
});
12801302

12811303
context('when document has length less than or equal to maxDocumentLength', function () {
@@ -1289,7 +1311,6 @@ describe('class MongoLogger', async function () {
12891311
/^.*\.\.\./
12901312
);
12911313
});
1292-
12931314
it('produces valid relaxed EJSON', function () {
12941315
expect(() => {
12951316
EJSON.parse(stringifyWithMaxLen(smallDoc, DEFAULT_MAX_DOCUMENT_LENGTH));

0 commit comments

Comments
 (0)