SettingsStore: Change feature_rust_crypto to default true (#12203)

* Set default crypto stack to rust

* Update test for default to rust crypto

* Fix labs settings tests

* Fix test not working with rust crypto
This commit is contained in:
Valere 2024-02-02 13:20:13 +01:00 committed by GitHub
parent f36b6035f4
commit cb7fd5118e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 225 additions and 68 deletions

View file

@ -24,6 +24,7 @@ import SettingsStore from "../src/settings/SettingsStore";
import Modal from "../src/Modal";
import PlatformPeg from "../src/PlatformPeg";
import { SettingLevel } from "../src/settings/SettingLevel";
import { Features } from "../src/settings/Settings";
jest.useFakeTimers();
@ -91,45 +92,74 @@ describe("MatrixClientPeg", () => {
});
});
it("should initialise client crypto", async () => {
const mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockResolvedValue(undefined);
const mockSetTrustCrossSignedDevices = jest
.spyOn(testPeg.safeGet(), "setCryptoTrustCrossSignedDevices")
.mockImplementation(() => {});
const mockStartClient = jest.spyOn(testPeg.safeGet(), "startClient").mockResolvedValue(undefined);
describe("legacy crypto", () => {
beforeEach(() => {
const originalGetValue = SettingsStore.getValue;
jest.spyOn(SettingsStore, "getValue").mockImplementation(
(settingName: string, roomId: string | null = null, excludeDefault = false) => {
if (settingName === "feature_rust_crypto") {
return false;
}
return originalGetValue(settingName, roomId, excludeDefault);
},
);
});
await testPeg.start();
expect(mockInitCrypto).toHaveBeenCalledTimes(1);
expect(mockSetTrustCrossSignedDevices).toHaveBeenCalledTimes(1);
expect(mockStartClient).toHaveBeenCalledTimes(1);
it("should initialise client crypto", async () => {
const mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockResolvedValue(undefined);
const mockSetTrustCrossSignedDevices = jest
.spyOn(testPeg.safeGet(), "setCryptoTrustCrossSignedDevices")
.mockImplementation(() => {});
const mockStartClient = jest.spyOn(testPeg.safeGet(), "startClient").mockResolvedValue(undefined);
await testPeg.start();
expect(mockInitCrypto).toHaveBeenCalledTimes(1);
expect(mockSetTrustCrossSignedDevices).toHaveBeenCalledTimes(1);
expect(mockStartClient).toHaveBeenCalledTimes(1);
});
it("should carry on regardless if there is an error initialising crypto", async () => {
const e2eError = new Error("nope nope nope");
const mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockRejectedValue(e2eError);
const mockSetTrustCrossSignedDevices = jest
.spyOn(testPeg.safeGet(), "setCryptoTrustCrossSignedDevices")
.mockImplementation(() => {});
const mockStartClient = jest.spyOn(testPeg.safeGet(), "startClient").mockResolvedValue(undefined);
const mockWarning = jest.spyOn(logger, "warn").mockReturnValue(undefined);
await testPeg.start();
expect(mockInitCrypto).toHaveBeenCalledTimes(1);
expect(mockSetTrustCrossSignedDevices).not.toHaveBeenCalled();
expect(mockStartClient).toHaveBeenCalledTimes(1);
expect(mockWarning).toHaveBeenCalledWith(expect.stringMatching("Unable to initialise e2e"), e2eError);
});
it("should reload when store database closes for a guest user", async () => {
testPeg.safeGet().isGuest = () => true;
const emitter = new EventEmitter();
testPeg.safeGet().store.on = emitter.on.bind(emitter);
const platform: any = { reload: jest.fn() };
PlatformPeg.set(platform);
await testPeg.assign();
emitter.emit("closed" as any);
expect(platform.reload).toHaveBeenCalled();
});
it("should show error modal when store database closes", async () => {
testPeg.safeGet().isGuest = () => false;
const emitter = new EventEmitter();
const platform: any = { getHumanReadableName: jest.fn() };
PlatformPeg.set(platform);
testPeg.safeGet().store.on = emitter.on.bind(emitter);
const spy = jest.spyOn(Modal, "createDialog");
await testPeg.assign();
emitter.emit("closed" as any);
expect(spy).toHaveBeenCalled();
});
});
it("should carry on regardless if there is an error initialising crypto", async () => {
const e2eError = new Error("nope nope nope");
const mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockRejectedValue(e2eError);
const mockSetTrustCrossSignedDevices = jest
.spyOn(testPeg.safeGet(), "setCryptoTrustCrossSignedDevices")
.mockImplementation(() => {});
const mockStartClient = jest.spyOn(testPeg.safeGet(), "startClient").mockResolvedValue(undefined);
const mockWarning = jest.spyOn(logger, "warn").mockReturnValue(undefined);
await testPeg.start();
expect(mockInitCrypto).toHaveBeenCalledTimes(1);
expect(mockSetTrustCrossSignedDevices).not.toHaveBeenCalled();
expect(mockStartClient).toHaveBeenCalledTimes(1);
expect(mockWarning).toHaveBeenCalledWith(expect.stringMatching("Unable to initialise e2e"), e2eError);
});
it("should initialise the rust crypto library, if enabled", async () => {
const originalGetValue = SettingsStore.getValue;
jest.spyOn(SettingsStore, "getValue").mockImplementation(
(settingName: string, roomId: string | null = null, excludeDefault = false) => {
if (settingName === "feature_rust_crypto") {
return true;
}
return originalGetValue(settingName, roomId, excludeDefault);
},
);
it("should initialise the rust crypto library by default", async () => {
await SettingsStore.setValue(Features.RustCrypto, null, SettingLevel.DEVICE, null);
const mockSetValue = jest.spyOn(SettingsStore, "setValue").mockResolvedValue(undefined);
@ -144,6 +174,32 @@ describe("MatrixClientPeg", () => {
expect(mockSetValue).toHaveBeenCalledWith("feature_rust_crypto", null, SettingLevel.DEVICE, true);
});
it("should initialise the legacy crypto library if set", async () => {
await SettingsStore.setValue(Features.RustCrypto, null, SettingLevel.DEVICE, null);
const originalGetValue = SettingsStore.getValue;
jest.spyOn(SettingsStore, "getValue").mockImplementation(
(settingName: string, roomId: string | null = null, excludeDefault = false) => {
if (settingName === "feature_rust_crypto") {
return false;
}
return originalGetValue(settingName, roomId, excludeDefault);
},
);
const mockSetValue = jest.spyOn(SettingsStore, "setValue").mockResolvedValue(undefined);
const mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockResolvedValue(undefined);
const mockInitRustCrypto = jest.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
await testPeg.start();
expect(mockInitCrypto).toHaveBeenCalled();
expect(mockInitRustCrypto).not.toHaveBeenCalledTimes(1);
// we should have stashed the setting in the settings store
expect(mockSetValue).toHaveBeenCalledWith("feature_rust_crypto", null, SettingLevel.DEVICE, false);
});
describe("Rust staged rollout", () => {
function mockSettingStore(
userIsUsingRust: boolean,
@ -178,10 +234,12 @@ describe("MatrixClientPeg", () => {
let mockInitCrypto: jest.SpyInstance;
let mockInitRustCrypto: jest.SpyInstance;
beforeEach(() => {
beforeEach(async () => {
mockSetValue = jest.spyOn(SettingsStore, "setValue").mockResolvedValue(undefined);
mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockResolvedValue(undefined);
mockInitRustCrypto = jest.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
await SettingsStore.setValue(Features.RustCrypto, null, SettingLevel.DEVICE, null);
});
it("Should not migrate existing login if rollout is 0", async () => {
@ -254,28 +312,5 @@ describe("MatrixClientPeg", () => {
expect(mockSetValue).toHaveBeenCalledWith("feature_rust_crypto", null, SettingLevel.DEVICE, false);
});
});
it("should reload when store database closes for a guest user", async () => {
testPeg.safeGet().isGuest = () => true;
const emitter = new EventEmitter();
testPeg.safeGet().store.on = emitter.on.bind(emitter);
const platform: any = { reload: jest.fn() };
PlatformPeg.set(platform);
await testPeg.assign();
emitter.emit("closed" as any);
expect(platform.reload).toHaveBeenCalled();
});
it("should show error modal when store database closes", async () => {
testPeg.safeGet().isGuest = () => false;
const emitter = new EventEmitter();
const platform: any = { getHumanReadableName: jest.fn() };
PlatformPeg.set(platform);
testPeg.safeGet().store.on = emitter.on.bind(emitter);
const spy = jest.spyOn(Modal, "createDialog");
await testPeg.assign();
emitter.emit("closed" as any);
expect(spy).toHaveBeenCalled();
});
});
});

View file

@ -57,6 +57,7 @@ import * as Lifecycle from "../../../src/Lifecycle";
import { SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY } from "../../../src/BasePlatform";
import SettingsStore from "../../../src/settings/SettingsStore";
import { SettingLevel } from "../../../src/settings/SettingLevel";
import { MatrixClientPeg as peg } from "../../../src/MatrixClientPeg";
jest.mock("matrix-js-sdk/src/oidc/authorize", () => ({
completeAuthorizationCodeGrant: jest.fn(),
@ -84,6 +85,7 @@ describe("<MatrixChat />", () => {
getClientWellKnown: jest.fn().mockReturnValue({}),
isVersionSupported: jest.fn().mockResolvedValue(false),
isCryptoEnabled: jest.fn().mockReturnValue(false),
initRustCrypto: jest.fn(),
getRoom: jest.fn(),
getMediaHandler: jest.fn().mockReturnValue({
setVideoInput: jest.fn(),
@ -835,10 +837,22 @@ describe("<MatrixChat />", () => {
});
it("should show the soft-logout page", async () => {
// XXX This test is strange, it was working with legacy crypto
// without mocking the following but the initCrypto call was failing
// but as the exception was swallowed, the test was passing (see in `initClientCrypto`).
// There are several uses of the peg in the app, so during all these tests you might end-up
// with a real client instead of the mocked one. Not sure how reliable all these tests are.
const originalReplace = peg.replaceUsingCreds;
peg.replaceUsingCreds = jest.fn().mockResolvedValue(mockClient);
// @ts-ignore - need to mock this for the test
peg.matrixClient = mockClient;
const result = getComponent();
await result.findByText("You're signed out");
expect(result.container).toMatchSnapshot();
peg.replaceUsingCreds = originalReplace;
});
});
@ -1336,7 +1350,6 @@ describe("<MatrixChat />", () => {
await populateStorageForSession();
const client = new MockClientWithEventEmitter({
initCrypto: jest.fn(),
...getMockClientMethods(),
}) as unknown as Mocked<MatrixClient>;
jest.spyOn(MatrixJs, "createClient").mockReturnValue(client);
@ -1344,7 +1357,7 @@ describe("<MatrixChat />", () => {
// intercept initCrypto and have it block until we complete the deferred
const initCryptoCompleteDefer = defer();
const initCryptoCalled = new Promise<void>((resolve) => {
client.initCrypto.mockImplementation(() => {
client.initRustCrypto.mockImplementation(() => {
resolve();
return initCryptoCompleteDefer.promise;
});

View file

@ -71,6 +71,25 @@ describe("<LabsUserSettingsTab />", () => {
});
describe("Not enabled in config", () => {
// these tests only works if the feature is not enabled in the config by default?
const copyOfGetValueAt = SettingsStore.getValueAt;
beforeEach(() => {
SettingsStore.getValueAt = (
level: SettingLevel,
name: string,
roomId?: string,
isExplicit?: boolean,
) => {
if (level == SettingLevel.CONFIG && name === "feature_rust_crypto") return false;
return copyOfGetValueAt(level, name, roomId, isExplicit);
};
});
afterEach(() => {
SettingsStore.getValueAt = copyOfGetValueAt;
});
it("can be turned on if not already", async () => {
// By the time the settings panel is shown, `MatrixClientPeg.initClientCrypto` has saved the current
// value to the settings store.