Add tests for IntroductionManager and Validator

This commit is contained in:
Torsten Grote
2016-03-30 17:25:31 -03:00
parent d205f3b0d8
commit 823b95f7d6
5 changed files with 1211 additions and 0 deletions

View File

@@ -0,0 +1,283 @@
package org.briarproject.introduction;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.Bytes;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.introduction.IntroduceeProtocolState;
import org.briarproject.api.introduction.SessionId;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.system.Clock;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
import java.security.SecureRandom;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED;
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT;
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
import static org.briarproject.api.introduction.IntroductionConstants.EXISTS;
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
import static org.briarproject.api.introduction.IntroductionConstants.INTRODUCER;
import static org.briarproject.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID;
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_ID;
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE;
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
import static org.briarproject.api.introduction.IntroductionConstants.TIME;
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class IntroduceeManagerTest extends BriarTestCase {
final Mockery context;
final IntroduceeManager introduceeManager;
final DatabaseComponent db;
final CryptoComponent cryptoComponent;
final ClientHelper clientHelper;
final IntroductionGroupFactory introductionGroupFactory;
final MessageSender messageSender;
final TransportPropertyManager transportPropertyManager;
final AuthorFactory authorFactory;
final ContactManager contactManager;
final Clock clock;
final Contact introducer;
final Contact introducee1;
final Contact introducee2;
final Group localGroup1;
final Group introductionGroup1;
final Group introductionGroup2;
final Transaction txn;
final long time = 42L;
final Message localStateMessage;
final ClientId clientId;
final SessionId sessionId;
final Message message1;
public IntroduceeManagerTest() {
context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
messageSender = context.mock(MessageSender.class);
db = context.mock(DatabaseComponent.class);
cryptoComponent = context.mock(CryptoComponent.class);
clientHelper = context.mock(ClientHelper.class);
clock = context.mock(Clock.class);
introductionGroupFactory =
context.mock(IntroductionGroupFactory.class);
transportPropertyManager = context.mock(TransportPropertyManager.class);
authorFactory = context.mock(AuthorFactory.class);
contactManager = context.mock(ContactManager.class);
introduceeManager = new IntroduceeManager(messageSender, db,
clientHelper, clock, cryptoComponent, transportPropertyManager,
authorFactory, contactManager, introductionGroupFactory);
AuthorId authorId0 = new AuthorId(TestUtils.getRandomId());
Author author0 = new Author(authorId0, "Introducer",
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
AuthorId localAuthorId = new AuthorId(TestUtils.getRandomId());
ContactId contactId0 = new ContactId(234);
introducer = new Contact(contactId0, author0, localAuthorId, true);
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
Author author1 = new Author(authorId1, "Introducee1",
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId());
ContactId contactId1 = new ContactId(234);
introducee1 = new Contact(contactId1, author1, localAuthorId1, true);
AuthorId authorId2 = new AuthorId(TestUtils.getRandomId());
Author author2 = new Author(authorId2, "Introducee2",
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
ContactId contactId2 = new ContactId(235);
introducee2 = new Contact(contactId2, author2, localAuthorId, true);
clientId = IntroductionManagerImpl.CLIENT_ID;
localGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
clientId, new byte[0]);
introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
clientId, new byte[0]);
introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()),
clientId, new byte[0]);
sessionId = new SessionId(TestUtils.getRandomId());
localStateMessage = new Message(
new MessageId(TestUtils.getRandomId()),
localGroup1.getId(),
time,
TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
);
message1 = new Message(
new MessageId(TestUtils.getRandomId()),
introductionGroup1.getId(),
time,
TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
);
txn = new Transaction(null, false);
}
@Test
public void testIncomingRequestMessage()
throws DbException, FormatException {
final BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_REQUEST);
msg.put(GROUP_ID, introductionGroup1.getId());
msg.put(SESSION_ID, sessionId);
msg.put(MESSAGE_ID, message1.getId());
msg.put(MESSAGE_TIME, time);
msg.put(NAME, introducee2.getAuthor().getName());
msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
final BdfDictionary state =
initializeSessionState(txn, introductionGroup1.getId(), msg);
context.checking(new Expectations() {{
oneOf(clientHelper).mergeMessageMetadata(txn,
localStateMessage.getId(), state);
}});
introduceeManager.incomingMessage(txn, state, msg);
context.assertIsSatisfied();
assertFalse(txn.isComplete());
}
@Test
public void testIncomingResponseMessage()
throws DbException, FormatException {
final BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_RESPONSE);
msg.put(GROUP_ID, introductionGroup1.getId());
msg.put(SESSION_ID, sessionId);
msg.put(MESSAGE_ID, message1.getId());
msg.put(MESSAGE_TIME, time);
msg.put(NAME, introducee2.getAuthor().getName());
msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
final BdfDictionary state =
initializeSessionState(txn, introductionGroup1.getId(), msg);
state.put(STATE, IntroduceeProtocolState.AWAIT_RESPONSES.ordinal());
// turn request message into a response
msg.put(ACCEPT, true);
msg.put(TIME, time);
msg.put(E_PUBLIC_KEY, TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
msg.put(TRANSPORT, new BdfDictionary());
context.checking(new Expectations() {{
oneOf(clientHelper).mergeMessageMetadata(txn,
localStateMessage.getId(), state);
}});
introduceeManager.incomingMessage(txn, state, msg);
context.assertIsSatisfied();
assertFalse(txn.isComplete());
}
private BdfDictionary initializeSessionState(final Transaction txn,
final GroupId groupId, final BdfDictionary msg)
throws DbException, FormatException {
final SecureRandom secureRandom = context.mock(SecureRandom.class);
final Bytes salt = new Bytes(new byte[64]);
final BdfDictionary groupMetadata = BdfDictionary.of(
new BdfEntry(CONTACT, introducee1.getId().getInt())
);
final boolean contactExists = true;
final BdfDictionary state = new BdfDictionary();
state.put(STORAGE_ID, localStateMessage.getId());
state.put(STATE, AWAIT_REQUEST.getValue());
state.put(ROLE, ROLE_INTRODUCEE);
state.put(GROUP_ID, groupId);
state.put(INTRODUCER, introducer.getAuthor().getName());
state.put(CONTACT_ID_1, introducer.getId().getInt());
state.put(LOCAL_AUTHOR_ID, introducer.getLocalAuthorId().getBytes());
state.put(NOT_OUR_RESPONSE, localStateMessage.getId());
state.put(ANSWERED, false);
state.put(EXISTS, true);
state.put(REMOTE_AUTHOR_ID, introducee2.getAuthor().getId());
context.checking(new Expectations() {{
oneOf(clock).currentTimeMillis();
will(returnValue(time));
oneOf(cryptoComponent).getSecureRandom();
will(returnValue(secureRandom));
oneOf(secureRandom).nextBytes(salt.getBytes());
oneOf(introductionGroupFactory).createLocalGroup();
will(returnValue(localGroup1));
oneOf(clientHelper)
.createMessage(localGroup1.getId(), time, BdfList.of(salt));
will(returnValue(localStateMessage));
// who is making the introduction? who is the introducer?
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
groupId);
will(returnValue(groupMetadata));
oneOf(db).getContact(txn, introducer.getId());
will(returnValue(introducer));
// create remote author to check if contact exists
oneOf(authorFactory).createAuthor(introducee2.getAuthor().getName(),
introducee2.getAuthor().getPublicKey());
will(returnValue(introducee2.getAuthor()));
oneOf(contactManager)
.contactExists(txn, introducee2.getAuthor().getId(),
introducer.getLocalAuthorId());
will(returnValue(contactExists));
// store session state
oneOf(clientHelper)
.addLocalMessage(txn, localStateMessage, clientId, state,
false);
}});
BdfDictionary result = introduceeManager.initialize(txn, groupId, msg);
context.assertIsSatisfied();
return result;
}
}

