diff --git a/bench/snippets/path-parse.mjs b/bench/snippets/path-parse.mjs new file mode 100644 index 0000000000..414d3e7e9a --- /dev/null +++ b/bench/snippets/path-parse.mjs @@ -0,0 +1,18 @@ +import { posix, win32 } from "path"; +import { bench, run } from "../runner.mjs"; + +const paths = ["/home/user/dir/file.txt", "/home/user/dir/", "file.txt", "/root", ""]; + +paths.forEach(p => { + bench(`posix.parse(${JSON.stringify(p)})`, () => { + globalThis.abc = posix.parse(p); + }); +}); + +paths.forEach(p => { + bench(`win32.parse(${JSON.stringify(p)})`, () => { + globalThis.abc = win32.parse(p); + }); +}); + +await run(); diff --git a/src/StandaloneModuleGraph.zig b/src/StandaloneModuleGraph.zig index 3985a6aee6..dd63115ee9 100644 --- a/src/StandaloneModuleGraph.zig +++ b/src/StandaloneModuleGraph.zig @@ -935,7 +935,7 @@ pub const StandaloneModuleGraph = struct { var remain = bytes; while (remain.len > 0) { - switch (Syscall.write(cloned_executable_fd, bytes)) { + switch (Syscall.write(cloned_executable_fd, remain)) { .result => |written| remain = remain[written..], .err => |err| { Output.prettyErrorln("error: failed to write to temporary file\n{f}", .{err}); diff --git a/src/bun.js/bindings/Path.cpp b/src/bun.js/bindings/Path.cpp index d8b110a425..3578ba23c9 100644 --- a/src/bun.js/bindings/Path.cpp +++ b/src/bun.js/bindings/Path.cpp @@ -114,6 +114,25 @@ static JSC::JSObject* createPath(JSGlobalObject* globalThis, bool isWindows) } // namespace Zig +extern "C" JSC::EncodedJSValue PathParsedObject__create( + JSC::JSGlobalObject* globalObject, + JSC::EncodedJSValue root, + JSC::EncodedJSValue dir, + JSC::EncodedJSValue base, + JSC::EncodedJSValue ext, + JSC::EncodedJSValue name) +{ + auto* global = JSC::jsCast(globalObject); + auto& vm = JSC::getVM(globalObject); + JSC::JSObject* result = JSC::constructEmptyObject(vm, global->pathParsedObjectStructure()); + result->putDirectOffset(vm, 0, JSC::JSValue::decode(root)); + result->putDirectOffset(vm, 1, JSC::JSValue::decode(dir)); + result->putDirectOffset(vm, 2, JSC::JSValue::decode(base)); + result->putDirectOffset(vm, 3, JSC::JSValue::decode(ext)); + result->putDirectOffset(vm, 4, JSC::JSValue::decode(name)); + return JSC::JSValue::encode(result); +} + namespace Bun { JSC::JSValue createNodePathBinding(Zig::GlobalObject* globalObject) diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 31373b7359..9cb9399815 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -2067,6 +2067,30 @@ void GlobalObject::finishCreation(VM& vm) init.set(structure); }); + this->m_pathParsedObjectStructure.initLater( + [](const Initializer& init) { + // { root, dir, base, ext, name } — path.parse() result + Structure* structure = init.owner->structureCache().emptyObjectStructureForPrototype( + init.owner, init.owner->objectPrototype(), 5); + PropertyOffset offset; + structure = Structure::addPropertyTransition(init.vm, structure, + Identifier::fromString(init.vm, "root"_s), 0, offset); + RELEASE_ASSERT(offset == 0); + structure = Structure::addPropertyTransition(init.vm, structure, + Identifier::fromString(init.vm, "dir"_s), 0, offset); + RELEASE_ASSERT(offset == 1); + structure = Structure::addPropertyTransition(init.vm, structure, + Identifier::fromString(init.vm, "base"_s), 0, offset); + RELEASE_ASSERT(offset == 2); + structure = Structure::addPropertyTransition(init.vm, structure, + Identifier::fromString(init.vm, "ext"_s), 0, offset); + RELEASE_ASSERT(offset == 3); + structure = Structure::addPropertyTransition(init.vm, structure, + init.vm.propertyNames->name, 0, offset); + RELEASE_ASSERT(offset == 4); + init.set(structure); + }); + this->m_pendingVirtualModuleResultStructure.initLater( [](const Initializer& init) { init.set(Bun::PendingVirtualModuleResult::createStructure(init.vm, init.owner, init.owner->objectPrototype())); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 21c8d41f5c..5225b67ddd 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -567,6 +567,7 @@ public: V(public, LazyClassStructure, m_JSHTTPParserClassStructure) \ \ V(private, LazyPropertyOfGlobalObject, m_jsonlParseResultStructure) \ + V(private, LazyPropertyOfGlobalObject, m_pathParsedObjectStructure) \ V(private, LazyPropertyOfGlobalObject, m_pendingVirtualModuleResultStructure) \ V(private, LazyPropertyOfGlobalObject, m_performMicrotaskFunction) \ V(private, LazyPropertyOfGlobalObject, m_nativeMicrotaskTrampoline) \ @@ -702,6 +703,7 @@ public: void reload(); JSC::Structure* jsonlParseResultStructure() { return m_jsonlParseResultStructure.get(this); } + JSC::Structure* pathParsedObjectStructure() { return m_pathParsedObjectStructure.get(this); } JSC::Structure* pendingVirtualModuleResultStructure() { return m_pendingVirtualModuleResultStructure.get(this); } // We need to know if the napi module registered itself or we registered it. diff --git a/src/bun.js/node/path.zig b/src/bun.js/node/path.zig index 6f2103c58f..76f09cf1dc 100644 --- a/src/bun.js/node/path.zig +++ b/src/bun.js/node/path.zig @@ -71,13 +71,12 @@ fn PathParsed(comptime T: type) type { name: []const T = "", pub fn toJSObject(this: @This(), globalObject: *jsc.JSGlobalObject) bun.JSError!jsc.JSValue { - var jsObject = jsc.JSValue.createEmptyObject(globalObject, 5); - jsObject.put(globalObject, jsc.ZigString.static("root"), try bun.String.createUTF8ForJS(globalObject, this.root)); - jsObject.put(globalObject, jsc.ZigString.static("dir"), try bun.String.createUTF8ForJS(globalObject, this.dir)); - jsObject.put(globalObject, jsc.ZigString.static("base"), try bun.String.createUTF8ForJS(globalObject, this.base)); - jsObject.put(globalObject, jsc.ZigString.static("ext"), try bun.String.createUTF8ForJS(globalObject, this.ext)); - jsObject.put(globalObject, jsc.ZigString.static("name"), try bun.String.createUTF8ForJS(globalObject, this.name)); - return jsObject; + const root = try bun.String.createUTF8ForJS(globalObject, this.root); + const dir = try bun.String.createUTF8ForJS(globalObject, this.dir); + const base = try bun.String.createUTF8ForJS(globalObject, this.base); + const ext = try bun.String.createUTF8ForJS(globalObject, this.ext); + const name_val = try bun.String.createUTF8ForJS(globalObject, this.name); + return PathParsedObject__create(globalObject, root, dir, base, ext, name_val); } }; } @@ -2748,6 +2747,14 @@ pub fn resolveJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, allocato } extern "c" fn Process__getCachedCwd(*jsc.JSGlobalObject) jsc.JSValue; +extern "c" fn PathParsedObject__create( + *jsc.JSGlobalObject, + jsc.JSValue, + jsc.JSValue, + jsc.JSValue, + jsc.JSValue, + jsc.JSValue, +) jsc.JSValue; pub fn resolve(globalObject: *jsc.JSGlobalObject, isWindows: bool, args_ptr: [*]jsc.JSValue, args_len: u16) bun.JSError!jsc.JSValue { var arena = bun.ArenaAllocator.init(bun.default_allocator); diff --git a/src/js/node/crypto.ts b/src/js/node/crypto.ts index d6c3218b87..8b5edca9cf 100644 --- a/src/js/node/crypto.ts +++ b/src/js/node/crypto.ts @@ -199,18 +199,18 @@ function Sign(algorithm, options): void { } $toClass(Sign, "Sign", Writable); -Sign.prototype._write = function _write(chunk, encoding, callback) { - this.update(chunk, encoding); - callback(); -}; - -Sign.prototype.update = function update(data, encoding) { - return this[kHandle].update(this, data, encoding); -}; - -Sign.prototype.sign = function sign(options, encoding) { - return this[kHandle].sign(options, encoding); -}; +Object.assign(Sign.prototype, { + _write: function (chunk, encoding, callback) { + this.update(chunk, encoding); + callback(); + }, + update: function (data, encoding) { + return this[kHandle].update(this, data, encoding); + }, + sign: function (options, encoding) { + return this[kHandle].sign(options, encoding); + }, +}); crypto_exports.Sign = Sign; crypto_exports.sign = sign; @@ -237,9 +237,11 @@ $toClass(Verify, "Verify", Writable); Verify.prototype._write = Sign.prototype._write; Verify.prototype.update = Sign.prototype.update; -Verify.prototype.verify = function verify(options, signature, sigEncoding) { - return this[kHandle].verify(options, signature, sigEncoding); -}; +Object.assign(Verify.prototype, { + verify: function (options, signature, sigEncoding) { + return this[kHandle].verify(options, signature, sigEncoding); + }, +}); crypto_exports.Verify = Verify; crypto_exports.verify = verify; @@ -250,82 +252,76 @@ function createVerify(algorithm, options?) { crypto_exports.createVerify = createVerify; -{ - function Hash(algorithm, options?): void { - if (!new.target) { - return new Hash(algorithm, options); - } - - const handle = new _Hash(algorithm, options); - this[kHandle] = handle; - - LazyTransform.$apply(this, [options]); +function Hash(algorithm, options?): void { + if (!new.target) { + return new Hash(algorithm, options); } - $toClass(Hash, "Hash", LazyTransform); - Hash.prototype.copy = function copy(options) { + const handle = new _Hash(algorithm, options); + this[kHandle] = handle; + + LazyTransform.$apply(this, [options]); +} +$toClass(Hash, "Hash", LazyTransform); + +Object.assign(Hash.prototype, { + copy: function (options) { return new Hash(this[kHandle], options); - }; - - Hash.prototype._transform = function _transform(chunk, encoding, callback) { + }, + _transform: function (chunk, encoding, callback) { this[kHandle].update(this, chunk, encoding); callback(); - }; - - Hash.prototype._flush = function _flush(callback) { + }, + _flush: function (callback) { this.push(this[kHandle].digest(null, false)); callback(); - }; - - Hash.prototype.update = function update(data, encoding) { + }, + update: function (data, encoding) { return this[kHandle].update(this, data, encoding); - }; - - Hash.prototype.digest = function digest(outputEncoding) { + }, + digest: function (outputEncoding) { return this[kHandle].digest(outputEncoding); - }; + }, +}); - crypto_exports.Hash = Hash; - crypto_exports.createHash = function createHash(algorithm, options) { - return new Hash(algorithm, options); - }; -} +crypto_exports.Hash = Hash; +crypto_exports.createHash = function createHash(algorithm, options) { + return new Hash(algorithm, options); +}; -{ - function Hmac(hmac, key, options?): void { - if (!new.target) { - return new Hmac(hmac, key, options); - } - - const handle = new _Hmac(hmac, key, options); - this[kHandle] = handle; - - LazyTransform.$apply(this, [options]); +function Hmac(hmac, key, options?): void { + if (!new.target) { + return new Hmac(hmac, key, options); } - $toClass(Hmac, "Hmac", LazyTransform); - Hmac.prototype.update = function update(data, encoding) { + const handle = new _Hmac(hmac, key, options); + this[kHandle] = handle; + + LazyTransform.$apply(this, [options]); +} +$toClass(Hmac, "Hmac", LazyTransform); + +Object.assign(Hmac.prototype, { + update: function (data, encoding) { return this[kHandle].update(this, data, encoding); - }; - - Hmac.prototype.digest = function digest(outputEncoding) { + }, + digest: function (outputEncoding) { return this[kHandle].digest(outputEncoding); - }; - - Hmac.prototype._transform = function _transform(chunk, encoding, callback) { + }, + _transform: function (chunk, encoding, callback) { this[kHandle].update(this, chunk, encoding); callback(); - }; - Hmac.prototype._flush = function _flush(callback) { + }, + _flush: function (callback) { this.push(this[kHandle].digest()); callback(); - }; + }, +}); - crypto_exports.Hmac = Hmac; - crypto_exports.createHmac = function createHmac(hmac, key, options) { - return new Hmac(hmac, key, options); - }; -} +crypto_exports.Hmac = Hmac; +crypto_exports.createHmac = function createHmac(hmac, key, options) { + return new Hmac(hmac, key, options); +}; crypto_exports.getHashes = getHashes; @@ -390,62 +386,6 @@ crypto_exports.createECDH = function createECDH(curve) { return decoder; } - function setAutoPadding(ap) { - this[kHandle].setAutoPadding(ap); - return this; - } - - function getAuthTag() { - return this[kHandle].getAuthTag(); - } - - function setAuthTag(tagbuf, encoding) { - this[kHandle].setAuthTag(tagbuf, encoding); - return this; - } - - function setAAD(aadbuf, options) { - this[kHandle].setAAD(aadbuf, options); - return this; - } - - function _transform(chunk, encoding, callback) { - this.push(this[kHandle].update(chunk, encoding)); - callback(); - } - - function _flush(callback) { - try { - this.push(this[kHandle].final()); - } catch (e) { - callback(e); - return; - } - callback(); - } - - function update(data, inputEncoding, outputEncoding) { - const res = this[kHandle].update(data, inputEncoding); - - if (outputEncoding && outputEncoding !== "buffer") { - this._decoder = getDecoder(this._decoder, outputEncoding); - return this._decoder.write(res); - } - - return res; - } - - function final(outputEncoding) { - const res = this[kHandle].final(); - - if (outputEncoding && outputEncoding !== "buffer") { - this._decoder = getDecoder(this._decoder, outputEncoding); - return this._decoder.end(res); - } - - return res; - } - function Cipheriv(cipher, key, iv, options): void { if (!new.target) { return new Cipheriv(cipher, key, iv, options); @@ -457,13 +397,52 @@ crypto_exports.createECDH = function createECDH(curve) { } $toClass(Cipheriv, "Cipheriv", LazyTransform); - Cipheriv.prototype.setAutoPadding = setAutoPadding; - Cipheriv.prototype.getAuthTag = getAuthTag; - Cipheriv.prototype.setAAD = setAAD; - Cipheriv.prototype._transform = _transform; - Cipheriv.prototype._flush = _flush; - Cipheriv.prototype.update = update; - Cipheriv.prototype.final = final; + Object.assign(Cipheriv.prototype, { + setAutoPadding: function (ap) { + this[kHandle].setAutoPadding(ap); + return this; + }, + getAuthTag: function () { + return this[kHandle].getAuthTag(); + }, + setAAD: function (aadbuf, options) { + this[kHandle].setAAD(aadbuf, options); + return this; + }, + _transform: function (chunk, encoding, callback) { + this.push(this[kHandle].update(chunk, encoding)); + callback(); + }, + _flush: function (callback) { + try { + this.push(this[kHandle].final()); + } catch (e) { + callback(e); + return; + } + callback(); + }, + update: function (data, inputEncoding, outputEncoding) { + const res = this[kHandle].update(data, inputEncoding); + + if (outputEncoding && outputEncoding !== "buffer") { + this._decoder = getDecoder(this._decoder, outputEncoding); + return this._decoder.write(res); + } + + return res; + }, + final: function (outputEncoding) { + const res = this[kHandle].final(); + + if (outputEncoding && outputEncoding !== "buffer") { + this._decoder = getDecoder(this._decoder, outputEncoding); + return this._decoder.end(res); + } + + return res; + }, + }); function Decipheriv(cipher, key, iv, options): void { if (!new.target) { @@ -476,13 +455,18 @@ crypto_exports.createECDH = function createECDH(curve) { } $toClass(Decipheriv, "Decipheriv", LazyTransform); - Decipheriv.prototype.setAutoPadding = setAutoPadding; - Decipheriv.prototype.setAuthTag = setAuthTag; - Decipheriv.prototype.setAAD = setAAD; - Decipheriv.prototype._transform = _transform; - Decipheriv.prototype._flush = _flush; - Decipheriv.prototype.update = update; - Decipheriv.prototype.final = final; + Object.assign(Decipheriv.prototype, { + setAutoPadding: Cipheriv.prototype.setAutoPadding, + setAuthTag: function (tagbuf, encoding) { + this[kHandle].setAuthTag(tagbuf, encoding); + return this; + }, + setAAD: Cipheriv.prototype.setAAD, + _transform: Cipheriv.prototype._transform, + _flush: Cipheriv.prototype._flush, + update: Cipheriv.prototype.update, + final: Cipheriv.prototype.final, + }); crypto_exports.Cipheriv = Cipheriv; crypto_exports.Decipheriv = Decipheriv; diff --git a/src/sys.zig b/src/sys.zig index 285878683d..cb606398f4 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -4092,6 +4092,12 @@ pub fn copyFileZSlowWithHandle(in_handle: bun.FileDescriptor, to_dir: bun.FileDe _ = std.os.linux.fallocate(out_handle.cast(), 0, 0, @intCast(stat_.size)); } + // Seek input to beginning — the caller may have written to this fd, + // leaving the file offset at EOF. copy_file_range / sendfile / read + // all use the current offset when called with null offsets. + // Ignore errors: the fd may be non-seekable (e.g. a pipe). + _ = setFileOffset(in_handle, 0); + switch (bun.copyFile(in_handle, out_handle)) { .err => |e| return .{ .err = e }, .result => {}, diff --git a/test/bundler/bun-build-compile.test.ts b/test/bundler/bun-build-compile.test.ts index 8004a7bb78..491e3b8438 100644 --- a/test/bundler/bun-build-compile.test.ts +++ b/test/bundler/bun-build-compile.test.ts @@ -121,4 +121,71 @@ describe("Bun.build compile", () => { }); }); +describe("compiled binary validity", () => { + test("output binary has valid executable header", async () => { + using dir = tempDir("build-compile-valid-header", { + "app.js": `console.log("hello");`, + }); + + const outfile = join(dir + "", "app-out"); + const result = await Bun.build({ + entrypoints: [join(dir + "", "app.js")], + compile: { + outfile, + }, + }); + + expect(result.success).toBe(true); + + // Read the first 4 bytes and verify it's a valid executable magic number + const file = Bun.file(result.outputs[0].path); + const header = new Uint8Array(await file.slice(0, 4).arrayBuffer()); + + if (isMacOS) { + // MachO magic: 0xCFFAEDFE (little-endian) + expect(header[0]).toBe(0xcf); + expect(header[1]).toBe(0xfa); + expect(header[2]).toBe(0xed); + expect(header[3]).toBe(0xfe); + } else if (isLinux) { + // ELF magic: 0x7F 'E' 'L' 'F' + expect(header[0]).toBe(0x7f); + expect(header[1]).toBe(0x45); // 'E' + expect(header[2]).toBe(0x4c); // 'L' + expect(header[3]).toBe(0x46); // 'F' + } else if (isWindows) { + // PE magic: 'M' 'Z' + expect(header[0]).toBe(0x4d); // 'M' + expect(header[1]).toBe(0x5a); // 'Z' + } + }); + + test("compiled binary runs and produces expected output", async () => { + using dir = tempDir("build-compile-runs", { + "app.js": `console.log("compile-test-output");`, + }); + + const outfile = join(dir + "", "app-run"); + const result = await Bun.build({ + entrypoints: [join(dir + "", "app.js")], + compile: { + outfile, + }, + }); + + expect(result.success).toBe(true); + + await using proc = Bun.spawn({ + cmd: [result.outputs[0].path], + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stdout.trim()).toBe("compile-test-output"); + expect(exitCode).toBe(0); + }); +}); + // file command test works well diff --git a/test/js/node/crypto/node-crypto.test.js b/test/js/node/crypto/node-crypto.test.js index 2316d0ba5e..9d6ea12efa 100644 --- a/test/js/node/crypto/node-crypto.test.js +++ b/test/js/node/crypto/node-crypto.test.js @@ -504,6 +504,110 @@ describe("Hash", () => { expect(hash.update.name).toBe("update"); expect(hash.digest.name).toBe("digest"); expect(hash.copy.name).toBe("copy"); + expect(hash._transform.name).toBe("_transform"); + expect(hash._flush.name).toBe("_flush"); + }); +}); + +describe("Hmac", () => { + it("should have correct method names", () => { + const hmac = crypto.createHmac("sha256", "key"); + expect(hmac.update.name).toBe("update"); + expect(hmac.digest.name).toBe("digest"); + expect(hmac._transform.name).toBe("_transform"); + expect(hmac._flush.name).toBe("_flush"); + }); +}); + +describe("Sign", () => { + it("should have correct method names", () => { + const sign = crypto.createSign("sha256"); + expect(sign.update.name).toBe("update"); + expect(sign.sign.name).toBe("sign"); + expect(sign._write.name).toBe("_write"); + }); +}); + +describe("Verify", () => { + it("should have correct method names", () => { + const verify = crypto.createVerify("sha256"); + expect(verify.update.name).toBe("update"); + expect(verify.verify.name).toBe("verify"); + expect(verify._write.name).toBe("_write"); + }); +}); + +describe("Cipheriv", () => { + it("should have correct method names", () => { + const cipher = crypto.createCipheriv("aes-256-cbc", Buffer.alloc(32), Buffer.alloc(16)); + expect(cipher.update.name).toBe("update"); + expect(cipher.final.name).toBe("final"); + expect(cipher.setAutoPadding.name).toBe("setAutoPadding"); + expect(cipher.getAuthTag.name).toBe("getAuthTag"); + expect(cipher.setAAD.name).toBe("setAAD"); + expect(cipher._transform.name).toBe("_transform"); + expect(cipher._flush.name).toBe("_flush"); + }); +}); + +describe("Decipheriv", () => { + it("should have correct method names", () => { + const decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.alloc(32), Buffer.alloc(16)); + expect(decipher.update.name).toBe("update"); + expect(decipher.final.name).toBe("final"); + expect(decipher.setAutoPadding.name).toBe("setAutoPadding"); + expect(decipher.setAuthTag.name).toBe("setAuthTag"); + expect(decipher.setAAD.name).toBe("setAAD"); + expect(decipher._transform.name).toBe("_transform"); + expect(decipher._flush.name).toBe("_flush"); + }); +}); + +describe("DiffieHellman", () => { + it("should have correct method names", () => { + const dh = crypto.createDiffieHellman(512); + expect(dh.generateKeys.name).toBe("generateKeys"); + expect(dh.computeSecret.name).toBe("computeSecret"); + expect(dh.getPrime.name).toBe("getPrime"); + expect(dh.getGenerator.name).toBe("getGenerator"); + expect(dh.getPublicKey.name).toBe("getPublicKey"); + expect(dh.getPrivateKey.name).toBe("getPrivateKey"); + expect(dh.setPublicKey.name).toBe("setPublicKey"); + expect(dh.setPrivateKey.name).toBe("setPrivateKey"); + }); +}); + +describe("ECDH", () => { + it("should have correct method names", () => { + const ecdh = crypto.createECDH("prime256v1"); + expect(ecdh.generateKeys.name).toBe("generateKeys"); + expect(ecdh.computeSecret.name).toBe("computeSecret"); + expect(ecdh.getPublicKey.name).toBe("getPublicKey"); + expect(ecdh.getPrivateKey.name).toBe("getPrivateKey"); + expect(ecdh.setPublicKey.name).toBe("setPublicKey"); + expect(ecdh.setPrivateKey.name).toBe("setPrivateKey"); + }); +}); + +describe("crypto module", () => { + it("should have correct factory function names", () => { + expect(crypto.createHash.name).toBe("createHash"); + expect(crypto.createHmac.name).toBe("createHmac"); + expect(crypto.createSign.name).toBe("createSign"); + expect(crypto.createVerify.name).toBe("createVerify"); + expect(crypto.createCipheriv.name).toBe("createCipheriv"); + expect(crypto.createDecipheriv.name).toBe("createDecipheriv"); + expect(crypto.createDiffieHellman.name).toBe("createDiffieHellman"); + expect(crypto.createECDH.name).toBe("createECDH"); + expect(crypto.hash.name).toBe("hash"); + expect(crypto.pbkdf2.name).toBe("pbkdf2"); + }); + + it("should have correct constructor names", () => { + expect(crypto.Hash.name).toBe("Hash"); + expect(crypto.Hmac.name).toBe("Hmac"); + expect(crypto.Sign.name).toBe("Sign"); + expect(crypto.Verify.name).toBe("Verify"); }); });