Merge pull request #1597 from famedly/nico/stricter-lints
stricter lints
This commit is contained in:
commit
ee14fb7116
|
|
@ -2,19 +2,44 @@ include: package:lints/recommended.yaml
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
rules:
|
rules:
|
||||||
camel_case_types: true
|
# We enable a few additional rules not in the default and recommended sets.
|
||||||
avoid_print: true
|
# Those in general have low impact on correct code apart from possibly requiring an additional keyword (like async,
|
||||||
constant_identifier_names: true
|
# await, final) or asking you to move a statement to a different line. However many of these linter rules make the
|
||||||
prefer_final_locals: true
|
# code either more uniform or avoid bug prone behaviour. For example not awaiting a future usually is a mistake.
|
||||||
|
# You can always disable a linter warning with a comment like `// ignore: unawaited_futures`. Please add reasoning,
|
||||||
|
# why you disable the rule in such cases. This helps others understand, that you explicitly want a behaviour, that
|
||||||
|
# usually would be considered a mistake.
|
||||||
|
|
||||||
|
# Performance (or potential bugs)
|
||||||
|
# Fixing these warnings makes code easier to optimize for the compiler or prevents leaks.
|
||||||
|
cancel_subscriptions: true
|
||||||
prefer_final_in_for_each: true
|
prefer_final_in_for_each: true
|
||||||
sort_pub_dependencies: true
|
prefer_final_locals: true
|
||||||
always_use_package_imports: true
|
|
||||||
|
# Warn about possible bugs
|
||||||
|
# Usually code with these warnings indicates a bug.
|
||||||
|
# Please document if your code explicitly wants such a behaviour.
|
||||||
always_declare_return_types: true
|
always_declare_return_types: true
|
||||||
|
discarded_futures: true
|
||||||
|
no_adjacent_strings_in_list: true
|
||||||
|
test_types_in_equals: true
|
||||||
|
throw_in_finally: true
|
||||||
|
unawaited_futures: true
|
||||||
|
unnecessary_statements: true
|
||||||
|
unsafe_html: true
|
||||||
|
|
||||||
|
# Readability & Style
|
||||||
|
# These are opinionated choices, where Dart gives us 2 ways to express the same thing.
|
||||||
|
# This avoids mental overhead by not having to make a choice and making code more uniform to read.
|
||||||
|
always_use_package_imports: true
|
||||||
|
avoid_bool_literals_in_conditional_expressions: true
|
||||||
prefer_single_quotes: true
|
prefer_single_quotes: true
|
||||||
sort_child_properties_last: true
|
sort_child_properties_last: true
|
||||||
unawaited_futures: true
|
sort_pub_dependencies: true
|
||||||
unsafe_html: true
|
|
||||||
avoid_function_literals_in_foreach_calls: false
|
# Be nice to our users and allow them to configure what gets logged.
|
||||||
|
avoid_print: true
|
||||||
|
|
||||||
non_constant_identifier_names: false # seems to wrongly diagnose static const variables
|
non_constant_identifier_names: false # seems to wrongly diagnose static const variables
|
||||||
|
|
||||||
analyzer:
|
analyzer:
|
||||||
|
|
@ -22,6 +47,4 @@ analyzer:
|
||||||
todo: ignore
|
todo: ignore
|
||||||
exclude:
|
exclude:
|
||||||
- example/main.dart
|
- example/main.dart
|
||||||
# needed until crypto packages upgrade
|
|
||||||
- lib/src/database/database.g.dart
|
|
||||||
plugins:
|
plugins:
|
||||||
|
|
|
||||||
|
|
@ -100,11 +100,12 @@ class Encryption {
|
||||||
|
|
||||||
void handleDeviceOneTimeKeysCount(
|
void handleDeviceOneTimeKeysCount(
|
||||||
Map<String, int>? countJson, List<String>? unusedFallbackKeyTypes) {
|
Map<String, int>? countJson, List<String>? unusedFallbackKeyTypes) {
|
||||||
runInRoot(() => olmManager.handleDeviceOneTimeKeysCount(
|
runInRoot(() async => olmManager.handleDeviceOneTimeKeysCount(
|
||||||
countJson, unusedFallbackKeyTypes));
|
countJson, unusedFallbackKeyTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSync() {
|
void onSync() {
|
||||||
|
// ignore: discarded_futures
|
||||||
keyVerificationManager.cleanup();
|
keyVerificationManager.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,30 +119,25 @@ class Encryption {
|
||||||
.contains(event.type)) {
|
.contains(event.type)) {
|
||||||
// "just" room key request things. We don't need these asap, so we handle
|
// "just" room key request things. We don't need these asap, so we handle
|
||||||
// them in the background
|
// them in the background
|
||||||
// ignore: unawaited_futures
|
|
||||||
runInRoot(() => keyManager.handleToDeviceEvent(event));
|
runInRoot(() => keyManager.handleToDeviceEvent(event));
|
||||||
}
|
}
|
||||||
if (event.type == EventTypes.Dummy) {
|
if (event.type == EventTypes.Dummy) {
|
||||||
// the previous device just had to create a new olm session, due to olm session
|
// the previous device just had to create a new olm session, due to olm session
|
||||||
// corruption. We want to try to send it the last message we just sent it, if possible
|
// corruption. We want to try to send it the last message we just sent it, if possible
|
||||||
// ignore: unawaited_futures
|
|
||||||
runInRoot(() => olmManager.handleToDeviceEvent(event));
|
runInRoot(() => olmManager.handleToDeviceEvent(event));
|
||||||
}
|
}
|
||||||
if (event.type.startsWith('m.key.verification.')) {
|
if (event.type.startsWith('m.key.verification.')) {
|
||||||
// some key verification event. No need to handle it now, we can easily
|
// some key verification event. No need to handle it now, we can easily
|
||||||
// do this in the background
|
// do this in the background
|
||||||
|
|
||||||
// ignore: unawaited_futures
|
|
||||||
runInRoot(() => keyVerificationManager.handleToDeviceEvent(event));
|
runInRoot(() => keyVerificationManager.handleToDeviceEvent(event));
|
||||||
}
|
}
|
||||||
if (event.type.startsWith('m.secret.')) {
|
if (event.type.startsWith('m.secret.')) {
|
||||||
// some ssss thing. We can do this in the background
|
// some ssss thing. We can do this in the background
|
||||||
// ignore: unawaited_futures
|
|
||||||
runInRoot(() => ssss.handleToDeviceEvent(event));
|
runInRoot(() => ssss.handleToDeviceEvent(event));
|
||||||
}
|
}
|
||||||
if (event.sender == client.userID) {
|
if (event.sender == client.userID) {
|
||||||
// maybe we need to re-try SSSS secrets
|
// maybe we need to re-try SSSS secrets
|
||||||
// ignore: unawaited_futures
|
|
||||||
runInRoot(() => ssss.periodicallyRequestMissingCache());
|
runInRoot(() => ssss.periodicallyRequestMissingCache());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,14 +153,11 @@ class Encryption {
|
||||||
update.content['content']['msgtype']
|
update.content['content']['msgtype']
|
||||||
.startsWith('m.key.verification.'))) {
|
.startsWith('m.key.verification.'))) {
|
||||||
// "just" key verification, no need to do this in sync
|
// "just" key verification, no need to do this in sync
|
||||||
|
|
||||||
// ignore: unawaited_futures
|
|
||||||
runInRoot(() => keyVerificationManager.handleEventUpdate(update));
|
runInRoot(() => keyVerificationManager.handleEventUpdate(update));
|
||||||
}
|
}
|
||||||
if (update.content['sender'] == client.userID &&
|
if (update.content['sender'] == client.userID &&
|
||||||
update.content['unsigned']?['transaction_id'] == null) {
|
update.content['unsigned']?['transaction_id'] == null) {
|
||||||
// maybe we need to re-try SSSS secrets
|
// maybe we need to re-try SSSS secrets
|
||||||
// ignore: unawaited_futures
|
|
||||||
runInRoot(() => ssss.periodicallyRequestMissingCache());
|
runInRoot(() => ssss.periodicallyRequestMissingCache());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -239,6 +232,7 @@ class Encryption {
|
||||||
// the entry should always exist. In the case it doesn't, the following
|
// the entry should always exist. In the case it doesn't, the following
|
||||||
// line *could* throw an error. As that is a future, though, and we call
|
// line *could* throw an error. As that is a future, though, and we call
|
||||||
// it un-awaited here, nothing happens, which is exactly the result we want
|
// it un-awaited here, nothing happens, which is exactly the result we want
|
||||||
|
// ignore: discarded_futures
|
||||||
client.database?.updateInboundGroupSessionIndexes(
|
client.database?.updateInboundGroupSessionIndexes(
|
||||||
json.encode(inboundGroupSession.indexes), roomId, sessionId);
|
json.encode(inboundGroupSession.indexes), roomId, sessionId);
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +246,7 @@ class Encryption {
|
||||||
?.session_id() ??
|
?.session_id() ??
|
||||||
'') ==
|
'') ==
|
||||||
content.sessionId) {
|
content.sessionId) {
|
||||||
runInRoot(() =>
|
runInRoot(() async =>
|
||||||
keyManager.clearOrUseOutboundGroupSession(roomId, wipe: true));
|
keyManager.clearOrUseOutboundGroupSession(roomId, wipe: true));
|
||||||
}
|
}
|
||||||
if (canRequestSession) {
|
if (canRequestSession) {
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,8 @@ class KeyManager {
|
||||||
!client.isUnknownSession) {
|
!client.isUnknownSession) {
|
||||||
// do e2ee recovery
|
// do e2ee recovery
|
||||||
_requestedSessionIds.add(requestIdent);
|
_requestedSessionIds.add(requestIdent);
|
||||||
runInRoot(() => request(
|
|
||||||
|
runInRoot(() async => request(
|
||||||
room,
|
room,
|
||||||
sessionId,
|
sessionId,
|
||||||
senderKey,
|
senderKey,
|
||||||
|
|
@ -775,8 +776,8 @@ class KeyManager {
|
||||||
Future<void>? _uploadingFuture;
|
Future<void>? _uploadingFuture;
|
||||||
|
|
||||||
void startAutoUploadKeys() {
|
void startAutoUploadKeys() {
|
||||||
_uploadKeysOnSync = encryption.client.onSync.stream
|
_uploadKeysOnSync = encryption.client.onSync.stream.listen(
|
||||||
.listen((_) => uploadInboundGroupSessions(skipIfInProgress: true));
|
(_) async => uploadInboundGroupSessions(skipIfInProgress: true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This task should be performed after sync processing but should not block
|
/// This task should be performed after sync processing but should not block
|
||||||
|
|
@ -1064,6 +1065,7 @@ class KeyManager {
|
||||||
StreamSubscription<SyncUpdate>? _uploadKeysOnSync;
|
StreamSubscription<SyncUpdate>? _uploadKeysOnSync;
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
// ignore: discarded_futures
|
||||||
_uploadKeysOnSync?.cancel();
|
_uploadKeysOnSync?.cancel();
|
||||||
for (final sess in _outboundGroupSessions.values) {
|
for (final sess in _outboundGroupSessions.values) {
|
||||||
sess.dispose();
|
sess.dispose();
|
||||||
|
|
|
||||||
|
|
@ -396,20 +396,24 @@ class OlmManager {
|
||||||
final device = client.userDeviceKeys[event.sender]?.deviceKeys.values
|
final device = client.userDeviceKeys[event.sender]?.deviceKeys.values
|
||||||
.firstWhereOrNull((d) => d.curve25519Key == senderKey);
|
.firstWhereOrNull((d) => d.curve25519Key == senderKey);
|
||||||
final existingSessions = olmSessions[senderKey];
|
final existingSessions = olmSessions[senderKey];
|
||||||
Future<void> updateSessionUsage([OlmSession? session]) =>
|
Future<void> updateSessionUsage([OlmSession? session]) async {
|
||||||
runInRoot(() async {
|
try {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
session.lastReceived = DateTime.now();
|
session.lastReceived = DateTime.now();
|
||||||
await storeOlmSession(session);
|
await storeOlmSession(session);
|
||||||
}
|
}
|
||||||
if (device != null) {
|
if (device != null) {
|
||||||
device.lastActive = DateTime.now();
|
device.lastActive = DateTime.now();
|
||||||
await encryption.olmDatabase?.setLastActiveUserDeviceKey(
|
await encryption.olmDatabase?.setLastActiveUserDeviceKey(
|
||||||
device.lastActive.millisecondsSinceEpoch,
|
device.lastActive.millisecondsSinceEpoch,
|
||||||
device.userId,
|
device.userId,
|
||||||
device.deviceId!);
|
device.deviceId!);
|
||||||
}
|
}
|
||||||
});
|
} catch (e, s) {
|
||||||
|
Logs().e('Error while updating olm session timestamp', e, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (existingSessions != null) {
|
if (existingSessions != null) {
|
||||||
for (final session in existingSessions) {
|
for (final session in existingSessions) {
|
||||||
if (session.session == null) {
|
if (session.session == null) {
|
||||||
|
|
@ -446,14 +450,16 @@ class OlmManager {
|
||||||
newSession.create_inbound_from(_olmAccount!, senderKey, body);
|
newSession.create_inbound_from(_olmAccount!, senderKey, body);
|
||||||
_olmAccount!.remove_one_time_keys(newSession);
|
_olmAccount!.remove_one_time_keys(newSession);
|
||||||
await encryption.olmDatabase?.updateClientKeys(pickledOlmAccount!);
|
await encryption.olmDatabase?.updateClientKeys(pickledOlmAccount!);
|
||||||
|
|
||||||
plaintext = newSession.decrypt(type, body);
|
plaintext = newSession.decrypt(type, body);
|
||||||
await runInRoot(() => storeOlmSession(OlmSession(
|
|
||||||
key: client.userID!,
|
await storeOlmSession(OlmSession(
|
||||||
identityKey: senderKey,
|
key: client.userID!,
|
||||||
sessionId: newSession.session_id(),
|
identityKey: senderKey,
|
||||||
session: newSession,
|
sessionId: newSession.session_id(),
|
||||||
lastReceived: DateTime.now(),
|
session: newSession,
|
||||||
)));
|
lastReceived: DateTime.now(),
|
||||||
|
));
|
||||||
await updateSessionUsage();
|
await updateSessionUsage();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
newSession.free();
|
newSession.free();
|
||||||
|
|
@ -570,8 +576,6 @@ class OlmManager {
|
||||||
return _decryptToDeviceEvent(event);
|
return _decryptToDeviceEvent(event);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// okay, the thing errored while decrypting. It is safe to assume that the olm session is corrupt and we should generate a new one
|
// okay, the thing errored while decrypting. It is safe to assume that the olm session is corrupt and we should generate a new one
|
||||||
|
|
||||||
// ignore: unawaited_futures
|
|
||||||
runInRoot(() => restoreOlmSession(event.senderId, senderKey));
|
runInRoot(() => restoreOlmSession(event.senderId, senderKey));
|
||||||
|
|
||||||
rethrow;
|
rethrow;
|
||||||
|
|
@ -658,14 +662,18 @@ class OlmManager {
|
||||||
final encryptResult = sess.first.session!.encrypt(json.encode(fullPayload));
|
final encryptResult = sess.first.session!.encrypt(json.encode(fullPayload));
|
||||||
await storeOlmSession(sess.first);
|
await storeOlmSession(sess.first);
|
||||||
if (encryption.olmDatabase != null) {
|
if (encryption.olmDatabase != null) {
|
||||||
await runInRoot(
|
try {
|
||||||
() async => encryption.olmDatabase?.setLastSentMessageUserDeviceKey(
|
await encryption.olmDatabase?.setLastSentMessageUserDeviceKey(
|
||||||
json.encode({
|
json.encode({
|
||||||
'type': type,
|
'type': type,
|
||||||
'content': payload,
|
'content': payload,
|
||||||
}),
|
}),
|
||||||
device.userId,
|
device.userId,
|
||||||
device.deviceId!));
|
device.deviceId!);
|
||||||
|
} catch (e, s) {
|
||||||
|
// we can ignore this error, since it would just make us use a different olm session possibly
|
||||||
|
Logs().w('Error while updating olm usage timestamp', e, s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
final encryptedBody = <String, dynamic>{
|
final encryptedBody = <String, dynamic>{
|
||||||
'algorithm': AlgorithmTypes.olmV1Curve25519AesSha2,
|
'algorithm': AlgorithmTypes.olmV1Curve25519AesSha2,
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ import 'package:matrix/encryption/utils/ssss_cache.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||||
import 'package:matrix/src/utils/crypto/crypto.dart' as uc;
|
import 'package:matrix/src/utils/crypto/crypto.dart' as uc;
|
||||||
import 'package:matrix/src/utils/run_in_root.dart';
|
|
||||||
|
|
||||||
const cacheTypes = <String>{
|
const cacheTypes = <String>{
|
||||||
EventTypes.CrossSigningSelfSigning,
|
EventTypes.CrossSigningSelfSigning,
|
||||||
|
|
@ -722,7 +721,11 @@ class OpenSSSS {
|
||||||
throw InvalidPassphraseException('Inalid key');
|
throw InvalidPassphraseException('Inalid key');
|
||||||
}
|
}
|
||||||
if (postUnlock) {
|
if (postUnlock) {
|
||||||
await runInRoot(() => _postUnlock());
|
try {
|
||||||
|
await _postUnlock();
|
||||||
|
} catch (e, s) {
|
||||||
|
Logs().e('Error during post unlock', e, s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'dart:typed_data';
|
||||||
|
|
||||||
/// decodes base64
|
/// decodes base64
|
||||||
///
|
///
|
||||||
/// Dart's native [base64.decode] requires a padded base64 input String.
|
/// Dart's native [base64.decode()] requires a padded base64 input String.
|
||||||
/// This function allows unpadded base64 too.
|
/// This function allows unpadded base64 too.
|
||||||
///
|
///
|
||||||
/// See: https://github.com/dart-lang/sdk/issues/39510
|
/// See: https://github.com/dart-lang/sdk/issues/39510
|
||||||
|
|
|
||||||
|
|
@ -267,11 +267,8 @@ class KeyVerification {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `qrCanWork` - qr cannot work if we are verifying another master key but our own is unverified
|
/// `qrCanWork` - qr cannot work if we are verifying another master key but our own is unverified
|
||||||
final qrCanWork = (userId != client.userID)
|
final qrCanWork = (userId == client.userID) ||
|
||||||
? ((client.userDeviceKeys[client.userID]?.masterKey?.verified ?? false)
|
((client.userDeviceKeys[client.userID]?.masterKey?.verified ?? false));
|
||||||
? true
|
|
||||||
: false)
|
|
||||||
: true;
|
|
||||||
|
|
||||||
if (client.verificationMethods.contains(KeyVerificationMethod.qrShow) &&
|
if (client.verificationMethods.contains(KeyVerificationMethod.qrShow) &&
|
||||||
qrCanWork) {
|
qrCanWork) {
|
||||||
|
|
|
||||||
|
|
@ -1636,7 +1636,7 @@ class Client extends MatrixApi {
|
||||||
set backgroundSync(bool enabled) {
|
set backgroundSync(bool enabled) {
|
||||||
_backgroundSync = enabled;
|
_backgroundSync = enabled;
|
||||||
if (_backgroundSync) {
|
if (_backgroundSync) {
|
||||||
_sync();
|
runInRoot(() async => _sync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2232,7 +2232,7 @@ class Client extends MatrixApi {
|
||||||
requestHistoryOnLimitedTimeline) {
|
requestHistoryOnLimitedTimeline) {
|
||||||
Logs().v(
|
Logs().v(
|
||||||
'Limited timeline for ${rooms[roomIndex].id} request history now');
|
'Limited timeline for ${rooms[roomIndex].id} request history now');
|
||||||
unawaited(runInRoot(rooms[roomIndex].requestHistory));
|
runInRoot(rooms[roomIndex].requestHistory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return room;
|
return room;
|
||||||
|
|
|
||||||
|
|
@ -612,7 +612,7 @@ class HiveCollectionsDatabase extends DatabaseApi {
|
||||||
// post-load the heroes
|
// post-load the heroes
|
||||||
final heroes = room.summary.mHeroes;
|
final heroes = room.summary.mHeroes;
|
||||||
if (heroes != null) {
|
if (heroes != null) {
|
||||||
heroes.forEach((hero) => membersToPostload.add(hero));
|
membersToPostload.addAll(heroes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Load members
|
// Load members
|
||||||
|
|
@ -771,9 +771,9 @@ class HiveCollectionsDatabase extends DatabaseApi {
|
||||||
.toList();
|
.toList();
|
||||||
final states = await _roomMembersBox.getAll(keys);
|
final states = await _roomMembersBox.getAll(keys);
|
||||||
states.removeWhere((state) => state == null);
|
states.removeWhere((state) => state == null);
|
||||||
states.forEach(
|
for (final state in states) {
|
||||||
(state) => users.add(Event.fromJson(copyMap(state!), room).asUser),
|
users.add(Event.fromJson(copyMap(state!), room).asUser);
|
||||||
);
|
}
|
||||||
|
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@ class Event extends MatrixEvent {
|
||||||
final json = toJson();
|
final json = toJson();
|
||||||
json['unsigned'] ??= <String, dynamic>{};
|
json['unsigned'] ??= <String, dynamic>{};
|
||||||
json['unsigned'][messageSendingStatusKey] = EventStatus.error.intValue;
|
json['unsigned'][messageSendingStatusKey] = EventStatus.error.intValue;
|
||||||
|
// ignore: discarded_futures
|
||||||
room.client.handleSync(
|
room.client.handleSync(
|
||||||
SyncUpdate(
|
SyncUpdate(
|
||||||
nextBatch: '',
|
nextBatch: '',
|
||||||
|
|
@ -154,6 +155,7 @@ class Event extends MatrixEvent {
|
||||||
MessageTypes.File,
|
MessageTypes.File,
|
||||||
}.contains(messageType) &&
|
}.contains(messageType) &&
|
||||||
!room.sendingFilePlaceholders.containsKey(eventId)) {
|
!room.sendingFilePlaceholders.containsKey(eventId)) {
|
||||||
|
// ignore: discarded_futures
|
||||||
remove();
|
remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1620,6 +1620,7 @@ class Room {
|
||||||
return user.asUser;
|
return user.asUser;
|
||||||
} else {
|
} else {
|
||||||
if (mxID.isValidMatrixId) {
|
if (mxID.isValidMatrixId) {
|
||||||
|
// ignore: discarded_futures
|
||||||
requestUser(
|
requestUser(
|
||||||
mxID,
|
mxID,
|
||||||
ignoreErrors: true,
|
ignoreErrors: true,
|
||||||
|
|
@ -2280,7 +2281,7 @@ class Room {
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, int> servers = {};
|
final Map<String, int> servers = {};
|
||||||
users.forEach((user) {
|
for (final user in users) {
|
||||||
if (user.id.domain != null) {
|
if (user.id.domain != null) {
|
||||||
if (servers.containsKey(user.id.domain!)) {
|
if (servers.containsKey(user.id.domain!)) {
|
||||||
servers[user.id.domain!] = servers[user.id.domain!]! + 1;
|
servers[user.id.domain!] = servers[user.id.domain!]! + 1;
|
||||||
|
|
@ -2288,7 +2289,7 @@ class Room {
|
||||||
servers[user.id.domain!] = 1;
|
servers[user.id.domain!] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
final sortedServers = Map.fromEntries(servers.entries.toList()
|
final sortedServers = Map.fromEntries(servers.entries.toList()
|
||||||
..sort((e1, e2) => e1.value.compareTo(e2.value)));
|
..sort((e1, e2) => e1.value.compareTo(e2.value)));
|
||||||
for (var i = 0; i <= 2; i++) {
|
for (var i = 0; i <= 2; i++) {
|
||||||
|
|
|
||||||
|
|
@ -300,8 +300,11 @@ class Timeline {
|
||||||
|
|
||||||
/// Don't forget to call this before you dismiss this object!
|
/// Don't forget to call this before you dismiss this object!
|
||||||
void cancelSubscriptions() {
|
void cancelSubscriptions() {
|
||||||
|
// ignore: discarded_futures
|
||||||
sub?.cancel();
|
sub?.cancel();
|
||||||
|
// ignore: discarded_futures
|
||||||
roomSub?.cancel();
|
roomSub?.cancel();
|
||||||
|
// ignore: discarded_futures
|
||||||
sessionIdReceivedSub?.cancel();
|
sessionIdReceivedSub?.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -433,17 +433,16 @@ class DeviceKeys extends SignableKey {
|
||||||
bool? _validSelfSignature;
|
bool? _validSelfSignature;
|
||||||
bool get selfSigned =>
|
bool get selfSigned =>
|
||||||
_validSelfSignature ??
|
_validSelfSignature ??
|
||||||
(_validSelfSignature = (deviceId != null &&
|
(_validSelfSignature = deviceId != null &&
|
||||||
signatures
|
signatures
|
||||||
?.tryGetMap<String, Object?>(userId)
|
?.tryGetMap<String, Object?>(userId)
|
||||||
?.tryGet<String>('ed25519:$deviceId') ==
|
?.tryGet<String>('ed25519:$deviceId') !=
|
||||||
null
|
null &&
|
||||||
? false
|
|
||||||
// without libolm we still want to be able to add devices. In that case we ofc just can't
|
// without libolm we still want to be able to add devices. In that case we ofc just can't
|
||||||
// verify the signature
|
// verify the signature
|
||||||
: _verifySignature(
|
_verifySignature(
|
||||||
ed25519Key!, signatures![userId]!['ed25519:$deviceId']!,
|
ed25519Key!, signatures![userId]!['ed25519:$deviceId']!,
|
||||||
isSignatureWithoutLibolmValid: true)));
|
isSignatureWithoutLibolmValid: true));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get blocked => super.blocked || !selfSigned;
|
bool get blocked => super.blocked || !selfSigned;
|
||||||
|
|
@ -505,7 +504,7 @@ class DeviceKeys extends SignableKey {
|
||||||
lastActive = DateTime.fromMillisecondsSinceEpoch(0);
|
lastActive = DateTime.fromMillisecondsSinceEpoch(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyVerification startVerification() {
|
Future<KeyVerification> startVerification() async {
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
throw Exception('setVerification called on invalid key');
|
throw Exception('setVerification called on invalid key');
|
||||||
}
|
}
|
||||||
|
|
@ -517,7 +516,7 @@ class DeviceKeys extends SignableKey {
|
||||||
final request = KeyVerification(
|
final request = KeyVerification(
|
||||||
encryption: encryption, userId: userId, deviceId: deviceId!);
|
encryption: encryption, userId: userId, deviceId: deviceId!);
|
||||||
|
|
||||||
request.start();
|
await request.start();
|
||||||
encryption.keyVerificationManager.addRequest(request);
|
encryption.keyVerificationManager.addRequest(request);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,9 @@ abstract class NativeImplementations {
|
||||||
bool retryInDummy = false,
|
bool retryInDummy = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
|
||||||
|
|
||||||
/// this implementation will catch any non-implemented method
|
/// this implementation will catch any non-implemented method
|
||||||
dynamic noSuchMethod(Invocation invocation) {
|
@override
|
||||||
|
dynamic noSuchMethod(Invocation invocation) async {
|
||||||
final dynamic argument = invocation.positionalArguments.single;
|
final dynamic argument = invocation.positionalArguments.single;
|
||||||
final memberName = invocation.memberName.toString().split('"')[1];
|
final memberName = invocation.memberName.toString().split('"')[1];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,13 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
Future<T?> runInRoot<T>(FutureOr<T> Function() fn) async {
|
void runInRoot<T>(FutureOr<T> Function() fn) {
|
||||||
return await Zone.root.run(() async {
|
// ignore: discarded_futures
|
||||||
|
Zone.root.run(() async {
|
||||||
try {
|
try {
|
||||||
return await fn();
|
await fn();
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logs().e('Error thrown in root zone', e, s);
|
Logs().e('Error thrown in root zone', e, s);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ class UiaRequest<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
UiaRequest({this.onUpdate, required this.request}) {
|
UiaRequest({this.onUpdate, required this.request}) {
|
||||||
|
// ignore: discarded_futures
|
||||||
_run();
|
_run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class NativeImplementationsWebWorker extends NativeImplementations {
|
||||||
return completer.future.timeout(timeout);
|
return completer.future.timeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleIncomingMessage(MessageEvent event) {
|
Future<void> _handleIncomingMessage(MessageEvent event) async {
|
||||||
final data = event.data;
|
final data = event.data;
|
||||||
// don't forget handling errors of our second thread...
|
// don't forget handling errors of our second thread...
|
||||||
if (data['label'] == 'stacktrace') {
|
if (data['label'] == 'stacktrace') {
|
||||||
|
|
@ -46,12 +46,10 @@ class NativeImplementationsWebWorker extends NativeImplementations {
|
||||||
|
|
||||||
final error = event.data['error']!;
|
final error = event.data['error']!;
|
||||||
|
|
||||||
Future.value(
|
final stackTrace =
|
||||||
onStackTrace.call(event.data['stacktrace'] as String),
|
await onStackTrace.call(event.data['stacktrace'] as String);
|
||||||
).then(
|
completer?.completeError(
|
||||||
(stackTrace) => completer?.completeError(
|
WebWorkerError(error: error, stackTrace: stackTrace),
|
||||||
WebWorkerError(error: error, stackTrace: stackTrace),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final response = WebWorkerData.fromJson(event.data);
|
final response = WebWorkerData.fromJson(event.data);
|
||||||
|
|
|
||||||
|
|
@ -522,17 +522,18 @@ class CallSession {
|
||||||
await gotCallFeedsForAnswer(callFeeds);
|
await gotCallFeedsForAnswer(callFeeds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void replacedBy(CallSession newCall) {
|
Future<void> replacedBy(CallSession newCall) async {
|
||||||
if (state == CallState.kWaitLocalMedia) {
|
if (state == CallState.kWaitLocalMedia) {
|
||||||
Logs().v('Telling new call to wait for local media');
|
Logs().v('Telling new call to wait for local media');
|
||||||
newCall.waitForLocalAVStream = true;
|
newCall.waitForLocalAVStream = true;
|
||||||
} else if (state == CallState.kCreateOffer ||
|
} else if (state == CallState.kCreateOffer ||
|
||||||
state == CallState.kInviteSent) {
|
state == CallState.kInviteSent) {
|
||||||
Logs().v('Handing local stream to new call');
|
Logs().v('Handing local stream to new call');
|
||||||
newCall.gotCallFeedsForAnswer(getLocalStreams);
|
await newCall.gotCallFeedsForAnswer(getLocalStreams);
|
||||||
}
|
}
|
||||||
successor = newCall;
|
successor = newCall;
|
||||||
onCallReplaced.add(newCall);
|
onCallReplaced.add(newCall);
|
||||||
|
// ignore: unawaited_futures
|
||||||
hangup(CallErrorCode.Replaced, true);
|
hangup(CallErrorCode.Replaced, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -698,7 +699,7 @@ class CallSession {
|
||||||
Logs().i(
|
Logs().i(
|
||||||
'Stream purpose update: \nid = "$streamId", \npurpose = "${sdpStreamMetadata.purpose}", \naudio_muted = ${sdpStreamMetadata.audio_muted}, \nvideo_muted = ${sdpStreamMetadata.video_muted}');
|
'Stream purpose update: \nid = "$streamId", \npurpose = "${sdpStreamMetadata.purpose}", \naudio_muted = ${sdpStreamMetadata.audio_muted}, \nvideo_muted = ${sdpStreamMetadata.video_muted}');
|
||||||
});
|
});
|
||||||
getRemoteStreams.forEach((wpstream) {
|
for (final wpstream in getRemoteStreams) {
|
||||||
final streamId = wpstream.stream!.id;
|
final streamId = wpstream.stream!.id;
|
||||||
final purpose = metadata.sdpStreamMetadatas[streamId];
|
final purpose = metadata.sdpStreamMetadatas[streamId];
|
||||||
if (purpose != null) {
|
if (purpose != null) {
|
||||||
|
|
@ -712,7 +713,7 @@ class CallSession {
|
||||||
wpstream.stopped = true;
|
wpstream.stopped = true;
|
||||||
fireCallEvent(CallEvent.kFeedsChanged);
|
fireCallEvent(CallEvent.kFeedsChanged);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onSDPStreamMetadataReceived(SDPStreamMetadata metadata) async {
|
Future<void> onSDPStreamMetadataReceived(SDPStreamMetadata metadata) async {
|
||||||
|
|
@ -1510,17 +1511,18 @@ class CallSession {
|
||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void createDataChannel(String label, RTCDataChannelInit dataChannelDict) {
|
Future<void> createDataChannel(
|
||||||
pc?.createDataChannel(label, dataChannelDict);
|
String label, RTCDataChannelInit dataChannelDict) async {
|
||||||
|
await pc?.createDataChannel(label, dataChannelDict);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> tryRemoveStopedStreams() async {
|
Future<void> tryRemoveStopedStreams() async {
|
||||||
final removedStreams = <String, WrappedMediaStream>{};
|
final removedStreams = <String, WrappedMediaStream>{};
|
||||||
streams.forEach((stream) {
|
for (final stream in streams) {
|
||||||
if (stream.stopped) {
|
if (stream.stopped) {
|
||||||
removedStreams[stream.stream!.id] = stream;
|
removedStreams[stream.stream!.id] = stream;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
streams
|
streams
|
||||||
.removeWhere((stream) => removedStreams.containsKey(stream.stream!.id));
|
.removeWhere((stream) => removedStreams.containsKey(stream.stream!.id));
|
||||||
for (final element in removedStreams.entries) {
|
for (final element in removedStreams.entries) {
|
||||||
|
|
@ -1561,9 +1563,9 @@ class CallSession {
|
||||||
try {
|
try {
|
||||||
if (candidatesQueue.isNotEmpty) {
|
if (candidatesQueue.isNotEmpty) {
|
||||||
final candidates = <Map<String, dynamic>>[];
|
final candidates = <Map<String, dynamic>>[];
|
||||||
candidatesQueue.forEach((element) {
|
for (final element in candidatesQueue) {
|
||||||
candidates.add(element.toMap());
|
candidates.add(element.toMap());
|
||||||
});
|
}
|
||||||
localCandidates = [];
|
localCandidates = [];
|
||||||
final res = await sendCallCandidates(
|
final res = await sendCallCandidates(
|
||||||
opts.room, callId, localPartyId, candidates);
|
opts.room, callId, localPartyId, candidates);
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,15 @@ class ConnectionTester {
|
||||||
|
|
||||||
await pc1!.setRemoteDescription(answer);
|
await pc1!.setRemoteDescription(answer);
|
||||||
|
|
||||||
void dispose() {
|
Future<void> dispose() async {
|
||||||
pc1!.close();
|
await Future.wait([
|
||||||
pc1!.dispose();
|
pc1!.close(),
|
||||||
pc2!.close();
|
pc2!.close(),
|
||||||
pc2!.dispose();
|
]);
|
||||||
|
await Future.wait([
|
||||||
|
pc1!.dispose(),
|
||||||
|
pc2!.dispose(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
|
|
@ -69,6 +73,7 @@ class ConnectionTester {
|
||||||
.e('[VOIP] ConnectionTester Error while testing TURN server: ', e, s);
|
.e('[VOIP] ConnectionTester Error while testing TURN server: ', e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ignore: unawaited_futures
|
||||||
dispose();
|
dispose();
|
||||||
return connected;
|
return connected;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,8 +150,9 @@ class IGroupCallRoomMemberState {
|
||||||
List<IGroupCallRoomMemberCallState> calls = [];
|
List<IGroupCallRoomMemberCallState> calls = [];
|
||||||
IGroupCallRoomMemberState.fromJson(MatrixEvent event) {
|
IGroupCallRoomMemberState.fromJson(MatrixEvent event) {
|
||||||
if (event.content['m.calls'] != null) {
|
if (event.content['m.calls'] != null) {
|
||||||
(event.content['m.calls'] as List<dynamic>).forEach(
|
for (final call in (event.content['m.calls'] as List<dynamic>)) {
|
||||||
(call) => calls.add(IGroupCallRoomMemberCallState.fromJson(call)));
|
calls.add(IGroupCallRoomMemberCallState.fromJson(call));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -231,11 +232,11 @@ class GroupCall {
|
||||||
this.groupCallId = groupCallId ?? genCallID();
|
this.groupCallId = groupCallId ?? genCallID();
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupCall create() {
|
Future<GroupCall> create() async {
|
||||||
voip.groupCalls[groupCallId] = this;
|
voip.groupCalls[groupCallId] = this;
|
||||||
voip.groupCalls[room.id] = this;
|
voip.groupCalls[room.id] = this;
|
||||||
|
|
||||||
client.setRoomStateWithKey(
|
await client.setRoomStateWithKey(
|
||||||
room.id,
|
room.id,
|
||||||
EventTypes.GroupCallPrefix,
|
EventTypes.GroupCallPrefix,
|
||||||
groupCallId,
|
groupCallId,
|
||||||
|
|
@ -276,12 +277,12 @@ class GroupCall {
|
||||||
final List<MatrixEvent> events = [];
|
final List<MatrixEvent> events = [];
|
||||||
final roomStates = await client.getRoomState(room.id);
|
final roomStates = await client.getRoomState(room.id);
|
||||||
roomStates.sort((a, b) => a.originServerTs.compareTo(b.originServerTs));
|
roomStates.sort((a, b) => a.originServerTs.compareTo(b.originServerTs));
|
||||||
roomStates.forEach((value) {
|
for (final value in roomStates) {
|
||||||
if (value.type == EventTypes.GroupCallMemberPrefix &&
|
if (value.type == EventTypes.GroupCallMemberPrefix &&
|
||||||
!room.callMemberStateIsExpired(value, groupCallId)) {
|
!room.callMemberStateIsExpired(value, groupCallId)) {
|
||||||
events.add(value);
|
events.add(value);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -412,6 +413,7 @@ class GroupCall {
|
||||||
if (localUserMediaStream != null) {
|
if (localUserMediaStream != null) {
|
||||||
final oldStream = localUserMediaStream!.stream;
|
final oldStream = localUserMediaStream!.stream;
|
||||||
localUserMediaStream!.setNewStream(stream.stream!);
|
localUserMediaStream!.setNewStream(stream.stream!);
|
||||||
|
// ignore: discarded_futures
|
||||||
stopMediaStream(oldStream);
|
stopMediaStream(oldStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ Future<void> stopMediaStream(MediaStream? stream) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTracksEnabled(List<MediaStreamTrack> tracks, bool enabled) {
|
void setTracksEnabled(List<MediaStreamTrack> tracks, bool enabled) {
|
||||||
tracks.forEach((element) {
|
for (final element in tracks) {
|
||||||
element.enabled = enabled;
|
element.enabled = enabled;
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> hasAudioDevice() async {
|
Future<bool> hasAudioDevice() async {
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ class VoIP {
|
||||||
for (final room in client.rooms) {
|
for (final room in client.rooms) {
|
||||||
if (room.activeGroupCallEvents.isNotEmpty) {
|
if (room.activeGroupCallEvents.isNotEmpty) {
|
||||||
for (final groupCall in room.activeGroupCallEvents) {
|
for (final groupCall in room.activeGroupCallEvents) {
|
||||||
|
// ignore: discarded_futures
|
||||||
createGroupCallFromRoomStateEvent(groupCall,
|
createGroupCallFromRoomStateEvent(groupCall,
|
||||||
emitHandleNewGroupCall: false);
|
emitHandleNewGroupCall: false);
|
||||||
}
|
}
|
||||||
|
|
@ -589,7 +590,7 @@ class VoIP {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final groupId = genCallID();
|
final groupId = genCallID();
|
||||||
final groupCall = GroupCall(
|
final groupCall = await GroupCall(
|
||||||
groupCallId: groupId,
|
groupCallId: groupId,
|
||||||
client: client,
|
client: client,
|
||||||
voip: this,
|
voip: this,
|
||||||
|
|
|
||||||
|
|
@ -1137,8 +1137,8 @@ void main() {
|
||||||
reason: '!5345234235:example.com not found as archived room');
|
reason: '!5345234235:example.com not found as archived room');
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() {
|
tearDown(() async {
|
||||||
matrix.dispose(closeDatabase: true);
|
await matrix.dispose(closeDatabase: true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,450 @@ import 'fake_database.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('HiveCollections Database Test', () {
|
group('HiveCollections Database Test', () {
|
||||||
testDatabase(
|
late DatabaseApi database;
|
||||||
getHiveCollectionsDatabase(null),
|
late int toDeviceQueueIndex;
|
||||||
);
|
test('Open', () async {
|
||||||
|
database = await getHiveCollectionsDatabase(null);
|
||||||
|
});
|
||||||
|
test('transaction', () async {
|
||||||
|
var counter = 0;
|
||||||
|
await database.transaction(() async {
|
||||||
|
expect(counter++, 0);
|
||||||
|
await database.transaction(() async {
|
||||||
|
expect(counter++, 1);
|
||||||
|
await Future.delayed(Duration(milliseconds: 50));
|
||||||
|
expect(counter++, 2);
|
||||||
|
});
|
||||||
|
expect(counter++, 3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('insertIntoToDeviceQueue', () async {
|
||||||
|
toDeviceQueueIndex = await database.insertIntoToDeviceQueue(
|
||||||
|
'm.test',
|
||||||
|
'txnId',
|
||||||
|
'{"foo":"bar"}',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('getToDeviceEventQueue', () async {
|
||||||
|
final toDeviceQueue = await database.getToDeviceEventQueue();
|
||||||
|
expect(toDeviceQueue.first.type, 'm.test');
|
||||||
|
});
|
||||||
|
test('deleteFromToDeviceQueue', () async {
|
||||||
|
await database.deleteFromToDeviceQueue(toDeviceQueueIndex);
|
||||||
|
final toDeviceQueue = await database.getToDeviceEventQueue();
|
||||||
|
expect(toDeviceQueue.isEmpty, true);
|
||||||
|
});
|
||||||
|
test('storeFile', () async {
|
||||||
|
await database.storeFile(
|
||||||
|
Uri.parse('mxc://test'), Uint8List.fromList([0]), 0);
|
||||||
|
final file = await database.getFile(Uri.parse('mxc://test'));
|
||||||
|
expect(file != null, database.supportsFileStoring);
|
||||||
|
});
|
||||||
|
test('getFile', () async {
|
||||||
|
await database.getFile(Uri.parse('mxc://test'));
|
||||||
|
});
|
||||||
|
test('deleteOldFiles', () async {
|
||||||
|
await database.deleteOldFiles(1);
|
||||||
|
final file = await database.getFile(Uri.parse('mxc://test'));
|
||||||
|
expect(file == null, true);
|
||||||
|
});
|
||||||
|
test('storeRoomUpdate', () async {
|
||||||
|
final roomUpdate = JoinedRoomUpdate.fromJson({
|
||||||
|
'highlight_count': 0,
|
||||||
|
'notification_count': 0,
|
||||||
|
'limited_timeline': false,
|
||||||
|
'membership': Membership.join,
|
||||||
|
});
|
||||||
|
final client = Client('testclient');
|
||||||
|
await database.storeRoomUpdate('!testroom', roomUpdate, client);
|
||||||
|
final rooms = await database.getRoomList(client);
|
||||||
|
expect(rooms.single.id, '!testroom');
|
||||||
|
});
|
||||||
|
test('getRoomList', () async {
|
||||||
|
final room =
|
||||||
|
await database.getSingleRoom(Client('testclient'), '!testroom');
|
||||||
|
expect(room?.id, '!testroom');
|
||||||
|
});
|
||||||
|
test('getRoomList', () async {
|
||||||
|
final list = await database.getRoomList(Client('testclient'));
|
||||||
|
expect(list.single.id, '!testroom');
|
||||||
|
});
|
||||||
|
test('setRoomPrevBatch', () async {
|
||||||
|
final client = Client('testclient');
|
||||||
|
await database.setRoomPrevBatch('1234', '!testroom', client);
|
||||||
|
final rooms = await database.getRoomList(client);
|
||||||
|
expect(rooms.single.prev_batch, '1234');
|
||||||
|
});
|
||||||
|
test('forgetRoom', () async {
|
||||||
|
await database.forgetRoom('!testroom');
|
||||||
|
final rooms = await database.getRoomList(Client('testclient'));
|
||||||
|
expect(rooms.isEmpty, true);
|
||||||
|
});
|
||||||
|
test('getClient', () async {
|
||||||
|
await database.getClient('name');
|
||||||
|
});
|
||||||
|
test('insertClient', () async {
|
||||||
|
await database.insertClient(
|
||||||
|
'name',
|
||||||
|
'homeserverUrl',
|
||||||
|
'token',
|
||||||
|
'userId',
|
||||||
|
'deviceId',
|
||||||
|
'deviceName',
|
||||||
|
'prevBatch',
|
||||||
|
'olmAccount',
|
||||||
|
);
|
||||||
|
|
||||||
|
final client = await database.getClient('name');
|
||||||
|
expect(client?['token'], 'token');
|
||||||
|
});
|
||||||
|
test('updateClient', () async {
|
||||||
|
await database.updateClient(
|
||||||
|
'homeserverUrl',
|
||||||
|
'token_different',
|
||||||
|
'userId',
|
||||||
|
'deviceId',
|
||||||
|
'deviceName',
|
||||||
|
'prevBatch',
|
||||||
|
'olmAccount',
|
||||||
|
);
|
||||||
|
final client = await database.getClient('name');
|
||||||
|
expect(client?['token'], 'token_different');
|
||||||
|
});
|
||||||
|
test('updateClientKeys', () async {
|
||||||
|
await database.updateClientKeys(
|
||||||
|
'olmAccount2',
|
||||||
|
);
|
||||||
|
final client = await database.getClient('name');
|
||||||
|
expect(client?['olm_account'], 'olmAccount2');
|
||||||
|
});
|
||||||
|
test('storeSyncFilterId', () async {
|
||||||
|
await database.storeSyncFilterId(
|
||||||
|
'1234',
|
||||||
|
);
|
||||||
|
final client = await database.getClient('name');
|
||||||
|
expect(client?['sync_filter_id'], '1234');
|
||||||
|
});
|
||||||
|
test('getAccountData', () async {
|
||||||
|
await database.getAccountData();
|
||||||
|
});
|
||||||
|
test('storeAccountData', () async {
|
||||||
|
await database.storeAccountData('m.test', '{"foo":"bar"}');
|
||||||
|
final events = await database.getAccountData();
|
||||||
|
expect(events.values.single.type, 'm.test');
|
||||||
|
|
||||||
|
await database.storeAccountData('m.abc+de', '{"foo":"bar"}');
|
||||||
|
final events2 = await database.getAccountData();
|
||||||
|
expect(events2.values.any((element) => element.type == 'm.abc+de'), true);
|
||||||
|
});
|
||||||
|
test('storeEventUpdate', () async {
|
||||||
|
await database.storeEventUpdate(
|
||||||
|
EventUpdate(
|
||||||
|
roomID: '!testroom:example.com',
|
||||||
|
type: EventUpdateType.timeline,
|
||||||
|
content: {
|
||||||
|
'type': EventTypes.Message,
|
||||||
|
'content': {
|
||||||
|
'body': '* edit 3',
|
||||||
|
'msgtype': 'm.text',
|
||||||
|
'm.new_content': {
|
||||||
|
'body': 'edit 3',
|
||||||
|
'msgtype': 'm.text',
|
||||||
|
},
|
||||||
|
'm.relates_to': {
|
||||||
|
'event_id': '\$source',
|
||||||
|
'rel_type': RelationshipTypes.edit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'event_id': '\$event:example.com',
|
||||||
|
'sender': '@bob:example.org',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Client('testclient'));
|
||||||
|
});
|
||||||
|
test('getEventById', () async {
|
||||||
|
final event = await database.getEventById('\$event:example.com',
|
||||||
|
Room(id: '!testroom:example.com', client: Client('testclient')));
|
||||||
|
expect(event?.type, EventTypes.Message);
|
||||||
|
});
|
||||||
|
test('getEventList', () async {
|
||||||
|
final events = await database.getEventList(
|
||||||
|
Room(id: '!testroom:example.com', client: Client('testclient')),
|
||||||
|
);
|
||||||
|
expect(events.single.type, EventTypes.Message);
|
||||||
|
});
|
||||||
|
test('getUser', () async {
|
||||||
|
final user = await database.getUser('@bob:example.org',
|
||||||
|
Room(id: '!testroom:example.com', client: Client('testclient')));
|
||||||
|
expect(user, null);
|
||||||
|
});
|
||||||
|
test('getUsers', () async {
|
||||||
|
final users = await database.getUsers(
|
||||||
|
Room(id: '!testroom:example.com', client: Client('testclient')));
|
||||||
|
expect(users.isEmpty, true);
|
||||||
|
});
|
||||||
|
test('removeEvent', () async {
|
||||||
|
await database.removeEvent(
|
||||||
|
'\$event:example.com', '!testroom:example.com');
|
||||||
|
final event = await database.getEventById('\$event:example.com',
|
||||||
|
Room(id: '!testroom:example.com', client: Client('testclient')));
|
||||||
|
expect(event, null);
|
||||||
|
});
|
||||||
|
test('getAllInboundGroupSessions', () async {
|
||||||
|
final result = await database.getAllInboundGroupSessions();
|
||||||
|
expect(result.isEmpty, true);
|
||||||
|
});
|
||||||
|
test('getInboundGroupSession', () async {
|
||||||
|
await database.getInboundGroupSession(
|
||||||
|
'!testroom:example.com', 'sessionId');
|
||||||
|
});
|
||||||
|
test('getInboundGroupSessionsToUpload', () async {
|
||||||
|
await database.getInboundGroupSessionsToUpload();
|
||||||
|
});
|
||||||
|
test('storeInboundGroupSession', () async {
|
||||||
|
await database.storeInboundGroupSession(
|
||||||
|
'!testroom:example.com',
|
||||||
|
'sessionId',
|
||||||
|
'pickle',
|
||||||
|
'{"foo":"bar"}',
|
||||||
|
'{}',
|
||||||
|
'{}',
|
||||||
|
'senderKey',
|
||||||
|
'{}',
|
||||||
|
);
|
||||||
|
final session = await database.getInboundGroupSession(
|
||||||
|
'!testroom:example.com',
|
||||||
|
'sessionId',
|
||||||
|
);
|
||||||
|
expect(jsonDecode(session!.content)['foo'], 'bar');
|
||||||
|
});
|
||||||
|
test('markInboundGroupSessionAsUploaded', () async {
|
||||||
|
await database.markInboundGroupSessionAsUploaded(
|
||||||
|
'!testroom:example.com', 'sessionId');
|
||||||
|
});
|
||||||
|
test('markInboundGroupSessionsAsNeedingUpload', () async {
|
||||||
|
await database.markInboundGroupSessionsAsNeedingUpload();
|
||||||
|
});
|
||||||
|
test('updateInboundGroupSessionAllowedAtIndex', () async {
|
||||||
|
await database.updateInboundGroupSessionAllowedAtIndex(
|
||||||
|
'{}',
|
||||||
|
'!testroom:example.com',
|
||||||
|
'sessionId',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('updateInboundGroupSessionIndexes', () async {
|
||||||
|
await database.updateInboundGroupSessionIndexes(
|
||||||
|
'{}',
|
||||||
|
'!testroom:example.com',
|
||||||
|
'sessionId',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('getSSSSCache', () async {
|
||||||
|
final cache = await database.getSSSSCache('type');
|
||||||
|
expect(cache, null);
|
||||||
|
});
|
||||||
|
test('storeSSSSCache', () async {
|
||||||
|
await database.storeSSSSCache('type', 'keyId', 'ciphertext', '{}');
|
||||||
|
final cache = (await database.getSSSSCache('type'))!;
|
||||||
|
expect(cache.type, 'type');
|
||||||
|
expect(cache.keyId, 'keyId');
|
||||||
|
expect(cache.ciphertext, 'ciphertext');
|
||||||
|
expect(cache.content, '{}');
|
||||||
|
});
|
||||||
|
test('getOlmSessions', () async {
|
||||||
|
final olm = await database.getOlmSessions(
|
||||||
|
'identityKey',
|
||||||
|
'userId',
|
||||||
|
);
|
||||||
|
expect(olm.isEmpty, true);
|
||||||
|
});
|
||||||
|
test('getAllOlmSessions', () async {
|
||||||
|
var sessions = await database.getAllOlmSessions();
|
||||||
|
expect(sessions.isEmpty, true);
|
||||||
|
await database.storeOlmSession(
|
||||||
|
'identityKey',
|
||||||
|
'sessionId',
|
||||||
|
'pickle',
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
await database.storeOlmSession(
|
||||||
|
'identityKey',
|
||||||
|
'sessionId2',
|
||||||
|
'pickle',
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
sessions = await database.getAllOlmSessions();
|
||||||
|
expect(
|
||||||
|
sessions,
|
||||||
|
{
|
||||||
|
'identityKey': {
|
||||||
|
'sessionId': {
|
||||||
|
'identity_key': 'identityKey',
|
||||||
|
'pickle': 'pickle',
|
||||||
|
'session_id': 'sessionId',
|
||||||
|
'last_received': 0
|
||||||
|
},
|
||||||
|
'sessionId2': {
|
||||||
|
'identity_key': 'identityKey',
|
||||||
|
'pickle': 'pickle',
|
||||||
|
'session_id': 'sessionId2',
|
||||||
|
'last_received': 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('getOlmSessionsForDevices', () async {
|
||||||
|
final olm = await database.getOlmSessionsForDevices(
|
||||||
|
['identityKeys'],
|
||||||
|
'userId',
|
||||||
|
);
|
||||||
|
expect(olm.isEmpty, true);
|
||||||
|
});
|
||||||
|
test('storeOlmSession', () async {
|
||||||
|
if (!(await olmEnabled())) return;
|
||||||
|
await database.storeOlmSession(
|
||||||
|
'identityKey',
|
||||||
|
'sessionId',
|
||||||
|
'pickle',
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
final olm = await database.getOlmSessions(
|
||||||
|
'identityKey',
|
||||||
|
'userId',
|
||||||
|
);
|
||||||
|
expect(olm.isNotEmpty, true);
|
||||||
|
});
|
||||||
|
test('getOutboundGroupSession', () async {
|
||||||
|
final session = await database.getOutboundGroupSession(
|
||||||
|
'!testroom:example.com',
|
||||||
|
'@alice:example.com',
|
||||||
|
);
|
||||||
|
expect(session, null);
|
||||||
|
});
|
||||||
|
test('storeOutboundGroupSession', () async {
|
||||||
|
if (!(await olmEnabled())) return;
|
||||||
|
await database.storeOutboundGroupSession(
|
||||||
|
'!testroom:example.com',
|
||||||
|
'pickle',
|
||||||
|
'{}',
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
final session = await database.getOutboundGroupSession(
|
||||||
|
'!testroom:example.com',
|
||||||
|
'@alice:example.com',
|
||||||
|
);
|
||||||
|
expect(session?.devices.isEmpty, true);
|
||||||
|
});
|
||||||
|
test('getLastSentMessageUserDeviceKey', () async {
|
||||||
|
final list = await database.getLastSentMessageUserDeviceKey(
|
||||||
|
'userId',
|
||||||
|
'deviceId',
|
||||||
|
);
|
||||||
|
expect(list.isEmpty, true);
|
||||||
|
});
|
||||||
|
test('getUnimportantRoomEventStatesForRoom', () async {
|
||||||
|
final events = await database.getUnimportantRoomEventStatesForRoom(
|
||||||
|
['events'],
|
||||||
|
Room(id: '!mep', client: Client('testclient')),
|
||||||
|
);
|
||||||
|
expect(events.isEmpty, true);
|
||||||
|
});
|
||||||
|
test('getUserDeviceKeys', () async {
|
||||||
|
await database.getUserDeviceKeys(Client('testclient'));
|
||||||
|
});
|
||||||
|
test('storeUserCrossSigningKey', () async {
|
||||||
|
await database.storeUserCrossSigningKey(
|
||||||
|
'@alice:example.com',
|
||||||
|
'publicKey',
|
||||||
|
'{}',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('setVerifiedUserCrossSigningKey', () async {
|
||||||
|
await database.setVerifiedUserCrossSigningKey(
|
||||||
|
true,
|
||||||
|
'@alice:example.com',
|
||||||
|
'publicKey',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('setBlockedUserCrossSigningKey', () async {
|
||||||
|
await database.setBlockedUserCrossSigningKey(
|
||||||
|
true,
|
||||||
|
'@alice:example.com',
|
||||||
|
'publicKey',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('removeUserCrossSigningKey', () async {
|
||||||
|
await database.removeUserCrossSigningKey(
|
||||||
|
'@alice:example.com',
|
||||||
|
'publicKey',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('storeUserDeviceKeysInfo', () async {
|
||||||
|
await database.storeUserDeviceKeysInfo(
|
||||||
|
'@alice:example.com',
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('storeUserDeviceKey', () async {
|
||||||
|
await database.storeUserDeviceKey(
|
||||||
|
'@alice:example.com',
|
||||||
|
'deviceId',
|
||||||
|
'{}',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('setVerifiedUserDeviceKey', () async {
|
||||||
|
await database.setVerifiedUserDeviceKey(
|
||||||
|
true,
|
||||||
|
'@alice:example.com',
|
||||||
|
'deviceId',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('setBlockedUserDeviceKey', () async {
|
||||||
|
await database.setBlockedUserDeviceKey(
|
||||||
|
true,
|
||||||
|
'@alice:example.com',
|
||||||
|
'deviceId',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('getStorePresences', () async {
|
||||||
|
const userId = '@alice:example.com';
|
||||||
|
final presence = CachedPresence(
|
||||||
|
PresenceType.online,
|
||||||
|
100,
|
||||||
|
'test message',
|
||||||
|
true,
|
||||||
|
'@alice:example.com',
|
||||||
|
);
|
||||||
|
await database.storePresence(
|
||||||
|
userId,
|
||||||
|
presence,
|
||||||
|
);
|
||||||
|
final storedPresence = await database.getPresence(userId);
|
||||||
|
expect(
|
||||||
|
presence.toJson(),
|
||||||
|
storedPresence?.toJson(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clearing up from here
|
||||||
|
test('clearSSSSCache', () async {
|
||||||
|
await database.clearSSSSCache();
|
||||||
|
});
|
||||||
|
test('clearCache', () async {
|
||||||
|
await database.clearCache();
|
||||||
|
});
|
||||||
|
test('clear', () async {
|
||||||
|
await database.clear();
|
||||||
|
});
|
||||||
|
test('Close', () async {
|
||||||
|
await database.close();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,451 +485,3 @@ Future<bool> olmEnabled() async {
|
||||||
}
|
}
|
||||||
return olmEnabled;
|
return olmEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void testDatabase(
|
|
||||||
Future<DatabaseApi> futureDatabase,
|
|
||||||
) {
|
|
||||||
late DatabaseApi database;
|
|
||||||
late int toDeviceQueueIndex;
|
|
||||||
test('Open', () async {
|
|
||||||
database = await futureDatabase;
|
|
||||||
});
|
|
||||||
test('transaction', () async {
|
|
||||||
var counter = 0;
|
|
||||||
await database.transaction(() async {
|
|
||||||
expect(counter++, 0);
|
|
||||||
await database.transaction(() async {
|
|
||||||
expect(counter++, 1);
|
|
||||||
await Future.delayed(Duration(milliseconds: 50));
|
|
||||||
expect(counter++, 2);
|
|
||||||
});
|
|
||||||
expect(counter++, 3);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
test('insertIntoToDeviceQueue', () async {
|
|
||||||
toDeviceQueueIndex = await database.insertIntoToDeviceQueue(
|
|
||||||
'm.test',
|
|
||||||
'txnId',
|
|
||||||
'{"foo":"bar"}',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('getToDeviceEventQueue', () async {
|
|
||||||
final toDeviceQueue = await database.getToDeviceEventQueue();
|
|
||||||
expect(toDeviceQueue.first.type, 'm.test');
|
|
||||||
});
|
|
||||||
test('deleteFromToDeviceQueue', () async {
|
|
||||||
await database.deleteFromToDeviceQueue(toDeviceQueueIndex);
|
|
||||||
final toDeviceQueue = await database.getToDeviceEventQueue();
|
|
||||||
expect(toDeviceQueue.isEmpty, true);
|
|
||||||
});
|
|
||||||
test('storeFile', () async {
|
|
||||||
await database.storeFile(
|
|
||||||
Uri.parse('mxc://test'), Uint8List.fromList([0]), 0);
|
|
||||||
final file = await database.getFile(Uri.parse('mxc://test'));
|
|
||||||
expect(file != null, database.supportsFileStoring);
|
|
||||||
});
|
|
||||||
test('getFile', () async {
|
|
||||||
await database.getFile(Uri.parse('mxc://test'));
|
|
||||||
});
|
|
||||||
test('deleteOldFiles', () async {
|
|
||||||
await database.deleteOldFiles(1);
|
|
||||||
final file = await database.getFile(Uri.parse('mxc://test'));
|
|
||||||
expect(file == null, true);
|
|
||||||
});
|
|
||||||
test('storeRoomUpdate', () async {
|
|
||||||
final roomUpdate = JoinedRoomUpdate.fromJson({
|
|
||||||
'highlight_count': 0,
|
|
||||||
'notification_count': 0,
|
|
||||||
'limited_timeline': false,
|
|
||||||
'membership': Membership.join,
|
|
||||||
});
|
|
||||||
final client = Client('testclient');
|
|
||||||
await database.storeRoomUpdate('!testroom', roomUpdate, client);
|
|
||||||
final rooms = await database.getRoomList(client);
|
|
||||||
expect(rooms.single.id, '!testroom');
|
|
||||||
});
|
|
||||||
test('getRoomList', () async {
|
|
||||||
final room =
|
|
||||||
await database.getSingleRoom(Client('testclient'), '!testroom');
|
|
||||||
expect(room?.id, '!testroom');
|
|
||||||
});
|
|
||||||
test('getRoomList', () async {
|
|
||||||
final list = await database.getRoomList(Client('testclient'));
|
|
||||||
expect(list.single.id, '!testroom');
|
|
||||||
});
|
|
||||||
test('setRoomPrevBatch', () async {
|
|
||||||
final client = Client('testclient');
|
|
||||||
await database.setRoomPrevBatch('1234', '!testroom', client);
|
|
||||||
final rooms = await database.getRoomList(client);
|
|
||||||
expect(rooms.single.prev_batch, '1234');
|
|
||||||
});
|
|
||||||
test('forgetRoom', () async {
|
|
||||||
await database.forgetRoom('!testroom');
|
|
||||||
final rooms = await database.getRoomList(Client('testclient'));
|
|
||||||
expect(rooms.isEmpty, true);
|
|
||||||
});
|
|
||||||
test('getClient', () async {
|
|
||||||
await database.getClient('name');
|
|
||||||
});
|
|
||||||
test('insertClient', () async {
|
|
||||||
await database.insertClient(
|
|
||||||
'name',
|
|
||||||
'homeserverUrl',
|
|
||||||
'token',
|
|
||||||
'userId',
|
|
||||||
'deviceId',
|
|
||||||
'deviceName',
|
|
||||||
'prevBatch',
|
|
||||||
'olmAccount',
|
|
||||||
);
|
|
||||||
|
|
||||||
final client = await database.getClient('name');
|
|
||||||
expect(client?['token'], 'token');
|
|
||||||
});
|
|
||||||
test('updateClient', () async {
|
|
||||||
await database.updateClient(
|
|
||||||
'homeserverUrl',
|
|
||||||
'token_different',
|
|
||||||
'userId',
|
|
||||||
'deviceId',
|
|
||||||
'deviceName',
|
|
||||||
'prevBatch',
|
|
||||||
'olmAccount',
|
|
||||||
);
|
|
||||||
final client = await database.getClient('name');
|
|
||||||
expect(client?['token'], 'token_different');
|
|
||||||
});
|
|
||||||
test('updateClientKeys', () async {
|
|
||||||
await database.updateClientKeys(
|
|
||||||
'olmAccount2',
|
|
||||||
);
|
|
||||||
final client = await database.getClient('name');
|
|
||||||
expect(client?['olm_account'], 'olmAccount2');
|
|
||||||
});
|
|
||||||
test('storeSyncFilterId', () async {
|
|
||||||
await database.storeSyncFilterId(
|
|
||||||
'1234',
|
|
||||||
);
|
|
||||||
final client = await database.getClient('name');
|
|
||||||
expect(client?['sync_filter_id'], '1234');
|
|
||||||
});
|
|
||||||
test('getAccountData', () async {
|
|
||||||
await database.getAccountData();
|
|
||||||
});
|
|
||||||
test('storeAccountData', () async {
|
|
||||||
await database.storeAccountData('m.test', '{"foo":"bar"}');
|
|
||||||
final events = await database.getAccountData();
|
|
||||||
expect(events.values.single.type, 'm.test');
|
|
||||||
|
|
||||||
await database.storeAccountData('m.abc+de', '{"foo":"bar"}');
|
|
||||||
final events2 = await database.getAccountData();
|
|
||||||
expect(events2.values.any((element) => element.type == 'm.abc+de'), true);
|
|
||||||
});
|
|
||||||
test('storeEventUpdate', () async {
|
|
||||||
await database.storeEventUpdate(
|
|
||||||
EventUpdate(
|
|
||||||
roomID: '!testroom:example.com',
|
|
||||||
type: EventUpdateType.timeline,
|
|
||||||
content: {
|
|
||||||
'type': EventTypes.Message,
|
|
||||||
'content': {
|
|
||||||
'body': '* edit 3',
|
|
||||||
'msgtype': 'm.text',
|
|
||||||
'm.new_content': {
|
|
||||||
'body': 'edit 3',
|
|
||||||
'msgtype': 'm.text',
|
|
||||||
},
|
|
||||||
'm.relates_to': {
|
|
||||||
'event_id': '\$source',
|
|
||||||
'rel_type': RelationshipTypes.edit,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'event_id': '\$event:example.com',
|
|
||||||
'sender': '@bob:example.org',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Client('testclient'));
|
|
||||||
});
|
|
||||||
test('getEventById', () async {
|
|
||||||
final event = await database.getEventById('\$event:example.com',
|
|
||||||
Room(id: '!testroom:example.com', client: Client('testclient')));
|
|
||||||
expect(event?.type, EventTypes.Message);
|
|
||||||
});
|
|
||||||
test('getEventList', () async {
|
|
||||||
final events = await database.getEventList(
|
|
||||||
Room(id: '!testroom:example.com', client: Client('testclient')),
|
|
||||||
);
|
|
||||||
expect(events.single.type, EventTypes.Message);
|
|
||||||
});
|
|
||||||
test('getUser', () async {
|
|
||||||
final user = await database.getUser('@bob:example.org',
|
|
||||||
Room(id: '!testroom:example.com', client: Client('testclient')));
|
|
||||||
expect(user, null);
|
|
||||||
});
|
|
||||||
test('getUsers', () async {
|
|
||||||
final users = await database.getUsers(
|
|
||||||
Room(id: '!testroom:example.com', client: Client('testclient')));
|
|
||||||
expect(users.isEmpty, true);
|
|
||||||
});
|
|
||||||
test('removeEvent', () async {
|
|
||||||
await database.removeEvent('\$event:example.com', '!testroom:example.com');
|
|
||||||
final event = await database.getEventById('\$event:example.com',
|
|
||||||
Room(id: '!testroom:example.com', client: Client('testclient')));
|
|
||||||
expect(event, null);
|
|
||||||
});
|
|
||||||
test('getAllInboundGroupSessions', () async {
|
|
||||||
final result = await database.getAllInboundGroupSessions();
|
|
||||||
expect(result.isEmpty, true);
|
|
||||||
});
|
|
||||||
test('getInboundGroupSession', () async {
|
|
||||||
await database.getInboundGroupSession('!testroom:example.com', 'sessionId');
|
|
||||||
});
|
|
||||||
test('getInboundGroupSessionsToUpload', () async {
|
|
||||||
await database.getInboundGroupSessionsToUpload();
|
|
||||||
});
|
|
||||||
test('storeInboundGroupSession', () async {
|
|
||||||
await database.storeInboundGroupSession(
|
|
||||||
'!testroom:example.com',
|
|
||||||
'sessionId',
|
|
||||||
'pickle',
|
|
||||||
'{"foo":"bar"}',
|
|
||||||
'{}',
|
|
||||||
'{}',
|
|
||||||
'senderKey',
|
|
||||||
'{}',
|
|
||||||
);
|
|
||||||
final session = await database.getInboundGroupSession(
|
|
||||||
'!testroom:example.com',
|
|
||||||
'sessionId',
|
|
||||||
);
|
|
||||||
expect(jsonDecode(session!.content)['foo'], 'bar');
|
|
||||||
});
|
|
||||||
test('markInboundGroupSessionAsUploaded', () async {
|
|
||||||
await database.markInboundGroupSessionAsUploaded(
|
|
||||||
'!testroom:example.com', 'sessionId');
|
|
||||||
});
|
|
||||||
test('markInboundGroupSessionsAsNeedingUpload', () async {
|
|
||||||
await database.markInboundGroupSessionsAsNeedingUpload();
|
|
||||||
});
|
|
||||||
test('updateInboundGroupSessionAllowedAtIndex', () async {
|
|
||||||
await database.updateInboundGroupSessionAllowedAtIndex(
|
|
||||||
'{}',
|
|
||||||
'!testroom:example.com',
|
|
||||||
'sessionId',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('updateInboundGroupSessionIndexes', () async {
|
|
||||||
await database.updateInboundGroupSessionIndexes(
|
|
||||||
'{}',
|
|
||||||
'!testroom:example.com',
|
|
||||||
'sessionId',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('getSSSSCache', () async {
|
|
||||||
final cache = await database.getSSSSCache('type');
|
|
||||||
expect(cache, null);
|
|
||||||
});
|
|
||||||
test('storeSSSSCache', () async {
|
|
||||||
await database.storeSSSSCache('type', 'keyId', 'ciphertext', '{}');
|
|
||||||
final cache = (await database.getSSSSCache('type'))!;
|
|
||||||
expect(cache.type, 'type');
|
|
||||||
expect(cache.keyId, 'keyId');
|
|
||||||
expect(cache.ciphertext, 'ciphertext');
|
|
||||||
expect(cache.content, '{}');
|
|
||||||
});
|
|
||||||
test('getOlmSessions', () async {
|
|
||||||
final olm = await database.getOlmSessions(
|
|
||||||
'identityKey',
|
|
||||||
'userId',
|
|
||||||
);
|
|
||||||
expect(olm.isEmpty, true);
|
|
||||||
});
|
|
||||||
test('getAllOlmSessions', () async {
|
|
||||||
var sessions = await database.getAllOlmSessions();
|
|
||||||
expect(sessions.isEmpty, true);
|
|
||||||
await database.storeOlmSession(
|
|
||||||
'identityKey',
|
|
||||||
'sessionId',
|
|
||||||
'pickle',
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
await database.storeOlmSession(
|
|
||||||
'identityKey',
|
|
||||||
'sessionId2',
|
|
||||||
'pickle',
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
sessions = await database.getAllOlmSessions();
|
|
||||||
expect(
|
|
||||||
sessions,
|
|
||||||
{
|
|
||||||
'identityKey': {
|
|
||||||
'sessionId': {
|
|
||||||
'identity_key': 'identityKey',
|
|
||||||
'pickle': 'pickle',
|
|
||||||
'session_id': 'sessionId',
|
|
||||||
'last_received': 0
|
|
||||||
},
|
|
||||||
'sessionId2': {
|
|
||||||
'identity_key': 'identityKey',
|
|
||||||
'pickle': 'pickle',
|
|
||||||
'session_id': 'sessionId2',
|
|
||||||
'last_received': 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('getOlmSessionsForDevices', () async {
|
|
||||||
final olm = await database.getOlmSessionsForDevices(
|
|
||||||
['identityKeys'],
|
|
||||||
'userId',
|
|
||||||
);
|
|
||||||
expect(olm.isEmpty, true);
|
|
||||||
});
|
|
||||||
test('storeOlmSession', () async {
|
|
||||||
if (!(await olmEnabled())) return;
|
|
||||||
await database.storeOlmSession(
|
|
||||||
'identityKey',
|
|
||||||
'sessionId',
|
|
||||||
'pickle',
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
final olm = await database.getOlmSessions(
|
|
||||||
'identityKey',
|
|
||||||
'userId',
|
|
||||||
);
|
|
||||||
expect(olm.isNotEmpty, true);
|
|
||||||
});
|
|
||||||
test('getOutboundGroupSession', () async {
|
|
||||||
final session = await database.getOutboundGroupSession(
|
|
||||||
'!testroom:example.com',
|
|
||||||
'@alice:example.com',
|
|
||||||
);
|
|
||||||
expect(session, null);
|
|
||||||
});
|
|
||||||
test('storeOutboundGroupSession', () async {
|
|
||||||
if (!(await olmEnabled())) return;
|
|
||||||
await database.storeOutboundGroupSession(
|
|
||||||
'!testroom:example.com',
|
|
||||||
'pickle',
|
|
||||||
'{}',
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
final session = await database.getOutboundGroupSession(
|
|
||||||
'!testroom:example.com',
|
|
||||||
'@alice:example.com',
|
|
||||||
);
|
|
||||||
expect(session?.devices.isEmpty, true);
|
|
||||||
});
|
|
||||||
test('getLastSentMessageUserDeviceKey', () async {
|
|
||||||
final list = await database.getLastSentMessageUserDeviceKey(
|
|
||||||
'userId',
|
|
||||||
'deviceId',
|
|
||||||
);
|
|
||||||
expect(list.isEmpty, true);
|
|
||||||
});
|
|
||||||
test('getUnimportantRoomEventStatesForRoom', () async {
|
|
||||||
final events = await database.getUnimportantRoomEventStatesForRoom(
|
|
||||||
['events'],
|
|
||||||
Room(id: '!mep', client: Client('testclient')),
|
|
||||||
);
|
|
||||||
expect(events.isEmpty, true);
|
|
||||||
});
|
|
||||||
test('getUserDeviceKeys', () async {
|
|
||||||
await database.getUserDeviceKeys(Client('testclient'));
|
|
||||||
});
|
|
||||||
test('storeUserCrossSigningKey', () async {
|
|
||||||
await database.storeUserCrossSigningKey(
|
|
||||||
'@alice:example.com',
|
|
||||||
'publicKey',
|
|
||||||
'{}',
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('setVerifiedUserCrossSigningKey', () async {
|
|
||||||
await database.setVerifiedUserCrossSigningKey(
|
|
||||||
true,
|
|
||||||
'@alice:example.com',
|
|
||||||
'publicKey',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('setBlockedUserCrossSigningKey', () async {
|
|
||||||
await database.setBlockedUserCrossSigningKey(
|
|
||||||
true,
|
|
||||||
'@alice:example.com',
|
|
||||||
'publicKey',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('removeUserCrossSigningKey', () async {
|
|
||||||
await database.removeUserCrossSigningKey(
|
|
||||||
'@alice:example.com',
|
|
||||||
'publicKey',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('storeUserDeviceKeysInfo', () async {
|
|
||||||
await database.storeUserDeviceKeysInfo(
|
|
||||||
'@alice:example.com',
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('storeUserDeviceKey', () async {
|
|
||||||
await database.storeUserDeviceKey(
|
|
||||||
'@alice:example.com',
|
|
||||||
'deviceId',
|
|
||||||
'{}',
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('setVerifiedUserDeviceKey', () async {
|
|
||||||
await database.setVerifiedUserDeviceKey(
|
|
||||||
true,
|
|
||||||
'@alice:example.com',
|
|
||||||
'deviceId',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('setBlockedUserDeviceKey', () async {
|
|
||||||
await database.setBlockedUserDeviceKey(
|
|
||||||
true,
|
|
||||||
'@alice:example.com',
|
|
||||||
'deviceId',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('getStorePresences', () async {
|
|
||||||
const userId = '@alice:example.com';
|
|
||||||
final presence = CachedPresence(
|
|
||||||
PresenceType.online,
|
|
||||||
100,
|
|
||||||
'test message',
|
|
||||||
true,
|
|
||||||
'@alice:example.com',
|
|
||||||
);
|
|
||||||
await database.storePresence(
|
|
||||||
userId,
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
final storedPresence = await database.getPresence(userId);
|
|
||||||
expect(
|
|
||||||
presence.toJson(),
|
|
||||||
storedPresence?.toJson(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clearing up from here
|
|
||||||
test('clearSSSSCache', () async {
|
|
||||||
await database.clearSSSSCache();
|
|
||||||
});
|
|
||||||
test('clearCache', () async {
|
|
||||||
await database.clearCache();
|
|
||||||
});
|
|
||||||
test('clear', () async {
|
|
||||||
await database.clear();
|
|
||||||
});
|
|
||||||
test('Close', () async {
|
|
||||||
await database.close();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ void main() {
|
||||||
|
|
||||||
test('start verification', () async {
|
test('start verification', () async {
|
||||||
if (!olmEnabled) return;
|
if (!olmEnabled) return;
|
||||||
var req = client
|
var req = await client
|
||||||
.userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS']
|
.userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS']
|
||||||
?.startVerification();
|
?.startVerification();
|
||||||
expect(req != null, true);
|
expect(req != null, true);
|
||||||
|
|
|
||||||
|
|
@ -123,9 +123,9 @@ void main() {
|
||||||
0);
|
0);
|
||||||
|
|
||||||
// rotate after too many messages
|
// rotate after too many messages
|
||||||
Iterable.generate(300).forEach((_) {
|
for (final _ in Iterable.generate(300)) {
|
||||||
sess.outboundGroupSession!.encrypt('some string');
|
sess.outboundGroupSession!.encrypt('some string');
|
||||||
});
|
}
|
||||||
await client.encryption!.keyManager
|
await client.encryption!.keyManager
|
||||||
.clearOrUseOutboundGroupSession(roomId);
|
.clearOrUseOutboundGroupSession(roomId);
|
||||||
expect(
|
expect(
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,7 @@ class FakeMatrixApi extends BaseClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// and generate a fake sync
|
// and generate a fake sync
|
||||||
|
// ignore: discarded_futures
|
||||||
_client!.handleSync(sdk.SyncUpdate(nextBatch: ''));
|
_client!.handleSync(sdk.SyncUpdate(nextBatch: ''));
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ void main() {
|
||||||
insertList.clear();
|
insertList.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() => client.dispose().onError((e, s) {}));
|
tearDown(() async => client.dispose().onError((e, s) {}));
|
||||||
|
|
||||||
test('archive room not loaded', () async {
|
test('archive room not loaded', () async {
|
||||||
final archiveRoom =
|
final archiveRoom =
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ void main() {
|
||||||
var olmEnabled = true;
|
var olmEnabled = true;
|
||||||
|
|
||||||
final countStream = StreamController<int>.broadcast();
|
final countStream = StreamController<int>.broadcast();
|
||||||
Future<int> waitForCount(int count) {
|
Future<int> waitForCount(int count) async {
|
||||||
if (updateCount == count) {
|
if (updateCount == count) {
|
||||||
return Future.value(updateCount);
|
return Future.value(updateCount);
|
||||||
}
|
}
|
||||||
|
|
@ -46,9 +46,9 @@ void main() {
|
||||||
final completer = Completer<int>();
|
final completer = Completer<int>();
|
||||||
|
|
||||||
StreamSubscription<int>? sub;
|
StreamSubscription<int>? sub;
|
||||||
sub = countStream.stream.listen((newCount) {
|
sub = countStream.stream.listen((newCount) async {
|
||||||
if (newCount == count) {
|
if (newCount == count) {
|
||||||
sub?.cancel();
|
await sub?.cancel();
|
||||||
completer.complete(count);
|
completer.complete(count);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -100,7 +100,8 @@ void main() {
|
||||||
testTimeStamp = DateTime.now().millisecondsSinceEpoch;
|
testTimeStamp = DateTime.now().millisecondsSinceEpoch;
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() => client.dispose(closeDatabase: true).onError((e, s) {}));
|
tearDown(
|
||||||
|
() async => client.dispose(closeDatabase: true).onError((e, s) {}));
|
||||||
|
|
||||||
test('Request future', () async {
|
test('Request future', () async {
|
||||||
timeline.events.clear();
|
timeline.events.clear();
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ void main() {
|
||||||
var currentPoison = 0;
|
var currentPoison = 0;
|
||||||
|
|
||||||
final countStream = StreamController<int>.broadcast();
|
final countStream = StreamController<int>.broadcast();
|
||||||
Future<int> waitForCount(int count) {
|
Future<int> waitForCount(int count) async {
|
||||||
if (updateCount == count) {
|
if (updateCount == count) {
|
||||||
return Future.value(updateCount);
|
return Future.value(updateCount);
|
||||||
}
|
}
|
||||||
|
|
@ -47,9 +47,9 @@ void main() {
|
||||||
final completer = Completer<int>();
|
final completer = Completer<int>();
|
||||||
|
|
||||||
StreamSubscription<int>? sub;
|
StreamSubscription<int>? sub;
|
||||||
sub = countStream.stream.listen((newCount) {
|
sub = countStream.stream.listen((newCount) async {
|
||||||
if (newCount == count) {
|
if (newCount == count) {
|
||||||
sub?.cancel();
|
await sub?.cancel();
|
||||||
completer.complete(count);
|
completer.complete(count);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -109,7 +109,8 @@ void main() {
|
||||||
testTimeStamp = DateTime.now().millisecondsSinceEpoch;
|
testTimeStamp = DateTime.now().millisecondsSinceEpoch;
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() => client.dispose(closeDatabase: true).onError((e, s) {}));
|
tearDown(
|
||||||
|
() async => client.dispose(closeDatabase: true).onError((e, s) {}));
|
||||||
|
|
||||||
test('Create', () async {
|
test('Create', () async {
|
||||||
client.onEvent.add(EventUpdate(
|
client.onEvent.add(EventUpdate(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue