Skip to content

test(assets-controller): websocket price-update integration tests - #10476

Merged
Prithpal-Sooriya merged 10 commits into
mainfrom
feature/use-memory-integrati-zdo
Sep 28, 2026
Merged

Prithpal-Sooriya merged 10 commits into
mainfrom
feature/use-memory-integrati-zdo

Conversation

@Prithpal-Sooriya

@Prithpal-Sooriya Prithpal-Sooriya commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Explanation

Code Walkthrough:
https://www.loom.com/share/a4226f00271249e0aa65d002468914f5

Adds integration tests covering the websocket (AccountActivity) balance-update pipeline behavior from #10410 — after a balanceUpdated event, the pipeline now requests metadata and price enrichment so both existing holdings and tokens first surfaced by the websocket get Tokens API metadata and a Price API spot price in the same pass (instead of waiting for the next price poll).

To make that pass testable in isolation (mirroring buildFastFetchSources for the fast pipeline, which serves both the v5 and v6 fast-fetch lanes), the websocket-update source construction is extracted from AssetsController#handleAssetsUpdateV5 and #handleAssetsUpdateV6 into buildWsUpdateSources. Like buildFastFetchSources, the builder takes an includeCustomAssetGraduation option (true on the v5 lane; the v6 lane never graduates custom assets and instead runs the RPC fallback ahead of detection), and is tested at three layers:

  • buildWsUpdateSources.test.ts — unit tests of the builder.
  • buildWsUpdateSources.price-updates.integration.test.ts — drives the real AccountActivityDataSource and executeAssetsPipeline (via buildWsUpdateSources) against recorded API responses:
  • AssetsController.ws-price-updates.integration.test.ts — full-controller integration test: a closed lifecycle, AccountActivityService:balanceUpdated published on the root messenger, asserting state balances, metadata, and spot prices all land from the same pipeline pass — on the default v5 lane and again with assetsAccountsApiV6: true (the v6 lane, where the RPC fallback replaces graduation).

API fixtures are captured from the live APIs into __fixtures__/ws-price-updates/ (re-runnable via captureWsApiResponses.ts), mirroring the scam-token-cleanup fixture layout. Like that folder, captureWsApiResponses.ts uses the async writeFile helpers from @metamask/utils/node so it passes import-x/no-nodejs-modules.

No consumer-facing behavior changes: buildWsUpdateSources is internal-only (the pipeline module is not re-exported from the package index), so no changelog entry is needed.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Low Risk
Changes are an internal pipeline refactor plus tests; PR description states no consumer-facing behavior change.

Overview
Refactors AssetsController update enrichment so inline middleware lists in #handleAssetsUpdateV5 / #handleAssetsUpdateV6 are replaced by buildUpdateSources (Accounts API and other non-websocket updates) and buildWsUpdateSources (AccountActivity websocket updates), mirroring the existing buildFastFetchSources pattern. Websocket lanes still differ by v5 vs v6 flags (custom-asset graduation vs RPC fallback, plus occurrence filtering on basic functionality).

Adds integration and unit coverage for the websocket balance-update path: metadata and spot prices land in the same pass as a AccountActivityService:balanceUpdated event (new holdings, held-but-unpriced natives, and skipping re-price/refetch for already-enriched tokens), exercised both through executeAssetsPipeline and full AssetsController runs on v5 and assetsAccountsApiV6 v6 lanes.

Ships recorded API fixtures under __fixtures__/ws-price-updates/ and a captureWsApiResponses.ts script to refresh them.

Reviewed by Cursor Bugbot for commit 1a942e6. Bugbot is set up for automated code reviews on this repo. Configure here.

Extract the websocket-update pipeline construction into
`buildWsUpdateSources` (mirroring `buildFastFetchSources` for the fast
pipeline) so the post-websocket-event enrichment pass can be tested in
isolation from `AssetsController.handleAssetsUpdate`.

Cover the behavior from #10410 at both layers:

- `buildWsUpdateSources.price-updates.integration.test.ts` drives the
  real `AccountActivityDataSource` + `executeAssetsPipeline` against
  recorded API responses, asserting detected/existing assets receive
  Tokens API metadata and Price API spot prices in the same pass.
- `AssetsController.ws-price-updates.integration.test.ts` runs the full
  controller on a closed lifecycle, publishing
  `AccountActivityService:balanceUpdated` events and asserting state
  balances, metadata, and spot prices land from the same pipeline pass.

