feat: Add a management class for roster state
This commit is contained in:
parent
763c93857d
commit
e2c8f79429
@ -29,6 +29,7 @@ export 'package:moxxmpp/src/rfcs/rfc_2782.dart';
|
|||||||
export 'package:moxxmpp/src/rfcs/rfc_4790.dart';
|
export 'package:moxxmpp/src/rfcs/rfc_4790.dart';
|
||||||
export 'package:moxxmpp/src/roster/errors.dart';
|
export 'package:moxxmpp/src/roster/errors.dart';
|
||||||
export 'package:moxxmpp/src/roster/roster.dart';
|
export 'package:moxxmpp/src/roster/roster.dart';
|
||||||
|
export 'package:moxxmpp/src/roster/state.dart';
|
||||||
export 'package:moxxmpp/src/settings.dart';
|
export 'package:moxxmpp/src/settings.dart';
|
||||||
export 'package:moxxmpp/src/socket.dart';
|
export 'package:moxxmpp/src/socket.dart';
|
||||||
export 'package:moxxmpp/src/stanza.dart';
|
export 'package:moxxmpp/src/stanza.dart';
|
||||||
|
@ -65,7 +65,6 @@ class RosterFeatureNegotiator extends XmppFeatureNegotiatorBase {
|
|||||||
|
|
||||||
/// This manager requires a RosterFeatureNegotiator to be registered.
|
/// This manager requires a RosterFeatureNegotiator to be registered.
|
||||||
class RosterManager extends XmppManagerBase {
|
class RosterManager extends XmppManagerBase {
|
||||||
|
|
||||||
RosterManager() : _rosterVersion = null, super();
|
RosterManager() : _rosterVersion = null, super();
|
||||||
String? _rosterVersion;
|
String? _rosterVersion;
|
||||||
|
|
||||||
@ -88,8 +87,10 @@ class RosterManager extends XmppManagerBase {
|
|||||||
@override
|
@override
|
||||||
Future<bool> isSupported() async => true;
|
Future<bool> isSupported() async => true;
|
||||||
|
|
||||||
/// Override-able functions
|
/// Commit the current roster to storage.
|
||||||
Future<void> commitLastRosterVersion(String version) async {}
|
Future<void> commitLastRosterVersion(String version) async {}
|
||||||
|
|
||||||
|
/// Load the last roster data
|
||||||
Future<void> loadLastRosterVersion() async {}
|
Future<void> loadLastRosterVersion() async {}
|
||||||
|
|
||||||
void setRosterVersion(String ver) {
|
void setRosterVersion(String ver) {
|
||||||
|
142
packages/moxxmpp/lib/src/roster/state.dart
Normal file
142
packages/moxxmpp/lib/src/roster/state.dart
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:moxxmpp/src/roster/roster.dart';
|
||||||
|
|
||||||
|
class _RosterProcessTriple {
|
||||||
|
const _RosterProcessTriple(this.removed, this.modified, this.added);
|
||||||
|
final String? removed;
|
||||||
|
final XmppRosterItem? modified;
|
||||||
|
final XmppRosterItem? added;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RosterCacheLoadResult {
|
||||||
|
const RosterCacheLoadResult(this.version, this.roster);
|
||||||
|
final String? version;
|
||||||
|
final List<XmppRosterItem> roster;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BaseRosterStateManager {
|
||||||
|
List<XmppRosterItem>? currentRoster;
|
||||||
|
String? currentVersion;
|
||||||
|
|
||||||
|
Future<RosterCacheLoadResult> loadRosterCache();
|
||||||
|
|
||||||
|
Future<void> commitRoster(String? version, List<String> removed, List<XmppRosterItem> modified, List<XmppRosterItem> added);
|
||||||
|
|
||||||
|
Future<void> _loadRosterCache() async {
|
||||||
|
if (currentRoster == null) {
|
||||||
|
final result = await loadRosterCache();
|
||||||
|
|
||||||
|
currentRoster = result.roster;
|
||||||
|
currentVersion = result.version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_RosterProcessTriple _handleRosterItem(XmppRosterItem item) {
|
||||||
|
if (item.subscription == 'remove') {
|
||||||
|
// The item has been removed
|
||||||
|
currentRoster!.removeWhere((i) => i.jid == item.jid);
|
||||||
|
return _RosterProcessTriple(
|
||||||
|
item.jid,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final index = currentRoster!.indexWhere((i) => i.jid == item.jid);
|
||||||
|
if (index == -1) {
|
||||||
|
// The item does not exist
|
||||||
|
currentRoster!.add(item);
|
||||||
|
return _RosterProcessTriple(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
item,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// The item is updated
|
||||||
|
currentRoster![index] = item;
|
||||||
|
return _RosterProcessTriple(
|
||||||
|
null,
|
||||||
|
item,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> handleRosterPush(RosterPushEvent event) async {
|
||||||
|
await _loadRosterCache();
|
||||||
|
|
||||||
|
currentVersion = event.ver;
|
||||||
|
final result = _handleRosterItem(event.item);
|
||||||
|
|
||||||
|
if (result.removed != null) {
|
||||||
|
return commitRoster(
|
||||||
|
currentVersion,
|
||||||
|
[result.removed!],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
} else if (result.modified != null) {
|
||||||
|
return commitRoster(
|
||||||
|
currentVersion,
|
||||||
|
[],
|
||||||
|
[result.modified!],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
} else if (result.added != null) {
|
||||||
|
return commitRoster(
|
||||||
|
currentVersion,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[result.added!],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> handleRosterFetch(RosterRequestResult result) async {
|
||||||
|
final removed = List<String>.empty(growable: true);
|
||||||
|
final modified = List<XmppRosterItem>.empty(growable: true);
|
||||||
|
final added = List<XmppRosterItem>.empty(growable: true);
|
||||||
|
|
||||||
|
await _loadRosterCache();
|
||||||
|
|
||||||
|
currentVersion = result.ver;
|
||||||
|
for (final item in result.items) {
|
||||||
|
final result = _handleRosterItem(item);
|
||||||
|
|
||||||
|
if (result.removed != null) removed.add(result.removed!);
|
||||||
|
if (result.modified != null) modified.add(result.modified!);
|
||||||
|
if (result.added != null) added.add(result.added!);
|
||||||
|
}
|
||||||
|
|
||||||
|
await commitRoster(
|
||||||
|
currentVersion,
|
||||||
|
removed,
|
||||||
|
modified,
|
||||||
|
added,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
class TestingRosterStateManager extends BaseRosterStateManager {
|
||||||
|
TestingRosterStateManager(
|
||||||
|
this.initialRosterVersion,
|
||||||
|
this.initialRoster,
|
||||||
|
);
|
||||||
|
final String? initialRosterVersion;
|
||||||
|
final List<XmppRosterItem> initialRoster;
|
||||||
|
int loadCount = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<RosterCacheLoadResult> loadRosterCache() async {
|
||||||
|
loadCount++;
|
||||||
|
return RosterCacheLoadResult(
|
||||||
|
initialRosterVersion,
|
||||||
|
initialRoster,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> commitRoster(String? version, List<String> removed, List<XmppRosterItem> modified, List<XmppRosterItem> added) async {}
|
||||||
|
|
||||||
|
}
|
93
packages/moxxmpp/test/roster_state_test.dart
Normal file
93
packages/moxxmpp/test/roster_state_test.dart
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('Test receiving a roster push', () async {
|
||||||
|
final rs = TestingRosterStateManager(null, []);
|
||||||
|
|
||||||
|
await rs.handleRosterPush(
|
||||||
|
RosterPushEvent(
|
||||||
|
item: XmppRosterItem(
|
||||||
|
jid: 'testuser@server.example',
|
||||||
|
subscription: 'both',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
rs.currentRoster!.indexWhere((item) => item.jid == 'testuser@server.example') != -1,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(rs.loadCount, 1);
|
||||||
|
expect(rs.currentRoster!.length, 1);
|
||||||
|
|
||||||
|
// Receive another roster push
|
||||||
|
await rs.handleRosterPush(
|
||||||
|
RosterPushEvent(
|
||||||
|
item: XmppRosterItem(
|
||||||
|
jid: 'testuser2@server2.example',
|
||||||
|
subscription: 'to',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
rs.currentRoster!.indexWhere((item) => item.jid == 'testuser2@server2.example') != -1,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(rs.loadCount, 1);
|
||||||
|
expect(rs.currentRoster!.length, 2);
|
||||||
|
|
||||||
|
// Remove one of the items
|
||||||
|
await rs.handleRosterPush(
|
||||||
|
RosterPushEvent(
|
||||||
|
item: XmppRosterItem(
|
||||||
|
jid: 'testuser2@server2.example',
|
||||||
|
subscription: 'remove',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
rs.currentRoster!.indexWhere((item) => item.jid == 'testuser2@server2.example') == -1,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
rs.currentRoster!.indexWhere((item) => item.jid == 'testuser@server.example') != 1,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(rs.loadCount, 1);
|
||||||
|
expect(rs.currentRoster!.length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Test a roster fetch', () async {
|
||||||
|
final rs = TestingRosterStateManager(null, []);
|
||||||
|
|
||||||
|
// Fetch the roster
|
||||||
|
await rs.handleRosterFetch(
|
||||||
|
RosterRequestResult(
|
||||||
|
items: [
|
||||||
|
XmppRosterItem(
|
||||||
|
jid: 'testuser@server.example',
|
||||||
|
subscription: 'both',
|
||||||
|
),
|
||||||
|
XmppRosterItem(
|
||||||
|
jid: 'testuser2@server2.example',
|
||||||
|
subscription: 'to',
|
||||||
|
),
|
||||||
|
XmppRosterItem(
|
||||||
|
jid: 'testuser3@server3.example',
|
||||||
|
subscription: 'from',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ver: 'aaaaaaaa',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(rs.loadCount, 1);
|
||||||
|
expect(rs.currentRoster!.length, 3);
|
||||||
|
expect(rs.currentRoster!.indexWhere((item) => item.jid == 'testuser@server.example') != -1, true);
|
||||||
|
expect(rs.currentRoster!.indexWhere((item) => item.jid == 'testuser2@server2.example') != -1, true);
|
||||||
|
expect(rs.currentRoster!.indexWhere((item) => item.jid == 'testuser3@server3.example') != -1, true);
|
||||||
|
});
|
||||||
|
}
|
@ -1,322 +0,0 @@
|
|||||||
import 'package:moxxmpp/moxxmpp.dart';
|
|
||||||
import 'package:test/test.dart';
|
|
||||||
|
|
||||||
// TODO(PapaTutuWawa): Fix tests
|
|
||||||
|
|
||||||
typedef AddRosterItemFunction = Future<RosterItem> Function(
|
|
||||||
String avatarUrl,
|
|
||||||
String avatarHash,
|
|
||||||
String jid,
|
|
||||||
String title,
|
|
||||||
String subscription,
|
|
||||||
String ask,
|
|
||||||
{
|
|
||||||
List<String> groups,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
typedef UpdateRosterItemFunction = Future<RosterItem> Function(
|
|
||||||
int id, {
|
|
||||||
String? avatarUrl,
|
|
||||||
String? avatarHash,
|
|
||||||
String? title,
|
|
||||||
String? subscription,
|
|
||||||
String? ask,
|
|
||||||
List<String>? groups,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
AddRosterItemFunction mkAddRosterItem(void Function(String) callback) {
|
|
||||||
return (
|
|
||||||
String avatarUrl,
|
|
||||||
String avatarHash,
|
|
||||||
String jid,
|
|
||||||
String title,
|
|
||||||
String subscription,
|
|
||||||
String ask,
|
|
||||||
{
|
|
||||||
List<String> groups = const [],
|
|
||||||
}
|
|
||||||
) async {
|
|
||||||
callback(jid);
|
|
||||||
return await addRosterItemFromData(
|
|
||||||
avatarUrl,
|
|
||||||
avatarHash,
|
|
||||||
jid,
|
|
||||||
title,
|
|
||||||
subscription,
|
|
||||||
ask,
|
|
||||||
groups: groups,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<RosterItem> addRosterItemFromData(
|
|
||||||
String avatarUrl,
|
|
||||||
String avatarHash,
|
|
||||||
String jid,
|
|
||||||
String title,
|
|
||||||
String subscription,
|
|
||||||
String ask,
|
|
||||||
{
|
|
||||||
List<String> groups = const [],
|
|
||||||
}
|
|
||||||
) async => RosterItem(
|
|
||||||
0,
|
|
||||||
avatarUrl,
|
|
||||||
avatarHash,
|
|
||||||
jid,
|
|
||||||
title,
|
|
||||||
subscription,
|
|
||||||
ask,
|
|
||||||
groups,
|
|
||||||
);
|
|
||||||
|
|
||||||
UpdateRosterItemFunction mkRosterUpdate(List<RosterItem> roster) {
|
|
||||||
return (
|
|
||||||
int id, {
|
|
||||||
String? avatarUrl,
|
|
||||||
String? avatarHash,
|
|
||||||
String? title,
|
|
||||||
String? subscription,
|
|
||||||
String? ask,
|
|
||||||
List<String>? groups,
|
|
||||||
}
|
|
||||||
) async {
|
|
||||||
final item = firstWhereOrNull(roster, (RosterItem item) => item.id == id)!;
|
|
||||||
return item.copyWith(
|
|
||||||
avatarUrl: avatarUrl ?? item.avatarUrl,
|
|
||||||
avatarHash: avatarHash ?? item.avatarHash,
|
|
||||||
title: title ?? item.title,
|
|
||||||
subscription: subscription ?? item.subscription,
|
|
||||||
ask: ask ?? item.ask,
|
|
||||||
groups: groups ?? item.groups,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
final localRosterSingle = [
|
|
||||||
RosterItem(
|
|
||||||
0,
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
'hallo@server.example',
|
|
||||||
'hallo',
|
|
||||||
'none',
|
|
||||||
'',
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
];
|
|
||||||
final localRosterDouble = [
|
|
||||||
RosterItem(
|
|
||||||
0,
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
'hallo@server.example',
|
|
||||||
'hallo',
|
|
||||||
'none',
|
|
||||||
'',
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
RosterItem(
|
|
||||||
1,
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
'welt@different.server.example',
|
|
||||||
'welt',
|
|
||||||
'from',
|
|
||||||
'',
|
|
||||||
[ 'Friends' ],
|
|
||||||
)
|
|
||||||
];
|
|
||||||
|
|
||||||
group('Test roster pushes', () {
|
|
||||||
test('Test removing an item', () async {
|
|
||||||
var removeCalled = false;
|
|
||||||
var addCalled = false;
|
|
||||||
final result = await processRosterDiff(
|
|
||||||
localRosterDouble,
|
|
||||||
[
|
|
||||||
XmppRosterItem(
|
|
||||||
jid: 'hallo@server.example', subscription: 'remove',
|
|
||||||
)
|
|
||||||
],
|
|
||||||
true,
|
|
||||||
mkAddRosterItem((_) { addCalled = true; }),
|
|
||||||
mkRosterUpdate(localRosterDouble),
|
|
||||||
(jid) async {
|
|
||||||
if (jid == 'hallo@server.example') {
|
|
||||||
removeCalled = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(_) async => null,
|
|
||||||
(_, { String? id }) async {},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.removed, [ 'hallo@server.example' ]);
|
|
||||||
expect(result.modified.length, 0);
|
|
||||||
expect(result.added.length, 0);
|
|
||||||
expect(removeCalled, true);
|
|
||||||
expect(addCalled, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Test adding an item', () async {
|
|
||||||
var removeCalled = false;
|
|
||||||
var addCalled = false;
|
|
||||||
final result = await processRosterDiff(
|
|
||||||
localRosterSingle,
|
|
||||||
[
|
|
||||||
XmppRosterItem(
|
|
||||||
jid: 'welt@different.server.example',
|
|
||||||
subscription: 'from',
|
|
||||||
)
|
|
||||||
],
|
|
||||||
true,
|
|
||||||
mkAddRosterItem(
|
|
||||||
(jid) {
|
|
||||||
if (jid == 'welt@different.server.example') {
|
|
||||||
addCalled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
mkRosterUpdate(localRosterSingle),
|
|
||||||
(_) async { removeCalled = true; },
|
|
||||||
(_) async => null,
|
|
||||||
(_, { String? id }) async {},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.removed, [ ]);
|
|
||||||
expect(result.modified.length, 0);
|
|
||||||
expect(result.added.length, 1);
|
|
||||||
expect(result.added.first.subscription, 'from');
|
|
||||||
expect(removeCalled, false);
|
|
||||||
expect(addCalled, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Test modifying an item', () async {
|
|
||||||
var removeCalled = false;
|
|
||||||
var addCalled = false;
|
|
||||||
final result = await processRosterDiff(
|
|
||||||
localRosterDouble,
|
|
||||||
[
|
|
||||||
XmppRosterItem(
|
|
||||||
jid: 'welt@different.server.example',
|
|
||||||
subscription: 'both',
|
|
||||||
name: 'The World',
|
|
||||||
)
|
|
||||||
],
|
|
||||||
true,
|
|
||||||
mkAddRosterItem((_) { addCalled = false; }),
|
|
||||||
mkRosterUpdate(localRosterDouble),
|
|
||||||
(_) async { removeCalled = true; },
|
|
||||||
(_) async => null,
|
|
||||||
(_, { String? id }) async {},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.removed, [ ]);
|
|
||||||
expect(result.modified.length, 1);
|
|
||||||
expect(result.added.length, 0);
|
|
||||||
expect(result.modified.first.subscription, 'both');
|
|
||||||
expect(result.modified.first.jid, 'welt@different.server.example');
|
|
||||||
expect(result.modified.first.title, 'The World');
|
|
||||||
expect(removeCalled, false);
|
|
||||||
expect(addCalled, false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('Test roster requests', () {
|
|
||||||
test('Test removing an item', () async {
|
|
||||||
var removeCalled = false;
|
|
||||||
var addCalled = false;
|
|
||||||
final result = await processRosterDiff(
|
|
||||||
localRosterSingle,
|
|
||||||
[],
|
|
||||||
false,
|
|
||||||
mkAddRosterItem((_) { addCalled = true; }),
|
|
||||||
mkRosterUpdate(localRosterDouble),
|
|
||||||
(jid) async {
|
|
||||||
if (jid == 'hallo@server.example') {
|
|
||||||
removeCalled = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(_) async => null,
|
|
||||||
(_, { String? id }) async {},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.removed, [ 'hallo@server.example' ]);
|
|
||||||
expect(result.modified.length, 0);
|
|
||||||
expect(result.added.length, 0);
|
|
||||||
expect(removeCalled, true);
|
|
||||||
expect(addCalled, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Test adding an item', () async {
|
|
||||||
var removeCalled = false;
|
|
||||||
var addCalled = false;
|
|
||||||
final result = await processRosterDiff(
|
|
||||||
localRosterSingle,
|
|
||||||
[
|
|
||||||
XmppRosterItem(
|
|
||||||
jid: 'hallo@server.example',
|
|
||||||
name: 'hallo',
|
|
||||||
subscription: 'none',
|
|
||||||
),
|
|
||||||
XmppRosterItem(
|
|
||||||
jid: 'welt@different.server.example',
|
|
||||||
subscription: 'both',
|
|
||||||
)
|
|
||||||
],
|
|
||||||
false,
|
|
||||||
mkAddRosterItem(
|
|
||||||
(jid) {
|
|
||||||
if (jid == 'welt@different.server.example') {
|
|
||||||
addCalled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
mkRosterUpdate(localRosterSingle),
|
|
||||||
(_) async { removeCalled = true; },
|
|
||||||
(_) async => null,
|
|
||||||
(_, { String? id }) async {},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.removed, [ ]);
|
|
||||||
expect(result.modified.length, 0);
|
|
||||||
expect(result.added.length, 1);
|
|
||||||
expect(result.added.first.subscription, 'both');
|
|
||||||
expect(removeCalled, false);
|
|
||||||
expect(addCalled, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Test modifying an item', () async {
|
|
||||||
var removeCalled = false;
|
|
||||||
var addCalled = false;
|
|
||||||
final result = await processRosterDiff(
|
|
||||||
localRosterSingle,
|
|
||||||
[
|
|
||||||
XmppRosterItem(
|
|
||||||
jid: 'hallo@server.example',
|
|
||||||
subscription: 'both',
|
|
||||||
name: 'Hallo Welt',
|
|
||||||
)
|
|
||||||
],
|
|
||||||
false,
|
|
||||||
mkAddRosterItem((_) { addCalled = false; }),
|
|
||||||
mkRosterUpdate(localRosterDouble),
|
|
||||||
(_) async { removeCalled = true; },
|
|
||||||
(_) async => null,
|
|
||||||
(_, { String? id }) async {},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.removed, [ ]);
|
|
||||||
expect(result.modified.length, 1);
|
|
||||||
expect(result.added.length, 0);
|
|
||||||
expect(result.modified.first.subscription, 'both');
|
|
||||||
expect(result.modified.first.jid, 'hallo@server.example');
|
|
||||||
expect(result.modified.first.title, 'Hallo Welt');
|
|
||||||
expect(removeCalled, false);
|
|
||||||
expect(addCalled, false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user