test(e2e): serve snap tarballs from local binaries (MMQA-1366)#29282
test(e2e): serve snap tarballs from local binaries (MMQA-1366)#29282chrisleewilcox wants to merge 17 commits into
Conversation
…balls Ports the snap binary download tool from Extension for MMQA-1366. Downloads tarball and response headers from npm registry, saves as .txt and -headers.json files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Downloaded from npm registry for offline serving during E2E tests. Each snap has a .txt (raw tarball) and -headers.json (response headers). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Provides createSnapMock() and 21 individual mock functions that intercept npm registry tarball downloads via the /proxy pattern and return local binary files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…provider specs These specs already had testSpecificMock — adds the snap tarball mock call alongside existing feature flag and genesis block mocks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each spec now intercepts the npm registry tarball download for its snap and serves the local binary via MockServer's /proxy pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mocks both bip32 and json-rpc snap tarballs in the same testSpecificMock since the spec installs both snaps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Now that all snap tarballs are served locally via testSpecificMock, the bypass for malformed npm: URIs is no longer needed. Any unmocked npm registry requests will be caught by validateLiveRequests(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
- Add .gitattributes binary marker for snap tarball .txt files to prevent CRLF normalization corruption - Inline getDecodedProxiedURL to remove cross-layer import from smoke/notifications into the shared api-mocking layer - Add headersPath existence check in createSnapMock for a clear error message when the -headers.json file is missing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The ethereum-provider and multichain-provider snaps make a direct GET request using a malformed npm: URI that bypasses the /proxy route. The binary mocks only intercept proxied tarball requests, so these direct requests still need the allowlist bypass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #29282 +/- ##
==========================================
+ Coverage 81.54% 81.96% +0.41%
==========================================
Files 5343 5446 +103
Lines 142128 145459 +3331
Branches 32411 33227 +816
==========================================
+ Hits 115899 119224 +3325
+ Misses 18299 18105 -194
- Partials 7930 8130 +200 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…nary Address CodeQL "Network data written to file" finding by validating the snap name against path traversal characters and verifying resolved output paths stay within the target directory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f2ef5e4. Configure here.
…uplicating Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@copilot resolve the merge conflicts in this pull request |
…binaries # Conflicts: # .gitattributes # tests/smoke/snaps/test-snap-background-events.spec.ts # tests/smoke/snaps/test-snap-bip-32.spec.ts # tests/smoke/snaps/test-snap-bip-44.spec.ts # tests/smoke/snaps/test-snap-client-status.spec.ts # tests/smoke/snaps/test-snap-cronjob.spec.ts # tests/smoke/snaps/test-snap-dialog.spec.ts # tests/smoke/snaps/test-snap-get-entropy.spec.ts # tests/smoke/snaps/test-snap-get-file.spec.ts # tests/smoke/snaps/test-snap-get-preferences.spec.ts # tests/smoke/snaps/test-snap-image.spec.ts # tests/smoke/snaps/test-snap-installed.spec.ts # tests/smoke/snaps/test-snap-interactive-ui.spec.ts # tests/smoke/snaps/test-snap-jsx.spec.ts # tests/smoke/snaps/test-snap-lifecycle.spec.ts # tests/smoke/snaps/test-snap-manage-state.spec.ts # tests/smoke/snaps/test-snap-management.spec.ts # tests/smoke/snaps/test-snap-name-lookup.spec.ts # tests/smoke/snaps/test-snap-rpc.spec.ts # tests/smoke/snaps/test-snap-uilinks.spec.ts # tests/smoke/snaps/test-snap-wasm.spec.ts Co-authored-by: chrisleewilcox <6626407+chrisleewilcox@users.noreply.github.com>
Mocks for snap tarball downloads (added in this PR) never fired because ReactNativeBlobUtil.fetch() and config(...).fetch() invoke the native HTTP bridge directly, bypassing shim.js's fetch/XHR/expo-fetch patches. Tests have been silently hitting the live npm registry. Adds a JS-layer shim that wraps RNBlobUtil's fetch APIs so HTTP/HTTPS URLs are rewritten to MockServerE2E's /proxy?url= endpoint before the native call. Mirrors the existing shim.js pattern for fetch/XHR/WebSocket /expo-fetch. iOS and Android both work without OS-level proxy infra. - app/util/test/blobUtilShim.js: rewriteBlobUtilUrl + idempotent patchReactNativeBlobUtilForE2E that wraps fetch and config in place - app/util/test/blobUtilShim.test.js: 14 tests covering edge cases (https/http, file://, content://, RNBlobUtil-file://, already-proxied, non-strings, custom host, idempotency) plus an integration test that mirrors npm.ts's call pattern exactly - shim.js: wires the patch into the existing isMockServerAvailable block - tests/api-mocking/MockServerE2E.ts: extends handleDirectFetch to forward content-length / content-type / content-encoding / content-disposition / etag / last-modified upstream headers (npm.ts asserts content-length is present) Runtime evidence on Android emulator (recorded in MMQA-1805): - [SPIKE] blob-util GET -> status=200 len=17 logged from inside the app - /proxy?url=https://registry.npmjs.org/spike-test received by mock server Follow-up: the npm: regex bypass in MockServerE2E.ts (line 163) is left in place. The malformed https://https//npm:@metamask/... URIs from ethereum-provider and multichain-provider snaps now flow through the shim to /proxy, where the URL constructor parses host as "https" — not in any allowlist. Removing the bypass requires either a synthetic mock matcher for the malformed pattern or an upstream Snaps fix; tracked separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ing" This reverts commit 90806bb.
ReactNativeBlobUtil.fetch() bypasses shim.js's JS-layer fetch/XHR/expo-fetch patches because it dispatches directly to OkHttp/NSURLSession. As a result, the testSpecificMock rules registered for snap tarball downloads never fire, and tests silently hit the live npm registry. Rewrite the tarball URL at the source — inside NpmLocation.fetchNpmTarball — when running in E2E. The request body and headers contract with native ReactNativeBlobUtil stays untouched; only the target URL changes from `https://registry.npmjs.org/...` to `http://localhost:<mockServerPort>/proxy?url=<encoded-target>`. Changes: - app/core/Snaps/location/npm.ts: rewrite tarballUrl when isE2E - app/util/test/utils.js: export FALLBACK_MOCK_SERVER_PORT + getMockServerPortInApp helper, mirroring fixtureServerPort/commandQueueServerPort pattern - shim.js: store launchArgs.mockServerPort in testConfig so app code can read it - app/core/Snaps/location/npm.test.ts: 2 new tests covering the rewrite both ways (E2E on / off) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection:
The entire PR is about making SmokeSnaps tests work with local binary mocking instead of live npm registry access. The production code change in Only SmokeSnaps is needed - no other test areas are affected by these changes. Performance Test Selection: |
|




