mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-07-12 16:17:24 +01:00
gRPC mock tests
This commit is contained in:
+1
-1
@@ -155,7 +155,7 @@
|
||||
"@react-spring/web": "10.0.3",
|
||||
"@react-types/shared": "3.33.1",
|
||||
"@signalapp/minimask": "1.0.1",
|
||||
"@signalapp/mock-server": "19.1.0",
|
||||
"@signalapp/mock-server": "20.0.1",
|
||||
"@signalapp/quill-cjs": "2.1.2",
|
||||
"@storybook/addon-a11y": "8.4.4",
|
||||
"@storybook/addon-actions": "8.4.4",
|
||||
|
||||
Generated
+16
-16
@@ -185,8 +185,8 @@ importers:
|
||||
specifier: 1.0.1
|
||||
version: 1.0.1
|
||||
'@signalapp/mock-server':
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
specifier: 20.0.1
|
||||
version: 20.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
'@signalapp/quill-cjs':
|
||||
specifier: 2.1.2
|
||||
version: 2.1.2
|
||||
@@ -1692,14 +1692,14 @@ packages:
|
||||
peerDependencies:
|
||||
prettier: ^3.0.0
|
||||
|
||||
'@indutny/protopiler@3.2.1':
|
||||
resolution: {integrity: sha512-u3ZVLus/9wwkojVSO0es5tppdd+2f9zA6ogax3OytOwGueDWSj+eVU6sSWYiSY/zxMSgdnKxRik0D3D36u3iWw==}
|
||||
hasBin: true
|
||||
|
||||
'@indutny/protopiler@3.2.4':
|
||||
resolution: {integrity: sha512-uBz01wZxVz8+9DeGKN0QPvViDPq0Agcs5pAF8CkivULFP/sFGWpWaC9nPWIuVC9y44RrPQS+xpp1pFYlnX7BVg==}
|
||||
hasBin: true
|
||||
|
||||
'@indutny/protopiler@4.0.0':
|
||||
resolution: {integrity: sha512-QGVXaIBNXb0QwGHeWsbIdhwph72hYpYeE2M753Y91LGUTvBumFUMDEGBT8HQk1y4c2l80nvFAMGFFCWCCtk5gw==}
|
||||
hasBin: true
|
||||
|
||||
'@indutny/range-finder@1.3.4':
|
||||
resolution: {integrity: sha512-UWNRZZCp3f97NJuhP0IgBktob+kPDFsnyKx42PDwPQsAa/ZczyeLdhfz3tD8zK1wVC+OkOXAM6YkBAgEv9Xhaw==}
|
||||
|
||||
@@ -3787,8 +3787,8 @@ packages:
|
||||
'@signalapp/minimask@1.0.1':
|
||||
resolution: {integrity: sha512-QAwo0joA60urTNbW9RIz6vLKQjy+jdVtH7cvY0wD9PVooD46MAjE40MLssp4xUJrph91n2XvtJ3pbEUDrmT2AA==}
|
||||
|
||||
'@signalapp/mock-server@19.1.0':
|
||||
resolution: {integrity: sha512-NTOLGe/G+ptVd7xUOTJiWMn5CvxoLcalSlt5ePCFwtyDiJ4HFXR6O2d9f42Elm5PpLtbPULre5G1AeqijmyzUA==}
|
||||
'@signalapp/mock-server@20.0.1':
|
||||
resolution: {integrity: sha512-hpWuhfl0u1yX2KsJ9wa74tR/P3/eDDfZMRnlczeaty/q6EKxk8GP2HKkI13apj/redw0fJxq/fMdIg0qoBxM0Q==}
|
||||
|
||||
'@signalapp/parchment-cjs@3.0.1':
|
||||
resolution: {integrity: sha512-hSBMQ1M7wE4GcC8ZeNtvpJF+DAJg3eIRRf1SiHS3I3Algav/sgJJNm6HIYm6muHuK7IJmuEjkL3ILSXgmu0RfQ==}
|
||||
@@ -6874,8 +6874,8 @@ packages:
|
||||
intl-tel-input@26.8.1:
|
||||
resolution: {integrity: sha512-6UBRifs+nGtlS03SZGxxb9uVX7MTo1ePNJ4zIxO+qiwVwJ6fW7gYTcvYVFKjBn7jlbPw2ZqjGeNLaSUEwmDvIA==}
|
||||
|
||||
ioredis@5.10.0:
|
||||
resolution: {integrity: sha512-HVBe9OFuqs+Z6n64q09PQvP1/R4Bm+30PAyyD4wIEqssh3v9L21QjCVk4kRLucMBcDokJTcLjsGeVRlq/nH6DA==}
|
||||
ioredis@5.10.1:
|
||||
resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==}
|
||||
engines: {node: '>=12.22.0'}
|
||||
|
||||
ip-address@9.0.5:
|
||||
@@ -11293,10 +11293,10 @@ snapshots:
|
||||
prettier: 3.8.1
|
||||
rxjs: 6.6.7
|
||||
|
||||
'@indutny/protopiler@3.2.1': {}
|
||||
|
||||
'@indutny/protopiler@3.2.4': {}
|
||||
|
||||
'@indutny/protopiler@4.0.0': {}
|
||||
|
||||
'@indutny/range-finder@1.3.4':
|
||||
dependencies:
|
||||
readable-stream: 4.7.0
|
||||
@@ -13851,10 +13851,10 @@ snapshots:
|
||||
|
||||
'@signalapp/minimask@1.0.1': {}
|
||||
|
||||
'@signalapp/mock-server@19.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
|
||||
'@signalapp/mock-server@20.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@indutny/parallel-prettier': 3.0.0(prettier@3.8.1)
|
||||
'@indutny/protopiler': 3.2.1
|
||||
'@indutny/protopiler': 4.0.0
|
||||
'@signalapp/libsignal-client': 0.89.2
|
||||
'@tus/file-store': 1.5.1
|
||||
'@tus/server': 1.10.2
|
||||
@@ -14439,7 +14439,7 @@ snapshots:
|
||||
lodash.throttle: 4.1.1
|
||||
optionalDependencies:
|
||||
'@redis/client': 1.6.1
|
||||
ioredis: 5.10.0
|
||||
ioredis: 5.10.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -17547,7 +17547,7 @@ snapshots:
|
||||
|
||||
intl-tel-input@26.8.1: {}
|
||||
|
||||
ioredis@5.10.0:
|
||||
ioredis@5.10.1:
|
||||
dependencies:
|
||||
'@ioredis/commands': 1.5.1
|
||||
cluster-key-slot: 1.1.2
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from '@signalapp/libsignal-client/dist/net.js';
|
||||
|
||||
import { isProduction } from './util/version.std.ts';
|
||||
import { drop } from './util/drop.std.ts';
|
||||
import * as RemoteConfig from './RemoteConfig.dom.ts';
|
||||
import type { AddPrefix, ArrayValues } from './types/Util.std.ts';
|
||||
import { createLogger } from './logging/log.std.ts';
|
||||
@@ -21,13 +22,9 @@ function convertToDesktopRemoteConfigKey<
|
||||
|
||||
export function bindRemoteConfigToLibsignalNet(
|
||||
libsignalNet: Net,
|
||||
appVersion: string
|
||||
appVersion: string,
|
||||
reconnect: () => Promise<void>
|
||||
): void {
|
||||
// Calls setLibsignalRemoteConfig and is reset when any libsignal remote
|
||||
// config key changes. Doing that asynchronously allows the callbacks for
|
||||
// multiple keys that are triggered by the same config fetch from the server
|
||||
// to be coalesced into a single timeout.
|
||||
let reloadRemoteConfig: NodeJS.Immediate | undefined;
|
||||
const libsignalBuildVariant = isProduction(appVersion)
|
||||
? BuildVariant.Production
|
||||
: BuildVariant.Beta;
|
||||
@@ -46,18 +43,24 @@ export function bindRemoteConfigToLibsignalNet(
|
||||
Object.fromEntries(remoteConfigs)
|
||||
);
|
||||
libsignalNet.setRemoteConfig(remoteConfigs, libsignalBuildVariant);
|
||||
reloadRemoteConfig = undefined;
|
||||
};
|
||||
|
||||
setLibsignalRemoteConfig();
|
||||
|
||||
KeysExpectedByLibsignalNet.map(convertToDesktopRemoteConfigKey).forEach(
|
||||
key => {
|
||||
RemoteConfig.onChange(key, () => {
|
||||
if (reloadRemoteConfig === undefined) {
|
||||
reloadRemoteConfig = setImmediate(setLibsignalRemoteConfig);
|
||||
}
|
||||
});
|
||||
RemoteConfig.onChange(
|
||||
KeysExpectedByLibsignalNet.map(convertToDesktopRemoteConfigKey),
|
||||
() => {
|
||||
setLibsignalRemoteConfig();
|
||||
|
||||
// When linking for the first time in mock tests we start with an empty
|
||||
// remote config and fetch the latest values only when we connect the
|
||||
// socket. However, new values won't be applied until we reconnect so
|
||||
// we need to reconnect immediately to use remote config dependent
|
||||
// features like gRPC.
|
||||
if (window.SignalCI) {
|
||||
log.info('Reconnecting socket after remote config change');
|
||||
drop(reconnect());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
+29
-19
@@ -135,13 +135,13 @@ type ConfigValueType = {
|
||||
export type ConfigMapType = {
|
||||
[key in ConfigKeyType]?: ConfigValueType;
|
||||
};
|
||||
export type ConfigListenerType = (value: ConfigValueType) => unknown;
|
||||
type ConfigListenersMapType = {
|
||||
[key: string]: Array<ConfigListenerType>;
|
||||
};
|
||||
type ConfigListenerType = Readonly<{
|
||||
keys: Set<ConfigKeyType>;
|
||||
callback: () => unknown;
|
||||
}>;
|
||||
|
||||
let config: ConfigMapType | undefined;
|
||||
const listeners: ConfigListenersMapType = {};
|
||||
const listeners = new Set<ConfigListenerType>();
|
||||
|
||||
export type OptionsType = Readonly<{
|
||||
getConfig: typeof getConfig;
|
||||
@@ -155,17 +155,17 @@ export function restoreRemoteConfigFromStorage({
|
||||
}
|
||||
|
||||
export function onChange(
|
||||
key: ConfigKeyType,
|
||||
fn: ConfigListenerType
|
||||
keys: Array<ConfigKeyType>,
|
||||
callback: () => unknown
|
||||
): () => void {
|
||||
const keyListeners: Array<ConfigListenerType> = get(listeners, key, []);
|
||||
keyListeners.push(fn);
|
||||
listeners[key] = keyListeners;
|
||||
const listener: ConfigListenerType = {
|
||||
keys: new Set(keys),
|
||||
callback,
|
||||
};
|
||||
listeners.add(listener);
|
||||
|
||||
return () => {
|
||||
if (listeners[key]) {
|
||||
listeners[key] = listeners[key].filter(l => l !== fn);
|
||||
}
|
||||
listeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -210,6 +210,8 @@ export const _refreshRemoteConfig = async ({
|
||||
newConfigValues.set(name, value);
|
||||
}
|
||||
|
||||
const changedKeys = new Set<string>();
|
||||
|
||||
const oldConfig = config;
|
||||
let semverError = false;
|
||||
config = Array.from(newConfigValues.entries()).reduce(
|
||||
@@ -253,13 +255,8 @@ export const _refreshRemoteConfig = async ({
|
||||
semverError = true;
|
||||
}
|
||||
|
||||
// If enablement changes at all, notify listeners
|
||||
const currentListeners = listeners[name] || [];
|
||||
if (hasChanged) {
|
||||
log.info(`Remote Config: Flag ${name} has changed`);
|
||||
currentListeners.forEach(listener => {
|
||||
listener(configValue);
|
||||
});
|
||||
changedKeys.add(name);
|
||||
}
|
||||
|
||||
// Return new configuration object
|
||||
@@ -271,6 +268,19 @@ export const _refreshRemoteConfig = async ({
|
||||
{}
|
||||
);
|
||||
|
||||
if (changedKeys.size !== 0) {
|
||||
log.info(
|
||||
`Remote Config: Flags ${[...changedKeys].join(', ')} have changed`
|
||||
);
|
||||
|
||||
// If enablement changes at all, notify listeners
|
||||
for (const { keys, callback } of listeners) {
|
||||
if (!keys.isDisjointFrom(changedKeys)) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (semverError && config['desktop.internalUser']?.enabled) {
|
||||
window.reduxActions.toast.showToast({
|
||||
toastType: ToastType.Error,
|
||||
|
||||
@@ -248,6 +248,7 @@ import { encryptConversationAttachments } from './util/encryptConversationAttach
|
||||
import { DataReader, DataWriter } from './sql/Client.preload.ts';
|
||||
import {
|
||||
restoreRemoteConfigFromStorage,
|
||||
isEnabled as isRemoteConfigValueEnabled,
|
||||
getValue as getRemoteConfigValue,
|
||||
onChange as onRemoteConfigChange,
|
||||
maybeRefreshRemoteConfig,
|
||||
@@ -1557,8 +1558,12 @@ export async function startApp(): Promise<void> {
|
||||
});
|
||||
|
||||
// Listen for changes to the `desktop.clientExpiration` remote flag
|
||||
onRemoteConfigChange('desktop.clientExpiration', ({ enabled, value }) => {
|
||||
if (!enabled) {
|
||||
onRemoteConfigChange(['desktop.clientExpiration'], () => {
|
||||
if (!isRemoteConfigValueEnabled('desktop.clientExpiration')) {
|
||||
return;
|
||||
}
|
||||
const value = getRemoteConfigValue('desktop.clientExpiration');
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
const remoteBuildExpirationTimestamp = parseRemoteClientExpiration(value);
|
||||
|
||||
@@ -26,7 +26,7 @@ export const chatFolderCleanupService = createExpiringEntityCleanupService({
|
||||
},
|
||||
subscribeToTriggers: trigger => {
|
||||
let prevMessageQueueTime = getMessageQueueTime();
|
||||
return RemoteConfig.onChange('global.messageQueueTimeInSeconds', () => {
|
||||
return RemoteConfig.onChange(['global.messageQueueTimeInSeconds'], () => {
|
||||
const messageQueueTime = getMessageQueueTime();
|
||||
if (messageQueueTime !== prevMessageQueueTime) {
|
||||
trigger('messageQueueTime changed');
|
||||
|
||||
@@ -181,6 +181,10 @@ const DEFAULT_REMOTE_CONFIG = [
|
||||
['global.backups.mediaTierFallbackCdnNumber', { enabled: true, value: '3' }],
|
||||
['global.groupsv2.groupSizeHardLimit', { enabled: true, value: '64' }],
|
||||
['global.groupsv2.maxGroupSize', { enabled: true, value: '32' }],
|
||||
[
|
||||
'desktop.libsignalNet.grpc.MessagesAnonymousSendMultiRecipientMessage.2',
|
||||
{ enabled: true },
|
||||
],
|
||||
] as const;
|
||||
|
||||
//
|
||||
|
||||
@@ -6,7 +6,7 @@ import * as sinon from 'sinon';
|
||||
|
||||
import lodash from 'lodash';
|
||||
import { normalizeAci } from '../util/normalizeAci.std.ts';
|
||||
import type { ConfigKeyType, ConfigListenerType } from '../RemoteConfig.dom.ts';
|
||||
import type { ConfigKeyType } from '../RemoteConfig.dom.ts';
|
||||
import {
|
||||
COUNTRY_CODE_FALLBACK,
|
||||
getCountryCodeValue,
|
||||
@@ -187,8 +187,8 @@ describe('RemoteConfig', () => {
|
||||
await updateRemoteConfig([]);
|
||||
|
||||
// oxlint-disable-next-line typescript/no-empty-function
|
||||
const listener = sinon.spy<ConfigListenerType>(() => {});
|
||||
onChange('desktop.internalUser', listener);
|
||||
const listener = sinon.spy<() => void>(() => {});
|
||||
onChange(['desktop.internalUser', 'desktop.clientExpiration'], listener);
|
||||
|
||||
await updateRemoteConfig([
|
||||
{ name: 'desktop.internalUser', value: 'yes' },
|
||||
@@ -198,14 +198,16 @@ describe('RemoteConfig', () => {
|
||||
{ name: 'desktop.internalUser', value: 'yes' },
|
||||
]);
|
||||
|
||||
// should not trigger callback
|
||||
await updateRemoteConfig([
|
||||
{ name: 'desktop.internalUser', value: 'yes' },
|
||||
{ name: 'global.nicknames.max', value: '42' },
|
||||
]);
|
||||
|
||||
const calls = listener
|
||||
.getCalls()
|
||||
.map(call => omit(call.firstArg, 'enabledAt'));
|
||||
assert.deepEqual(calls, [
|
||||
{ name: 'desktop.internalUser', value: 'yes', enabled: true },
|
||||
{ name: 'desktop.internalUser', enabled: false },
|
||||
{ name: 'desktop.internalUser', value: 'yes', enabled: true },
|
||||
]);
|
||||
assert.deepEqual(calls, [{}, {}, {}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1803,7 +1803,11 @@ export async function connect({
|
||||
hasStoriesDisabled,
|
||||
hasBuildExpired,
|
||||
}: WebAPIConnectOptionsType): Promise<void> {
|
||||
bindRemoteConfigToLibsignalNet(getLibsignalNet(), window.getVersion());
|
||||
bindRemoteConfigToLibsignalNet(
|
||||
getLibsignalNet(),
|
||||
window.getVersion(),
|
||||
reconnect
|
||||
);
|
||||
|
||||
username = initialUsername;
|
||||
password = initialPassword;
|
||||
|
||||
Reference in New Issue
Block a user