'use strict'; const { test } = require('node:test'); const assert = require('node:assert'); const crypto = require('node:crypto'); const Sha256 = require('./sha256'); const ref = (buf) => crypto.createHash('sha256').update(buf).digest('hex'); test('known vectors', () => { assert.strictEqual(Sha256.hex(new Uint8Array(0)), 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); assert.strictEqual(Sha256.hex(new TextEncoder().encode('abc')), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'); assert.strictEqual( Sha256.hex(new TextEncoder().encode('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')), '248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1'); }); test('every length around block boundaries matches node:crypto', () => { for (let n = 0; n <= 200; n++) { const buf = crypto.randomBytes(n); assert.strictEqual(Sha256.hex(new Uint8Array(buf)), ref(buf), 'length ' + n); } }); test('random chunk boundaries give the same digest as one shot', () => { const buf = crypto.randomBytes(3 * 1024 * 1024 + 17); for (let round = 0; round < 5; round++) { const h = Sha256.create(); let pos = 0; while (pos < buf.length) { const n = Math.min(buf.length - pos, 1 + Math.floor(Math.random() * 200000)); h.update(new Uint8Array(buf.buffer, buf.byteOffset + pos, n)); pos += n; } assert.strictEqual(h.hex(), ref(buf)); assert.strictEqual(h.bytes, buf.length); } }); test('update after digest throws; isHex', () => { const h = Sha256.create(); h.hex(); assert.throws(() => h.update(new Uint8Array(1))); assert.ok(Sha256.isHex(ref(Buffer.from('x')))); assert.ok(!Sha256.isHex('ABC')); }); // ---- WebAssembly engine ---- test('wasm engine is available in Node and matches the JS engine and node:crypto', () => { const crypto = require('node:crypto'); assert.equal(Sha256.engine(), 'wasm'); const sizes = [0, 1, 55, 56, 63, 64, 65, 1000, (1 << 20) - 1, 1 << 20, (1 << 20) + 1, 3_000_001]; for (const n of sizes) { const buf = Buffer.alloc(n).map((_, i) => (i * 7 + n) & 255); const want = crypto.createHash('sha256').update(buf).digest('hex'); assert.equal(Sha256.create().update(buf).hex(), want, `wasm ${n}`); assert.equal(Sha256.create({ js: true }).update(buf).hex(), want, `js ${n}`); } }); test('wasm engine hashes streamed odd-sized chunks like one-shot', () => { const crypto = require('node:crypto'); const buf = Buffer.alloc(2_500_003).map((_, i) => (i * 13) & 255); const h = Sha256.create(); for (let o = 0; o < buf.length; o += 77_777) h.update(buf.subarray(o, o + 77_777)); assert.equal(h.hex(), crypto.createHash('sha256').update(buf).digest('hex')); }); test('falls back to the JS engine when WebAssembly is unavailable', () => { const { execFileSync } = require('node:child_process'); const out = execFileSync(process.execPath, ['-e', "delete globalThis.WebAssembly; const S=require('./frontend/sha256.js');" + "console.log(S.engine(), S.hex(new TextEncoder().encode('abc')))"], { cwd: require('node:path').join(__dirname, '..') }).toString().trim(); assert.equal(out, 'js ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'); });