mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 21:19:50 +01:00
757c104147
fixup: add support for `Object.create(null)` fixup: extend to any 1-argument Object.create call fixup: add tests PR-URL: https://github.com/nodejs/node/pull/46083 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
226 lines
5.0 KiB
JavaScript
226 lines
5.0 KiB
JavaScript
import { mustNotMutateObjectDeep } from '../common/index.mjs';
|
|
import assert from 'node:assert';
|
|
import { promisify } from 'node:util';
|
|
|
|
// Test common.mustNotMutateObjectDeep()
|
|
|
|
const original = {
|
|
foo: { bar: 'baz' },
|
|
qux: null,
|
|
quux: [
|
|
'quuz',
|
|
{ corge: 'grault' },
|
|
],
|
|
};
|
|
|
|
// Make a copy to make sure original doesn't get altered by the function itself.
|
|
const backup = structuredClone(original);
|
|
|
|
// Wrapper for convenience:
|
|
const obj = () => mustNotMutateObjectDeep(original);
|
|
|
|
function testOriginal(root) {
|
|
assert.deepStrictEqual(root, backup);
|
|
return root.foo.bar === 'baz' && root.quux[1].corge.length === 6;
|
|
}
|
|
|
|
function definePropertyOnRoot(root) {
|
|
Object.defineProperty(root, 'xyzzy', {});
|
|
}
|
|
|
|
function definePropertyOnFoo(root) {
|
|
Object.defineProperty(root.foo, 'xyzzy', {});
|
|
}
|
|
|
|
function deletePropertyOnRoot(root) {
|
|
delete root.foo;
|
|
}
|
|
|
|
function deletePropertyOnFoo(root) {
|
|
delete root.foo.bar;
|
|
}
|
|
|
|
function preventExtensionsOnRoot(root) {
|
|
Object.preventExtensions(root);
|
|
}
|
|
|
|
function preventExtensionsOnFoo(root) {
|
|
Object.preventExtensions(root.foo);
|
|
}
|
|
|
|
function preventExtensionsOnRootViaSeal(root) {
|
|
Object.seal(root);
|
|
}
|
|
|
|
function preventExtensionsOnFooViaSeal(root) {
|
|
Object.seal(root.foo);
|
|
}
|
|
|
|
function preventExtensionsOnRootViaFreeze(root) {
|
|
Object.freeze(root);
|
|
}
|
|
|
|
function preventExtensionsOnFooViaFreeze(root) {
|
|
Object.freeze(root.foo);
|
|
}
|
|
|
|
function setOnRoot(root) {
|
|
root.xyzzy = 'gwak';
|
|
}
|
|
|
|
function setOnFoo(root) {
|
|
root.foo.xyzzy = 'gwak';
|
|
}
|
|
|
|
function setQux(root) {
|
|
root.qux = 'gwak';
|
|
}
|
|
|
|
function setQuux(root) {
|
|
root.quux.push('gwak');
|
|
}
|
|
|
|
function setQuuxItem(root) {
|
|
root.quux[0] = 'gwak';
|
|
}
|
|
|
|
function setQuuxProperty(root) {
|
|
root.quux[1].corge = 'gwak';
|
|
}
|
|
|
|
function setPrototypeOfRoot(root) {
|
|
Object.setPrototypeOf(root, Array);
|
|
}
|
|
|
|
function setPrototypeOfFoo(root) {
|
|
Object.setPrototypeOf(root.foo, Array);
|
|
}
|
|
|
|
function setPrototypeOfQuux(root) {
|
|
Object.setPrototypeOf(root.quux, Array);
|
|
}
|
|
|
|
|
|
{
|
|
assert.ok(testOriginal(obj()));
|
|
|
|
assert.throws(
|
|
() => definePropertyOnRoot(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => definePropertyOnFoo(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => deletePropertyOnRoot(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => deletePropertyOnFoo(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => preventExtensionsOnRoot(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => preventExtensionsOnFoo(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => preventExtensionsOnRootViaSeal(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => preventExtensionsOnFooViaSeal(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => preventExtensionsOnRootViaFreeze(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => preventExtensionsOnFooViaFreeze(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => setOnRoot(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => setOnFoo(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => setQux(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => setQuux(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => setQuux(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => setQuuxItem(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => setQuuxProperty(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => setPrototypeOfRoot(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => setPrototypeOfFoo(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
assert.throws(
|
|
() => setPrototypeOfQuux(obj()),
|
|
{ code: 'ERR_ASSERTION' }
|
|
);
|
|
|
|
// Test that no mutation happened:
|
|
assert.ok(testOriginal(obj()));
|
|
}
|
|
|
|
// Test various supported types, directly and nested:
|
|
[
|
|
undefined, null, false, true, 42, 42n, Symbol('42'), NaN, Infinity, {}, [],
|
|
() => {}, async () => {}, Promise.resolve(), Math, { __proto__: null },
|
|
].forEach((target) => {
|
|
assert.deepStrictEqual(mustNotMutateObjectDeep(target), target);
|
|
assert.deepStrictEqual(mustNotMutateObjectDeep({ target }), { target });
|
|
assert.deepStrictEqual(mustNotMutateObjectDeep([ target ]), [ target ]);
|
|
});
|
|
|
|
// Test that passed functions keep working correctly:
|
|
{
|
|
const fn = () => 'blep';
|
|
fn.foo = {};
|
|
const fnImmutableView = mustNotMutateObjectDeep(fn);
|
|
assert.deepStrictEqual(fnImmutableView, fn);
|
|
|
|
// Test that the function still works:
|
|
assert.strictEqual(fn(), 'blep');
|
|
assert.strictEqual(fnImmutableView(), 'blep');
|
|
|
|
// Test that the original function is not deeply frozen:
|
|
fn.foo.bar = 'baz';
|
|
assert.strictEqual(fn.foo.bar, 'baz');
|
|
assert.strictEqual(fnImmutableView.foo.bar, 'baz');
|
|
|
|
// Test the original function is not frozen:
|
|
fn.qux = 'quux';
|
|
assert.strictEqual(fn.qux, 'quux');
|
|
assert.strictEqual(fnImmutableView.qux, 'quux');
|
|
|
|
// Redefining util.promisify.custom also works:
|
|
promisify(mustNotMutateObjectDeep(promisify(fn)));
|
|
}
|