View File

@@ -0,0 +1,189 @@
package org.briarproject.introduction;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.Bytes;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.system.Clock;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
import java.security.SecureRandom;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.introduction.IntroducerProtocolState.AWAIT_RESPONSES;
import static org.briarproject.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS;
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1;
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_2;
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_1;
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_2;
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_2;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2;
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.junit.Assert.assertFalse;
public class IntroducerManagerTest extends BriarTestCase {
final Mockery context;
final IntroducerManager introducerManager;
final CryptoComponent cryptoComponent;
final ClientHelper clientHelper;
final IntroductionGroupFactory introductionGroupFactory;
final MessageSender messageSender;
final Clock clock;
final Contact introducee1;
final Contact introducee2;
final Group localGroup0;
final Group introductionGroup1;
final Group introductionGroup2;
public IntroducerManagerTest() {
context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
messageSender = context.mock(MessageSender.class);
cryptoComponent = context.mock(CryptoComponent.class);
clientHelper = context.mock(ClientHelper.class);
clock = context.mock(Clock.class);
introductionGroupFactory =
context.mock(IntroductionGroupFactory.class);
introducerManager =
new IntroducerManager(messageSender, clientHelper, clock,
cryptoComponent, introductionGroupFactory);
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
Author author1 = new Author(authorId1, "Introducee1",
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId());
ContactId contactId1 = new ContactId(234);
introducee1 = new Contact(contactId1, author1, localAuthorId1, true);
AuthorId authorId2 = new AuthorId(TestUtils.getRandomId());
Author author2 = new Author(authorId2, "Introducee2",
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
AuthorId localAuthorId2 = new AuthorId(TestUtils.getRandomId());
ContactId contactId2 = new ContactId(235);
introducee2 = new Contact(contactId2, author2, localAuthorId2, true);
localGroup0 = new Group(new GroupId(TestUtils.getRandomId()),
getClientId(), new byte[0]);
introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
getClientId(), new byte[0]);
introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()),
getClientId(), new byte[0]);
context.assertIsSatisfied();
}
@Test
public void testMakeIntroduction() throws DbException, FormatException {
final Transaction txn = new Transaction(null, false);
final long time = 42L;
context.setImposteriser(ClassImposteriser.INSTANCE);
final SecureRandom secureRandom = context.mock(SecureRandom.class);
final Bytes salt = new Bytes(new byte[64]);
final Message msg = new Message(new MessageId(TestUtils.getRandomId()),
localGroup0.getId(), time, TestUtils.getRandomBytes(64));
final BdfDictionary state = new BdfDictionary();
state.put(SESSION_ID, msg.getId());
state.put(STORAGE_ID, msg.getId());
state.put(STATE, PREPARE_REQUESTS.getValue());
state.put(ROLE, ROLE_INTRODUCER);
state.put(GROUP_ID_1, introductionGroup1.getId());
state.put(GROUP_ID_2, introductionGroup2.getId());
state.put(CONTACT_1, introducee1.getAuthor().getName());
state.put(CONTACT_2, introducee2.getAuthor().getName());
state.put(CONTACT_ID_1, introducee1.getId().getInt());
state.put(CONTACT_ID_2, introducee2.getId().getInt());
state.put(AUTHOR_ID_1, introducee1.getAuthor().getId());
state.put(AUTHOR_ID_2, introducee2.getAuthor().getId());
final BdfDictionary state2 = (BdfDictionary) state.clone();
state2.put(STATE, AWAIT_RESPONSES.getValue());
final BdfDictionary msg1 = new BdfDictionary();
msg1.put(TYPE, TYPE_REQUEST);
msg1.put(SESSION_ID, state.getRaw(SESSION_ID));
msg1.put(GROUP_ID, state.getRaw(GROUP_ID_1));
msg1.put(NAME, state.getString(CONTACT_2));
msg1.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
final BdfDictionary msg1send = (BdfDictionary) msg1.clone();
msg1send.put(MESSAGE_TIME, time);
final BdfDictionary msg2 = new BdfDictionary();
msg2.put(TYPE, TYPE_REQUEST);
msg2.put(SESSION_ID, state.getRaw(SESSION_ID));
msg2.put(GROUP_ID, state.getRaw(GROUP_ID_2));
msg2.put(NAME, state.getString(CONTACT_1));
msg2.put(PUBLIC_KEY, introducee1.getAuthor().getPublicKey());
final BdfDictionary msg2send = (BdfDictionary) msg2.clone();
msg2send.put(MESSAGE_TIME, time);
context.checking(new Expectations() {{
// initialize and store session state
oneOf(clock).currentTimeMillis();
will(returnValue(time));
oneOf(cryptoComponent).getSecureRandom();
will(returnValue(secureRandom));
oneOf(secureRandom).nextBytes(salt.getBytes());
oneOf(introductionGroupFactory).createLocalGroup();
will(returnValue(localGroup0));
oneOf(clientHelper).createMessage(localGroup0.getId(), time,
BdfList.of(salt));
will(returnValue(msg));
oneOf(introductionGroupFactory)
.createIntroductionGroup(introducee1);
will(returnValue(introductionGroup1));
oneOf(introductionGroupFactory)
.createIntroductionGroup(introducee2);
will(returnValue(introductionGroup2));
oneOf(clientHelper).addLocalMessage(txn, msg, getClientId(), state,
false);
// send message
oneOf(clientHelper).mergeMessageMetadata(txn, msg.getId(), state2);
oneOf(messageSender).sendMessage(txn, msg1send);
oneOf(messageSender).sendMessage(txn, msg2send);
}});
introducerManager
.makeIntroduction(txn, introducee1, introducee2, null, time);
context.assertIsSatisfied();
assertFalse(txn.isComplete());
}
private ClientId getClientId() {
return IntroductionManagerImpl.CLIENT_ID;
}
}

View File

@@ -0,0 +1,287 @@
package org.briarproject.introduction;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.data.MetadataParser;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.introduction.SessionId;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageStatus;
import org.briarproject.api.system.Clock;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import static junit.framework.TestCase.assertTrue;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.junit.Assert.assertFalse;
public class IntroductionManagerImplTest extends BriarTestCase {
final Mockery context;
final IntroductionManagerImpl introductionManager;
final IntroducerManager introducerManager;
final IntroduceeManager introduceeManager;
final DatabaseComponent db;
final PrivateGroupFactory privateGroupFactory;
final ClientHelper clientHelper;
final MetadataEncoder metadataEncoder;
final MessageQueueManager messageQueueManager;
final IntroductionGroupFactory introductionGroupFactory;
final Clock clock;
final SessionId sessionId = new SessionId(TestUtils.getRandomId());
final long time = 42L;
final Contact introducee1;
final Contact introducee2;
final Group localGroup0;
final Group introductionGroup1;
final Group introductionGroup2;
final Message message1;
Transaction txn;
public IntroductionManagerImplTest() {
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
Author author1 = new Author(authorId1, "Introducee1",
new byte[MAX_PUBLIC_KEY_LENGTH]);
AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId());
ContactId contactId1 = new ContactId(234);
introducee1 = new Contact(contactId1, author1, localAuthorId1, true);
AuthorId authorId2 = new AuthorId(TestUtils.getRandomId());
Author author2 = new Author(authorId2, "Introducee2",
new byte[MAX_PUBLIC_KEY_LENGTH]);
AuthorId localAuthorId2 = new AuthorId(TestUtils.getRandomId());
ContactId contactId2 = new ContactId(235);
introducee2 = new Contact(contactId2, author2, localAuthorId2, true);
ClientId clientId = new ClientId(TestUtils.getRandomId());
localGroup0 = new Group(new GroupId(TestUtils.getRandomId()),
clientId, new byte[0]);
introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
clientId, new byte[0]);
introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()),
clientId, new byte[0]);
message1 = new Message(
new MessageId(TestUtils.getRandomId()),
introductionGroup1.getId(),
time,
TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
);
// mock ALL THE THINGS!!!
context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
introducerManager = context.mock(IntroducerManager.class);
introduceeManager = context.mock(IntroduceeManager.class);
db = context.mock(DatabaseComponent.class);
privateGroupFactory = context.mock(PrivateGroupFactory.class);
clientHelper = context.mock(ClientHelper.class);
metadataEncoder =
context.mock(MetadataEncoder.class);
messageQueueManager =
context.mock(MessageQueueManager.class);
MetadataParser metadataParser = context.mock(MetadataParser.class);
introductionGroupFactory = context.mock(IntroductionGroupFactory.class);
clock = context.mock(Clock.class);
introductionManager = new IntroductionManagerImpl(
db, clientHelper, metadataParser, clock, introducerManager,
introduceeManager, introductionGroupFactory
);
}
@Test
public void testMakeIntroduction() throws DbException, FormatException {
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
oneOf(introducerManager)
.makeIntroduction(txn, introducee1, introducee2, null,
time);
oneOf(db).endTransaction(txn);
}});
introductionManager
.makeIntroduction(introducee1, introducee2, null, time);
context.assertIsSatisfied();
assertTrue(txn.isComplete());
}
@Test
public void testAcceptIntroduction() throws DbException, FormatException {
final BdfDictionary state = BdfDictionary.of(
new BdfEntry(GROUP_ID_1, introductionGroup1.getId()),
new BdfEntry(GROUP_ID_2, introductionGroup2.getId())
);
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
oneOf(db).getContact(txn, introducee1.getId());
will(returnValue(introducee1));
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
will(returnValue(introductionGroup1));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
will(returnValue(state));
oneOf(introduceeManager).acceptIntroduction(txn, state, time);
oneOf(db).endTransaction(txn);
}});
introductionManager
.acceptIntroduction(introducee1.getId(), sessionId, time);
context.assertIsSatisfied();
assertTrue(txn.isComplete());
}
@Test
public void testDeclineIntroduction() throws DbException, FormatException {
final BdfDictionary state = BdfDictionary.of(
new BdfEntry(GROUP_ID_1, introductionGroup1.getId()),
new BdfEntry(GROUP_ID_2, introductionGroup2.getId())
);
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
oneOf(db).getContact(txn, introducee1.getId());
will(returnValue(introducee1));
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
will(returnValue(introductionGroup1));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
will(returnValue(state));
oneOf(introduceeManager).declineIntroduction(txn, state, time);
oneOf(db).endTransaction(txn);
}});
introductionManager
.declineIntroduction(introducee1.getId(), sessionId, time);
context.assertIsSatisfied();
assertTrue(txn.isComplete());
}
@Test
public void testGetIntroductionMessages()
throws DbException, FormatException {
final Map<MessageId, BdfDictionary> metadata = Collections.emptyMap();
final Collection<MessageStatus> statuses = Collections.emptyList();
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getContact(txn, introducee1.getId());
will(returnValue(introducee1));
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
will(returnValue(introductionGroup1));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
introductionGroup1.getId());
will(returnValue(metadata));
oneOf(db).getMessageStatus(txn, introducee1.getId(),
introductionGroup1.getId());
will(returnValue(statuses));
oneOf(db).endTransaction(txn);
}});
introductionManager.getIntroductionMessages(introducee1.getId());
context.assertIsSatisfied();
assertTrue(txn.isComplete());
}
@Test
public void testIncomingRequestMessage()
throws DbException, FormatException {
final BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_REQUEST);
final BdfDictionary state = new BdfDictionary();
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(introduceeManager)
.initialize(txn, introductionGroup1.getId(), msg);
will(returnValue(state));
oneOf(introduceeManager)
.incomingMessage(txn, state, msg);
}});
introductionManager
.incomingMessage(txn, message1, new BdfList(), msg);
context.assertIsSatisfied();
assertFalse(txn.isComplete());
}
@Test
public void testIncomingResponseMessage()
throws DbException, FormatException {
final BdfDictionary msg = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_RESPONSE),
new BdfEntry(SESSION_ID, sessionId)
);
final BdfDictionary state = new BdfDictionary();
state.put(ROLE, ROLE_INTRODUCER);
state.put(GROUP_ID_1, introductionGroup1.getId());
state.put(GROUP_ID_2, introductionGroup2.getId());
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
will(returnValue(state));
oneOf(introducerManager).incomingMessage(txn, state, msg);
}});
introductionManager
.incomingMessage(txn, message1, new BdfList(), msg);
context.assertIsSatisfied();
assertFalse(txn.isComplete());
}
}

