Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING Adds messenger to token controller #1166

Merged
merged 9 commits into from
Apr 18, 2023
Merged

Conversation

bergarces
Copy link
Contributor

@bergarces bergarces commented Apr 5, 2023

BREAKING Adds messenger to token controller

Description

Adds a messenger to TokensController so that the confirmation can be triggered by sending a message to ApprovalController.

  • BREAKING:

    • The new messenger field in the constructor introduces a breaking change.
  • CHANGED:

    • It uses ApprovalController messages to initiate and complete the request instead of an event.

Checklist

  • Tests are included if applicable
  • Any added code is fully documented

Issue

Resolves https://github.com/MetaMask/MetaMask-planning/issues/409

@bergarces bergarces force-pushed the token-controller-messenger branch 2 times, most recently from 80cf8ad to 33e207d Compare April 5, 2023 19:09
@bergarces bergarces changed the title Add messenger to token controller Adds messenger to token controller Apr 5, 2023
@bergarces bergarces changed the title Adds messenger to token controller BREAKING Adds messenger to token controller Apr 5, 2023
@bergarces bergarces marked this pull request as ready for review April 5, 2023 19:22
@bergarces bergarces requested a review from a team as a code owner April 5, 2023 19:22
@@ -142,6 +142,7 @@ describe('TokenBalancesController', () => {
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
onNetworkStateChange: (listener) =>
messenger.subscribe('NetworkController:stateChange', listener),
messenger: undefined as any,
Copy link
Contributor

Choose a reason for hiding this comment

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

Any chance we could provide a real messenger object instead of nothing? We'd like to avoid using any across the monorepo if we can, even in tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've created real messenger objects for those tests to avoid use of any.

Having said that, I don't think it's going to be scalable if we have to instantiate real dependencies instead of mocking them. If we are instantiating real TokensController with real messengers for other controllers, these are no longer unit tests.

Copy link
Member

Choose a reason for hiding this comment

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

Is there a compromise where we can still provide a mock for these arguments to reduce coupling, but cast to the correct type rather than any, so a as unknown as TokensControllerMessenger?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've pushed another commit with the as unknown as TokensControllerMessenger. The messenger is not even used during those tests, so it's set to undefined.

We should not have to use real object instantiations on unit tests when that is not the subject of the test.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see what you mean, but I'm not so sure that using a real messenger would make this an integration test. Unless we're using it to talk to another controller — that would be unnecessary. But if we're just using it to satisfy the argument, then I don't see why we can't use a real one.

Copy link
Member

@matthewwalsh0 matthewwalsh0 Apr 12, 2023

Choose a reason for hiding this comment

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

I'd argue it's just a matter of future-proofing and minimising the code being executed in the test for the sake of avoiding fragility and ensuring we're testing a isolated unit whenever possible, rather than dependency code, in this case the ControllerMessenger.

As an example, if we were to change the code in future to invoke a method in the TokensController from the TokenBalancesController that did use the messenger, any related errors in the messenger code would needlessly cause the TokenBalancesController tests to fail and they would be harder to diagnose as the errors would be originating from code the test wasn't intending to validate.

There's also a performance argument with this pattern if we're consistently loading additional dependencies and code that isn't functionally required for a test.

Copy link
Member

Choose a reason for hiding this comment

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

This change seems fine to me as a short-term fix because the tests shouldn't have a TokenController in the fist place, but fixing that would be out-of-scope of this PR.

But in general I would recommend using a real controller messenger in tests, if only because it's such a simple construct that mocking it would add complexity. The messenger is meant as a way of doing dependency injection, it's a way for us to pass mocks into the code under test.

It's the use of additional controllers that aren't under test that makes this example needlessly complex. Not the messenger. In your example here @matthewwalsh0 you seem to be talking about one such case. I don't think @mcmire meant to suggest that using real controllers that aren't under-test is a good idea.

matthewwalsh0
matthewwalsh0 previously approved these changes Apr 12, 2023

this._acceptApproval(suggestedAssetID);

const acceptedSuggestedAssetMeta = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had to remove the line that updates status directly (suggestedAssetMeta.status = SuggestedAssetStatus.accepted;) and replace it by a new object using the spread operator, as otherwise that line throws a read error when using this controller in mobile codebase with react-native.

Attempted to assign to readonly property

const rejectedSuggestedAssetMeta = {
...suggestedAssetMeta,
status: SuggestedAssetStatus.rejected,
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as with the previous one, I had to create a new object instead of updating status directly to prevent an error in react-native codebase.

id: suggestedAssetMeta.id,
origin: ORIGIN_METAMASK,
type: APPROVAL_TYPE,
requestData: {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For mobile, we will need requestData to be populated with the following values, as it currently relies on the suggestedAssetMeta object sent by the pendingSuggestedAsset event.

Unfortunately, the type SuggestedAssetMeta is not compatible with requestData as it has several optional and an unknown field within the Token type, so we are sending precisely the data it needs.

matthewwalsh0
matthewwalsh0 previously approved these changes Apr 17, 2023
vinistevam
vinistevam previously approved these changes Apr 18, 2023
@bergarces bergarces dismissed stale reviews from vinistevam and matthewwalsh0 via 77afb2f April 18, 2023 09:48
@bergarces bergarces merged commit 1970dc1 into main Apr 18, 2023
@bergarces bergarces deleted the token-controller-messenger branch April 18, 2023 11:15
MajorLift pushed a commit that referenced this pull request Oct 11, 2023
* BREAKING - add messenger to token controller

* trigger confirmation via approval controller

* unit tests
MajorLift pushed a commit that referenced this pull request Oct 11, 2023
* BREAKING - add messenger to token controller

* trigger confirmation via approval controller

* unit tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants