-
Notifications
You must be signed in to change notification settings - Fork 5k
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
refactor: modular controller init #28948
base: main
Are you sure you want to change the base?
Changes from 22 commits
2934f38
013de6f
a41b2b1
57a0608
7571c94
dede03f
6c090f5
496a700
3ccc1a8
93ff27c
ec6f202
69479a8
e32edbb
495db5c
b1ffbd5
0c12760
ab3c0b8
c94d6e0
55da18e
dd59867
fc39319
8d2801c
cf22570
584c15b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { | ||
PPOMController, | ||
PPOMControllerMessenger, | ||
} from '@metamask/ppom-validator'; | ||
import { PPOMControllerInitMessenger } from '../messengers/ppom-controller-messenger'; | ||
import { | ||
buildControllerInitRequestMock, | ||
CHAIN_ID_MOCK, | ||
expectValidMessengerCallback, | ||
} from '../test/utils'; | ||
import { PPOMControllerInit } from './ppom-controller-init'; | ||
|
||
type PPOMControllerOptions = ConstructorParameters<typeof PPOMController>[0]; | ||
|
||
jest.mock('@metamask/ppom-validator'); | ||
|
||
function buildInitRequestMock() { | ||
const requestMock = buildControllerInitRequestMock< | ||
PPOMControllerMessenger, | ||
PPOMControllerInitMessenger | ||
>(); | ||
|
||
requestMock.getController.mockReturnValue({ | ||
state: { securityAlertsEnabled: true }, | ||
}); | ||
|
||
return requestMock; | ||
} | ||
|
||
describe('PPOM Controller Init', () => { | ||
const ppomControllerClassMock = jest.mocked(PPOMController); | ||
|
||
function testConstructorProperty<T extends keyof PPOMControllerOptions>( | ||
property: T, | ||
controllerProperties: Record<string, unknown> = {}, | ||
): PPOMControllerOptions[T] { | ||
const requestMock = buildInitRequestMock(); | ||
|
||
requestMock.getController.mockReturnValue({ | ||
state: { securityAlertsEnabled: true }, | ||
...controllerProperties, | ||
}); | ||
|
||
new PPOMControllerInit().init(requestMock); | ||
|
||
return ppomControllerClassMock.mock.calls[0][0][property]; | ||
} | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
describe('init', () => { | ||
it('returns controller instance', () => { | ||
const requestMock = buildInitRequestMock(); | ||
expect(new PPOMControllerInit().init(requestMock)).toBeInstanceOf( | ||
PPOMController, | ||
); | ||
}); | ||
|
||
it('determines if security alerts enabled using preference', () => { | ||
const securityAlertsEnabled = testConstructorProperty( | ||
'securityAlertsEnabled', | ||
{ | ||
state: { securityAlertsEnabled: true }, | ||
}, | ||
); | ||
|
||
expect(securityAlertsEnabled).toBe(true); | ||
}); | ||
|
||
it('sets chain ID to global chain ID', () => { | ||
const chainId = testConstructorProperty('chainId'); | ||
expect(chainId).toBe(CHAIN_ID_MOCK); | ||
}); | ||
}); | ||
|
||
describe('getControllerMessengerCallback', () => { | ||
it('returns a valid messenger callback', () => { | ||
expectValidMessengerCallback( | ||
new PPOMControllerInit().getControllerMessengerCallback(), | ||
); | ||
}); | ||
|
||
describe('getInitMessengerCallback', () => { | ||
it('returns a valid messenger callback', () => { | ||
expectValidMessengerCallback( | ||
new PPOMControllerInit().getInitMessengerCallback(), | ||
); | ||
}); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,74 @@ | ||||||||
import { | ||||||||
PPOMController, | ||||||||
PPOMControllerMessenger, | ||||||||
} from '@metamask/ppom-validator'; | ||||||||
import { PreferencesController } from '@metamask/preferences-controller'; | ||||||||
import { IndexedDBPPOMStorage } from '../../lib/ppom/indexed-db-backend'; | ||||||||
import * as PPOMModule from '../../lib/ppom/ppom'; | ||||||||
import { | ||||||||
ControllerInit, | ||||||||
ControllerInitRequest, | ||||||||
ControllerName, | ||||||||
} from '../types'; | ||||||||
import { | ||||||||
getPPOMControllerInitMessenger, | ||||||||
getPPOMControllerMessenger, | ||||||||
PPOMControllerInitMessenger, | ||||||||
} from '../messengers/ppom-controller-messenger'; | ||||||||
|
||||||||
export class PPOMControllerInit extends ControllerInit< | ||||||||
PPOMController, | ||||||||
PPOMControllerMessenger, | ||||||||
PPOMControllerInitMessenger | ||||||||
> { | ||||||||
init( | ||||||||
request: ControllerInitRequest< | ||||||||
PPOMControllerMessenger, | ||||||||
PPOMControllerInitMessenger | ||||||||
>, | ||||||||
) { | ||||||||
const { | ||||||||
controllerMessenger, | ||||||||
getController, | ||||||||
getGlobalChainId, | ||||||||
getProvider, | ||||||||
initMessenger, | ||||||||
persistedState, | ||||||||
} = request; | ||||||||
|
||||||||
const preferencesController = () => | ||||||||
getController<PreferencesController>( | ||||||||
ControllerName.PreferencesController, | ||||||||
); | ||||||||
|
||||||||
return new PPOMController({ | ||||||||
messenger: controllerMessenger, | ||||||||
storageBackend: new IndexedDBPPOMStorage('PPOMDB', 1), | ||||||||
provider: getProvider(), | ||||||||
ppomProvider: { | ||||||||
// @ts-expect-error Mismatched types | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any idea why there is a type mismatch here? |
||||||||
PPOM: PPOMModule.PPOM, | ||||||||
ppomInit: () => PPOMModule.default(process.env.PPOM_URI), | ||||||||
}, | ||||||||
state: persistedState.PPOMController as PPOMController['state'], | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Hmm, this isn't ideal. How can we ensure the persisted state is correctly typed? It would be nice to avoid casting, as this is contrary to our TypeScript best practices. At the very least we should explain in a comment why we're casting here when we normally wouldn't. Unfortunately it would be an overly large amount of work to avoid type safety problems altogether, as we don't validate it at any point. That would be beyond the scope of this PR. But a great deal of the handling of this state is still in JavaScript, so it should be easily avoided. At this point in the initialization process, it's fair to say that we expect the type to be |
||||||||
chainId: getGlobalChainId(), | ||||||||
securityAlertsEnabled: | ||||||||
preferencesController().state.securityAlertsEnabled, | ||||||||
// @ts-expect-error Mismatched types | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Here is a bit more detail on the type issue here:
Suggested change
|
||||||||
onPreferencesChange: initMessenger.subscribe.bind( | ||||||||
initMessenger, | ||||||||
'PreferencesController:stateChange', | ||||||||
), | ||||||||
cdnBaseUrl: process.env.BLOCKAID_FILE_CDN as string, | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Here is more detail on why we're casting here:
Suggested change
|
||||||||
blockaidPublicKey: process.env.BLOCKAID_PUBLIC_KEY as string, | ||||||||
}); | ||||||||
} | ||||||||
|
||||||||
getControllerMessengerCallback() { | ||||||||
return getPPOMControllerMessenger; | ||||||||
} | ||||||||
|
||||||||
getInitMessengerCallback() { | ||||||||
return getPPOMControllerInitMessenger; | ||||||||
} | ||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Not sure what to expect from tests at this layer. I see that every constructor parameter isn't tested for example, and for the messenger callbacks we're not testing what capabilities they have. But maybe that's OK.
What do you see as the goals of these tests?