From db30ef6ee48f91453eaa96a5e88f1377fe7bb982 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 17 May 2022 16:55:25 +0200 Subject: [PATCH] test(crypto): Add tests for the Wasm API. --- crates/matrix-sdk-crypto/tests/js/events.js | 10 ++ .../matrix-sdk-crypto/tests/js/identifiers.js | 37 ++++++ crates/matrix-sdk-crypto/tests/js/machine.js | 112 ++++++++++++++++++ crates/matrix-sdk-crypto/tests/js/requests.js | 41 +++++++ .../matrix-sdk-crypto/tests/js/sync_events.js | 23 ++++ examples/js/Makefile | 3 + 6 files changed, 226 insertions(+) create mode 100644 crates/matrix-sdk-crypto/tests/js/events.js create mode 100644 crates/matrix-sdk-crypto/tests/js/identifiers.js create mode 100644 crates/matrix-sdk-crypto/tests/js/machine.js create mode 100644 crates/matrix-sdk-crypto/tests/js/requests.js create mode 100644 crates/matrix-sdk-crypto/tests/js/sync_events.js diff --git a/crates/matrix-sdk-crypto/tests/js/events.js b/crates/matrix-sdk-crypto/tests/js/events.js new file mode 100644 index 000000000..a572c3e2f --- /dev/null +++ b/crates/matrix-sdk-crypto/tests/js/events.js @@ -0,0 +1,10 @@ +const { HistoryVisibility } = require('../../pkg/matrix_sdk_crypto'); +const test = require('node:test'); +const assert = require('node:assert/strict'); + +test('HistoryVisibility', (t) => { + assert.equal(HistoryVisibility.Invited, 0); + assert.equal(HistoryVisibility.Joined, 1); + assert.equal(HistoryVisibility.Shared, 2); + assert.equal(HistoryVisibility.WorldReadable, 3); +}); diff --git a/crates/matrix-sdk-crypto/tests/js/identifiers.js b/crates/matrix-sdk-crypto/tests/js/identifiers.js new file mode 100644 index 000000000..0315b4e1a --- /dev/null +++ b/crates/matrix-sdk-crypto/tests/js/identifiers.js @@ -0,0 +1,37 @@ +const { UserId, DeviceId, RoomId, ServerName } = require('../../pkg/matrix_sdk_crypto'); +const test = require('node:test'); +const assert = require('node:assert/strict'); + +test('UserId', (t) => { + assert.throws(() => { new UserId('@foobar') }, Error, 'An invalid user ID must throw an error'); + + const user = new UserId('@foo:bar.org'); + + assert.equal(user.localpart(), 'foo', 'Localpart is present'); + assert.ok(user.serverName() instanceof ServerName, 'Server name is present'); + assert.equal(user.isHistorical, false, 'User ID is not historical'); + assert.equal(user.toString(), '@foo:bar.org', 'Can read the user ID as a string'); +}); + +test('DeviceId', (t) => { + assert.equal(new DeviceId('foo').toString(), 'foo', 'Can read the device ID as a string'); +}); + +test('RoomId', (t) => { + assert.throws(() => { new UserId('!foo') }, Error, 'An invalid room ID must throw an error'); + + const room = new RoomId('!foo:bar.org'); + + assert.equal(room.localpart(), 'foo', 'Localpart is present'); + assert.ok(room.serverName() instanceof ServerName, 'Server name is present'); + assert.equal(room.toString(), '!foo:bar.org', 'Can read the room ID as a string'); +}); + +test('ServerName', (t) => { + assert.throws(() => { new ServerName('@foobar') }, Error, 'An invalid server name must throw an error'); + + assert.equal(new ServerName('foo.org').host(), 'foo.org', 'Host is present'); + assert.equal(new ServerName('foo.org').port(), undefined, 'Port is absent'); + assert.equal(new ServerName('foo.org:1234').port(), 1234, 'Port is present'); + assert.equal(new ServerName('foo.org').isIpLiteral(), false, 'Server name is not an IP literal'); +}); diff --git a/crates/matrix-sdk-crypto/tests/js/machine.js b/crates/matrix-sdk-crypto/tests/js/machine.js new file mode 100644 index 000000000..10c0e89ea --- /dev/null +++ b/crates/matrix-sdk-crypto/tests/js/machine.js @@ -0,0 +1,112 @@ +const { EncryptionAlgorithm, EncryptionSettings, HistoryVisibility, UserId, DeviceId, OlmMachine, DeviceLists, KeysUploadRequest, KeysQueryRequest } = require('../../pkg/matrix_sdk_crypto'); +const test = require('node:test'); +const assert = require('node:assert/strict'); + +test('EncryptionAlgorithm', (t) => { + assert.equal(EncryptionAlgorithm.OlmV1Curve25519AesSha2, 0); + assert.equal(EncryptionAlgorithm.MegolmV1AesSha2, 1); +}); + +test('EncryptionSettings', (t) => { + let es = new EncryptionSettings(); + + assert.equal(es.algorithm, EncryptionAlgorithm.MegolmV1AesSha2, 'It has a default algorithm'); + assert.equal(es.rotationPeriod, 604800000000n, 'It has a default rotation period'); + assert.equal(es.rotationPeriodMessages, 100n, 'It has a default message rotation period'); + assert.equal(es.historyVisibility, HistoryVisibility.Shared, 'It has a default history visibility'); + + es.algorithm = EncryptionSettings.OlmV1Curve25519AesSha2; + assert.equal(es.algorithm, EncryptionAlgorithm.OlmV1Curve25519AesSha2, 'It has a new algorithm'); + assert.throws(() => { es.algorithm = 42 }, Error, 'Enum values are validated'); + + es.rotationPeriod = 42n; + assert.equal(es.rotationPeriod, 42n, 'It has a new rotation period'); + + es.rotationPeriodMessages = 153n; + assert.equal(es.rotationPeriodMessages, 153n, 'It has a new message rotation period'); + + es.historyVisibility = HistoryVisibility.WorldReadable; + assert.equal(es.historyVisibility, HistoryVisibility.WorldReadable, 'It has a new history visibility'); + assert.throws(() => { es.historyVisibility = 42 }, Error, 'Enum values are validated'); +}); + +test('OlmMachine', async (t) => { + const user_id = new UserId('@foo:bar.org'); + const device_id = new DeviceId('baz'); + + await t.test('Construct', async (t) => { + const machine = await new OlmMachine(user_id, device_id); + + assert.ok(machine instanceof OlmMachine); + assert.equal(machine.userId().toString(), '@foo:bar.org', 'User ID is present'); + assert.equal(machine.deviceId().toString(), 'baz', 'Device ID is present'); + }); + + await t.test('Identity keys', async (t) => { + const machine = await new OlmMachine(user_id, device_id); + const identity_keys = machine.identityKeys(); + + assert.match(identity_keys.ed25519.toBase64(), /^[A-Za-z0-9+/]+$/, 'Ed25519 can be base64-encoded'); + assert.match(identity_keys.curve25519.toBase64(), /^[A-Za-z0-9+/]+$/, 'Curve25519 can be base64-encoded'); + assert.ok(identity_keys.curve25519.length > 0, 'Curve25519\'s length is greater than zero'); + }); + + await t.test('Display name', async (t) => { + const machine = await new OlmMachine(user_id, device_id); + + assert.equal(await machine.displayName(), undefined, 'Display name is absent by default'); + }); + + await t.test('Tracked users', async (t) => { + const machine = await new OlmMachine(user_id, device_id); + const tracked_users = machine.trackedUsers(); + + assert.ok(tracked_users instanceof Set, 'Tracket users are stored in a `Set`'); + assert.equal(tracked_users.size, 0, 'No tracked users by default'); + }); + + await t.test('Receive sync changes', async (t) => { + const machine = await new OlmMachine(user_id, device_id); + const to_device_events = JSON.stringify({}); + const changed_devices = new DeviceLists( + [new UserId('@foo:matrix.org'), new UserId('@bar:matrix.org')], + [new UserId('@baz:matrix.org'), new UserId('@qux:matrix.org')], + ); + const one_time_key_counts = new Map(); + one_time_key_counts.set('foo', 42); + one_time_key_counts.set('bar', 153); + const unused_fallback_keys = new Set(); + unused_fallback_keys.add('baz'); + unused_fallback_keys.add('qux'); + + const decrypted_to_device = JSON.parse( + await machine.receiveSyncChanges( + to_device_events, + changed_devices, + one_time_key_counts, + unused_fallback_keys, + ) + ); + + assert.deepEqual(decrypted_to_device, {}, 'Nothing to do by default'); + }); + + await t.test('Outgoing requests', async (t) => { + const machine = await new OlmMachine(user_id, device_id); + const outgoing_requests = await machine.outgoingRequests(); + + assert.ok(outgoing_requests instanceof Array, 'Outgoing requests are stored in an `Array`'); + assert.equal(outgoing_requests.length, 2, 'There is 2 outgoing requests'); + + const request1 = outgoing_requests[0]; + const request2 = outgoing_requests[1]; + + assert.ok(request1 instanceof KeysUploadRequest, 'First request is `KeysUploadRequest'); + assert.ok(request1.request_id.length > 0, 'First request has an ID'); + assert.ok(JSON.parse(request1.body) instanceof Object, 'First request has a valid body'); + + assert.ok(request2 instanceof KeysQueryRequest, 'Second request is `KeysQueryRequest`'); + assert.ok(request2.request_id.length > 0, 'Second request has an ID'); + assert.ok(JSON.parse(request2.body) instanceof Object, 'Second request has a valid body'); + }); +}); diff --git a/crates/matrix-sdk-crypto/tests/js/requests.js b/crates/matrix-sdk-crypto/tests/js/requests.js new file mode 100644 index 000000000..9c12decf3 --- /dev/null +++ b/crates/matrix-sdk-crypto/tests/js/requests.js @@ -0,0 +1,41 @@ +const { RequestType, KeysUploadRequest, KeysQueryRequest, KeysClaimRequest, ToDeviceRequest, SignatureUploadRequest, RoomMessageRequest, KeysBackupRequest } = require('../../pkg/matrix_sdk_crypto'); +const test = require('node:test'); +const assert = require('node:assert/strict'); + +test('RequestType', (t) => { + assert.equal(RequestType.KeysUpload, 0); + assert.equal(RequestType.KeysQuery, 1); + assert.equal(RequestType.KeysClaim, 2); + assert.equal(RequestType.ToDevice, 3); + assert.equal(RequestType.SignatureUpload, 4); + assert.equal(RequestType.RoomMessage, 5); + assert.equal(RequestType.KeysBackup, 6); +}); + +test('KeysUploadRequest', (t) => { + assert.ok(new KeysUploadRequest()); +}); + +test('KeysQueryRequest', (t) => { + assert.ok(new KeysQueryRequest()); +}); + +test('KeysClaimRequest', (t) => { + assert.ok(new KeysClaimRequest()); +}); + +test('ToDeviceRequest', (t) => { + assert.ok(new ToDeviceRequest()); +}); + +test('SignatureUploadRequest', (t) => { + assert.ok(new SignatureUploadRequest()); +}); + +test('RoomMessageRequest', (t) => { + assert.ok(new RoomMessageRequest()); +}); + +test('KeysBackupRequest', (t) => { + assert.ok(new KeysBackupRequest()); +}); diff --git a/crates/matrix-sdk-crypto/tests/js/sync_events.js b/crates/matrix-sdk-crypto/tests/js/sync_events.js new file mode 100644 index 000000000..9d5ec7215 --- /dev/null +++ b/crates/matrix-sdk-crypto/tests/js/sync_events.js @@ -0,0 +1,23 @@ +const { DeviceLists, UserId } = require('../../pkg/matrix_sdk_crypto'); +const test = require('node:test'); +const assert = require('node:assert/strict'); + +test('DeviceLists', (t) => { + const empty = new DeviceLists([], []); + + assert.equal(empty.isEmpty(), true, 'List is empty'); + assert.equal(empty.changed().length, 0, 'No user ID changed'); + assert.equal(empty.left().length, 0, 'No user ID left'); + + const list = new DeviceLists([new UserId('@foo:bar.org')], [new UserId('@baz:qux.org')]); + + assert.equal(list.isEmpty(), false, 'List is not empty'); + + const changed = list.changed(); + assert.equal(changed.length, 1, 'There is one user ID changed'); + assert.equal(changed[0].toString(), '@foo:bar.org', 'The user ID changed is correct'); + + const left = list.left(); + assert.equal(left.length, 1, 'There is one user ID left'); + assert.equal(left[0].toString(), '@baz:qux.org', 'The user ID left is correct'); +}); diff --git a/examples/js/Makefile b/examples/js/Makefile index 4813643a6..a47b7a27f 100644 --- a/examples/js/Makefile +++ b/examples/js/Makefile @@ -1,2 +1,5 @@ build: RUSTFLAGS='-C opt-level=z' wasm-pack build --release --target nodejs ../../crates/matrix-sdk-crypto --features js + +test: + node --test ../../crates/matrix-sdk-crypto/tests/js/**.js