[core] allow messages from private group sessions with responses get deleted

This commit is contained in:
Torsten Grote
2019-10-09 09:31:04 -03:00
parent 41676065c5
commit 9736f9d31f
8 changed files with 337 additions and 65 deletions

View File

@@ -38,6 +38,11 @@ enum CreatorState implements State {
return visibility; return visibility;
} }
@Override
public boolean isAwaitingResponse() {
return this == INVITED;
}
static CreatorState fromValue(int value) throws FormatException { static CreatorState fromValue(int value) throws FormatException {
for (CreatorState s : values()) if (s.value == value) return s; for (CreatorState s : values()) if (s.value == value) return s;
throw new FormatException(); throw new FormatException();

View File

@@ -40,6 +40,7 @@ import org.briarproject.briar.client.ConversationClientImpl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -369,7 +370,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} }
@Override @Override
public Collection<ConversationMessageHeader> getMessageHeaders(Transaction txn, public Collection<ConversationMessageHeader> getMessageHeaders(
Transaction txn,
ContactId c) throws DbException { ContactId c) throws DbException {
try { try {
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
@@ -633,8 +635,98 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
@Override @Override
public boolean deleteAllMessages(Transaction txn, ContactId c) public boolean deleteAllMessages(Transaction txn, ContactId c)
throws DbException { throws DbException {
// TODO actually delete messages (#1627 and #1629) // get ID of the contact group
return getMessageIds(txn, c).size() == 0; GroupId g = getContactGroup(db.getContact(txn, c)).getId();
// get metadata for all messages in the
// (these are sessions *and* protocol messages)
Map<MessageId, BdfDictionary> metadata;
try {
metadata = clientHelper.getMessageMetadataAsDictionary(txn, g);
} catch (FormatException e) {
throw new DbException(e);
}
// get all sessions and their states
Map<GroupId, DeletableSession> sessions = new HashMap<>();
for (BdfDictionary d : metadata.values()) {
if (!sessionParser.isSession(d)) continue;
Session session;
try {
Role role = sessionParser.getRole(d);
if (role == CREATOR) {
session = sessionParser.parseCreatorSession(g, d);
} else if (role == INVITEE) {
session = sessionParser.parseInviteeSession(g, d);
} else if (role == PEER) {
session = sessionParser.parsePeerSession(g, d);
} else throw new AssertionError("unknown role");
} catch (FormatException e) {
throw new DbException(e);
}
sessions.put(session.getPrivateGroupId(),
new DeletableSession(session.getState()));
}
// assign protocol messages to their sessions
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
// skip all sessions, we are only interested in messages
BdfDictionary d = entry.getValue();
if (sessionParser.isSession(d)) continue;
// parse message metadata and skip messages not visible in UI
MessageMetadata m;
try {
m = messageParser.parseMetadata(d);
} catch (FormatException e) {
throw new DbException(e);
}
if (!m.isVisibleInConversation()) continue;
// add visible messages to session
DeletableSession session = sessions.get(m.getPrivateGroupId());
session.messages.add(entry.getKey());
}
// get a set of all messages which were not ACKed by the contact
Set<MessageId> notAcked = new HashSet<>();
for (MessageStatus status : db.getMessageStatus(txn, c, g)) {
if (!status.isSeen()) notAcked.add(status.getMessageId());
}
boolean allDeleted =
deleteCompletedSessions(txn, sessions.values(), notAcked);
recalculateGroupCount(txn, g);
return allDeleted;
}
private boolean deleteCompletedSessions(Transaction txn,
Collection<DeletableSession> sessions, Set<MessageId> notAcked)
throws DbException {
// find completed sessions to delete
boolean allDeleted = true;
for (DeletableSession session : sessions) {
if (session.state.isAwaitingResponse()) {
allDeleted = false;
continue;
}
// we can only delete sessions
// where delivery of all messages was confirmed (aka ACKed)
boolean allAcked = true;
for (MessageId m : session.messages) {
if (notAcked.contains(m)) {
allAcked = false;
allDeleted = false;
break;
}
}
if (allAcked) {
for (MessageId m : session.messages) {
db.deleteMessage(txn, m);
db.deleteMessageMetadata(txn, m);
}
}
}
return allDeleted;
} }
private Set<MessageId> getMessageIds(Transaction txn, ContactId c) private Set<MessageId> getMessageIds(Transaction txn, ContactId c)
@@ -650,6 +742,31 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} }
} }
private void recalculateGroupCount(Transaction txn, GroupId g)
throws DbException {
BdfDictionary query = messageParser.getMessagesVisibleInUiQuery();
Map<MessageId, BdfDictionary> results;
try {
results =
clientHelper.getMessageMetadataAsDictionary(txn, g, query);
} catch (FormatException e) {
throw new DbException(e);
}
int msgCount = 0;
int unreadCount = 0;
for (Entry<MessageId, BdfDictionary> entry : results.entrySet()) {
MessageMetadata meta;
try {
meta = messageParser.parseMetadata(entry.getValue());
} catch (FormatException e) {
throw new DbException(e);
}
msgCount++;
if (!meta.isRead()) unreadCount++;
}
messageTracker.resetGroupCount(txn, g, msgCount, unreadCount);
}
private static class StoredSession { private static class StoredSession {
private final MessageId storageId; private final MessageId storageId;
@@ -660,4 +777,14 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
this.bdfSession = bdfSession; this.bdfSession = bdfSession;
} }
} }
private static class DeletableSession {
private final State state;
private final List<MessageId> messages = new ArrayList<>();
private DeletableSession(State state) {
this.state = state;
}
}
} }