Description
Eliminates live npm registry tarball downloads during flask E2E tests by serving snap binaries from local files via MockServer.
Problem: iOS flask shards were timing out at 35m (25% shard failure rate) because
installSnap()fetched tarballs live fromregistry.npmjs.org. The first test in a shard would hang on the registry fetch, cascade-failing every subsequent test.Solution: Pre-download all 21 snap tarballs and serve them from MockServer's
/proxyhandler viatestSpecificMock, matching the pattern used by the Extension team in MetaMask/metamask-extension#32555.Changes:
scripts/update-snap-binary.ts— CLI tool to download snap tarballs from npm (yarn update-snap-binary --<snap>@<version>). Includes input sanitization and path traversal guards (CodeQL).tests/api-mocking/mock-response-data/snaps/snap-binaries-and-headers/— pre-downloaded tarballs + response headers for all 21 snapstests/api-mocking/mock-response-data/snaps/snap-binary-mocks.ts—createSnapMock()registers mockttp rules to intercept npm registry tarball URLs and return local binaries. 21 exported convenience functions, one per snap.testSpecificMockcalling the appropriate mock function(s)tests/api-mocking/MockServerE2E.ts— retains thenpm:@metamask/regex allowlist bypass inisUrlAllowed(), since the ethereum-provider and multichain-provider snaps make a direct GET to a malformednpm:URI that bypasses the/proxyroute.gitattributes— marks snap binary.txtfiles as binary to prevent CRLF corruptionHow requests are protected:
There are two distinct request paths for snap installation:
Tarball downloads (via
/proxy) — fully protected. The app fetches tarballs throughGET /proxy?url=https://registry.npmjs.org/@metamask/.... OurtestSpecificMockintercepts these with aforGet('/proxy').matching(...)rule. If the mock doesn't match (e.g. snap version updated but local binary wasn't refreshed), the request falls through to the proxy catch-all handler, which decodes the URL toregistry.npmjs.org/.... Sinceregistry.npmjs.orgis not in the allowlist,isUrlAllowed()returns false → the request is tracked in_liveRequests→validateLiveRequests()fails the test. A stale binary will never silently hit the live registry.Malformed
npm:URI requests (direct, not via/proxy) — harmless. The ethereum-provider and multichain-provider snaps make a direct GET to a malformed URL likehttps://https//npm:@metamask/ethereum-provider-example-snap. These hit the unmatched request handler, not/proxy. The npm URI bypass inisUrlAllowed()catches these so they don't failvalidateLiveRequests(). They always fail with DNS errors (getaddrinfo ENOTFOUND https) because the URL is unparseable — they can never reach a live server.Keeping binaries up to date:
Local binaries are updated manually via
yarn update-snap-binary --<snap-name>@<version>. This is the same pattern the Extension team uses (MetaMask/metamask-extension#32555) — neither repo has CI automation to detect stale binaries. The safety net is that a version mismatch causes the mock to not match, triggering a clear test failure viavalidateLiveRequests()rather than silently falling through to the live registry.Changelog
CHANGELOG entry: null
Related issues
Fixes: MMQA-1366
Parent: MMQA-1364
Manual testing steps
Screenshots/Recordings
Before
Specs had no
testSpecificMock— snap tarballs were fetched live fromregistry.npmjs.orgthrough the mock server's/proxyfallback:After
Each spec now declares a
testSpecificMockthat registers mockttp rules to intercept the npm tarball URL and return the pre-downloaded local binary:Multi-snap tests call multiple mock functions:
Pre-merge author checklist
Pre-merge reviewer checklist
Note
Medium Risk
Medium risk because it changes Snap tarball download behavior in
NpmLocation(URL rewriting in E2E) and adds a large set of binary fixtures; failures would mainly impact E2E reliability rather than production behavior due to theisE2Egate.Overview
Stops live npm tarball downloads during E2E Snap installs by rewriting
NpmLocation.fetchNpmTarballto route registry tarball fetches through the local MockServer/proxywhenisE2Eis true (using a newgetMockServerPortInAppvalue populated from launch args).Adds a local snap-binary fixture system: a new
yarn update-snap-binaryscript to download and persist tarballs + selected response headers,.gitattributesrules to treat the.txttarballs as binary, andsnap-binary-mocks.tshelpers that registermockttprules to serve those local binaries.Updates Snap smoke specs to use
testSpecificMockto install snaps from local binaries (and tweaks a few assertions/sync behaviors for iOS vs Android), and expands unit coverage innpm.test.tsto verify the E2E URL rewrite behavior.Reviewed by Cursor Bugbot for commit d9a4ae6. Bugbot is set up for automated code reviews on this repo. Configure here.