-
Notifications
You must be signed in to change notification settings - Fork 256
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(NODE-4855): add hex and base64 ctor methods to Binary and ObjectId #569
Changes from all commits
baf659d
4e6f3c9
c7645fa
20aa2d4
ea9d591
2ec6695
ba8fc52
ef1fb6d
0a3deec
9e0a86b
450dfca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import { expect } from 'chai'; | ||
import * as vm from 'node:vm'; | ||
import { Binary, BSON } from '../register-bson'; | ||
|
||
describe('class Binary', () => { | ||
context('constructor()', () => { | ||
it('creates an 256 byte Binary with subtype 0 by default', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just testing the existing behavior here. |
||
const binary = new Binary(); | ||
expect(binary).to.have.property('buffer'); | ||
expect(binary).to.have.property('position', 0); | ||
expect(binary).to.have.property('sub_type', 0); | ||
expect(binary).to.have.nested.property('buffer.byteLength', 256); | ||
const emptyZeroedArray = new Uint8Array(256); | ||
emptyZeroedArray.fill(0x00); | ||
expect(binary.buffer).to.deep.equal(emptyZeroedArray); | ||
}); | ||
}); | ||
|
||
context('createFromHexString()', () => { | ||
context('when called with a hex sequence', () => { | ||
it('returns a Binary instance with the decoded bytes', () => { | ||
const bytes = Buffer.from('abc', 'utf8'); | ||
const binary = Binary.createFromHexString(bytes.toString('hex')); | ||
expect(binary).to.have.deep.property('buffer', bytes); | ||
expect(binary).to.have.property('sub_type', 0); | ||
}); | ||
|
||
it('returns a Binary instance with the decoded bytes and subtype', () => { | ||
const bytes = Buffer.from('abc', 'utf8'); | ||
const binary = Binary.createFromHexString(bytes.toString('hex'), 0x23); | ||
expect(binary).to.have.deep.property('buffer', bytes); | ||
expect(binary).to.have.property('sub_type', 0x23); | ||
}); | ||
}); | ||
|
||
context('when called with an empty string', () => { | ||
it('creates an empty binary', () => { | ||
const binary = Binary.createFromHexString(''); | ||
expect(binary).to.have.deep.property('buffer', new Uint8Array(0)); | ||
expect(binary).to.have.property('sub_type', 0); | ||
}); | ||
|
||
it('creates an empty binary with subtype', () => { | ||
const binary = Binary.createFromHexString('', 0x42); | ||
expect(binary).to.have.deep.property('buffer', new Uint8Array(0)); | ||
expect(binary).to.have.property('sub_type', 0x42); | ||
}); | ||
}); | ||
}); | ||
|
||
context('createFromBase64()', () => { | ||
context('when called with a base64 sequence', () => { | ||
it('returns a Binary instance with the decoded bytes', () => { | ||
const bytes = Buffer.from('abc', 'utf8'); | ||
const binary = Binary.createFromBase64(bytes.toString('base64')); | ||
expect(binary).to.have.deep.property('buffer', bytes); | ||
expect(binary).to.have.property('sub_type', 0); | ||
}); | ||
|
||
it('returns a Binary instance with the decoded bytes and subtype', () => { | ||
const bytes = Buffer.from('abc', 'utf8'); | ||
const binary = Binary.createFromBase64(bytes.toString('base64'), 0x23); | ||
expect(binary).to.have.deep.property('buffer', bytes); | ||
expect(binary).to.have.property('sub_type', 0x23); | ||
}); | ||
}); | ||
|
||
context('when called with an empty string', () => { | ||
it('creates an empty binary', () => { | ||
const binary = Binary.createFromBase64(''); | ||
expect(binary).to.have.deep.property('buffer', new Uint8Array(0)); | ||
expect(binary).to.have.property('sub_type', 0); | ||
}); | ||
|
||
it('creates an empty binary with subtype', () => { | ||
const binary = Binary.createFromBase64('', 0x42); | ||
expect(binary).to.have.deep.property('buffer', new Uint8Array(0)); | ||
expect(binary).to.have.property('sub_type', 0x42); | ||
}); | ||
}); | ||
}); | ||
|
||
context('inspect()', () => { | ||
it('when value is default returns "Binary.createFromBase64("", 0)"', () => { | ||
expect(new Binary().inspect()).to.equal('Binary.createFromBase64("", 0)'); | ||
}); | ||
|
||
it('when value is empty returns "Binary.createFromBase64("", 0)"', () => { | ||
expect(new Binary(new Uint8Array(0)).inspect()).to.equal('Binary.createFromBase64("", 0)'); | ||
}); | ||
|
||
it('when value is default with a subtype returns "Binary.createFromBase64("", 35)"', () => { | ||
expect(new Binary(null, 0x23).inspect()).to.equal('Binary.createFromBase64("", 35)'); | ||
}); | ||
|
||
it('when value is empty with a subtype returns "Binary.createFromBase64("", 35)"', () => { | ||
expect(new Binary(new Uint8Array(0), 0x23).inspect()).to.equal( | ||
'Binary.createFromBase64("", 35)' | ||
); | ||
}); | ||
|
||
it('when value has utf8 "abcdef" encoded returns "Binary.createFromBase64("YWJjZGVm", 0)"', () => { | ||
expect(new Binary(Buffer.from('abcdef', 'utf8')).inspect()).to.equal( | ||
'Binary.createFromBase64("YWJjZGVm", 0)' | ||
); | ||
}); | ||
|
||
context('when result is executed', () => { | ||
it('has a position of zero when constructed with default space', () => { | ||
const bsonValue = new Binary(); | ||
const ctx = { ...BSON, module: { exports: { result: null } } }; | ||
vm.runInNewContext(`module.exports.result = ${bsonValue.inspect()}`, ctx); | ||
expect(ctx.module.exports.result).to.have.property('position', 0); | ||
expect(ctx.module.exports.result).to.have.property('sub_type', 0); | ||
|
||
// While the default Binary has 256 bytes the newly constructed one will have 0 | ||
// both will have a position of zero so when serialized to BSON they are the equivalent. | ||
expect(ctx.module.exports.result).to.have.nested.property('buffer.byteLength', 0); | ||
expect(bsonValue).to.have.nested.property('buffer.byteLength', 256); | ||
}); | ||
|
||
it('is deep equal with a Binary that has no data', () => { | ||
const bsonValue = new Binary(new Uint8Array(0)); | ||
const ctx = { ...BSON, module: { exports: { result: null } } }; | ||
vm.runInNewContext(`module.exports.result = ${bsonValue.inspect()}`, ctx); | ||
expect(ctx.module.exports.result).to.deep.equal(bsonValue); | ||
}); | ||
|
||
it('is deep equal with a Binary that has a subtype but no data', () => { | ||
const bsonValue = new Binary(new Uint8Array(0), 0x23); | ||
const ctx = { ...BSON, module: { exports: { result: null } } }; | ||
vm.runInNewContext(`module.exports.result = ${bsonValue.inspect()}`, ctx); | ||
expect(ctx.module.exports.result).to.deep.equal(bsonValue); | ||
}); | ||
|
||
it('is deep equal with a Binary that has data', () => { | ||
const bsonValue = new Binary(Buffer.from('abc', 'utf8')); | ||
const ctx = { ...BSON, module: { exports: { result: null } } }; | ||
vm.runInNewContext(`module.exports.result = ${bsonValue.inspect()}`, ctx); | ||
expect(ctx.module.exports.result).to.deep.equal(bsonValue); | ||
}); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ import { | |
UUID, | ||
BSONValue | ||
} from '../register-bson'; | ||
import * as vm from 'node:vm'; | ||
|
||
const BSONTypeClasses = [ | ||
Binary, | ||
|
@@ -36,7 +37,7 @@ const BSONTypeClasses = [ | |
]; | ||
|
||
const BSONTypeClassCtors = new Map<string, () => BSONValue>([ | ||
['Binary', () => new Binary()], | ||
['Binary', () => new Binary(new Uint8Array(0), 0)], | ||
['Code', () => new Code('function () {}')], | ||
['DBRef', () => new DBRef('name', new ObjectId('00'.repeat(12)))], | ||
['Decimal128', () => new Decimal128('1.23')], | ||
|
@@ -97,4 +98,20 @@ describe('BSON Type classes common interfaces', () => { | |
.that.is.a('function')); | ||
}); | ||
} | ||
|
||
context(`when inspect() is called`, () => { | ||
for (const [name, factory] of BSONTypeClassCtors) { | ||
it(`${name} returns string that is runnable and has deep equality`, () => { | ||
const bsonValue = factory(); | ||
// All BSON types should only need exactly their constructor available on the global | ||
const ctx = { [name]: bsonValue.constructor, module: { exports: { result: null } } }; | ||
if (name === 'DBRef') { | ||
// DBRef is the only type that requires another BSON type | ||
ctx.ObjectId = ObjectId; | ||
} | ||
vm.runInNewContext(`module.exports.result = ${bsonValue.inspect()}`, ctx); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will throw if we mistakenly introduce a syntax error or if we produce strings that when invoked lead to runtime errors |
||
expect(ctx.module.exports.result).to.deep.equal(bsonValue); | ||
}); | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a copy pasta mistake, the if stmt asserts for 24 characters, so a 12 byte string wouldn't work here.