test(storage): adopt run-test runner and fix Bun shim caching and mocking for storage - #9458
Conversation
Adds bin/run-test.cjs and bin/proxyquire-bun-shim.cjs to run Mocha tests across both Node.js and Bun without breaking Node coverage or parallelism. When invoked under Node.js, bin/run-test.cjs delegates to c8 and Mocha with worker-thread parallelism enabled. When invoked under Bun (via bun --bun or JS_RUNTIME=bun), it skips c8, disables Mocha worker threads (--no-parallel), preloads the Bun proxyquire compatibility shim, and executes Mocha directly in-process so #!/usr/bin/env node shebangs do not silently switch execution back to Node.js.
…gnal timeout override
…ogging-bunyan, logging-winston, pubsub, spanner, spanner-driver, and storage
…into bun-runtime/1-test-runner-handwritten-libraries # Conflicts: # core/packages/gax/.mocharc.js
This reverts commit 5a29104.
This reverts commit c9446ab.
…ps://github.com/googleapis/google-cloud-node into bun-runtime/1-test-runner-handwritten-libraries
This reverts commit 4c4a74d.
…ime" This reverts commit 486a301.
…bun runtime"" This reverts commit 687e386.
This reverts commit 756cc45.
…into bun-runtime/1-test-runner-handwritten-libraries-2
…ttps://github.com/googleapis/google-cloud-node into bun-runtime/1-test-runner-handwritten-libraries-2
…th bun tests instead
…gging, logging-bunyan, logging-winston, pubsub, spanner, spanner-driver, and storage" This reverts commit 8088cf4.
There was a problem hiding this comment.
Code Review
This pull request introduces a Bun proxyquire shim (bin/proxyquire-bun-shim.cjs) that overrides module caching and implements a custom __googleCloudBunFetch utility using Node's http/https modules to ensure compatibility under Bun. It also standardizes test execution across multiple packages by routing them through run-test.cjs, and resolves specific test issues in logging and pubsub to prevent deadlocks and unexpected logs. The review feedback highlights three key issues in the proxyquire shim: first, checking init.headers.entries is insufficient because arrays also have an entries method, which could corrupt headers; second, __googleCloudBunFetch should explicitly support Uint8Array and ArrayBuffer request bodies to avoid sending empty payloads; and third, Module._load should be called statically rather than with an instance context (this) to prevent errors in monkeypatched environments.
| if ( | ||
| init.headers instanceof Headers || | ||
| (init.headers && typeof init.headers.entries === 'function') | ||
| ) { | ||
| for (const [k, v] of init.headers.entries()) { | ||
| headers[k] = v; | ||
| } | ||
| } else { | ||
| headers = {...init.headers}; | ||
| } |
There was a problem hiding this comment.
Checking typeof init.headers.entries === 'function' is not sufficient to distinguish a Headers object from a plain Array, because Array.prototype.entries also exists but returns an iterator of [index, element] instead of [key, value]. If init.headers is passed as an array of key-value pairs, this will corrupt the headers object. We should explicitly handle the Array case first.
if (init.headers instanceof Headers) {
for (const [k, v] of init.headers.entries()) {
headers[k] = v;
}
} else if (Array.isArray(init.headers)) {
for (const [k, v] of init.headers) {
headers[k] = v;
}
} else if (typeof init.headers.entries === 'function') {
for (const [k, v] of init.headers.entries()) {
headers[k] = v;
}
} else {
headers = {...init.headers};
}
| } else if ( | ||
| typeof init.body === 'string' || | ||
| Buffer.isBuffer(init.body) | ||
| ) { | ||
| req.write(init.body); | ||
| req.end(); |
There was a problem hiding this comment.
The current implementation only writes the request body if it is a string or a Node Buffer. However, in modern JS runtimes (and especially under Bun), request bodies are frequently passed as Uint8Array or ArrayBuffer (e.g., when sending serialized protobufs). If a Uint8Array is passed, it will fall through to the else block and send an empty body. We should explicitly support Uint8Array and ArrayBuffer.
} else if (
typeof init.body === 'string' ||
Buffer.isBuffer(init.body) ||
init.body instanceof Uint8Array ||
init.body instanceof ArrayBuffer
) {
const chunk = init.body instanceof ArrayBuffer ? new Uint8Array(init.body) : init.body;
req.write(chunk);
req.end();
| return patchGaxiosIfPresent( | ||
| Module._load.call(this, id, this, /* isMain */ false), | ||
| ); |
There was a problem hiding this comment.
Module._load is a static method on the Module class, not an instance method. Calling it with this (which is a Module instance inside Module.prototype.require) as the this context can cause unexpected behavior or errors in monkeypatched implementations of _load that expect this to be the Module class. It should be called directly as Module._load.
return patchGaxiosIfPresent(
Module._load(id, this, /* isMain */ false),
);
… and fetch sections
…() in Bun fetch shim
…stance this context
18f3f92 to
5101c0b
Compare
5101c0b to
1a5185d
Compare
Description
handwritten/storage/package.jsonto invokebin/run-test.cjs.Module._loaddelegation toModule.prototype.requireinbin/proxyquire-bun-shim.cjsso tests that temporarily monkeypatchModule._loadfor dynamic import recovery execute properly in Bun.Module._cacheandrequire.cacheinbin/proxyquire-bun-shim.cjsso module mocking tools likemockeryandproxyquirecan isolate clean caches and restore previously loaded modules without cross-test pollution.fetchusage inglobalThis.__googleCloudBunFetchwith Node'shttp.requestandhttps.requestimplementations inbin/proxyquire-bun-shim.cjsso network requests under Bun are transparently intercepted bynock.Impact
This change allows the test suites for Storage to execute seamlessly under both Node.js and Bun runtimes without modifying library implementation code. It resolves critical Bun test failures where HTTP requests bypassed
nockinterception and module cache wipes corrupted cached class definitions across test files. As a result, all 1,278 unit tests in Storage now pass cleanly under the Bun runtime while fully preserving standard test execution and coverage under Node.js.Testing
Ran
bun --bun run test(as well asJS_RUNTIME=bun node ../../bin/run-test.cjs build/cjs/test) inhandwritten/storageto verify that all 1,278 unit tests pass with zero failures under Bun. In addition, rannode ../../bin/run-test.cjs --no-c8 build/cjs/testto ensure backward compatibility and verify all 1,278 unit tests continue passing under Node.js.Note that now we only use the fetch shim for unit tests because unit tests use nock which bun avoids without the shim, but with the system tests we do not want to use the shim to steer the tests toward the mock because we are evaluating the behaviour of the whole client.