Skip to content

Commit

Permalink
chore(runway): cherry-pick feat: customize fetchInterval for remoteFe…
Browse files Browse the repository at this point in the history
…atureFlagController to 15min (#13342)

- feat: customize fetchInterval for remoteFeatureFlagController to 15min
(#13341)

## **Description**

- add a new app constant for the 15 min fetch interval
- add fetch interval param using the constant
- add test that controller builder function passes fetch interval param
- remove out of scope unit tests (this was testing the controller. This
is [already tested in controller

itself](https://github.com/MetaMask/core/blob/main/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.test.ts)
and adds a dangerous and blocking link to non local code.

## **Related issues**

Fixes mobile app part of
https://github.com/MetaMask/MetaMask-planning/issues/4098

## **Manual testing steps**

(Manual testing only be done by someone with Launch Darkly access)
1. activate a remote feature flag
2. Load app
3. Check feature is activated
4. disable remote feature flag
5. Wait for 15 min
6. Reload the app
7. Check feature is deactivated

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I’ve followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding

Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling

guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
[508a5f7](508a5f7)

Co-authored-by: Nico MASSART <[email protected]>
  • Loading branch information
runway-github[bot] and NicolasMassart authored Feb 5, 2025
1 parent 2650560 commit d30a58d
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 58 deletions.
1 change: 1 addition & 0 deletions app/core/AppConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,5 +222,6 @@ export default {
FEATURE_FLAGS_API: {
BASE_URL: 'https://client-config.api.cx.metamask.io',
VERSION: 'v1',
DEFAULT_FETCH_INTERVAL: 15 * 60 * 1000, // 15 minutes
},
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export interface RemoteFeatureFlagInitParamTypes {
messenger: RemoteFeatureFlagControllerMessenger;
disabled: boolean;
getMetaMetricsId: () => string;
fetchInterval?: number;
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ import {
import { createRemoteFeatureFlagController } from './utils';
import { v4 as uuidv4 } from 'uuid';

const mockUpdateRemoteFeatureFlags = jest.fn().mockResolvedValue(undefined);

jest.mock('@metamask/remote-feature-flag-controller', () => {
const originalModule = jest.requireActual('@metamask/remote-feature-flag-controller');
return {
...originalModule,
RemoteFeatureFlagController: jest.fn().mockImplementation((params) => ({
updateRemoteFeatureFlags: mockUpdateRemoteFeatureFlags, // Ensures it returns a resolved promise
...params, // Ensure that fetchInterval and other params are stored
})),
};
});

describe('RemoteFeatureFlagController utils', () => {
let messenger: RemoteFeatureFlagControllerMessenger;

Expand All @@ -16,91 +29,45 @@ describe('RemoteFeatureFlagController utils', () => {
});

describe('createRemoteFeatureFlagController', () => {
it('creates controller with initial undefined state', () => {
const controller = createRemoteFeatureFlagController({
state: undefined,
messenger,
disabled: false,
getMetaMetricsId: () => uuidv4(),
});

expect(controller).toBeDefined();

// Initializing with am empty object should return an empty obj?
expect(controller.state).toStrictEqual({
cacheTimestamp: 0,
remoteFeatureFlags: {},
});
});

it('internal state matches initial state', () => {
const initialState = {
remoteFeatureFlags: {
testFlag: true,
},
cacheTimestamp: 123,
};

const controller = createRemoteFeatureFlagController({
state: initialState,
messenger,
disabled: false,
getMetaMetricsId: () => uuidv4(),
});

expect(controller.state).toStrictEqual(initialState);
});

it('calls updateRemoteFeatureFlags when enabled', () => {
const spy = jest.spyOn(
RemoteFeatureFlagController.prototype,
'updateRemoteFeatureFlags',
);

createRemoteFeatureFlagController({
state: undefined,
messenger,
disabled: false,
getMetaMetricsId: () => uuidv4(),
});

expect(spy).toHaveBeenCalled();
expect(mockUpdateRemoteFeatureFlags).toHaveBeenCalled();
});

it('does not call updateRemoteFeatureFlagscontroller when controller is disabled', () => {
const spy = jest.spyOn(
RemoteFeatureFlagController.prototype,
'updateRemoteFeatureFlags',
);

createRemoteFeatureFlagController({
state: undefined,
messenger,
disabled: true,
getMetaMetricsId: () => uuidv4(),
});

expect(spy).not.toHaveBeenCalled();
expect(mockUpdateRemoteFeatureFlags).not.toHaveBeenCalled();
});

it('controller keeps initial extra data into its state', () => {
const initialState = {
extraData: true,
};
it('passes fetchInterval to RemoteFeatureFlagController', async () => {
const fetchInterval = 6000;

const controller = createRemoteFeatureFlagController({
// @ts-expect-error giving a wrong initial state
state: initialState,
createRemoteFeatureFlagController({
state: undefined,
messenger,
disabled: false,
getMetaMetricsId: () => uuidv4(),
fetchInterval,
});

expect(controller.state).toStrictEqual({
cacheTimestamp: 0,
extraData: true,
remoteFeatureFlags: {},
});
// Ensure the constructor was called with fetchInterval
expect(RemoteFeatureFlagController).toHaveBeenCalledWith(
expect.objectContaining({ fetchInterval })
);
});

});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import Logger from '../../../../util/Logger';

import { RemoteFeatureFlagInitParamTypes } from './types';
import AppConstants from '../../../AppConstants';

const getFeatureFlagAppEnvironment = () => {
const env = process.env.METAMASK_ENVIRONMENT;
Expand All @@ -34,6 +35,7 @@ export const createRemoteFeatureFlagController = ({
messenger,
disabled,
getMetaMetricsId,
fetchInterval = AppConstants.FEATURE_FLAGS_API.DEFAULT_FETCH_INTERVAL,
}: RemoteFeatureFlagInitParamTypes) => {
const remoteFeatureFlagController = new RemoteFeatureFlagController({
messenger,
Expand All @@ -48,6 +50,7 @@ export const createRemoteFeatureFlagController = ({
distribution: getFeatureFlagAppDistribution(),
},
}),
fetchInterval,
});

if (disabled) {
Expand Down

0 comments on commit d30a58d

Please sign in to comment.