View File

@@ -0,0 +1,357 @@
package org.briarproject.introduction;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.introduction.SessionId;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.system.Clock;
import org.briarproject.system.SystemClock;
import org.jmock.Mockery;
import org.junit.Test;
import java.io.IOException;
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
import static org.briarproject.api.introduction.IntroductionConstants.MSG;
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.api.introduction.IntroductionConstants.TIME;
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class IntroductionValidatorTest extends BriarTestCase {
private final Mockery context = new Mockery();
private final Group group;
private final Message message;
private final IntroductionValidator validator;
private final Clock clock = new SystemClock();
public IntroductionValidatorTest() {
GroupId groupId = new GroupId(TestUtils.getRandomId());
ClientId clientId = new ClientId(TestUtils.getRandomId());
byte[] descriptor = TestUtils.getRandomBytes(12);
group = new Group(groupId, clientId, descriptor);
MessageId messageId = new MessageId(TestUtils.getRandomId());
long timestamp = System.currentTimeMillis();
byte[] raw = TestUtils.getRandomBytes(123);
message = new Message(messageId, group.getId(), timestamp, raw);
ClientHelper clientHelper = context.mock(ClientHelper.class);
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
validator = new IntroductionValidator(clientHelper, metadataEncoder,
clock);
context.assertIsSatisfied();
}
//
// Introduction Requests
//
@Test
public void testValidateProperIntroductionRequest() throws IOException {
final byte[] sessionId = TestUtils.getRandomId();
final String name = TestUtils.getRandomString(MAX_AUTHOR_NAME_LENGTH);
final byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
final String text = TestUtils.getRandomString(MAX_MESSAGE_BODY_LENGTH);
BdfList body = BdfList.of(TYPE_REQUEST, sessionId,
name, publicKey, text);
final BdfDictionary result =
validator.validateMessage(message, group, body);
assertEquals(Long.valueOf(TYPE_REQUEST), result.getLong(TYPE));
assertEquals(sessionId, result.getRaw(SESSION_ID));
assertEquals(name, result.getString(NAME));
assertEquals(publicKey, result.getRaw(PUBLIC_KEY));
assertEquals(text, result.getString(MSG));
context.assertIsSatisfied();
}
@Test(expected = FormatException.class)
public void testValidateIntroductionRequestWithNoName() throws IOException {
BdfDictionary msg = getValidIntroductionRequest();
// no NAME is message
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getRaw(PUBLIC_KEY));
if (msg.containsKey(MSG)) body.add(msg.getString(MSG));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionRequestWithLongName() throws IOException {
// too long NAME in message
BdfDictionary msg = getValidIntroductionRequest();
msg.put(NAME, msg.get(NAME) + "x");
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getString(NAME), msg.getRaw(PUBLIC_KEY));
if (msg.containsKey(MSG)) body.add(msg.getString(MSG));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionRequestWithWrongType()
throws IOException {
// wrong message type
BdfDictionary msg = getValidIntroductionRequest();
msg.put(TYPE, 324234);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getString(NAME), msg.getRaw(PUBLIC_KEY));
if (msg.containsKey(MSG)) body.add(msg.getString(MSG));
validator.validateMessage(message, group, body);
}
private BdfDictionary getValidIntroductionRequest() throws FormatException {
byte[] sessionId = TestUtils.getRandomId();
String name = TestUtils.getRandomString(MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
String text = TestUtils.getRandomString(MAX_MESSAGE_BODY_LENGTH);
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_REQUEST);
msg.put(SESSION_ID, sessionId);
msg.put(NAME, name);
msg.put(PUBLIC_KEY, publicKey);
msg.put(MSG, text);
return msg;
}
//
// Introduction Responses
//
@Test
public void testValidateIntroductionAcceptResponse() throws IOException {
byte[] groupId = TestUtils.getRandomId();
byte[] sessionId = TestUtils.getRandomId();
long time = clock.currentTimeMillis();
byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
String transportId = TestUtils
.getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH);
BdfDictionary tProps = BdfDictionary.of(
new BdfEntry(TestUtils.getRandomString(MAX_PROPERTY_LENGTH),
TestUtils.getRandomString(MAX_PROPERTY_LENGTH))
);
BdfDictionary tp = BdfDictionary.of(
new BdfEntry(transportId, tProps)
);
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_RESPONSE);
msg.put(GROUP_ID, groupId);
msg.put(SESSION_ID, sessionId);
msg.put(ACCEPT, true);
msg.put(TIME, time);
msg.put(E_PUBLIC_KEY, publicKey);
msg.put(TRANSPORT, tp);
BdfList body = BdfList.of(TYPE_RESPONSE, msg.getRaw(SESSION_ID),
msg.getBoolean(ACCEPT), msg.getLong(TIME),
msg.getRaw(E_PUBLIC_KEY), msg.getDictionary(TRANSPORT));
final BdfDictionary result =
validator.validateMessage(message, group, body);
assertEquals(Long.valueOf(TYPE_RESPONSE), result.getLong(TYPE));
assertEquals(sessionId, result.getRaw(SESSION_ID));
assertEquals(true, result.getBoolean(ACCEPT));
assertEquals(publicKey, result.getRaw(E_PUBLIC_KEY));
assertEquals(tp, result.getDictionary(TRANSPORT));
context.assertIsSatisfied();
}
@Test
public void testValidateIntroductionDeclineResponse()
throws IOException {
BdfDictionary msg = getValidIntroductionResponse(false);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getBoolean(ACCEPT));
BdfDictionary result = validator.validateMessage(message, group, body);
assertFalse(result.getBoolean(ACCEPT));
context.assertIsSatisfied();
}
@Test(expected = FormatException.class)
public void testValidateIntroductionResponseWithoutAccept()
throws IOException {
BdfDictionary msg = getValidIntroductionResponse(false);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionResponseWithBrokenTp()
throws IOException {
BdfDictionary msg = getValidIntroductionResponse(true);
BdfDictionary tp = msg.getDictionary(TRANSPORT);
tp.put(TestUtils
.getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH), "X");
msg.put(TRANSPORT, tp);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getBoolean(ACCEPT), msg.getLong(TIME),
msg.getRaw(E_PUBLIC_KEY), msg.getDictionary(TRANSPORT));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionResponseWithoutPublicKey()
throws IOException {
BdfDictionary msg = getValidIntroductionResponse(true);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getBoolean(ACCEPT), msg.getLong(TIME),
msg.getDictionary(TRANSPORT));
validator.validateMessage(message, group, body);
}
private BdfDictionary getValidIntroductionResponse(boolean accept)
throws FormatException {
byte[] groupId = TestUtils.getRandomId();
byte[] sessionId = TestUtils.getRandomId();
long time = clock.currentTimeMillis();
byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
String transportId = TestUtils
.getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH);
BdfDictionary tProps = BdfDictionary.of(
new BdfEntry(TestUtils.getRandomString(MAX_PROPERTY_LENGTH),
TestUtils.getRandomString(MAX_PROPERTY_LENGTH))
);
BdfDictionary tp = BdfDictionary.of(
new BdfEntry(transportId, tProps)
);
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_RESPONSE);
msg.put(GROUP_ID, groupId);
msg.put(SESSION_ID, sessionId);
msg.put(ACCEPT, accept);
if (accept) {
msg.put(TIME, time);
msg.put(E_PUBLIC_KEY, publicKey);
msg.put(TRANSPORT, tp);
}
return msg;
}
//
// Introduction ACK
//
@Test
public void testValidateProperIntroductionAck() throws IOException {
final byte[] sessionId = TestUtils.getRandomId();
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_ACK);
msg.put(SESSION_ID, sessionId);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
BdfDictionary result =
validator.validateMessage(message, group, body);
assertEquals(Long.valueOf(TYPE_ACK), result.getLong(TYPE));
assertEquals(sessionId, result.getRaw(SESSION_ID));
context.assertIsSatisfied();
}
@Test(expected = FormatException.class)
public void testValidateTooLongIntroductionAck() throws IOException {
BdfDictionary msg = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_ACK),
new BdfEntry(SESSION_ID, TestUtils.getRandomId()),
new BdfEntry("garbage", TestUtils.getRandomString(255))
);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getString("garbage"));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionAckWithLongSessionId() throws IOException {
BdfDictionary msg = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_ACK),
new BdfEntry(SESSION_ID, new byte[SessionId.LENGTH + 1])
);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
validator.validateMessage(message, group, body);
}
//
// Introduction Abort
//
@Test
public void testValidateProperIntroductionAbort() throws IOException {
byte[] sessionId = TestUtils.getRandomId();
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_ABORT);
msg.put(SESSION_ID, sessionId);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
BdfDictionary result =
validator.validateMessage(message, group, body);
assertEquals(Long.valueOf(TYPE_ABORT), result.getLong(TYPE));
assertEquals(sessionId, result.getRaw(SESSION_ID));
context.assertIsSatisfied();
}
@Test(expected = FormatException.class)
public void testValidateTooLongIntroductionAbort() throws IOException {
BdfDictionary msg = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_ABORT),
new BdfEntry(SESSION_ID, TestUtils.getRandomId()),
new BdfEntry("garbage", TestUtils.getRandomString(255))
);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getString("garbage"));
validator.validateMessage(message, group, body);
}
}

