Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit cb7fd51

Browse files
authored
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
1 parent f36b603 commit cb7fd51

File tree

6 files changed

+216
-59
lines changed

6 files changed

+216
-59
lines changed

playwright/e2e/crypto/staged-rollout.spec.ts

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,44 @@ limitations under the License.
1717
import { test, expect } from "../../element-web-test";
1818
import { logIntoElement } from "./utils";
1919

20-
test.describe("Migration of existing logins", () => {
20+
test.describe("Adoption of rust stack", () => {
2121
test("Test migration of existing logins when rollout is 100%", async ({
2222
page,
2323
context,
2424
app,
2525
credentials,
2626
homeserver,
2727
}, workerInfo) => {
28-
test.skip(workerInfo.project.name === "Rust Crypto", "This test only works with Rust crypto.");
28+
test.skip(
29+
workerInfo.project.name === "Rust Crypto",
30+
"No need to test this on Rust Crypto as we override the config manually",
31+
);
2932
await page.goto("/#/login");
3033

3134
let featureRustCrypto = false;
3235
let stagedRolloutPercent = 0;
3336

3437
await context.route(`http://localhost:8080/config.json*`, async (route) => {
35-
const json = {};
38+
const json = {
39+
default_server_config: {
40+
"m.homeserver": {
41+
base_url: "https://server.invalid",
42+
},
43+
},
44+
};
3645
json["features"] = {
3746
feature_rust_crypto: featureRustCrypto,
3847
};
3948
json["setting_defaults"] = {
49+
"language": "en-GB",
4050
"RustCrypto.staged_rollout_percent": stagedRolloutPercent,
4151
};
4252
await route.fulfill({ json });
4353
});
4454

55+
// reload to ensure we read the config
56+
await page.reload();
57+
4558
await logIntoElement(page, homeserver, credentials);
4659

4760
await app.settings.openUserSettings("Help & About");
@@ -61,4 +74,80 @@ test.describe("Migration of existing logins", () => {
6174
await app.settings.openUserSettings("Help & About");
6275
await expect(page.getByText("Crypto version: Rust SDK")).toBeVisible();
6376
});
77+
78+
test("Test new logins by default on rust stack", async ({
79+
page,
80+
context,
81+
app,
82+
credentials,
83+
homeserver,
84+
}, workerInfo) => {
85+
test.skip(
86+
workerInfo.project.name === "Rust Crypto",
87+
"No need to test this on Rust Crypto as we override the config manually",
88+
);
89+
await page.goto("/#/login");
90+
91+
await context.route(`http://localhost:8080/config.json*`, async (route) => {
92+
const json = {
93+
default_server_config: {
94+
"m.homeserver": {
95+
base_url: "https://server.invalid",
96+
},
97+
},
98+
};
99+
// we only want to test the default
100+
json["features"] = {};
101+
json["setting_defaults"] = {
102+
language: "en-GB",
103+
};
104+
await route.fulfill({ json });
105+
});
106+
107+
// reload to get the new config
108+
await page.reload();
109+
await logIntoElement(page, homeserver, credentials);
110+
111+
await app.settings.openUserSettings("Help & About");
112+
await expect(page.getByText("Crypto version: Rust SDK")).toBeVisible();
113+
});
114+
115+
test("Test default is to not rollout existing logins", async ({
116+
page,
117+
context,
118+
app,
119+
credentials,
120+
homeserver,
121+
}, workerInfo) => {
122+
test.skip(
123+
workerInfo.project.name === "Rust Crypto",
124+
"No need to test this on Rust Crypto as we override the config manually",
125+
);
126+
127+
await page.goto("/#/login");
128+
129+
// In the project.name = "Legacy crypto" it will be olm crypto
130+
await logIntoElement(page, homeserver, credentials);
131+
132+
await app.settings.openUserSettings("Help & About");
133+
await expect(page.getByText("Crypto version: Olm")).toBeVisible();
134+
135+
// Now simulate a refresh with `feature_rust_crypto` enabled but ensure we use the default rollout
136+
await context.route(`http://localhost:8080/config.json*`, async (route) => {
137+
const json = {};
138+
json["features"] = {
139+
feature_rust_crypto: true,
140+
};
141+
json["setting_defaults"] = {
142+
// We want to test the default so we don't set this
143+
// "RustCrypto.staged_rollout_percent": 0,
144+
};
145+
await route.fulfill({ json });
146+
});
147+
148+
await page.reload();
149+
150+
await app.settings.openUserSettings("Help & About");
151+
await expect(page.getByText("Crypto version: Olm")).toBeVisible();
152+
});
64153
});

playwright/element-web-test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,9 @@ export const test = base.extend<
111111
return obj;
112112
}, {}),
113113
};
114-
if (cryptoBackend === "rust") {
115-
json.features.feature_rust_crypto = true;
114+
// the default is to use rust now, so set to `false` if on legacy backend
115+
if (cryptoBackend === "legacy") {
116+
json.features.feature_rust_crypto = false;
116117
}
117118
await route.fulfill({ json });
118119
});