View File

@@ -40,6 +40,11 @@ enum InviteeState implements State {
return visibility; return visibility;
} }
@Override
public boolean isAwaitingResponse() {
return this == INVITED;
}
static InviteeState fromValue(int value) throws FormatException { static InviteeState fromValue(int value) throws FormatException {
for (InviteeState s : values()) if (s.value == value) return s; for (InviteeState s : values()) if (s.value == value) return s;
throw new FormatException(); throw new FormatException();

View File

@@ -40,6 +40,11 @@ enum PeerState implements State {
return visibility; return visibility;
} }
@Override
public boolean isAwaitingResponse() {
return false;
}
static PeerState fromValue(int value) throws FormatException { static PeerState fromValue(int value) throws FormatException {
for (PeerState s : values()) if (s.value == value) return s; for (PeerState s : values()) if (s.value == value) return s;
throw new FormatException(); throw new FormatException();

View File

@@ -15,6 +15,8 @@ interface SessionParser {
Role getRole(BdfDictionary d) throws FormatException; Role getRole(BdfDictionary d) throws FormatException;
boolean isSession(BdfDictionary d);
CreatorSession parseCreatorSession(GroupId contactGroupId, BdfDictionary d) CreatorSession parseCreatorSession(GroupId contactGroupId, BdfDictionary d)
throws FormatException; throws FormatException;

View File

@@ -48,6 +48,11 @@ class SessionParserImpl implements SessionParser {
return Role.fromValue(d.getLong(SESSION_KEY_ROLE).intValue()); return Role.fromValue(d.getLong(SESSION_KEY_ROLE).intValue());
} }
@Override
public boolean isSession(BdfDictionary d) {
return d.getBoolean(SESSION_KEY_IS_SESSION, false);
}
@Override @Override
public CreatorSession parseCreatorSession(GroupId contactGroupId, public CreatorSession parseCreatorSession(GroupId contactGroupId,
BdfDictionary d) throws FormatException { BdfDictionary d) throws FormatException {

View File

@@ -7,4 +7,6 @@ interface State {
int getValue(); int getValue();
Visibility getVisibility(); Visibility getVisibility();
boolean isAwaitingResponse();
} }

View File

@@ -31,7 +31,7 @@ import static org.junit.Assert.fail;
public class GroupInvitationIntegrationTest public class GroupInvitationIntegrationTest
extends BriarIntegrationTest<BriarIntegrationTestComponent> { extends BriarIntegrationTest<BriarIntegrationTestComponent> {
private PrivateGroup privateGroup0; private PrivateGroup privateGroup;
private PrivateGroupManager groupManager0, groupManager1; private PrivateGroupManager groupManager0, groupManager1;
private GroupInvitationManager groupInvitationManager0, private GroupInvitationManager groupInvitationManager0,
groupInvitationManager1; groupInvitationManager1;
@@ -46,12 +46,12 @@ public class GroupInvitationIntegrationTest
groupInvitationManager0 = c0.getGroupInvitationManager(); groupInvitationManager0 = c0.getGroupInvitationManager();
groupInvitationManager1 = c1.getGroupInvitationManager(); groupInvitationManager1 = c1.getGroupInvitationManager();
privateGroup0 = privateGroup =
privateGroupFactory.createPrivateGroup("Testgroup", author0); privateGroupFactory.createPrivateGroup("Testgroup", author0);
long joinTime = clock.currentTimeMillis(); long joinTime = clock.currentTimeMillis();
GroupMessage joinMsg0 = groupMessageFactory GroupMessage joinMsg0 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author0); .createJoinMessage(privateGroup.getId(), joinTime, author0);
groupManager0.addPrivateGroup(privateGroup0, joinMsg0, true); groupManager0.addPrivateGroup(privateGroup, joinMsg0, true);
} }
@Override @Override
@@ -90,21 +90,19 @@ public class GroupInvitationIntegrationTest
assertEquals(1, invitations.size()); assertEquals(1, invitations.size());
GroupInvitationItem item = invitations.iterator().next(); GroupInvitationItem item = invitations.iterator().next();
assertEquals(contact0From1, item.getCreator()); assertEquals(contact0From1, item.getCreator());
assertEquals(privateGroup0, item.getShareable()); assertEquals(privateGroup, item.getShareable());
assertEquals(privateGroup0.getId(), item.getId()); assertEquals(privateGroup.getId(), item.getId());
assertEquals(privateGroup0.getName(), item.getName()); assertEquals(privateGroup.getName(), item.getName());
assertFalse(item.isSubscribed()); assertFalse(item.isSubscribed());
Collection<ConversationMessageHeader> messages = Collection<ConversationMessageHeader> messages = getMessages0From1();
db1.transactionWithResult(true, txn -> groupInvitationManager1
.getMessageHeaders(txn, contactId0From1));
assertEquals(1, messages.size()); assertEquals(1, messages.size());
GroupInvitationRequest request = GroupInvitationRequest request =
(GroupInvitationRequest) messages.iterator().next(); (GroupInvitationRequest) messages.iterator().next();
assertEquals(text, request.getText()); assertEquals(text, request.getText());
assertEquals(author0, request.getNameable().getCreator()); assertEquals(author0, request.getNameable().getCreator());
assertEquals(timestamp, request.getTimestamp()); assertEquals(timestamp, request.getTimestamp());
assertEquals(privateGroup0.getName(), request.getNameable().getName()); assertEquals(privateGroup.getName(), request.getNameable().getName());
assertFalse(request.isLocal()); assertFalse(request.isLocal());
assertFalse(request.isRead()); assertFalse(request.isRead());
assertFalse(request.canBeOpened()); assertFalse(request.canBeOpened());
@@ -120,18 +118,16 @@ public class GroupInvitationIntegrationTest
assertFalse(groupInvitationManager1.getInvitations().isEmpty()); assertFalse(groupInvitationManager1.getInvitations().isEmpty());
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, false); .respondToInvitation(contactId0From1, privateGroup, false);
Collection<ConversationMessageHeader> messages = Collection<ConversationMessageHeader> messages = getMessages0From1();
db1.transactionWithResult(true, txn -> groupInvitationManager1
.getMessageHeaders(txn, contactId0From1));
assertEquals(2, messages.size()); assertEquals(2, messages.size());
boolean foundResponse = false; boolean foundResponse = false;
for (ConversationMessageHeader m : messages) { for (ConversationMessageHeader m : messages) {
if (m instanceof GroupInvitationResponse) { if (m instanceof GroupInvitationResponse) {
foundResponse = true; foundResponse = true;
GroupInvitationResponse response = (GroupInvitationResponse) m; GroupInvitationResponse response = (GroupInvitationResponse) m;
assertEquals(privateGroup0.getId(), response.getShareableId()); assertEquals(privateGroup.getId(), response.getShareableId());
assertTrue(response.isLocal()); assertTrue(response.isLocal());
assertFalse(response.wasAccepted()); assertFalse(response.wasAccepted());
} }
@@ -140,16 +136,14 @@ public class GroupInvitationIntegrationTest
sync1To0(1, true); sync1To0(1, true);
messages = db0.transactionWithResult(true, txn -> messages = getMessages1From0();
groupInvitationManager0
.getMessageHeaders(txn, contactId1From0));
assertEquals(2, messages.size()); assertEquals(2, messages.size());
foundResponse = false; foundResponse = false;
for (ConversationMessageHeader m : messages) { for (ConversationMessageHeader m : messages) {
if (m instanceof GroupInvitationResponse) { if (m instanceof GroupInvitationResponse) {
foundResponse = true; foundResponse = true;
GroupInvitationResponse response = (GroupInvitationResponse) m; GroupInvitationResponse response = (GroupInvitationResponse) m;
assertEquals(privateGroup0.getId(), response.getShareableId()); assertEquals(privateGroup.getId(), response.getShareableId());
assertFalse(response.isLocal()); assertFalse(response.isLocal());
assertFalse(response.wasAccepted()); assertFalse(response.wasAccepted());
} }
@@ -168,9 +162,7 @@ public class GroupInvitationIntegrationTest
sendInvitation(timestamp, null); sendInvitation(timestamp, null);
// check that invitation message state is correct // check that invitation message state is correct
Collection<ConversationMessageHeader> messages = Collection<ConversationMessageHeader> messages = getMessages1From0();
db0.transactionWithResult(true, txn -> groupInvitationManager0
.getMessageHeaders(txn, contactId1From0));
assertEquals(1, messages.size()); assertEquals(1, messages.size());
assertMessageState(messages.iterator().next(), true, false, false); assertMessageState(messages.iterator().next(), true, false, false);
@@ -178,11 +170,9 @@ public class GroupInvitationIntegrationTest
assertFalse(groupInvitationManager1.getInvitations().isEmpty()); assertFalse(groupInvitationManager1.getInvitations().isEmpty());
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, true); .respondToInvitation(contactId0From1, privateGroup, true);
messages = db1.transactionWithResult(true, messages = getMessages0From1();
txn -> groupInvitationManager1
.getMessageHeaders(txn, contactId0From1));
assertEquals(2, messages.size()); assertEquals(2, messages.size());
boolean foundResponse = false; boolean foundResponse = false;
for (ConversationMessageHeader m : messages) { for (ConversationMessageHeader m : messages) {
@@ -190,11 +180,11 @@ public class GroupInvitationIntegrationTest
foundResponse = true; foundResponse = true;
GroupInvitationResponse response = (GroupInvitationResponse) m; GroupInvitationResponse response = (GroupInvitationResponse) m;
assertMessageState(response, true, false, false); assertMessageState(response, true, false, false);
assertEquals(privateGroup0.getId(), response.getShareableId()); assertEquals(privateGroup.getId(), response.getShareableId());
assertTrue(response.wasAccepted()); assertTrue(response.wasAccepted());
} else { } else {
GroupInvitationRequest request = (GroupInvitationRequest) m; GroupInvitationRequest request = (GroupInvitationRequest) m;
assertEquals(privateGroup0, request.getNameable()); assertEquals(privateGroup, request.getNameable());
assertTrue(request.wasAnswered()); assertTrue(request.wasAnswered());
assertTrue(request.canBeOpened()); assertTrue(request.canBeOpened());
} }
@@ -203,16 +193,14 @@ public class GroupInvitationIntegrationTest
sync1To0(1, true); sync1To0(1, true);
messages = db1.transactionWithResult(true, txn -> messages = getMessages1From0();
groupInvitationManager0
.getMessageHeaders(txn, contactId1From0));
assertEquals(2, messages.size()); assertEquals(2, messages.size());
foundResponse = false; foundResponse = false;
for (ConversationMessageHeader m : messages) { for (ConversationMessageHeader m : messages) {
if (m instanceof GroupInvitationResponse) { if (m instanceof GroupInvitationResponse) {
foundResponse = true; foundResponse = true;
GroupInvitationResponse response = (GroupInvitationResponse) m; GroupInvitationResponse response = (GroupInvitationResponse) m;
assertEquals(privateGroup0.getId(), response.getShareableId()); assertEquals(privateGroup.getId(), response.getShareableId());
assertTrue(response.wasAccepted()); assertTrue(response.wasAccepted());
} }
} }
@@ -223,7 +211,7 @@ public class GroupInvitationIntegrationTest
// group was added // group was added
Collection<PrivateGroup> groups = groupManager1.getPrivateGroups(); Collection<PrivateGroup> groups = groupManager1.getPrivateGroups();
assertEquals(1, groups.size()); assertEquals(1, groups.size());
assertEquals(privateGroup0, groups.iterator().next()); assertEquals(privateGroup, groups.iterator().next());
} }
@Test @Test
@@ -240,12 +228,10 @@ public class GroupInvitationIntegrationTest
// 1 has one unread message // 1 has one unread message
Group g0 = groupInvitationManager1.getContactGroup(contact0From1); Group g0 = groupInvitationManager1.getContactGroup(contact0From1);
assertGroupCount(messageTracker1, g0.getId(), 1, 1, timestamp); assertGroupCount(messageTracker1, g0.getId(), 1, 1, timestamp);
ConversationMessageHeader m = db1.transactionWithResult(true, txn -> ConversationMessageHeader m = getMessages0From1().iterator().next();
groupInvitationManager1.getMessageHeaders(txn, contactId0From1)
.iterator().next());
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, true); .respondToInvitation(contactId0From1, privateGroup, true);
// 1 has two messages, one still unread // 1 has two messages, one still unread
assertGroupCount(messageTracker1, g0.getId(), 2, 1); assertGroupCount(messageTracker1, g0.getId(), 2, 1);
@@ -266,28 +252,28 @@ public class GroupInvitationIntegrationTest
// invitation is not allowed before the first hasn't been answered // invitation is not allowed before the first hasn't been answered
assertFalse(groupInvitationManager0 assertFalse(groupInvitationManager0
.isInvitationAllowed(contact1From0, privateGroup0.getId())); .isInvitationAllowed(contact1From0, privateGroup.getId()));
// deliver invitation and response // deliver invitation and response
sync0To1(1, true); sync0To1(1, true);
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, false); .respondToInvitation(contactId0From1, privateGroup, false);
sync1To0(1, true); sync1To0(1, true);
// after invitation was declined, inviting again is possible // after invitation was declined, inviting again is possible
assertTrue(groupInvitationManager0 assertTrue(groupInvitationManager0
.isInvitationAllowed(contact1From0, privateGroup0.getId())); .isInvitationAllowed(contact1From0, privateGroup.getId()));
// send and accept the second invitation // send and accept the second invitation
sendInvitation(clock.currentTimeMillis(), "Second Invitation"); sendInvitation(clock.currentTimeMillis(), "Second Invitation");
sync0To1(1, true); sync0To1(1, true);
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, true); .respondToInvitation(contactId0From1, privateGroup, true);
sync1To0(1, true); sync1To0(1, true);
// invitation is not allowed since the member joined the group now // invitation is not allowed since the member joined the group now
assertFalse(groupInvitationManager0 assertFalse(groupInvitationManager0
.isInvitationAllowed(contact1From0, privateGroup0.getId())); .isInvitationAllowed(contact1From0, privateGroup.getId()));
// don't allow another invitation request // don't allow another invitation request
try { try {
@@ -305,14 +291,14 @@ public class GroupInvitationIntegrationTest
sync0To1(1, true); sync0To1(1, true);
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, false); .respondToInvitation(contactId0From1, privateGroup, false);
sync1To0(1, true); sync1To0(1, true);
sendInvitation(timestamp, "Second Invitation"); sendInvitation(timestamp, "Second Invitation");
sync0To1(1, true); sync0To1(1, true);
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, true); .respondToInvitation(contactId0From1, privateGroup, true);
} }
@Test(expected = ProtocolStateException.class) @Test(expected = ProtocolStateException.class)
@@ -325,7 +311,7 @@ public class GroupInvitationIntegrationTest
// Creator leaves group // Creator leaves group
assertEquals(1, groupManager0.getPrivateGroups().size()); assertEquals(1, groupManager0.getPrivateGroups().size());
groupManager0.removePrivateGroup(privateGroup0.getId()); groupManager0.removePrivateGroup(privateGroup.getId());
assertEquals(0, groupManager0.getPrivateGroups().size()); assertEquals(0, groupManager0.getPrivateGroups().size());
// Creator's leave message is delivered to invitee // Creator's leave message is delivered to invitee
@@ -335,7 +321,7 @@ public class GroupInvitationIntegrationTest
// thrown as the action has failed // thrown as the action has failed
assertEquals(0, groupManager1.getPrivateGroups().size()); assertEquals(0, groupManager1.getPrivateGroups().size());
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, true); .respondToInvitation(contactId0From1, privateGroup, true);
} }
@Test @Test
@@ -348,7 +334,7 @@ public class GroupInvitationIntegrationTest
// Creator leaves group // Creator leaves group
assertEquals(1, groupManager0.getPrivateGroups().size()); assertEquals(1, groupManager0.getPrivateGroups().size());
groupManager0.removePrivateGroup(privateGroup0.getId()); groupManager0.removePrivateGroup(privateGroup.getId());
assertEquals(0, groupManager0.getPrivateGroups().size()); assertEquals(0, groupManager0.getPrivateGroups().size());
// Creator's leave message is delivered to invitee // Creator's leave message is delivered to invitee
@@ -361,7 +347,7 @@ public class GroupInvitationIntegrationTest
// as the action has succeeded // as the action has succeeded
assertEquals(0, groupManager1.getPrivateGroups().size()); assertEquals(0, groupManager1.getPrivateGroups().size());
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, false); .respondToInvitation(contactId0From1, privateGroup, false);
} }
@Test @Test
@@ -375,15 +361,15 @@ public class GroupInvitationIntegrationTest
// Creator leaves group // Creator leaves group
assertEquals(1, groupManager0.getPrivateGroups().size()); assertEquals(1, groupManager0.getPrivateGroups().size());
groupManager0.removePrivateGroup(privateGroup0.getId()); groupManager0.removePrivateGroup(privateGroup.getId());
assertEquals(0, groupManager0.getPrivateGroups().size()); assertEquals(0, groupManager0.getPrivateGroups().size());
// Invitee accepts invitation // Invitee accepts invitation
assertEquals(0, groupManager1.getPrivateGroups().size()); assertEquals(0, groupManager1.getPrivateGroups().size());
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, true); .respondToInvitation(contactId0From1, privateGroup, true);
assertEquals(1, groupManager1.getPrivateGroups().size()); assertEquals(1, groupManager1.getPrivateGroups().size());
assertFalse(groupManager1.isDissolved(privateGroup0.getId())); assertFalse(groupManager1.isDissolved(privateGroup.getId()));
// Invitee's join message is delivered to creator // Invitee's join message is delivered to creator
sync1To0(1, true); sync1To0(1, true);
@@ -392,7 +378,7 @@ public class GroupInvitationIntegrationTest
sync0To1(1, true); sync0To1(1, true);
// Group is marked as dissolved // Group is marked as dissolved
assertTrue(groupManager1.isDissolved(privateGroup0.getId())); assertTrue(groupManager1.isDissolved(privateGroup.getId()));
} }
@Test @Test
@@ -406,13 +392,13 @@ public class GroupInvitationIntegrationTest
// Creator leaves group // Creator leaves group
assertEquals(1, groupManager0.getPrivateGroups().size()); assertEquals(1, groupManager0.getPrivateGroups().size());
groupManager0.removePrivateGroup(privateGroup0.getId()); groupManager0.removePrivateGroup(privateGroup.getId());
assertEquals(0, groupManager0.getPrivateGroups().size()); assertEquals(0, groupManager0.getPrivateGroups().size());
// Invitee declines invitation // Invitee declines invitation
assertEquals(0, groupManager1.getPrivateGroups().size()); assertEquals(0, groupManager1.getPrivateGroups().size());
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, false); .respondToInvitation(contactId0From1, privateGroup, false);
assertEquals(0, groupManager1.getPrivateGroups().size()); assertEquals(0, groupManager1.getPrivateGroups().size());
// Invitee's leave message is delivered to creator // Invitee's leave message is delivered to creator
@@ -434,7 +420,7 @@ public class GroupInvitationIntegrationTest
// Invitee responds to invitation // Invitee responds to invitation
assertEquals(0, groupManager1.getPrivateGroups().size()); assertEquals(0, groupManager1.getPrivateGroups().size());
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup0, true); .respondToInvitation(contactId0From1, privateGroup, true);
assertEquals(1, groupManager1.getPrivateGroups().size()); assertEquals(1, groupManager1.getPrivateGroups().size());
// Invitee's (sharing) join message is delivered to creator // Invitee's (sharing) join message is delivered to creator
@@ -448,11 +434,11 @@ public class GroupInvitationIntegrationTest
// Creator leaves group // Creator leaves group
assertEquals(1, groupManager0.getPrivateGroups().size()); assertEquals(1, groupManager0.getPrivateGroups().size());
groupManager0.removePrivateGroup(privateGroup0.getId()); groupManager0.removePrivateGroup(privateGroup.getId());
assertEquals(0, groupManager0.getPrivateGroups().size()); assertEquals(0, groupManager0.getPrivateGroups().size());
// Invitee leaves group // Invitee leaves group
groupManager1.removePrivateGroup(privateGroup0.getId()); groupManager1.removePrivateGroup(privateGroup.getId());
assertEquals(0, groupManager1.getPrivateGroups().size()); assertEquals(0, groupManager1.getPrivateGroups().size());
// Creator's leave message is delivered to invitee // Creator's leave message is delivered to invitee
@@ -462,13 +448,148 @@ public class GroupInvitationIntegrationTest
sync1To0(1, true); sync1To0(1, true);
} }
@Test
public void testDeletingAllMessagesWhenCompletingSession()
throws Exception {
// send invitation
sendInvitation(clock.currentTimeMillis(), null);
sync0To1(1, true);
// messages can not be deleted
assertFalse(deleteAllMessages1From0());
assertEquals(1, getMessages1From0().size());
assertFalse(deleteAllMessages0From1());
assertEquals(1, getMessages0From1().size());
// respond
groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup, true);
sync1To0(1, true);
// check group count
Group g1From0 = groupInvitationManager0.getContactGroup(contact1From0);
Group g0From1 = groupInvitationManager1.getContactGroup(contact0From1);
assertGroupCount(messageTracker0, g1From0.getId(), 2, 1);
assertGroupCount(messageTracker1, g0From1.getId(), 2, 1);
// messages can be deleted now by creator, invitee needs to wait for ACK
assertTrue(deleteAllMessages1From0());
assertEquals(0, getMessages1From0().size());
assertTrue(deleteAllMessages1From0()); // a second time nothing happens
assertGroupCount(messageTracker0, g1From0.getId(), 0, 0);
// trying to delete fails for invitee
assertFalse(deleteAllMessages0From1());
assertEquals(2, getMessages0From1().size());
// creator sends JOIN message and ACK for response
sync0To1(1, true);
// now invitee can also delete messages
assertTrue(deleteAllMessages0From1());
assertEquals(0, getMessages0From1().size());
assertTrue(deleteAllMessages0From1()); // a second time nothing happens
assertGroupCount(messageTracker1, g0From1.getId(), 0, 0);
// invitee now leaves
groupManager1.removePrivateGroup(privateGroup.getId());
sync1To0(1, true);
// no new messages to delete
assertEquals(0, getMessages1From0().size());
assertEquals(0, getMessages0From1().size());
}
@Test
public void testDeletingAllMessagesWhenDeclining() throws Exception {
// send invitation
sendInvitation(clock.currentTimeMillis(), null);
sync0To1(1, true);
// respond
groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup, false);
sync1To0(1, true);
// check group count
Group g1From0 = groupInvitationManager0.getContactGroup(contact1From0);
Group g0From1 = groupInvitationManager1.getContactGroup(contact0From1);
assertGroupCount(messageTracker0, g1From0.getId(), 2, 1);
assertGroupCount(messageTracker1, g0From1.getId(), 2, 1);
// messages can be deleted now by creator, invitee needs to wait for ACK
assertTrue(deleteAllMessages1From0());
assertEquals(0, getMessages1From0().size());
assertTrue(deleteAllMessages1From0()); // a second time nothing happens
// trying to delete fails for invitee
assertFalse(deleteAllMessages0From1());
assertEquals(2, getMessages0From1().size());
// creator sends ACK
sendAcks(c0, c1, contactId1From0, 1);
// now invitee can also delete messages
assertTrue(deleteAllMessages0From1());
assertEquals(0, getMessages0From1().size());
assertTrue(deleteAllMessages0From1()); // a second time nothing happens
assertGroupCount(messageTracker1, g0From1.getId(), 0, 0);
// creator can re-invite
sendInvitation(clock.currentTimeMillis(), null);
sync0To1(1, true);
// now new messages can not be deleted anymore
assertFalse(deleteAllMessages1From0());
assertFalse(deleteAllMessages0From1());
// responding again
groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup, false);
sync1To0(1, true);
// creator sends ACK
sendAcks(c0, c1, contactId1From0, 1);
// asserting group counts
assertGroupCount(messageTracker1, g0From1.getId(), 2, 1);
assertGroupCount(messageTracker0, g1From0.getId(), 2, 1);
// deleting is possible again
assertTrue(deleteAllMessages1From0());
assertTrue(deleteAllMessages0From1());
assertGroupCount(messageTracker1, g0From1.getId(), 0, 0);
assertGroupCount(messageTracker0, g1From0.getId(), 0, 0);
}
private Collection<ConversationMessageHeader> getMessages1From0()
throws DbException {
return db0.transactionWithResult(true, txn -> groupInvitationManager0
.getMessageHeaders(txn, contactId1From0));
}
private Collection<ConversationMessageHeader> getMessages0From1()
throws DbException {
return db1.transactionWithResult(true, txn -> groupInvitationManager1
.getMessageHeaders(txn, contactId0From1));
}
private boolean deleteAllMessages1From0() throws DbException {
return db0.transactionWithResult(false, txn -> groupInvitationManager0
.deleteAllMessages(txn, contactId1From0));
}
private boolean deleteAllMessages0From1() throws DbException {
return db1.transactionWithResult(false, txn -> groupInvitationManager1
.deleteAllMessages(txn, contactId0From1));
}
private void sendInvitation(long timestamp, @Nullable String text) private void sendInvitation(long timestamp, @Nullable String text)
throws DbException { throws DbException {
byte[] signature = groupInvitationFactory.signInvitation(contact1From0, byte[] signature = groupInvitationFactory.signInvitation(contact1From0,
privateGroup0.getId(), timestamp, author0.getPrivateKey()); privateGroup.getId(), timestamp, author0.getPrivateKey());
groupInvitationManager0 groupInvitationManager0.sendInvitation(privateGroup.getId(),
.sendInvitation(privateGroup0.getId(), contactId1From0, text, contactId1From0, text, timestamp, signature);
timestamp, signature);
} }
} }