View File

@@ -0,0 +1,95 @@
package org.briarproject.introduction;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.introduction.SessionId;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.system.Clock;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import static junit.framework.Assert.assertFalse;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
public class MessageSenderTest extends BriarTestCase {
final Mockery context;
final MessageSender messageSender;
final DatabaseComponent db;
final PrivateGroupFactory privateGroupFactory;
final ClientHelper clientHelper;
final MetadataEncoder metadataEncoder;
final MessageQueueManager messageQueueManager;
final Clock clock;
public MessageSenderTest() {
context = new Mockery();
db = context.mock(DatabaseComponent.class);
privateGroupFactory = context.mock(PrivateGroupFactory.class);
clientHelper = context.mock(ClientHelper.class);
metadataEncoder =
context.mock(MetadataEncoder.class);
messageQueueManager =
context.mock(MessageQueueManager.class);
clock = context.mock(Clock.class);
messageSender =
new MessageSender(db, clientHelper, clock, metadataEncoder,
messageQueueManager);
}
@Test
public void testSendMessage() throws DbException, FormatException {
final Transaction txn = new Transaction(null, false);
final Group privateGroup = new Group(new GroupId(TestUtils.getRandomId()),
new ClientId(TestUtils.getRandomId()), new byte[0]);
final SessionId sessionId = new SessionId(TestUtils.getRandomId());
final long time = 42L;
final BdfDictionary msg = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_ACK),
new BdfEntry(GROUP_ID, privateGroup.getId()),
new BdfEntry(SESSION_ID, sessionId)
);
final BdfList bodyList =
BdfList.of(TYPE_ACK, msg.getRaw(SESSION_ID));
final byte[] body = TestUtils.getRandomBytes(8);
final Metadata metadata = new Metadata();
context.checking(new Expectations() {{
oneOf(clientHelper).toByteArray(bodyList);
will(returnValue(body));
oneOf(db).getGroup(txn, privateGroup.getId());
will(returnValue(privateGroup));
oneOf(metadataEncoder).encode(msg);
will(returnValue(metadata));
oneOf(clock).currentTimeMillis();
will(returnValue(time));
oneOf(messageQueueManager)
.sendMessage(txn, privateGroup, time, body, metadata);
}});
messageSender.sendMessage(txn, msg);
context.assertIsSatisfied();
assertFalse(txn.isComplete());
}
}