Fixtures are captured from the live APIs into
`__fixtures__/ws-price-updates/` via `captureWsApiResponses.ts`,
mirroring the `scam-token-cleanup` fixture layout.
@Prithpal-Sooriya
Prithpal-Sooriya requested a review from a team as a code owner September 25, 2026 14:34
Resolve the AssetsController.ts conflict: keep the WS-update lane
construction (buildWsUpdateSources) from this branch and adopt main's
#getUpdatePipelineRequest helper and named source booleans. Adapt the new
WS pipeline tests to main's constructor-level getAssetsState API and the
renamed AssetsControllerState type, and pass oxlint (new on main).
Match the surrounding code's comment density: drop the inline rationale
comments from `#handleAssetsUpdateV5` (the lane ordering rationale lives
in `buildWsUpdateSources`' JSDoc, mirroring `buildFastFetchSources`) and
cut the capture script's multi-paragraph header (with its shell example)
down to a two-line summary — operational details stay in the PR
description.
Clean merge: dependency bumps and release changelogs only.
Route `#handleAssetsUpdateV6`'s websocket lane through
`buildWsUpdateSources`, mirroring how `buildFastFetchSources` serves the
v5 and v6 fast-fetch lanes: the builder now takes
`includeCustomAssetGraduation` (true on the v5 lane; the v6 lane never
graduates custom assets and instead runs the RPC fallback ahead of
detection) and the v6 handler's inline occurrence-filter/RPC-fallback
construction is gone.

Cover both generations: unit rows for the v6 lane shapes, a v6 pass in
the pipeline integration test (RPC fallback passthrough on a
successful response), and a controller-level v6 scenario booted with
`assetsAccountsApiV6: true` via the fixture's remote feature flags.
Keep one-line summaries and only the required @param/@returns tags; drop
the rationale paragraphs, scenario explanations that repeat the test
titles, and multi-sentence constant/fixture docs.
Route both update handlers' non-websocket lanes through a sibling
builder of `buildWsUpdateSources`: the gating booleans
(`includeCustomAssetGraduation`, `includeRpcFallback`) stay at the call
sites, so each handler's exact lane composition is preserved. Unit
tests cover the five lane shapes.
@Prithpal-Sooriya
Prithpal-Sooriya marked this pull request as draft September 28, 2026 11:13
Follow the bsc-spam-token integration-test format: an
"Integration Expectation" header, response-surface tables driving
`it.each('$surface - …')` assertions, `describe.each` rows sharing the
v5/v6 lane its, shared fixture builders for the combined event and the
scenario states, and `askedAbout`/`priceOf` helpers instead of inline
set-building and type casts.
…ults

Assert the captured spot prices exactly (ETH 2688.8502994319642, USDC
0.999966) instead of numeric ranges, and nest each run's recorded
mock output under `mocks` (`priceAPI`, `tokenAPI`, `rpc`) so the
pipeline/controller results read apart from what the mocks observed.
One `it` per causal chain: the held-but-unpriced asset is queued,
fetched and priced in one test (pipeline and controller level), and the
already-priced token's not-queued/not-invoked/not-overwritten checks
merge into one. The lane describes keep their per-mechanism `it`s,
where each covers the full two-asset set.
Comment on lines +414 to +417
it('does not re-price the already-priced token', () => {
expect(result.request.assetsForPriceUpdate ?? []).toStrictEqual([]);
expect(result.mocks.priceAPI.priceBatches).toStrictEqual([]);
});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kriys94 Is this a legitimate behaviour or a bug?

If I'm a user that has an ERC20 token (that has balance/metadata/prices).
If a WS event comes through am I:

  • Meant to see a price update?
  • Not meant to see a price update?

@Prithpal-Sooriya
Prithpal-Sooriya marked this pull request as ready for review September 28, 2026 13:18
@Prithpal-Sooriya
Prithpal-Sooriya added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 5bc6a04 Sep 28, 2026
57 checks passed
@Prithpal-Sooriya
Prithpal-Sooriya deleted the feature/use-memory-integrati-zdo branch September 28, 2026 14:34

This branch was successfully deployed

1 active deployment
default-branch — 1a942e6b Deployed Sep 28, 2026 by Prithpal-Sooriya via Determine whether this PR is a release PR #4767
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants