diff --git a/core/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java b/core/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java index 90c3c50..4bfaba4 100644 --- a/core/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java +++ b/core/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java @@ -22,13 +22,11 @@ import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.exception.ApplicationException; import ch.dissem.bitmessage.factory.Factory; -import ch.dissem.bitmessage.utils.Decode; -import ch.dissem.bitmessage.utils.Encode; -import ch.dissem.bitmessage.utils.TTL; -import ch.dissem.bitmessage.utils.UnixTime; +import ch.dissem.bitmessage.utils.*; import java.io.*; import java.util.*; +import java.util.Collections; import static ch.dissem.bitmessage.utils.Singleton.cryptography; @@ -245,7 +243,7 @@ public class Plaintext implements Streamable { public void updateNextTry() { if (nextTry == null) { if (sent != null && to.has(Feature.DOES_ACK)) { - nextTry = sent + ttl; + nextTry = UnixTime.now(+ttl); retries++; } } else { diff --git a/core/src/test/java/ch/dissem/bitmessage/BitmessageContextTest.java b/core/src/test/java/ch/dissem/bitmessage/BitmessageContextTest.java index 6e49853..5ad91cb 100644 --- a/core/src/test/java/ch/dissem/bitmessage/BitmessageContextTest.java +++ b/core/src/test/java/ch/dissem/bitmessage/BitmessageContextTest.java @@ -20,6 +20,7 @@ import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.entity.Plaintext.Type; import ch.dissem.bitmessage.entity.payload.ObjectType; import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; @@ -63,6 +64,7 @@ public class BitmessageContextTest { .messageRepo(mock(MessageRepository.class)) .networkHandler(mock(NetworkHandler.class)) .nodeRegistry(mock(NodeRegistry.class)) + .labeler(spy(new DefaultLabeler())) .powRepo(spy(new ProofOfWorkRepository() { Map items = new HashMap<>(); @@ -205,7 +207,7 @@ public class BitmessageContextTest { assertEquals(2, ctx.internals().getProofOfWorkRepository().getItems().size()); verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce()) .putObject(object(MSG), eq(1000L), eq(1000L)); - verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Plaintext.Type.MSG)); + verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Type.MSG)); } @Test @@ -215,7 +217,7 @@ public class BitmessageContextTest { "Subject", "Message"); verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce()) .putObject(object(GET_PUBKEY), eq(1000L), eq(1000L)); - verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Plaintext.Type.MSG)); + verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Type.MSG)); } @Test(expected = IllegalArgumentException.class) @@ -234,12 +236,12 @@ public class BitmessageContextTest { verify(ctx.internals().getProofOfWorkEngine()) .calculateNonce(any(byte[].class), any(byte[].class), any(ProofOfWorkEngine.Callback.class)); verify(ctx.messages(), timeout(10000).atLeastOnce()) - .save(MessageMatchers.plaintext(Plaintext.Type.BROADCAST)); + .save(MessageMatchers.plaintext(Type.BROADCAST)); } @Test(expected = IllegalArgumentException.class) public void ensureSenderWithoutPrivateKeyThrowsException() { - Plaintext msg = new Plaintext.Builder(Plaintext.Type.BROADCAST) + Plaintext msg = new Plaintext.Builder(Type.BROADCAST) .from(new BitmessageAddress("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) .message("Subject", "Message") .build(); @@ -295,4 +297,19 @@ public class BitmessageContextTest { assertEquals(chan.getVersion(), Pubkey.LATEST_VERSION); assertTrue(chan.isChan()); } + + @Test + public void ensureUnacknowledgedMessageIsResent() throws Exception { + Plaintext plaintext = new Plaintext.Builder(Type.MSG) + .ttl(1) + .message("subject", "message") + .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) + .to(TestUtils.loadContact()) + .build(); + assertTrue(plaintext.getTo().has(Pubkey.Feature.DOES_ACK)); + when(ctx.messages().findMessagesToResend()).thenReturn(Collections.singletonList(plaintext)); + when(ctx.messages().getMessage(any(byte[].class))).thenReturn(plaintext); + ctx.internals().resendUnacknowledged(); + verify(ctx.labeler(), timeout(1000).times(1)).markAsSent(eq(plaintext)); + } } diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java index df8a42e..5fa88e7 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java @@ -26,6 +26,8 @@ import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MessageRepository; +import ch.dissem.bitmessage.utils.UnixTime; +import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; @@ -36,6 +38,7 @@ import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG; import static ch.dissem.bitmessage.entity.payload.Pubkey.Feature.DOES_ACK; import static ch.dissem.bitmessage.utils.Singleton.cryptography; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; public class JdbcMessageRepositoryTest extends TestBase { @@ -192,11 +195,40 @@ public class JdbcMessageRepositoryTest extends TestBase { } @Test - public void testRemove() throws Exception { + public void ensureMessageIsRemoved() throws Exception { Plaintext toRemove = repo.findMessages(Plaintext.Status.DRAFT, contactB).get(0); - repo.remove(toRemove); List messages = repo.findMessages(Plaintext.Status.DRAFT); - assertEquals(1, messages.size()); + assertEquals(2, messages.size()); + repo.remove(toRemove); + messages = repo.findMessages(Plaintext.Status.DRAFT); + assertThat(messages, hasSize(1)); + } + + @Test + public void ensureUnacknowledgedMessagesAreFoundForResend() throws Exception { + Plaintext message = new Plaintext.Builder(MSG) + .IV(new InventoryVector(cryptography().randomBytes(32))) + .from(identity) + .to(contactA) + .message("Subject", "Message") + .status(Plaintext.Status.SENT) + .ttl(1) + .build(); + message.updateNextTry(); + assertThat(message.getRetries(), is(1)); + assertThat(message.getNextTry(), greaterThan(UnixTime.now())); + assertThat(message.getNextTry(), lessThanOrEqualTo(UnixTime.now(+1))); + repo.save(message); + Thread.sleep(2100); + List<Plaintext> messagesToResend = repo.findMessagesToResend(); + assertThat(messagesToResend, hasSize(1)); + + message.updateNextTry(); + assertThat(message.getRetries(), is(2)); + assertThat(message.getNextTry(), greaterThan(UnixTime.now())); + repo.save(message); + messagesToResend = repo.findMessagesToResend(); + assertThat(messagesToResend, empty()); } private void addMessage(BitmessageAddress from, BitmessageAddress to, Plaintext.Status status, Label... labels) {