src/settings/Settings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ export const SETTINGS: { [setting: string]: ISetting } = {
501501
}
502502
},
503503
shouldWarn: true,
504-
default: false,
504+
default: true,
505505
controller: new RustCryptoSdkController(),
506506
},
507507
// Must be set under `setting_defaults` in config.json.

test/MatrixClientPeg-test.ts

Lines changed: 86 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import SettingsStore from "../src/settings/SettingsStore";
2424
import Modal from "../src/Modal";
2525
import PlatformPeg from "../src/PlatformPeg";
2626
import { SettingLevel } from "../src/settings/SettingLevel";
27+
import { Features } from "../src/settings/Settings";
2728

2829
jest.useFakeTimers();
2930

@@ -91,41 +92,96 @@ describe("MatrixClientPeg", () => {
9192
});
9293
});
9394

94-
it("should initialise client crypto", async () => {
95-
const mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockResolvedValue(undefined);
96-
const mockSetTrustCrossSignedDevices = jest
97-
.spyOn(testPeg.safeGet(), "setCryptoTrustCrossSignedDevices")
98-
.mockImplementation(() => {});
99-
const mockStartClient = jest.spyOn(testPeg.safeGet(), "startClient").mockResolvedValue(undefined);
95+
describe("legacy crypto", () => {
96+
beforeEach(() => {
97+
const originalGetValue = SettingsStore.getValue;
98+
jest.spyOn(SettingsStore, "getValue").mockImplementation(
99+
(settingName: string, roomId: string | null = null, excludeDefault = false) => {
100+
if (settingName === "feature_rust_crypto") {
101+
return false;
102+
}
103+
return originalGetValue(settingName, roomId, excludeDefault);
104+
},
105+
);
106+
});
100107

101-
await testPeg.start();
102-
expect(mockInitCrypto).toHaveBeenCalledTimes(1);
103-
expect(mockSetTrustCrossSignedDevices).toHaveBeenCalledTimes(1);
104-
expect(mockStartClient).toHaveBeenCalledTimes(1);
108+
it("should initialise client crypto", async () => {
109+
const mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockResolvedValue(undefined);
110+
const mockSetTrustCrossSignedDevices = jest
111+
.spyOn(testPeg.safeGet(), "setCryptoTrustCrossSignedDevices")
112+
.mockImplementation(() => {});
113+
const mockStartClient = jest.spyOn(testPeg.safeGet(), "startClient").mockResolvedValue(undefined);
114+
115+
await testPeg.start();
116+
expect(mockInitCrypto).toHaveBeenCalledTimes(1);
117+
expect(mockSetTrustCrossSignedDevices).toHaveBeenCalledTimes(1);
118+
expect(mockStartClient).toHaveBeenCalledTimes(1);
119+
});
120+
121+
it("should carry on regardless if there is an error initialising crypto", async () => {
122+
const e2eError = new Error("nope nope nope");
123+
const mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockRejectedValue(e2eError);
124+
const mockSetTrustCrossSignedDevices = jest
125+
.spyOn(testPeg.safeGet(), "setCryptoTrustCrossSignedDevices")
126+
.mockImplementation(() => {});
127+
const mockStartClient = jest.spyOn(testPeg.safeGet(), "startClient").mockResolvedValue(undefined);
128+
const mockWarning = jest.spyOn(logger, "warn").mockReturnValue(undefined);
129+
130+
await testPeg.start();
131+
expect(mockInitCrypto).toHaveBeenCalledTimes(1);
132+
expect(mockSetTrustCrossSignedDevices).not.toHaveBeenCalled();
133+
expect(mockStartClient).toHaveBeenCalledTimes(1);
134+
expect(mockWarning).toHaveBeenCalledWith(expect.stringMatching("Unable to initialise e2e"), e2eError);
135+
});
136+
137+
it("should reload when store database closes for a guest user", async () => {
138+
testPeg.safeGet().isGuest = () => true;
139+
const emitter = new EventEmitter();
140+
testPeg.safeGet().store.on = emitter.on.bind(emitter);
141+
const platform: any = { reload: jest.fn() };
142+
PlatformPeg.set(platform);
143+
await testPeg.assign();
144+
emitter.emit("closed" as any);
145+
expect(platform.reload).toHaveBeenCalled();
146+
});
147+
148+
it("should show error modal when store database closes", async () => {
149+
testPeg.safeGet().isGuest = () => false;
150+
const emitter = new EventEmitter();
151+
const platform: any = { getHumanReadableName: jest.fn() };
152+
PlatformPeg.set(platform);
153+
testPeg.safeGet().store.on = emitter.on.bind(emitter);
154+
const spy = jest.spyOn(Modal, "createDialog");
155+
await testPeg.assign();
156+
emitter.emit("closed" as any);
157+
expect(spy).toHaveBeenCalled();
158+
});
105159
});
106160

107-
it("should carry on regardless if there is an error initialising crypto", async () => {
108-
const e2eError = new Error("nope nope nope");
109-
const mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockRejectedValue(e2eError);
110-
const mockSetTrustCrossSignedDevices = jest
111-
.spyOn(testPeg.safeGet(), "setCryptoTrustCrossSignedDevices")
112-
.mockImplementation(() => {});
113-
const mockStartClient = jest.spyOn(testPeg.safeGet(), "startClient").mockResolvedValue(undefined);
114-
const mockWarning = jest.spyOn(logger, "warn").mockReturnValue(undefined);
161+
it("should initialise the rust crypto library by default", async () => {
162+
await SettingsStore.setValue(Features.RustCrypto, null, SettingLevel.DEVICE, null);
163+
164+
const mockSetValue = jest.spyOn(SettingsStore, "setValue").mockResolvedValue(undefined);
165+
166+
const mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockResolvedValue(undefined);
167+
const mockInitRustCrypto = jest.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
115168

116169
await testPeg.start();
117-
expect(mockInitCrypto).toHaveBeenCalledTimes(1);
118-
expect(mockSetTrustCrossSignedDevices).not.toHaveBeenCalled();
119-
expect(mockStartClient).toHaveBeenCalledTimes(1);
120-
expect(mockWarning).toHaveBeenCalledWith(expect.stringMatching("Unable to initialise e2e"), e2eError);
170+
expect(mockInitCrypto).not.toHaveBeenCalled();
171+
expect(mockInitRustCrypto).toHaveBeenCalledTimes(1);
172+
173+
// we should have stashed the setting in the settings store
174+
expect(mockSetValue).toHaveBeenCalledWith("feature_rust_crypto", null, SettingLevel.DEVICE, true);
121175
});
122176

123-
it("should initialise the rust crypto library, if enabled", async () => {
177+
it("should initialise the legacy crypto library if set", async () => {
178+
await SettingsStore.setValue(Features.RustCrypto, null, SettingLevel.DEVICE, null);
179+
124180
const originalGetValue = SettingsStore.getValue;
125181
jest.spyOn(SettingsStore, "getValue").mockImplementation(
126182
(settingName: string, roomId: string | null = null, excludeDefault = false) => {
127183
if (settingName === "feature_rust_crypto") {
128-
return true;
184+
return false;
129185
}
130186
return originalGetValue(settingName, roomId, excludeDefault);
131187
},
@@ -137,11 +193,11 @@ describe("MatrixClientPeg", () => {
137193
const mockInitRustCrypto = jest.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
138194

139195
await testPeg.start();
140-
expect(mockInitCrypto).not.toHaveBeenCalled();
141-
expect(mockInitRustCrypto).toHaveBeenCalledTimes(1);
196+
expect(mockInitCrypto).toHaveBeenCalled();
197+
expect(mockInitRustCrypto).not.toHaveBeenCalledTimes(1);
142198

143199
// we should have stashed the setting in the settings store
144-
expect(mockSetValue).toHaveBeenCalledWith("feature_rust_crypto", null, SettingLevel.DEVICE, true);
200+
expect(mockSetValue).toHaveBeenCalledWith("feature_rust_crypto", null, SettingLevel.DEVICE, false);
145201
});
146202

147203
describe("Rust staged rollout", () => {
@@ -178,10 +234,12 @@ describe("MatrixClientPeg", () => {
178234
let mockInitCrypto: jest.SpyInstance;
179235
let mockInitRustCrypto: jest.SpyInstance;
180236

181-
beforeEach(() => {
237+
beforeEach(async () => {
182238
mockSetValue = jest.spyOn(SettingsStore, "setValue").mockResolvedValue(undefined);
183239
mockInitCrypto = jest.spyOn(testPeg.safeGet(), "initCrypto").mockResolvedValue(undefined);
184240
mockInitRustCrypto = jest.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
241+
242+
await SettingsStore.setValue(Features.RustCrypto, null, SettingLevel.DEVICE, null);
185243
});
186244

187245
it("Should not migrate existing login if rollout is 0", async () => {
@@ -254,28 +312,5 @@ describe("MatrixClientPeg", () => {
254312
expect(mockSetValue).toHaveBeenCalledWith("feature_rust_crypto", null, SettingLevel.DEVICE, false);
255313
});
256314
});
257-
258-
it("should reload when store database closes for a guest user", async () => {
259-
testPeg.safeGet().isGuest = () => true;
260-
const emitter = new EventEmitter();
261-
testPeg.safeGet().store.on = emitter.on.bind(emitter);
262-
const platform: any = { reload: jest.fn() };
263-
PlatformPeg.set(platform);
264-
await testPeg.assign();
265-
emitter.emit("closed" as any);
266-
expect(platform.reload).toHaveBeenCalled();
267-
});
268-
269-
it("should show error modal when store database closes", async () => {
270-
testPeg.safeGet().isGuest = () => false;
271-
const emitter = new EventEmitter();
272-
const platform: any = { getHumanReadableName: jest.fn() };
273-
PlatformPeg.set(platform);
274-
testPeg.safeGet().store.on = emitter.on.bind(emitter);
275-
const spy = jest.spyOn(Modal, "createDialog");
276-
await testPeg.assign();
277-
emitter.emit("closed" as any);
278-
expect(spy).toHaveBeenCalled();
279-
});
280315
});
281316
});

test/components/structures/MatrixChat-test.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import * as Lifecycle from "../../../src/Lifecycle";
5757
import { SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY } from "../../../src/BasePlatform";
5858
import SettingsStore from "../../../src/settings/SettingsStore";
5959
import { SettingLevel } from "../../../src/settings/SettingLevel";
60+
import { MatrixClientPeg as peg } from "../../../src/MatrixClientPeg";
6061

6162
jest.mock("matrix-js-sdk/src/oidc/authorize", () => ({
6263
completeAuthorizationCodeGrant: jest.fn(),
@@ -84,6 +85,7 @@ describe("<MatrixChat />", () => {
8485
getClientWellKnown: jest.fn().mockReturnValue({}),
8586
isVersionSupported: jest.fn().mockResolvedValue(false),
8687
isCryptoEnabled: jest.fn().mockReturnValue(false),
88+
initRustCrypto: jest.fn(),
8789
getRoom: jest.fn(),
8890
getMediaHandler: jest.fn().mockReturnValue({
8991
setVideoInput: jest.fn(),
@@ -835,10 +837,22 @@ describe("<MatrixChat />", () => {
835837
});
836838

837839
it("should show the soft-logout page", async () => {
840+
// XXX This test is strange, it was working with legacy crypto
841+
// without mocking the following but the initCrypto call was failing
842+
// but as the exception was swallowed, the test was passing (see in `initClientCrypto`).
843+
// There are several uses of the peg in the app, so during all these tests you might end-up
844+
// with a real client instead of the mocked one. Not sure how reliable all these tests are.
845+
const originalReplace = peg.replaceUsingCreds;
846+
peg.replaceUsingCreds = jest.fn().mockResolvedValue(mockClient);
847+
// @ts-ignore - need to mock this for the test
848+
peg.matrixClient = mockClient;
849+
838850
const result = getComponent();
839851

840852
await result.findByText("You're signed out");
841853
expect(result.container).toMatchSnapshot();
854+
855+
peg.replaceUsingCreds = originalReplace;
842856
});
843857
});
844858

@@ -1336,15 +1350,14 @@ describe("<MatrixChat />", () => {
13361350
await populateStorageForSession();
13371351

13381352
const client = new MockClientWithEventEmitter({
1339-
initCrypto: jest.fn(),
13401353
...getMockClientMethods(),
13411354
}) as unknown as Mocked<MatrixClient>;
13421355
jest.spyOn(MatrixJs, "createClient").mockReturnValue(client);
13431356

13441357
// intercept initCrypto and have it block until we complete the deferred
13451358
const initCryptoCompleteDefer = defer();
13461359
const initCryptoCalled = new Promise<void>((resolve) => {
1347-
client.initCrypto.mockImplementation(() => {
1360+
client.initRustCrypto.mockImplementation(() => {
13481361
resolve();
13491362
return initCryptoCompleteDefer.promise;
13501363
});

0 commit comments

Comments
 (0)