mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 04:39:54 +01:00
Merge branch '1629-delete-message-subset' into 'master'
Support for deleting a subset of all conversation messages Closes #1629 See merge request briar/briar!1180
This commit is contained in:
@@ -12,6 +12,7 @@ import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
|||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface ConversationManager {
|
public interface ConversationManager {
|
||||||
@@ -43,6 +44,14 @@ public interface ConversationManager {
|
|||||||
*/
|
*/
|
||||||
boolean deleteAllMessages(ContactId c) throws DbException;
|
boolean deleteAllMessages(ContactId c) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given set of messages associated with the given contact.
|
||||||
|
*
|
||||||
|
* @return true if all given messages could be deleted, false otherwise
|
||||||
|
*/
|
||||||
|
boolean deleteMessages(ContactId c, Collection<MessageId> messageIds)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
interface ConversationClient {
|
interface ConversationClient {
|
||||||
|
|
||||||
@@ -51,6 +60,13 @@ public interface ConversationManager {
|
|||||||
Collection<ConversationMessageHeader> getMessageHeaders(Transaction txn,
|
Collection<ConversationMessageHeader> getMessageHeaders(Transaction txn,
|
||||||
ContactId contactId) throws DbException;
|
ContactId contactId) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all conversation {@link MessageId}s for the given contact
|
||||||
|
* this client is responsible for.
|
||||||
|
*/
|
||||||
|
Set<MessageId> getMessageIds(Transaction txn, ContactId contactId)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
GroupCount getGroupCount(Transaction txn, ContactId c)
|
GroupCount getGroupCount(Transaction txn, ContactId c)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
@@ -64,6 +80,17 @@ public interface ConversationManager {
|
|||||||
*/
|
*/
|
||||||
boolean deleteAllMessages(Transaction txn,
|
boolean deleteAllMessages(Transaction txn,
|
||||||
ContactId c) throws DbException;
|
ContactId c) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given set of messages associated with the given contact.
|
||||||
|
* <p>
|
||||||
|
* The set of message IDs must only include message IDs returned by
|
||||||
|
* {@link #getMessageIds}.
|
||||||
|
*
|
||||||
|
* @return true if all messages could be deleted, false otherwise
|
||||||
|
*/
|
||||||
|
boolean deleteMessages(Transaction txn, ContactId c,
|
||||||
|
Set<MessageId> messageIds) throws DbException;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -559,19 +559,68 @@ class IntroductionManagerImpl extends ConversationClientImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface MessageRetriever {
|
||||||
|
Map<MessageId, BdfDictionary> getMessages(Transaction txn,
|
||||||
|
GroupId contactGroup) throws DbException;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface MessageDeletionChecker {
|
||||||
|
/**
|
||||||
|
* This is called for all messages belonging to a session.
|
||||||
|
* It returns true if the given {@link MessageId} causes a problem
|
||||||
|
* so that the session can not be deleted.
|
||||||
|
*/
|
||||||
|
boolean causesProblem(MessageId messageId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteAllMessages(Transaction txn, ContactId c)
|
public boolean deleteAllMessages(Transaction txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
return deleteMessages(txn, c, (txn1, g) -> {
|
||||||
|
// get metadata for all messages in the group
|
||||||
|
Map<MessageId, BdfDictionary> messages;
|
||||||
|
try {
|
||||||
|
messages = clientHelper.getMessageMetadataAsDictionary(txn1, g);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
}, messageId -> false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteMessages(Transaction txn, ContactId c,
|
||||||
|
Set<MessageId> messageIds) throws DbException {
|
||||||
|
return deleteMessages(txn, c, (txn1, g) -> {
|
||||||
|
// get metadata for messages that shall be deleted
|
||||||
|
Map<MessageId, BdfDictionary> messages =
|
||||||
|
new HashMap<>(messageIds.size());
|
||||||
|
for (MessageId m : messageIds) {
|
||||||
|
BdfDictionary d;
|
||||||
|
try {
|
||||||
|
d = clientHelper.getMessageMetadataAsDictionary(txn1, m);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
// If message metadata does not exist,
|
||||||
|
// getMessageMetadataAsDictionary(txn, m) returns empty Metadata
|
||||||
|
if (!d.isEmpty()) messages.put(m, d);
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
// don't delete sessions if a message is not part of messageIds
|
||||||
|
}, messageId -> !messageIds.contains(messageId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages(Transaction txn, ContactId c,
|
||||||
|
MessageRetriever retriever, MessageDeletionChecker checker)
|
||||||
|
throws DbException {
|
||||||
// get ID of the contact group
|
// get ID of the contact group
|
||||||
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
||||||
|
|
||||||
// get metadata for all messages in the group
|
// get messages to be deleted
|
||||||
Map<MessageId, BdfDictionary> messages;
|
Map<MessageId, BdfDictionary> messages = retriever.getMessages(txn, g);
|
||||||
try {
|
|
||||||
messages = clientHelper.getMessageMetadataAsDictionary(txn, g);
|
|
||||||
} catch (FormatException e) {
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// assign protocol messages to their sessions
|
// assign protocol messages to their sessions
|
||||||
Map<SessionId, DeletableSession> sessions = new HashMap<>();
|
Map<SessionId, DeletableSession> sessions = new HashMap<>();
|
||||||
@@ -603,7 +652,8 @@ class IntroductionManagerImpl extends ConversationClientImpl
|
|||||||
for (MessageStatus status : db.getMessageStatus(txn, c, g)) {
|
for (MessageStatus status : db.getMessageStatus(txn, c, g)) {
|
||||||
if (!status.isSeen()) notAcked.add(status.getMessageId());
|
if (!status.isSeen()) notAcked.add(status.getMessageId());
|
||||||
}
|
}
|
||||||
boolean allDeleted = deleteCompletedSessions(txn, sessions, notAcked);
|
boolean allDeleted =
|
||||||
|
deleteCompletedSessions(txn, sessions, notAcked, checker);
|
||||||
recalculateGroupCount(txn, g);
|
recalculateGroupCount(txn, g);
|
||||||
return allDeleted;
|
return allDeleted;
|
||||||
}
|
}
|
||||||
@@ -628,8 +678,8 @@ class IntroductionManagerImpl extends ConversationClientImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean deleteCompletedSessions(Transaction txn,
|
private boolean deleteCompletedSessions(Transaction txn,
|
||||||
Map<SessionId, DeletableSession> sessions, Set<MessageId> notAcked)
|
Map<SessionId, DeletableSession> sessions, Set<MessageId> notAcked,
|
||||||
throws DbException {
|
MessageDeletionChecker checker) throws DbException {
|
||||||
// find completed sessions to delete
|
// find completed sessions to delete
|
||||||
boolean allDeleted = true;
|
boolean allDeleted = true;
|
||||||
for (DeletableSession session : sessions.values()) {
|
for (DeletableSession session : sessions.values()) {
|
||||||
@@ -641,7 +691,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
|
|||||||
// where delivery of all messages was confirmed (aka ACKed)
|
// where delivery of all messages was confirmed (aka ACKed)
|
||||||
boolean allAcked = true;
|
boolean allAcked = true;
|
||||||
for (MessageId m : session.messages) {
|
for (MessageId m : session.messages) {
|
||||||
if (notAcked.contains(m)) {
|
if (notAcked.contains(m) || checker.causesProblem(m)) {
|
||||||
allAcked = false;
|
allAcked = false;
|
||||||
allDeleted = false;
|
allDeleted = false;
|
||||||
break;
|
break;
|
||||||
@@ -660,7 +710,8 @@ class IntroductionManagerImpl extends ConversationClientImpl
|
|||||||
return allDeleted;
|
return allDeleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<MessageId> getMessageIds(Transaction txn, ContactId c)
|
@Override
|
||||||
|
public Set<MessageId> getMessageIds(Transaction txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
||||||
BdfDictionary query = messageParser.getMessagesVisibleInUiQuery();
|
BdfDictionary query = messageParser.getMessagesVisibleInUiQuery();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
|
|||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||||
import org.briarproject.briar.api.conversation.ConversationManager;
|
import org.briarproject.briar.api.conversation.ConversationManager;
|
||||||
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
||||||
@@ -84,4 +85,18 @@ class ConversationManagerImpl implements ConversationManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteMessages(ContactId c, Collection<MessageId> toDelete)
|
||||||
|
throws DbException {
|
||||||
|
return db.transactionWithResult(false, txn -> {
|
||||||
|
boolean allDeleted = true;
|
||||||
|
for (ConversationClient client : clients) {
|
||||||
|
Set<MessageId> idSet = client.getMessageIds(txn, c);
|
||||||
|
idSet.retainAll(toDelete);
|
||||||
|
allDeleted = client.deleteMessages(txn, c, idSet) && allDeleted;
|
||||||
|
}
|
||||||
|
return allDeleted;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ import org.briarproject.bramble.api.contact.Contact;
|
|||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
|
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
|
||||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.bramble.api.data.BdfEntry;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.data.MetadataParser;
|
import org.briarproject.bramble.api.data.MetadataParser;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
import org.briarproject.bramble.api.db.Metadata;
|
||||||
|
import org.briarproject.bramble.api.db.NoSuchMessageException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
@@ -47,12 +49,14 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
|
||||||
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||||
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
|
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
|
||||||
import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT;
|
import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT;
|
||||||
@@ -363,6 +367,21 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
|
|||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<MessageId> getMessageIds(Transaction txn, ContactId c)
|
||||||
|
throws DbException {
|
||||||
|
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
||||||
|
BdfDictionary query = BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_MSG_TYPE, PRIVATE_MESSAGE));
|
||||||
|
try {
|
||||||
|
Map<MessageId, BdfDictionary> results =
|
||||||
|
clientHelper.getMessageMetadataAsDictionary(txn, g, query);
|
||||||
|
return results.keySet();
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessageText(MessageId m) throws DbException {
|
public String getMessageText(MessageId m) throws DbException {
|
||||||
try {
|
try {
|
||||||
@@ -418,4 +437,74 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteMessages(Transaction txn, ContactId c,
|
||||||
|
Set<MessageId> messageIds) throws DbException {
|
||||||
|
boolean allDeleted = true;
|
||||||
|
for (MessageId m : messageIds) {
|
||||||
|
// get attachment headers
|
||||||
|
List<AttachmentHeader> headers;
|
||||||
|
try {
|
||||||
|
BdfDictionary meta =
|
||||||
|
clientHelper.getMessageMetadataAsDictionary(txn, m);
|
||||||
|
Long messageType = meta.getOptionalLong(MSG_KEY_MSG_TYPE);
|
||||||
|
if (messageType != null && messageType != PRIVATE_MESSAGE)
|
||||||
|
throw new AssertionError("not supported");
|
||||||
|
headers = parseAttachmentHeaders(meta);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
// check if all attachments have been delivered
|
||||||
|
boolean allAttachmentsDelivered = true;
|
||||||
|
try {
|
||||||
|
for (AttachmentHeader h : headers) {
|
||||||
|
if (db.getMessageState(txn, h.getMessageId()) != DELIVERED)
|
||||||
|
throw new NoSuchMessageException();
|
||||||
|
}
|
||||||
|
} catch (NoSuchMessageException e) {
|
||||||
|
allAttachmentsDelivered = false;
|
||||||
|
}
|
||||||
|
// delete messages, if all attachments were delivered
|
||||||
|
if (allAttachmentsDelivered) {
|
||||||
|
for (AttachmentHeader h : headers) {
|
||||||
|
db.deleteMessage(txn, h.getMessageId());
|
||||||
|
db.deleteMessageMetadata(txn, h.getMessageId());
|
||||||
|
}
|
||||||
|
db.deleteMessage(txn, m);
|
||||||
|
db.deleteMessageMetadata(txn, m);
|
||||||
|
} else {
|
||||||
|
allDeleted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
||||||
|
recalculateGroupCount(txn, g);
|
||||||
|
return allDeleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recalculateGroupCount(Transaction txn, GroupId g)
|
||||||
|
throws DbException {
|
||||||
|
BdfDictionary query = BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_MSG_TYPE, PRIVATE_MESSAGE));
|
||||||
|
Map<MessageId, BdfDictionary> results;
|
||||||
|
try {
|
||||||
|
results =
|
||||||
|
clientHelper.getMessageMetadataAsDictionary(txn, g, query);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
int msgCount = results.size();
|
||||||
|
int unreadCount = 0;
|
||||||
|
for (Map.Entry<MessageId, BdfDictionary> entry : results.entrySet()) {
|
||||||
|
BdfDictionary meta = entry.getValue();
|
||||||
|
boolean read;
|
||||||
|
try {
|
||||||
|
read = meta.getBoolean(MSG_KEY_READ);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
if (!read) unreadCount++;
|
||||||
|
}
|
||||||
|
messageTracker.resetGroupCount(txn, g, msgCount, unreadCount);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -612,29 +612,82 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
|||||||
.getMessageMetadataAsDictionary(txn, contactGroupId, query);
|
.getMessageMetadataAsDictionary(txn, contactGroupId, query);
|
||||||
Map<GroupId, Visibility> m = new HashMap<>();
|
Map<GroupId, Visibility> m = new HashMap<>();
|
||||||
for (BdfDictionary d : results.values()) {
|
for (BdfDictionary d : results.values()) {
|
||||||
Role role = sessionParser.getRole(d);
|
Session s = sessionParser.parseSession(contactGroupId, d);
|
||||||
if (role == CREATOR) {
|
m.put(s.getPrivateGroupId(), s.getState().getVisibility());
|
||||||
CreatorSession s =
|
|
||||||
sessionParser.parseCreatorSession(contactGroupId, d);
|
|
||||||
m.put(s.getPrivateGroupId(), s.getState().getVisibility());
|
|
||||||
} else if (role == INVITEE) {
|
|
||||||
InviteeSession s =
|
|
||||||
sessionParser.parseInviteeSession(contactGroupId, d);
|
|
||||||
m.put(s.getPrivateGroupId(), s.getState().getVisibility());
|
|
||||||
} else if (role == PEER) {
|
|
||||||
PeerSession s =
|
|
||||||
sessionParser.parsePeerSession(contactGroupId, d);
|
|
||||||
m.put(s.getPrivateGroupId(), s.getState().getVisibility());
|
|
||||||
} else {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface DeletableSessionRetriever {
|
||||||
|
Map<GroupId, DeletableSession> getDeletableSessions(Transaction txn,
|
||||||
|
GroupId contactGroup, Map<MessageId, BdfDictionary> metadata)
|
||||||
|
throws DbException;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface MessageDeletionChecker {
|
||||||
|
/**
|
||||||
|
* This is called for all messages belonging to a session.
|
||||||
|
* It returns true if the given {@link MessageId} causes a problem
|
||||||
|
* so that the session can not be deleted.
|
||||||
|
*/
|
||||||
|
boolean causesProblem(MessageId messageId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteAllMessages(Transaction txn, ContactId c)
|
public boolean deleteAllMessages(Transaction txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
return deleteMessages(txn, c, (txn1, g, metadata) -> {
|
||||||
|
// 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 {
|
||||||
|
session = sessionParser.parseSession(g, d);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
sessions.put(session.getPrivateGroupId(),
|
||||||
|
new DeletableSession(session.getState()));
|
||||||
|
}
|
||||||
|
return sessions;
|
||||||
|
}, messageId -> false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteMessages(Transaction txn, ContactId c,
|
||||||
|
Set<MessageId> messageIds) throws DbException {
|
||||||
|
return deleteMessages(txn, c, (txn1, g, metadata) -> {
|
||||||
|
// get only sessions from given messageIds
|
||||||
|
Map<GroupId, DeletableSession> sessions = new HashMap<>();
|
||||||
|
for (MessageId messageId : messageIds) {
|
||||||
|
BdfDictionary d = metadata.get(messageId);
|
||||||
|
if (d == null) continue; // throw new NoSuchMessageException()
|
||||||
|
try {
|
||||||
|
MessageMetadata messageMetadata =
|
||||||
|
messageParser.parseMetadata(d);
|
||||||
|
SessionId sessionId =
|
||||||
|
getSessionId(messageMetadata.getPrivateGroupId());
|
||||||
|
StoredSession ss = getSession(txn1, g, sessionId);
|
||||||
|
if (ss == null) throw new DbException();
|
||||||
|
Session session = sessionParser
|
||||||
|
.parseSession(g, metadata.get(ss.storageId));
|
||||||
|
sessions.put(session.getPrivateGroupId(),
|
||||||
|
new DeletableSession(session.getState()));
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sessions;
|
||||||
|
// don't delete sessions if a message is not part of messageIds
|
||||||
|
}, messageId -> !messageIds.contains(messageId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages(Transaction txn, ContactId c,
|
||||||
|
DeletableSessionRetriever retriever, MessageDeletionChecker checker)
|
||||||
|
throws DbException {
|
||||||
// get ID of the contact group
|
// get ID of the contact group
|
||||||
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
||||||
|
|
||||||
@@ -647,26 +700,9 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
|||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all sessions and their states
|
// get sessions and their states from retriever
|
||||||
Map<GroupId, DeletableSession> sessions = new HashMap<>();
|
Map<GroupId, DeletableSession> sessions =
|
||||||
for (BdfDictionary d : metadata.values()) {
|
retriever.getDeletableSessions(txn, g, metadata);
|
||||||
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
|
// assign protocol messages to their sessions
|
||||||
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
||||||
@@ -685,7 +721,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
|||||||
|
|
||||||
// add visible messages to session
|
// add visible messages to session
|
||||||
DeletableSession session = sessions.get(m.getPrivateGroupId());
|
DeletableSession session = sessions.get(m.getPrivateGroupId());
|
||||||
session.messages.add(entry.getKey());
|
if (session != null) session.messages.add(entry.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
// get a set of all messages which were not ACKed by the contact
|
// get a set of all messages which were not ACKed by the contact
|
||||||
@@ -693,15 +729,15 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
|||||||
for (MessageStatus status : db.getMessageStatus(txn, c, g)) {
|
for (MessageStatus status : db.getMessageStatus(txn, c, g)) {
|
||||||
if (!status.isSeen()) notAcked.add(status.getMessageId());
|
if (!status.isSeen()) notAcked.add(status.getMessageId());
|
||||||
}
|
}
|
||||||
boolean allDeleted =
|
boolean allDeleted = deleteCompletedSessions(txn, sessions.values(),
|
||||||
deleteCompletedSessions(txn, sessions.values(), notAcked);
|
notAcked, checker);
|
||||||
recalculateGroupCount(txn, g);
|
recalculateGroupCount(txn, g);
|
||||||
return allDeleted;
|
return allDeleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean deleteCompletedSessions(Transaction txn,
|
private boolean deleteCompletedSessions(Transaction txn,
|
||||||
Collection<DeletableSession> sessions, Set<MessageId> notAcked)
|
Collection<DeletableSession> sessions, Set<MessageId> notAcked,
|
||||||
throws DbException {
|
MessageDeletionChecker checker) throws DbException {
|
||||||
// find completed sessions to delete
|
// find completed sessions to delete
|
||||||
boolean allDeleted = true;
|
boolean allDeleted = true;
|
||||||
for (DeletableSession session : sessions) {
|
for (DeletableSession session : sessions) {
|
||||||
@@ -713,7 +749,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
|||||||
// where delivery of all messages was confirmed (aka ACKed)
|
// where delivery of all messages was confirmed (aka ACKed)
|
||||||
boolean allAcked = true;
|
boolean allAcked = true;
|
||||||
for (MessageId m : session.messages) {
|
for (MessageId m : session.messages) {
|
||||||
if (notAcked.contains(m)) {
|
if (notAcked.contains(m) || checker.causesProblem(m)) {
|
||||||
allAcked = false;
|
allAcked = false;
|
||||||
allDeleted = false;
|
allDeleted = false;
|
||||||
break;
|
break;
|
||||||
@@ -729,7 +765,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
|||||||
return allDeleted;
|
return allDeleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<MessageId> getMessageIds(Transaction txn, ContactId c)
|
@Override
|
||||||
|
public Set<MessageId> getMessageIds(Transaction txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
||||||
BdfDictionary query = messageParser.getMessagesVisibleInUiQuery();
|
BdfDictionary query = messageParser.getMessagesVisibleInUiQuery();
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ interface SessionParser {
|
|||||||
|
|
||||||
boolean isSession(BdfDictionary d);
|
boolean isSession(BdfDictionary d);
|
||||||
|
|
||||||
|
Session parseSession(GroupId contactGroupId, BdfDictionary d)
|
||||||
|
throws FormatException;
|
||||||
|
|
||||||
CreatorSession parseCreatorSession(GroupId contactGroupId, BdfDictionary d)
|
CreatorSession parseCreatorSession(GroupId contactGroupId, BdfDictionary d)
|
||||||
throws FormatException;
|
throws FormatException;
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,21 @@ class SessionParserImpl implements SessionParser {
|
|||||||
return d.getBoolean(SESSION_KEY_IS_SESSION, false);
|
return d.getBoolean(SESSION_KEY_IS_SESSION, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Session parseSession(GroupId contactGroupId, BdfDictionary d)
|
||||||
|
throws FormatException {
|
||||||
|
Session session;
|
||||||
|
Role role = getRole(d);
|
||||||
|
if (role == CREATOR) {
|
||||||
|
session = parseCreatorSession(contactGroupId, d);
|
||||||
|
} else if (role == INVITEE) {
|
||||||
|
session = parseInviteeSession(contactGroupId, d);
|
||||||
|
} else if (role == PEER) {
|
||||||
|
session = parsePeerSession(contactGroupId, d);
|
||||||
|
} else throw new AssertionError("unknown role");
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CreatorSession parseCreatorSession(GroupId contactGroupId,
|
public CreatorSession parseCreatorSession(GroupId contactGroupId,
|
||||||
BdfDictionary d) throws FormatException {
|
BdfDictionary d) throws FormatException {
|
||||||
|
|||||||
@@ -539,13 +539,80 @@ abstract class SharingManagerImpl<S extends Shareable>
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface DeletableSessionRetriever {
|
||||||
|
Map<GroupId, DeletableSession> getDeletableSessions(Transaction txn,
|
||||||
|
GroupId contactGroup, Map<MessageId, BdfDictionary> metadata)
|
||||||
|
throws DbException;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface MessageDeletionChecker {
|
||||||
|
/**
|
||||||
|
* This is called for all messages belonging to a session.
|
||||||
|
* It returns true if the given {@link MessageId} causes a problem
|
||||||
|
* so that the session can not be deleted.
|
||||||
|
*/
|
||||||
|
boolean causesProblem(MessageId messageId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteAllMessages(Transaction txn, ContactId c)
|
public boolean deleteAllMessages(Transaction txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
return deleteMessages(txn, c, (txn1, contactGroup, metadata) -> {
|
||||||
|
// 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 {
|
||||||
|
session = sessionParser.parseSession(contactGroup, d);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
sessions.put(session.getShareableId(),
|
||||||
|
new DeletableSession(session.getState()));
|
||||||
|
}
|
||||||
|
return sessions;
|
||||||
|
}, messageId -> false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteMessages(Transaction txn, ContactId c,
|
||||||
|
Set<MessageId> messageIds) throws DbException {
|
||||||
|
return deleteMessages(txn, c, (txn1, g, metadata) -> {
|
||||||
|
// get only sessions from given messageIds
|
||||||
|
Map<GroupId, DeletableSession> sessions = new HashMap<>();
|
||||||
|
for (MessageId messageId : messageIds) {
|
||||||
|
BdfDictionary d = metadata.get(messageId);
|
||||||
|
if (d == null) continue; // throw new NoSuchMessageException()
|
||||||
|
try {
|
||||||
|
MessageMetadata messageMetadata =
|
||||||
|
messageParser.parseMetadata(d);
|
||||||
|
SessionId sessionId =
|
||||||
|
getSessionId(messageMetadata.getShareableId());
|
||||||
|
StoredSession ss = getSession(txn1, g, sessionId);
|
||||||
|
if (ss == null) throw new DbException();
|
||||||
|
Session session = sessionParser
|
||||||
|
.parseSession(g, metadata.get(ss.storageId));
|
||||||
|
sessions.put(session.getShareableId(),
|
||||||
|
new DeletableSession(session.getState()));
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sessions;
|
||||||
|
// don't delete sessions if a message is not part of messageIds
|
||||||
|
}, messageId -> !messageIds.contains(messageId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages(Transaction txn, ContactId c,
|
||||||
|
DeletableSessionRetriever retriever, MessageDeletionChecker checker)
|
||||||
|
throws DbException {
|
||||||
// get ID of the contact group
|
// get ID of the contact group
|
||||||
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
||||||
|
|
||||||
// get metadata for all messages in the
|
// get metadata for all messages in the group
|
||||||
// (these are sessions *and* protocol messages)
|
// (these are sessions *and* protocol messages)
|
||||||
Map<MessageId, BdfDictionary> metadata;
|
Map<MessageId, BdfDictionary> metadata;
|
||||||
try {
|
try {
|
||||||
@@ -555,18 +622,8 @@ abstract class SharingManagerImpl<S extends Shareable>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get all sessions and their states
|
// get all sessions and their states
|
||||||
Map<GroupId, DeletableSession> sessions = new HashMap<>();
|
Map<GroupId, DeletableSession> sessions =
|
||||||
for (BdfDictionary d : metadata.values()) {
|
retriever.getDeletableSessions(txn, g, metadata);
|
||||||
if (!sessionParser.isSession(d)) continue;
|
|
||||||
Session session;
|
|
||||||
try {
|
|
||||||
session = sessionParser.parseSession(g, d);
|
|
||||||
} catch (FormatException e) {
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
sessions.put(session.getShareableId(),
|
|
||||||
new DeletableSession(session.getState()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// assign protocol messages to their sessions
|
// assign protocol messages to their sessions
|
||||||
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
||||||
@@ -585,7 +642,7 @@ abstract class SharingManagerImpl<S extends Shareable>
|
|||||||
|
|
||||||
// add visible messages to session
|
// add visible messages to session
|
||||||
DeletableSession session = sessions.get(m.getShareableId());
|
DeletableSession session = sessions.get(m.getShareableId());
|
||||||
session.messages.add(entry.getKey());
|
if (session != null) session.messages.add(entry.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
// get a set of all messages which were not ACKed by the contact
|
// get a set of all messages which were not ACKed by the contact
|
||||||
@@ -593,15 +650,15 @@ abstract class SharingManagerImpl<S extends Shareable>
|
|||||||
for (MessageStatus status : db.getMessageStatus(txn, c, g)) {
|
for (MessageStatus status : db.getMessageStatus(txn, c, g)) {
|
||||||
if (!status.isSeen()) notAcked.add(status.getMessageId());
|
if (!status.isSeen()) notAcked.add(status.getMessageId());
|
||||||
}
|
}
|
||||||
boolean allDeleted =
|
boolean allDeleted = deleteCompletedSessions(txn, sessions.values(),
|
||||||
deleteCompletedSessions(txn, sessions.values(), notAcked);
|
notAcked, checker);
|
||||||
recalculateGroupCount(txn, g);
|
recalculateGroupCount(txn, g);
|
||||||
return allDeleted;
|
return allDeleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean deleteCompletedSessions(Transaction txn,
|
private boolean deleteCompletedSessions(Transaction txn,
|
||||||
Collection<DeletableSession> sessions, Set<MessageId> notAcked)
|
Collection<DeletableSession> sessions, Set<MessageId> notAcked,
|
||||||
throws DbException {
|
MessageDeletionChecker checker) throws DbException {
|
||||||
// find completed sessions to delete
|
// find completed sessions to delete
|
||||||
boolean allDeleted = true;
|
boolean allDeleted = true;
|
||||||
for (DeletableSession session : sessions) {
|
for (DeletableSession session : sessions) {
|
||||||
@@ -613,7 +670,7 @@ abstract class SharingManagerImpl<S extends Shareable>
|
|||||||
// where delivery of all messages was confirmed (aka ACKed)
|
// where delivery of all messages was confirmed (aka ACKed)
|
||||||
boolean allAcked = true;
|
boolean allAcked = true;
|
||||||
for (MessageId m : session.messages) {
|
for (MessageId m : session.messages) {
|
||||||
if (notAcked.contains(m)) {
|
if (notAcked.contains(m) || checker.causesProblem(m)) {
|
||||||
allAcked = false;
|
allAcked = false;
|
||||||
allDeleted = false;
|
allDeleted = false;
|
||||||
break;
|
break;
|
||||||
@@ -629,7 +686,8 @@ abstract class SharingManagerImpl<S extends Shareable>
|
|||||||
return allDeleted;
|
return allDeleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<MessageId> getMessageIds(Transaction txn, ContactId c)
|
@Override
|
||||||
|
public Set<MessageId> getMessageIds(Transaction txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
||||||
BdfDictionary query = messageParser.getMessagesVisibleInUiQuery();
|
BdfDictionary query = messageParser.getMessagesVisibleInUiQuery();
|
||||||
|
|||||||
@@ -39,8 +39,11 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptySet;
|
||||||
import static org.briarproject.bramble.test.TestPluginConfigModule.SIMPLEX_TRANSPORT_ID;
|
import static org.briarproject.bramble.test.TestPluginConfigModule.SIMPLEX_TRANSPORT_ID;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
@@ -82,6 +85,8 @@ public class IntroductionIntegrationTest
|
|||||||
private IntroduceeListener listener1;
|
private IntroduceeListener listener1;
|
||||||
private IntroduceeListener listener2;
|
private IntroduceeListener listener2;
|
||||||
|
|
||||||
|
private Group g1, g2;
|
||||||
|
|
||||||
interface StateVisitor {
|
interface StateVisitor {
|
||||||
AcceptMessage visit(AcceptMessage response);
|
AcceptMessage visit(AcceptMessage response);
|
||||||
}
|
}
|
||||||
@@ -95,6 +100,9 @@ public class IntroductionIntegrationTest
|
|||||||
introductionManager1 = c1.getIntroductionManager();
|
introductionManager1 = c1.getIntroductionManager();
|
||||||
introductionManager2 = c2.getIntroductionManager();
|
introductionManager2 = c2.getIntroductionManager();
|
||||||
|
|
||||||
|
g1 = introductionManager0.getContactGroup(contact1From0);
|
||||||
|
g2 = introductionManager0.getContactGroup(contact2From0);
|
||||||
|
|
||||||
// initialize waiter fresh for each test
|
// initialize waiter fresh for each test
|
||||||
eventWaiter = new Waiter();
|
eventWaiter = new Waiter();
|
||||||
|
|
||||||
@@ -1210,7 +1218,6 @@ public class IntroductionIntegrationTest
|
|||||||
assertTrue(listener2.succeeded);
|
assertTrue(listener2.succeeded);
|
||||||
|
|
||||||
// check that introducer messages are tracked properly
|
// check that introducer messages are tracked properly
|
||||||
Group g1 = introductionManager0.getContactGroup(contact1From0);
|
|
||||||
assertGroupCount(messageTracker0, g1.getId(), 2, 1);
|
assertGroupCount(messageTracker0, g1.getId(), 2, 1);
|
||||||
|
|
||||||
// introducer can now remove messages
|
// introducer can now remove messages
|
||||||
@@ -1224,8 +1231,7 @@ public class IntroductionIntegrationTest
|
|||||||
assertEquals(2, getMessages0From1().size());
|
assertEquals(2, getMessages0From1().size());
|
||||||
|
|
||||||
// check that introducee1 messages are tracked properly
|
// check that introducee1 messages are tracked properly
|
||||||
Group g0 = introductionManager1.getContactGroup(contact0From1);
|
assertGroupCount(messageTracker1, g1.getId(), 2, 1);
|
||||||
assertGroupCount(messageTracker1, g0.getId(), 2, 1);
|
|
||||||
|
|
||||||
// ACK last message
|
// ACK last message
|
||||||
sendAcks(c0, c1, contactId1From0, 1);
|
sendAcks(c0, c1, contactId1From0, 1);
|
||||||
@@ -1234,17 +1240,16 @@ public class IntroductionIntegrationTest
|
|||||||
assertTrue(deleteAllMessages0From1());
|
assertTrue(deleteAllMessages0From1());
|
||||||
assertEquals(0, getMessages0From1().size());
|
assertEquals(0, getMessages0From1().size());
|
||||||
assertTrue(deleteAllMessages0From1()); // a second time returns true
|
assertTrue(deleteAllMessages0From1()); // a second time returns true
|
||||||
assertGroupCount(messageTracker1, g0.getId(), 0, 0);
|
assertGroupCount(messageTracker1, g1.getId(), 0, 0);
|
||||||
|
|
||||||
// check that introducee2 messages are tracked properly
|
// check that introducee2 messages are tracked properly
|
||||||
Group g0From2 = introductionManager2.getContactGroup(contact0From2);
|
assertGroupCount(messageTracker2, g2.getId(), 2, 1);
|
||||||
assertGroupCount(messageTracker2, g0From2.getId(), 2, 1);
|
|
||||||
|
|
||||||
// introducee2 can remove messages (last message was incoming)
|
// introducee2 can remove messages (last message was incoming)
|
||||||
assertTrue(deleteAllMessages0From2());
|
assertTrue(deleteAllMessages0From2());
|
||||||
assertEquals(0, getMessages0From2().size());
|
assertEquals(0, getMessages0From2().size());
|
||||||
assertTrue(deleteAllMessages0From2()); // a second time returns true
|
assertTrue(deleteAllMessages0From2()); // a second time returns true
|
||||||
assertGroupCount(messageTracker2, g0From2.getId(), 0, 0);
|
assertGroupCount(messageTracker2, g2.getId(), 0, 0);
|
||||||
|
|
||||||
// a new introduction is still possible
|
// a new introduction is still possible
|
||||||
assertTrue(introductionManager0
|
assertTrue(introductionManager0
|
||||||
@@ -1273,8 +1278,8 @@ public class IntroductionIntegrationTest
|
|||||||
|
|
||||||
// group counts get counted up again correctly
|
// group counts get counted up again correctly
|
||||||
assertGroupCount(messageTracker0, g1.getId(), 2, 1);
|
assertGroupCount(messageTracker0, g1.getId(), 2, 1);
|
||||||
assertGroupCount(messageTracker1, g0.getId(), 2, 1);
|
assertGroupCount(messageTracker1, g1.getId(), 2, 1);
|
||||||
assertGroupCount(messageTracker2, g0From2.getId(), 2, 1);
|
assertGroupCount(messageTracker2, g2.getId(), 2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1434,6 +1439,107 @@ public class IntroductionIntegrationTest
|
|||||||
assertFalse(listener2.aborted);
|
assertFalse(listener2.aborted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletingSomeMessages() throws Exception {
|
||||||
|
addListeners(false, false);
|
||||||
|
|
||||||
|
// make introduction
|
||||||
|
long time = clock.currentTimeMillis();
|
||||||
|
introductionManager0.makeIntroduction(contact1From0, contact2From0,
|
||||||
|
"Hi!", time);
|
||||||
|
|
||||||
|
// deleting the introduction for introducee1 will fail
|
||||||
|
Collection<ConversationMessageHeader> m1From0 = getMessages1From0();
|
||||||
|
assertEquals(1, m1From0.size());
|
||||||
|
MessageId messageId1 = m1From0.iterator().next().getId();
|
||||||
|
Set<MessageId> toDelete1 = new HashSet<>();
|
||||||
|
toDelete1.add(messageId1);
|
||||||
|
assertFalse(deleteMessages1From0(toDelete1));
|
||||||
|
|
||||||
|
// deleting the introduction for introducee2 will fail as well
|
||||||
|
Collection<ConversationMessageHeader> m2From0 = getMessages2From0();
|
||||||
|
assertEquals(1, m2From0.size());
|
||||||
|
MessageId messageId2 = m2From0.iterator().next().getId();
|
||||||
|
Set<MessageId> toDelete2 = new HashSet<>();
|
||||||
|
toDelete2.add(messageId2);
|
||||||
|
assertFalse(deleteMessages2From0(toDelete2));
|
||||||
|
|
||||||
|
// sync REQUEST messages
|
||||||
|
sync0To1(1, true);
|
||||||
|
eventWaiter.await(TIMEOUT, 1);
|
||||||
|
sync0To2(1, true);
|
||||||
|
eventWaiter.await(TIMEOUT, 1);
|
||||||
|
|
||||||
|
// deleting introduction fails for introducees,
|
||||||
|
// because response is not yet selected for deletion
|
||||||
|
assertFalse(deleteMessages0From1(toDelete1));
|
||||||
|
assertFalse(deleteMessages0From2(toDelete2));
|
||||||
|
|
||||||
|
// add response of introducee1 for deletion
|
||||||
|
Collection<ConversationMessageHeader> m0From1 = getMessages0From1();
|
||||||
|
assertEquals(2, m0From1.size());
|
||||||
|
for (ConversationMessageHeader h : m0From1) {
|
||||||
|
if (!h.getId().equals(messageId1)) toDelete1.add(h.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// add response of introducee2 for deletion
|
||||||
|
Collection<ConversationMessageHeader> m0From2 = getMessages0From2();
|
||||||
|
assertEquals(2, m0From2.size());
|
||||||
|
for (ConversationMessageHeader h : m0From2) {
|
||||||
|
if (!h.getId().equals(messageId2)) toDelete2.add(h.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// sync first DECLINE message
|
||||||
|
sync1To0(1, true);
|
||||||
|
eventWaiter.await(TIMEOUT, 1);
|
||||||
|
|
||||||
|
// introducer can not yet remove messages
|
||||||
|
assertFalse(deleteMessages1From0(toDelete1));
|
||||||
|
// introducee1 can not yet remove messages
|
||||||
|
assertFalse(deleteMessages0From1(toDelete1));
|
||||||
|
|
||||||
|
// sync second DECLINE message
|
||||||
|
sync2To0(1, true);
|
||||||
|
eventWaiter.await(TIMEOUT, 1);
|
||||||
|
|
||||||
|
// check group counts
|
||||||
|
assertGroupCount(messageTracker0, g1.getId(), 2, 1);
|
||||||
|
assertGroupCount(messageTracker0, g2.getId(), 2, 1);
|
||||||
|
assertGroupCount(messageTracker1, g1.getId(), 2, 1);
|
||||||
|
assertGroupCount(messageTracker2, g2.getId(), 2, 1);
|
||||||
|
|
||||||
|
// introducer can now remove messages with both introducees
|
||||||
|
assertTrue(deleteMessages1From0(toDelete1));
|
||||||
|
assertTrue(deleteMessages2From0(toDelete2));
|
||||||
|
assertGroupCount(messageTracker0, g1.getId(), 0, 0);
|
||||||
|
assertGroupCount(messageTracker0, g2.getId(), 0, 0);
|
||||||
|
// introducee2 can not yet remove messages, missing the other response
|
||||||
|
assertFalse(deleteMessages0From1(toDelete1));
|
||||||
|
|
||||||
|
// forward first DECLINE message
|
||||||
|
sync0To2(1, true);
|
||||||
|
|
||||||
|
// introducee2 can now remove messages
|
||||||
|
assertTrue(deleteMessages0From2(toDelete2));
|
||||||
|
assertEquals(0, getMessages0From2().size());
|
||||||
|
assertTrue(deleteMessages0From2(toDelete2)); // a second time nothing happens
|
||||||
|
assertGroupCount(messageTracker2, g2.getId(), 0, 0);
|
||||||
|
|
||||||
|
// forward second DECLINE message
|
||||||
|
sync0To1(1, true);
|
||||||
|
|
||||||
|
// introducee1 can now also remove messages
|
||||||
|
assertTrue(deleteMessages0From1(toDelete1));
|
||||||
|
assertEquals(0, getMessages0From1().size());
|
||||||
|
assertTrue(deleteMessages0From1(toDelete1)); // a second time nothing happens
|
||||||
|
assertGroupCount(messageTracker1, g1.getId(), 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletingEmptySet() throws Exception {
|
||||||
|
assertTrue(deleteMessages0From1(emptySet()));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean deleteAllMessages1From0() throws DbException {
|
private boolean deleteAllMessages1From0() throws DbException {
|
||||||
return db0.transactionWithResult(false, txn -> introductionManager0
|
return db0.transactionWithResult(false, txn -> introductionManager0
|
||||||
.deleteAllMessages(txn, contactId1From0));
|
.deleteAllMessages(txn, contactId1From0));
|
||||||
@@ -1454,6 +1560,30 @@ public class IntroductionIntegrationTest
|
|||||||
.deleteAllMessages(txn, contactId0From2));
|
.deleteAllMessages(txn, contactId0From2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages1From0(Set<MessageId> toDelete)
|
||||||
|
throws DbException {
|
||||||
|
return db0.transactionWithResult(false, txn -> introductionManager0
|
||||||
|
.deleteMessages(txn, contactId1From0, toDelete));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages2From0(Set<MessageId> toDelete)
|
||||||
|
throws DbException {
|
||||||
|
return db0.transactionWithResult(false, txn -> introductionManager0
|
||||||
|
.deleteMessages(txn, contactId2From0, toDelete));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages0From1(Set<MessageId> toDelete)
|
||||||
|
throws DbException {
|
||||||
|
return db1.transactionWithResult(false, txn -> introductionManager1
|
||||||
|
.deleteMessages(txn, contactId0From1, toDelete));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages0From2(Set<MessageId> toDelete)
|
||||||
|
throws DbException {
|
||||||
|
return db2.transactionWithResult(false, txn -> introductionManager2
|
||||||
|
.deleteMessages(txn, contactId0From2, toDelete));
|
||||||
|
}
|
||||||
|
|
||||||
private void addTransportProperties() throws Exception {
|
private void addTransportProperties() throws Exception {
|
||||||
TransportPropertyManager tpm0 = c0.getTransportPropertyManager();
|
TransportPropertyManager tpm0 = c0.getTransportPropertyManager();
|
||||||
TransportPropertyManager tpm1 = c1.getTransportPropertyManager();
|
TransportPropertyManager tpm1 = c1.getTransportPropertyManager();
|
||||||
|
|||||||
@@ -0,0 +1,351 @@
|
|||||||
|
package org.briarproject.briar.messaging;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.bramble.api.db.MessageDeletedException;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
||||||
|
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
||||||
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
|
import org.briarproject.briar.api.messaging.PrivateMessage;
|
||||||
|
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
||||||
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||||
|
import org.briarproject.briar.test.BriarIntegrationTest;
|
||||||
|
import org.briarproject.briar.test.BriarIntegrationTestComponent;
|
||||||
|
import org.briarproject.briar.test.DaggerBriarIntegrationTestComponent;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.Collections.emptySet;
|
||||||
|
import static java.util.Collections.singleton;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
|
||||||
|
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
|
import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class MessagingManagerIntegrationTest
|
||||||
|
extends BriarIntegrationTest<BriarIntegrationTestComponent> {
|
||||||
|
|
||||||
|
private DatabaseComponent db0, db1;
|
||||||
|
private MessagingManager messagingManager0, messagingManager1;
|
||||||
|
private PrivateMessageFactory messageFactory;
|
||||||
|
private ContactId contactId;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
db0 = c0.getDatabaseComponent();
|
||||||
|
db1 = c1.getDatabaseComponent();
|
||||||
|
messagingManager0 = c0.getMessagingManager();
|
||||||
|
messagingManager1 = c1.getMessagingManager();
|
||||||
|
messageFactory = c0.getPrivateMessageFactory();
|
||||||
|
assertEquals(contactId0From1, contactId1From0);
|
||||||
|
contactId = contactId0From1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createComponents() {
|
||||||
|
BriarIntegrationTestComponent component =
|
||||||
|
DaggerBriarIntegrationTestComponent.builder().build();
|
||||||
|
component.injectBriarEagerSingletons();
|
||||||
|
component.inject(this);
|
||||||
|
|
||||||
|
c0 = DaggerBriarIntegrationTestComponent.builder()
|
||||||
|
.testDatabaseConfigModule(new TestDatabaseConfigModule(t0Dir))
|
||||||
|
.build();
|
||||||
|
c0.injectBriarEagerSingletons();
|
||||||
|
|
||||||
|
c1 = DaggerBriarIntegrationTestComponent.builder()
|
||||||
|
.testDatabaseConfigModule(new TestDatabaseConfigModule(t1Dir))
|
||||||
|
.build();
|
||||||
|
c1.injectBriarEagerSingletons();
|
||||||
|
|
||||||
|
c2 = DaggerBriarIntegrationTestComponent.builder()
|
||||||
|
.testDatabaseConfigModule(new TestDatabaseConfigModule(t2Dir))
|
||||||
|
.build();
|
||||||
|
c2.injectBriarEagerSingletons();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleConversation() throws Exception {
|
||||||
|
// conversation start out empty
|
||||||
|
Collection<ConversationMessageHeader> messages0 = getMessages(c0);
|
||||||
|
Collection<ConversationMessageHeader> messages1 = getMessages(c1);
|
||||||
|
assertEquals(0, messages0.size());
|
||||||
|
assertEquals(0, messages1.size());
|
||||||
|
|
||||||
|
// message is sent/displayed properly
|
||||||
|
String text = getRandomString(42);
|
||||||
|
sendMessage(c0, c1, text);
|
||||||
|
messages0 = getMessages(c0);
|
||||||
|
messages1 = getMessages(c1);
|
||||||
|
assertEquals(1, messages0.size());
|
||||||
|
assertEquals(1, messages1.size());
|
||||||
|
PrivateMessageHeader m0 =
|
||||||
|
(PrivateMessageHeader) messages0.iterator().next();
|
||||||
|
PrivateMessageHeader m1 =
|
||||||
|
(PrivateMessageHeader) messages1.iterator().next();
|
||||||
|
assertTrue(m0.hasText());
|
||||||
|
assertTrue(m1.hasText());
|
||||||
|
assertTrue(m0.isRead());
|
||||||
|
assertFalse(m1.isRead());
|
||||||
|
assertGroupCounts(c0, 1, 0);
|
||||||
|
assertGroupCounts(c1, 1, 1);
|
||||||
|
|
||||||
|
// same for reply
|
||||||
|
String text2 = getRandomString(42);
|
||||||
|
sendMessage(c1, c0, text2);
|
||||||
|
messages0 = getMessages(c0);
|
||||||
|
messages1 = getMessages(c1);
|
||||||
|
assertEquals(2, messages0.size());
|
||||||
|
assertEquals(2, messages1.size());
|
||||||
|
assertGroupCounts(c0, 2, 1);
|
||||||
|
assertGroupCounts(c1, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAttachments() throws Exception {
|
||||||
|
// send message with attachment
|
||||||
|
AttachmentHeader h = addAttachment(c0);
|
||||||
|
sendMessage(c0, c1, null, singletonList(h));
|
||||||
|
|
||||||
|
// message with attachment is sent/displayed properly
|
||||||
|
Collection<ConversationMessageHeader> messages0 = getMessages(c0);
|
||||||
|
Collection<ConversationMessageHeader> messages1 = getMessages(c1);
|
||||||
|
assertEquals(1, messages0.size());
|
||||||
|
assertEquals(1, messages1.size());
|
||||||
|
PrivateMessageHeader m0 =
|
||||||
|
(PrivateMessageHeader) messages0.iterator().next();
|
||||||
|
PrivateMessageHeader m1 =
|
||||||
|
(PrivateMessageHeader) messages1.iterator().next();
|
||||||
|
assertFalse(m0.hasText());
|
||||||
|
assertFalse(m1.hasText());
|
||||||
|
assertEquals(1, m0.getAttachmentHeaders().size());
|
||||||
|
assertEquals(1, m1.getAttachmentHeaders().size());
|
||||||
|
assertGroupCounts(c0, 1, 0);
|
||||||
|
assertGroupCounts(c1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteAll() throws Exception {
|
||||||
|
// send 3 message (1 with attachment)
|
||||||
|
sendMessage(c0, c1, getRandomString(42));
|
||||||
|
sendMessage(c0, c1, getRandomString(23));
|
||||||
|
sendMessage(c0, c1, null, singletonList(addAttachment(c0)));
|
||||||
|
assertEquals(3, getMessages(c0).size());
|
||||||
|
assertEquals(3, getMessages(c1).size());
|
||||||
|
assertGroupCounts(c0, 3, 0);
|
||||||
|
assertGroupCounts(c1, 3, 3);
|
||||||
|
|
||||||
|
// delete all messages on both sides (deletes all, because returns true)
|
||||||
|
assertTrue(db0.transactionWithResult(false,
|
||||||
|
txn -> messagingManager0.deleteAllMessages(txn, contactId)));
|
||||||
|
assertTrue(db1.transactionWithResult(false,
|
||||||
|
txn -> messagingManager1.deleteAllMessages(txn, contactId)));
|
||||||
|
|
||||||
|
// all messages are gone
|
||||||
|
assertEquals(0, getMessages(c0).size());
|
||||||
|
assertEquals(0, getMessages(c1).size());
|
||||||
|
assertGroupCounts(c0, 0, 0);
|
||||||
|
assertGroupCounts(c1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteSubset() throws Exception {
|
||||||
|
// send 3 message (1 with attachment)
|
||||||
|
PrivateMessage m0 = sendMessage(c0, c1, getRandomString(42));
|
||||||
|
PrivateMessage m1 = sendMessage(c0, c1, getRandomString(23));
|
||||||
|
PrivateMessage m2 =
|
||||||
|
sendMessage(c0, c1, null, singletonList(addAttachment(c0)));
|
||||||
|
assertGroupCounts(c0, 3, 0);
|
||||||
|
assertGroupCounts(c1, 3, 3);
|
||||||
|
|
||||||
|
// delete 2 messages on both sides (deletes all, because returns true)
|
||||||
|
Set<MessageId> toDelete = new HashSet<>();
|
||||||
|
toDelete.add(m1.getMessage().getId());
|
||||||
|
toDelete.add(m2.getMessage().getId());
|
||||||
|
assertTrue(db0.transactionWithResult(false, txn ->
|
||||||
|
messagingManager0.deleteMessages(txn, contactId, toDelete)));
|
||||||
|
assertTrue(db1.transactionWithResult(false, txn ->
|
||||||
|
messagingManager1.deleteMessages(txn, contactId, toDelete)));
|
||||||
|
|
||||||
|
// all messages except 1 are gone
|
||||||
|
assertEquals(1, getMessages(c0).size());
|
||||||
|
assertEquals(1, getMessages(c1).size());
|
||||||
|
assertEquals(m0.getMessage().getId(),
|
||||||
|
getMessages(c0).iterator().next().getId());
|
||||||
|
assertEquals(m0.getMessage().getId(),
|
||||||
|
getMessages(c1).iterator().next().getId());
|
||||||
|
assertGroupCounts(c0, 1, 0);
|
||||||
|
assertGroupCounts(c1, 1, 1);
|
||||||
|
|
||||||
|
// remove also last message
|
||||||
|
toDelete.clear();
|
||||||
|
toDelete.add(m0.getMessage().getId());
|
||||||
|
assertTrue(db0.transactionWithResult(false, txn ->
|
||||||
|
messagingManager0.deleteMessages(txn, contactId, toDelete)));
|
||||||
|
assertEquals(0, getMessages(c0).size());
|
||||||
|
assertGroupCounts(c0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteAttachment() throws Exception {
|
||||||
|
// send one message with attachment
|
||||||
|
AttachmentHeader h = addAttachment(c0);
|
||||||
|
sendMessage(c0, c1, getRandomString(42), singletonList(h));
|
||||||
|
|
||||||
|
// attachment exists on both devices
|
||||||
|
db0.transaction(true, txn -> db0.getMessage(txn, h.getMessageId()));
|
||||||
|
db1.transaction(true, txn -> db1.getMessage(txn, h.getMessageId()));
|
||||||
|
|
||||||
|
// delete message on both sides (deletes all, because returns true)
|
||||||
|
assertTrue(db0.transactionWithResult(false,
|
||||||
|
txn -> messagingManager0.deleteAllMessages(txn, contactId)));
|
||||||
|
assertTrue(db1.transactionWithResult(false,
|
||||||
|
txn -> messagingManager1.deleteAllMessages(txn, contactId)));
|
||||||
|
|
||||||
|
// attachment was deleted on both devices
|
||||||
|
try {
|
||||||
|
db0.transaction(true, txn -> db0.getMessage(txn, h.getMessageId()));
|
||||||
|
fail();
|
||||||
|
} catch (MessageDeletedException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
db1.transaction(true, txn -> db1.getMessage(txn, h.getMessageId()));
|
||||||
|
fail();
|
||||||
|
} catch (MessageDeletedException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteSomeAttachment() throws Exception {
|
||||||
|
// send one message with attachment
|
||||||
|
AttachmentHeader h = addAttachment(c0);
|
||||||
|
PrivateMessage m =
|
||||||
|
sendMessage(c0, c1, getRandomString(42), singletonList(h));
|
||||||
|
|
||||||
|
// attachment exists on both devices, state is set to PENDING
|
||||||
|
db0.transaction(false, txn -> {
|
||||||
|
db0.getMessage(txn, h.getMessageId());
|
||||||
|
db0.setMessageState(txn, h.getMessageId(), PENDING);
|
||||||
|
});
|
||||||
|
db1.transaction(false, txn -> {
|
||||||
|
db1.getMessage(txn, h.getMessageId());
|
||||||
|
db1.setMessageState(txn, h.getMessageId(), PENDING);
|
||||||
|
});
|
||||||
|
|
||||||
|
// deleting message fails (on both sides),
|
||||||
|
// because attachment is not yet delivered
|
||||||
|
Set<MessageId> toDelete = singleton(m.getMessage().getId());
|
||||||
|
assertFalse(db0.transactionWithResult(false, txn ->
|
||||||
|
messagingManager0.deleteMessages(txn, contactId, toDelete)));
|
||||||
|
assertFalse(db1.transactionWithResult(false, txn ->
|
||||||
|
messagingManager1.deleteMessages(txn, contactId, toDelete)));
|
||||||
|
|
||||||
|
// deliver attachment
|
||||||
|
db0.transaction(false,
|
||||||
|
txn -> db0.setMessageState(txn, h.getMessageId(), DELIVERED));
|
||||||
|
db1.transaction(false,
|
||||||
|
txn -> db1.setMessageState(txn, h.getMessageId(), DELIVERED));
|
||||||
|
|
||||||
|
// deleting message and attachment on both sides works now
|
||||||
|
assertTrue(db0.transactionWithResult(false, txn ->
|
||||||
|
messagingManager0.deleteMessages(txn, contactId, toDelete)));
|
||||||
|
assertTrue(db1.transactionWithResult(false, txn ->
|
||||||
|
messagingManager1.deleteMessages(txn, contactId, toDelete)));
|
||||||
|
|
||||||
|
// attachment was deleted on both devices
|
||||||
|
try {
|
||||||
|
db0.transaction(true, txn -> db0.getMessage(txn, h.getMessageId()));
|
||||||
|
fail();
|
||||||
|
} catch (MessageDeletedException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
db1.transaction(true, txn -> db1.getMessage(txn, h.getMessageId()));
|
||||||
|
fail();
|
||||||
|
} catch (MessageDeletedException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletingEmptySet() throws Exception {
|
||||||
|
assertTrue(db0.transactionWithResult(false, txn ->
|
||||||
|
messagingManager0.deleteMessages(txn, contactId, emptySet())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrivateMessage sendMessage(BriarIntegrationTestComponent from,
|
||||||
|
BriarIntegrationTestComponent to, String text)
|
||||||
|
throws Exception {
|
||||||
|
return sendMessage(from, to, text, emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrivateMessage sendMessage(BriarIntegrationTestComponent from,
|
||||||
|
BriarIntegrationTestComponent to, @Nullable String text,
|
||||||
|
List<AttachmentHeader> attachments) throws Exception {
|
||||||
|
GroupId g = from.getMessagingManager().getConversationId(contactId);
|
||||||
|
PrivateMessage m = messageFactory.createPrivateMessage(g,
|
||||||
|
clock.currentTimeMillis(), text, attachments);
|
||||||
|
from.getMessagingManager().addLocalMessage(m);
|
||||||
|
syncMessage(from, to, contactId, 1 + attachments.size(), true);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AttachmentHeader addAttachment(BriarIntegrationTestComponent c)
|
||||||
|
throws Exception {
|
||||||
|
GroupId g = c.getMessagingManager().getConversationId(contactId);
|
||||||
|
InputStream stream = new ByteArrayInputStream(getRandomBytes(42));
|
||||||
|
return c.getMessagingManager().addLocalAttachment(g,
|
||||||
|
clock.currentTimeMillis(), "image/jpeg", stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<ConversationMessageHeader> getMessages(
|
||||||
|
BriarIntegrationTestComponent c)
|
||||||
|
throws Exception {
|
||||||
|
Collection<ConversationMessageHeader> messages =
|
||||||
|
c.getDatabaseComponent().transactionWithResult(true,
|
||||||
|
txn -> c.getMessagingManager()
|
||||||
|
.getMessageHeaders(txn, contactId));
|
||||||
|
Set<MessageId> ids =
|
||||||
|
c.getDatabaseComponent().transactionWithResult(true,
|
||||||
|
txn ->
|
||||||
|
c.getMessagingManager()
|
||||||
|
.getMessageIds(txn, contactId));
|
||||||
|
assertEquals(messages.size(), ids.size());
|
||||||
|
for (ConversationMessageHeader h : messages) {
|
||||||
|
assertTrue(ids.contains(h.getId()));
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertGroupCounts(BriarIntegrationTestComponent c,
|
||||||
|
long msgCount, long unreadCount) throws Exception {
|
||||||
|
GroupId g = c.getMessagingManager().getConversationId(contactId);
|
||||||
|
assertGroupCount(c.getMessageTracker(), g, msgCount, unreadCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.privategroup.invitation;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.sync.Group;
|
import org.briarproject.bramble.api.sync.Group;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
||||||
import org.briarproject.briar.api.client.ProtocolStateException;
|
import org.briarproject.briar.api.client.ProtocolStateException;
|
||||||
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
||||||
@@ -19,9 +20,12 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptySet;
|
||||||
import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount;
|
import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -35,6 +39,7 @@ public class GroupInvitationIntegrationTest
|
|||||||
private PrivateGroupManager groupManager0, groupManager1;
|
private PrivateGroupManager groupManager0, groupManager1;
|
||||||
private GroupInvitationManager groupInvitationManager0,
|
private GroupInvitationManager groupInvitationManager0,
|
||||||
groupInvitationManager1;
|
groupInvitationManager1;
|
||||||
|
private Group g1From0, g0From1;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@Override
|
@Override
|
||||||
@@ -45,6 +50,8 @@ public class GroupInvitationIntegrationTest
|
|||||||
groupManager1 = c1.getPrivateGroupManager();
|
groupManager1 = c1.getPrivateGroupManager();
|
||||||
groupInvitationManager0 = c0.getGroupInvitationManager();
|
groupInvitationManager0 = c0.getGroupInvitationManager();
|
||||||
groupInvitationManager1 = c1.getGroupInvitationManager();
|
groupInvitationManager1 = c1.getGroupInvitationManager();
|
||||||
|
g1From0 = groupInvitationManager0.getContactGroup(contact1From0);
|
||||||
|
g0From1 = groupInvitationManager1.getContactGroup(contact0From1);
|
||||||
|
|
||||||
privateGroup =
|
privateGroup =
|
||||||
privateGroupFactory.createPrivateGroup("Testgroup", author0);
|
privateGroupFactory.createPrivateGroup("Testgroup", author0);
|
||||||
@@ -467,8 +474,6 @@ public class GroupInvitationIntegrationTest
|
|||||||
sync1To0(1, true);
|
sync1To0(1, true);
|
||||||
|
|
||||||
// check group count
|
// check group count
|
||||||
Group g1From0 = groupInvitationManager0.getContactGroup(contact1From0);
|
|
||||||
Group g0From1 = groupInvitationManager1.getContactGroup(contact0From1);
|
|
||||||
assertGroupCount(messageTracker0, g1From0.getId(), 2, 1);
|
assertGroupCount(messageTracker0, g1From0.getId(), 2, 1);
|
||||||
assertGroupCount(messageTracker1, g0From1.getId(), 2, 1);
|
assertGroupCount(messageTracker1, g0From1.getId(), 2, 1);
|
||||||
|
|
||||||
@@ -563,6 +568,65 @@ public class GroupInvitationIntegrationTest
|
|||||||
assertGroupCount(messageTracker0, g1From0.getId(), 0, 0);
|
assertGroupCount(messageTracker0, g1From0.getId(), 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletingSomeMessages() throws Exception {
|
||||||
|
// send invitation
|
||||||
|
sendInvitation(clock.currentTimeMillis(), null);
|
||||||
|
sync0To1(1, true);
|
||||||
|
|
||||||
|
// deleting the invitation will fail for both
|
||||||
|
Collection<ConversationMessageHeader> m0 = getMessages1From0();
|
||||||
|
assertEquals(1, m0.size());
|
||||||
|
MessageId messageId = m0.iterator().next().getId();
|
||||||
|
Set<MessageId> toDelete = new HashSet<>();
|
||||||
|
toDelete.add(messageId);
|
||||||
|
assertFalse(deleteMessages1From0(toDelete));
|
||||||
|
assertFalse(deleteMessages0From1(toDelete));
|
||||||
|
|
||||||
|
// respond
|
||||||
|
groupInvitationManager1
|
||||||
|
.respondToInvitation(contactId0From1, privateGroup, false);
|
||||||
|
sync1To0(1, true);
|
||||||
|
|
||||||
|
// both can still not delete the invitation,
|
||||||
|
// because the response was not selected for deletion as well
|
||||||
|
assertFalse(deleteMessages1From0(toDelete));
|
||||||
|
assertFalse(deleteMessages0From1(toDelete));
|
||||||
|
|
||||||
|
// after selecting response, both messages can be deleted by creator
|
||||||
|
m0 = getMessages1From0();
|
||||||
|
assertEquals(2, m0.size());
|
||||||
|
for (ConversationMessageHeader h : m0) {
|
||||||
|
if (!h.getId().equals(messageId)) toDelete.add(h.getId());
|
||||||
|
}
|
||||||
|
assertGroupCount(messageTracker0, g1From0.getId(), 2, 1);
|
||||||
|
assertTrue(deleteMessages1From0(toDelete));
|
||||||
|
assertEquals(0, getMessages1From0().size());
|
||||||
|
// a second time nothing happens
|
||||||
|
assertTrue(deleteMessages1From0(toDelete));
|
||||||
|
assertGroupCount(messageTracker0, g1From0.getId(), 0, 0);
|
||||||
|
|
||||||
|
// 1 can still not delete the messages, as last one has not been ACKed
|
||||||
|
assertFalse(deleteMessages0From1(toDelete));
|
||||||
|
assertEquals(2, getMessages0From1().size());
|
||||||
|
assertGroupCount(messageTracker1, g0From1.getId(), 2, 1);
|
||||||
|
|
||||||
|
// 0 sends an ACK to their last message
|
||||||
|
sendAcks(c0, c1, contactId1From0, 1);
|
||||||
|
|
||||||
|
// 1 can now delete all messages, as last one has been ACKed
|
||||||
|
assertTrue(deleteMessages0From1(toDelete));
|
||||||
|
assertEquals(0, getMessages0From1().size());
|
||||||
|
assertGroupCount(messageTracker1, g0From1.getId(), 0, 0);
|
||||||
|
// a second time nothing happens
|
||||||
|
assertTrue(deleteMessages0From1(toDelete));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletingEmptySet() throws Exception {
|
||||||
|
assertTrue(deleteMessages0From1(emptySet()));
|
||||||
|
}
|
||||||
|
|
||||||
private Collection<ConversationMessageHeader> getMessages1From0()
|
private Collection<ConversationMessageHeader> getMessages1From0()
|
||||||
throws DbException {
|
throws DbException {
|
||||||
return db0.transactionWithResult(true, txn -> groupInvitationManager0
|
return db0.transactionWithResult(true, txn -> groupInvitationManager0
|
||||||
@@ -585,6 +649,18 @@ public class GroupInvitationIntegrationTest
|
|||||||
.deleteAllMessages(txn, contactId0From1));
|
.deleteAllMessages(txn, contactId0From1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages1From0(Set<MessageId> toDelete)
|
||||||
|
throws DbException {
|
||||||
|
return db0.transactionWithResult(false, txn -> groupInvitationManager0
|
||||||
|
.deleteMessages(txn, contactId1From0, toDelete));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages0From1(Set<MessageId> toDelete)
|
||||||
|
throws DbException {
|
||||||
|
return db1.transactionWithResult(false, txn -> groupInvitationManager1
|
||||||
|
.deleteMessages(txn, contactId0From1, toDelete));
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|||||||
@@ -35,9 +35,12 @@ import org.junit.Test;
|
|||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptySet;
|
||||||
import static junit.framework.Assert.assertNotNull;
|
import static junit.framework.Assert.assertNotNull;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.briarproject.briar.api.forum.ForumSharingManager.CLIENT_ID;
|
import static org.briarproject.briar.api.forum.ForumSharingManager.CLIENT_ID;
|
||||||
@@ -187,9 +190,8 @@ public class ForumSharingIntegrationTest
|
|||||||
@Test
|
@Test
|
||||||
public void testDeclinedSharing() throws Exception {
|
public void testDeclinedSharing() throws Exception {
|
||||||
// send invitation
|
// send invitation
|
||||||
forumSharingManager0
|
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0,
|
||||||
.sendInvitation(forum.getId(), contactId1From0, null,
|
null, clock.currentTimeMillis());
|
||||||
clock.currentTimeMillis());
|
|
||||||
|
|
||||||
// sync request message
|
// sync request message
|
||||||
sync0To1(1, true);
|
sync0To1(1, true);
|
||||||
@@ -256,9 +258,8 @@ public class ForumSharingIntegrationTest
|
|||||||
@Test
|
@Test
|
||||||
public void testInviteeLeavesAfterFinished() throws Exception {
|
public void testInviteeLeavesAfterFinished() throws Exception {
|
||||||
// send invitation
|
// send invitation
|
||||||
forumSharingManager0
|
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0,
|
||||||
.sendInvitation(forum.getId(), contactId1From0, "Hi!",
|
"Hi!", clock.currentTimeMillis());
|
||||||
clock.currentTimeMillis());
|
|
||||||
|
|
||||||
// sync request message
|
// sync request message
|
||||||
sync0To1(1, true);
|
sync0To1(1, true);
|
||||||
@@ -320,9 +321,8 @@ public class ForumSharingIntegrationTest
|
|||||||
@Test
|
@Test
|
||||||
public void testSharerLeavesAfterFinished() throws Exception {
|
public void testSharerLeavesAfterFinished() throws Exception {
|
||||||
// send invitation
|
// send invitation
|
||||||
forumSharingManager0
|
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0,
|
||||||
.sendInvitation(forum.getId(), contactId1From0, null,
|
null, clock.currentTimeMillis());
|
||||||
clock.currentTimeMillis());
|
|
||||||
|
|
||||||
// sync request message
|
// sync request message
|
||||||
sync0To1(1, true);
|
sync0To1(1, true);
|
||||||
@@ -398,9 +398,8 @@ public class ForumSharingIntegrationTest
|
|||||||
@Test
|
@Test
|
||||||
public void testSharerLeavesBeforeResponse() throws Exception {
|
public void testSharerLeavesBeforeResponse() throws Exception {
|
||||||
// send invitation
|
// send invitation
|
||||||
forumSharingManager0
|
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0,
|
||||||
.sendInvitation(forum.getId(), contactId1From0, null,
|
null, clock.currentTimeMillis());
|
||||||
clock.currentTimeMillis());
|
|
||||||
|
|
||||||
// sharer un-subscribes from forum
|
// sharer un-subscribes from forum
|
||||||
forumManager0.removeForum(forum);
|
forumManager0.removeForum(forum);
|
||||||
@@ -445,9 +444,8 @@ public class ForumSharingIntegrationTest
|
|||||||
@Test
|
@Test
|
||||||
public void testSharingSameForumWithEachOther() throws Exception {
|
public void testSharingSameForumWithEachOther() throws Exception {
|
||||||
// send invitation
|
// send invitation
|
||||||
forumSharingManager0
|
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0,
|
||||||
.sendInvitation(forum.getId(), contactId1From0, "Hi!",
|
"Hi!", clock.currentTimeMillis());
|
||||||
clock.currentTimeMillis());
|
|
||||||
|
|
||||||
// sync request message
|
// sync request message
|
||||||
sync0To1(1, true);
|
sync0To1(1, true);
|
||||||
@@ -486,9 +484,8 @@ public class ForumSharingIntegrationTest
|
|||||||
public void testSharingSameForumWithEachOtherBeforeAccept()
|
public void testSharingSameForumWithEachOtherBeforeAccept()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// send invitation
|
// send invitation
|
||||||
forumSharingManager0
|
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0,
|
||||||
.sendInvitation(forum.getId(), contactId1From0, "Hi!",
|
"Hi!", clock.currentTimeMillis());
|
||||||
clock.currentTimeMillis());
|
|
||||||
sync0To1(1, true);
|
sync0To1(1, true);
|
||||||
eventWaiter.await(TIMEOUT, 1);
|
eventWaiter.await(TIMEOUT, 1);
|
||||||
assertRequestReceived(listener1, contactId0From1);
|
assertRequestReceived(listener1, contactId0From1);
|
||||||
@@ -556,9 +553,8 @@ public class ForumSharingIntegrationTest
|
|||||||
@Test
|
@Test
|
||||||
public void testContactRemoved() throws Exception {
|
public void testContactRemoved() throws Exception {
|
||||||
// send invitation
|
// send invitation
|
||||||
forumSharingManager0
|
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0,
|
||||||
.sendInvitation(forum.getId(), contactId1From0, "Hi!",
|
"Hi!", clock.currentTimeMillis());
|
||||||
clock.currentTimeMillis());
|
|
||||||
|
|
||||||
// sync request message
|
// sync request message
|
||||||
sync0To1(1, true);
|
sync0To1(1, true);
|
||||||
@@ -683,9 +679,8 @@ public class ForumSharingIntegrationTest
|
|||||||
@Test
|
@Test
|
||||||
public void testSyncAfterReSharing() throws Exception {
|
public void testSyncAfterReSharing() throws Exception {
|
||||||
// send invitation
|
// send invitation
|
||||||
forumSharingManager0
|
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0,
|
||||||
.sendInvitation(forum.getId(), contactId1From0, "Hi!",
|
"Hi!", clock.currentTimeMillis());
|
||||||
clock.currentTimeMillis());
|
|
||||||
|
|
||||||
// sync request message
|
// sync request message
|
||||||
sync0To1(1, true);
|
sync0To1(1, true);
|
||||||
@@ -791,9 +786,8 @@ public class ForumSharingIntegrationTest
|
|||||||
@Test
|
@Test
|
||||||
public void testSessionResetAfterAbort() throws Exception {
|
public void testSessionResetAfterAbort() throws Exception {
|
||||||
// send invitation
|
// send invitation
|
||||||
forumSharingManager0
|
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0,
|
||||||
.sendInvitation(forum.getId(), contactId1From0, "Hi!",
|
"Hi!", clock.currentTimeMillis());
|
||||||
clock.currentTimeMillis());
|
|
||||||
|
|
||||||
// sync request message
|
// sync request message
|
||||||
sync0To1(1, true);
|
sync0To1(1, true);
|
||||||
@@ -981,6 +975,61 @@ public class ForumSharingIntegrationTest
|
|||||||
assertEquals(1, getMessages0From1().size());
|
assertEquals(1, getMessages0From1().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletingSomeMessages() throws Exception {
|
||||||
|
// send invitation
|
||||||
|
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0,
|
||||||
|
null, clock.currentTimeMillis());
|
||||||
|
sync0To1(1, true);
|
||||||
|
eventWaiter.await(TIMEOUT, 1);
|
||||||
|
|
||||||
|
// deleting the invitation will fail
|
||||||
|
Collection<ConversationMessageHeader> m0 = getMessages1From0();
|
||||||
|
assertEquals(1, m0.size());
|
||||||
|
MessageId messageId = m0.iterator().next().getId();
|
||||||
|
Set<MessageId> toDelete = new HashSet<>();
|
||||||
|
toDelete.add(messageId);
|
||||||
|
assertFalse(deleteMessages1From0(toDelete));
|
||||||
|
|
||||||
|
// decline invitation
|
||||||
|
respondToRequest(contactId0From1, true);
|
||||||
|
sync1To0(1, true);
|
||||||
|
eventWaiter.await(TIMEOUT, 1);
|
||||||
|
|
||||||
|
// both can still not delete the invitation,
|
||||||
|
// because the response was not selected for deletion as well
|
||||||
|
assertFalse(deleteMessages1From0(toDelete));
|
||||||
|
assertFalse(deleteMessages0From1(toDelete));
|
||||||
|
|
||||||
|
// after selecting response, both messages can be deleted
|
||||||
|
m0 = getMessages1From0();
|
||||||
|
assertEquals(2, m0.size());
|
||||||
|
for (ConversationMessageHeader h : m0) {
|
||||||
|
if (!h.getId().equals(messageId)) toDelete.add(h.getId());
|
||||||
|
}
|
||||||
|
assertTrue(deleteMessages1From0(toDelete));
|
||||||
|
assertEquals(0, getMessages1From0().size());
|
||||||
|
// a second time nothing happens
|
||||||
|
assertTrue(deleteMessages1From0(toDelete));
|
||||||
|
|
||||||
|
// 1 can still not delete the messages, as last one has not been ACKed
|
||||||
|
assertFalse(deleteMessages0From1(toDelete));
|
||||||
|
|
||||||
|
// 0 sends an ACK to their last message
|
||||||
|
sendAcks(c0, c1, contactId1From0, 1);
|
||||||
|
|
||||||
|
// 1 can now delete all messages, as last one has been ACKed
|
||||||
|
assertTrue(deleteMessages0From1(toDelete));
|
||||||
|
assertEquals(0, getMessages0From1().size());
|
||||||
|
// a second time nothing happens
|
||||||
|
assertTrue(deleteMessages0From1(toDelete));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletingEmptySet() throws Exception {
|
||||||
|
assertTrue(deleteMessages0From1(emptySet()));
|
||||||
|
}
|
||||||
|
|
||||||
private Collection<ConversationMessageHeader> getMessages1From0()
|
private Collection<ConversationMessageHeader> getMessages1From0()
|
||||||
throws DbException {
|
throws DbException {
|
||||||
return db0.transactionWithResult(true, txn -> forumSharingManager0
|
return db0.transactionWithResult(true, txn -> forumSharingManager0
|
||||||
@@ -1003,6 +1052,18 @@ public class ForumSharingIntegrationTest
|
|||||||
.deleteAllMessages(txn, contactId0From1));
|
.deleteAllMessages(txn, contactId0From1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages1From0(Set<MessageId> toDelete)
|
||||||
|
throws DbException {
|
||||||
|
return db0.transactionWithResult(false, txn -> forumSharingManager0
|
||||||
|
.deleteMessages(txn, contactId1From0, toDelete));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteMessages0From1(Set<MessageId> toDelete)
|
||||||
|
throws DbException {
|
||||||
|
return db1.transactionWithResult(false, txn -> forumSharingManager1
|
||||||
|
.deleteMessages(txn, contactId0From1, toDelete));
|
||||||
|
}
|
||||||
|
|
||||||
private void respondToRequest(ContactId contactId, boolean accept)
|
private void respondToRequest(ContactId contactId, boolean accept)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
assertEquals(1, forumSharingManager1.getInvitations().size());
|
assertEquals(1, forumSharingManager1.getInvitations().size());
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
syncMessage(c1, c2, contactId2From1, num, valid);
|
syncMessage(c1, c2, contactId2From1, num, valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void syncMessage(BriarIntegrationTestComponent fromComponent,
|
protected void syncMessage(BriarIntegrationTestComponent fromComponent,
|
||||||
BriarIntegrationTestComponent toComponent, ContactId toId, int num,
|
BriarIntegrationTestComponent toComponent, ContactId toId, int num,
|
||||||
boolean valid) throws Exception {
|
boolean valid) throws Exception {
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import org.briarproject.briar.api.client.MessageTracker;
|
|||||||
import org.briarproject.briar.api.forum.ForumManager;
|
import org.briarproject.briar.api.forum.ForumManager;
|
||||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||||
import org.briarproject.briar.api.introduction.IntroductionManager;
|
import org.briarproject.briar.api.introduction.IntroductionManager;
|
||||||
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
|
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
||||||
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||||
import org.briarproject.briar.blog.BlogModule;
|
import org.briarproject.briar.blog.BlogModule;
|
||||||
@@ -103,8 +105,12 @@ public interface BriarIntegrationTestComponent
|
|||||||
|
|
||||||
MessageTracker getMessageTracker();
|
MessageTracker getMessageTracker();
|
||||||
|
|
||||||
|
MessagingManager getMessagingManager();
|
||||||
|
|
||||||
PrivateGroupManager getPrivateGroupManager();
|
PrivateGroupManager getPrivateGroupManager();
|
||||||
|
|
||||||
|
PrivateMessageFactory getPrivateMessageFactory();
|
||||||
|
|
||||||
TransportPropertyManager getTransportPropertyManager();
|
TransportPropertyManager getTransportPropertyManager();
|
||||||
|
|
||||||
AuthorFactory getAuthorFactory();
|
AuthorFactory getAuthorFactory();
|
||||||
|
|||||||
Reference in New Issue
Block a user