From 855d64dd6e8b0622f3efca83f5d2e93dbbdf315b Mon Sep 17 00:00:00 2001 From: Vatavuk Date: Wed, 8 Aug 2018 22:58:28 +0200 Subject: [PATCH 1/7] #16 implementing merge --- src/main/java/io/zold/api/Wallet.java | 68 ++++++++++++++++++----- src/test/java/io/zold/api/WalletTest.java | 59 ++++++++++++++++++-- 2 files changed, 110 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/zold/api/Wallet.java b/src/main/java/io/zold/api/Wallet.java index e6e4ab9..cdb0bc9 100644 --- a/src/main/java/io/zold/api/Wallet.java +++ b/src/main/java/io/zold/api/Wallet.java @@ -27,17 +27,24 @@ import java.io.IOException; import java.io.Writer; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import org.cactoos.collection.Filtered; import org.cactoos.iterable.IterableOf; +import org.cactoos.iterable.Joined; import org.cactoos.iterable.Mapped; import org.cactoos.iterable.Skipped; import org.cactoos.list.ListOf; import org.cactoos.scalar.CheckedScalar; +import org.cactoos.scalar.Or; +import org.cactoos.scalar.UncheckedScalar; +import org.cactoos.text.FormattedText; import org.cactoos.text.SplitText; import org.cactoos.text.TextOf; +import org.cactoos.text.UncheckedText; /** * Wallet. - * * @since 0.1 */ @SuppressWarnings({"PMD.ShortMethodName", "PMD.TooManyMethods"}) @@ -64,7 +71,7 @@ public interface Wallet { * @param other Other wallet * @return The merged wallet */ - Wallet merge(Wallet other); + Wallet merge(Wallet other) throws IOException; /** * This wallet's ledger. @@ -74,7 +81,6 @@ public interface Wallet { /** * A Fake {@link Wallet}. - * * @since 1.0 */ final class Fake implements Wallet { @@ -84,13 +90,23 @@ final class Fake implements Wallet { */ private final long id; + private final Iterable transactions; + /** * Ctor. - * * @param id The wallet id. */ public Fake(final long id) { + this(id, new IterableOf<>()); + } + + public Fake(final long id, final Transaction... transactions) { + this(id, new IterableOf<>(transactions)); + } + + public Fake(final long id, final Iterable transactions) { this.id = id; + this.transactions = transactions; } @Override @@ -110,7 +126,7 @@ public Wallet merge(final Wallet other) { @Override public Iterable ledger() { - return new IterableOf<>(); + return this.transactions; } } @@ -158,17 +174,43 @@ public void pay(final long amt, final long bnf) throws IOException { } } - // @todo #6:30min Implement merge method. This should merge this wallet - // with a copy of the same wallet. It should throw an error if a - // wallet is provided. Also add a unit test to replace - // WalletTest.mergeIsNotYetImplemented(). @Override - public Wallet merge(final Wallet other) { - throw new UnsupportedOperationException( - "merge() not yet supported" - ); + public Wallet merge(final Wallet other) throws IOException { + if (other.id() != this.id()) { + throw new IOException( + new UncheckedText( + new FormattedText( + "Wallet ID mismatch, ours is %d, theirs is %d", + other.id(), + this.id() + ) + ).asString() + ); + } + final Iterable ledger = this.ledger(); + final Collection candidates = new ArrayList<>(); + for (final Transaction remote : other.ledger()) { + final Collection filtered = + new Filtered<>( + input -> new UncheckedScalar<>( + new Or( + () -> remote.equals(input), + () -> remote.id() == input.id() && + remote.bnf() == input.bnf(), + () -> remote.id() == input.id() && remote.amount() < 0L, + () -> remote.prefix().equals(input.prefix()) + ) + ).value(), + ledger + ); + if (filtered.isEmpty()) { + candidates.add(remote); + } + } + return new Wallet.Fake(this.id(), new Joined<>(ledger, candidates)); } + @Override public Iterable ledger() { return new Mapped<>( diff --git a/src/test/java/io/zold/api/WalletTest.java b/src/test/java/io/zold/api/WalletTest.java index 3601045..a707c5b 100644 --- a/src/test/java/io/zold/api/WalletTest.java +++ b/src/test/java/io/zold/api/WalletTest.java @@ -27,6 +27,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import org.cactoos.collection.CollectionOf; import org.cactoos.list.ListOf; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; @@ -94,10 +95,60 @@ public void pay() throws IOException { ); } - @Test(expected = UnsupportedOperationException.class) - public void mergeIsNotYetImplemented() throws IOException { - new Wallet.File(this.folder.newFile().toPath()).merge( - new Wallet.File(this.folder.newFile().toPath()) + @Test + public void mergesWallets() throws IOException { + final long id = 5124095577148911L; + final Wallet wallet = new Wallet.File(this.wallet(id)); + final Wallet merged = wallet.merge( + new Wallet.Fake( + id, + new RtTransaction("abcd;2017-07-19T21:25:07Z;0000000000a72366;xxsQuJa9;98bb82c81735c4ee;") + ) + ); + MatcherAssert.assertThat( + new CollectionOf<>(merged.ledger()).size(), + new IsEqual<>(new CollectionOf<>(wallet.ledger()).size() + 1) + ); + } + + @Test + public void doesNotMergeWalletsWithDifferentId() throws IOException { + this.error.expect(IOException.class); + this.error.expectMessage("Wallet ID mismatch, ours is 123, theirs is 5124095577148911"); + final long id = 5124095577148911L; + final Wallet wallet = new Wallet.File(this.wallet(id)); + wallet.merge(new Wallet.Fake(123L)); + } + + @Test + public void doesNotMergeExistingTransactions() throws IOException { + final long id = 5124095577148911L; + final Wallet wallet = new Wallet.File(this.wallet(id)); + final Wallet merged = wallet.merge( + new Wallet.Fake( + id, + new RtTransaction("003b;2017-07-19T21:25:07Z;ffffffffffa72367;xksQuJa9;98bb82c81735c4ee;For food;QCuLuVr4...") + ) + ); + MatcherAssert.assertThat( + new CollectionOf<>(merged.ledger()).size(), + new IsEqual<>(new CollectionOf<>(wallet.ledger()).size()) + ); + } + + @Test + public void doesNotMergeTransactionsWithSameIdAndBnf() throws IOException { + final long id = 5124095577148911L; + final Wallet wallet = new Wallet.File(this.wallet(id)); + final Wallet merged = wallet.merge( + new Wallet.Fake( + id, + new RtTransaction("003b;2017-07-18T21:25:07Z;ffffffffffa72367;xxsQuJa9;98bb82c81735c4ee;For food;QCuLuVr4...") + ) + ); + MatcherAssert.assertThat( + new CollectionOf<>(merged.ledger()).size(), + new IsEqual<>(new CollectionOf<>(wallet.ledger()).size()) ); } From 233dd33944fbfb4fb2e9fddd17b375d1ac8756f5 Mon Sep 17 00:00:00 2001 From: Vatavuk Date: Wed, 8 Aug 2018 23:25:34 +0200 Subject: [PATCH 2/7] #16 merge method implementation --- src/main/java/io/zold/api/Wallet.java | 45 ++++++++++++++++++++--- src/test/java/io/zold/api/WalletTest.java | 40 +++++++++++++++++++- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/zold/api/Wallet.java b/src/main/java/io/zold/api/Wallet.java index cdb0bc9..d8c952e 100644 --- a/src/main/java/io/zold/api/Wallet.java +++ b/src/main/java/io/zold/api/Wallet.java @@ -53,6 +53,7 @@ public interface Wallet { * This wallet's ID: an unsigned 64-bit integer. * @return This wallet's id * @throws IOException If an IO error occurs + * @checkstyle ClassDataAbstractionCouplingCheck (500 lines) * @checkstyle MethodName (2 lines) */ long id() throws IOException; @@ -70,6 +71,7 @@ public interface Wallet { * same wallet, as identified by their {@link #id() id}. * @param other Other wallet * @return The merged wallet + * @throws IOException If an IO error occurs */ Wallet merge(Wallet other) throws IOException; @@ -90,6 +92,9 @@ final class Fake implements Wallet { */ private final long id; + /** + * Transactions. + */ private final Iterable transactions; /** @@ -100,10 +105,20 @@ public Fake(final long id) { this(id, new IterableOf<>()); } + /** + * Ctor. + * @param id The wallet id. + * @param transactions Transactions. + */ public Fake(final long id, final Transaction... transactions) { this(id, new IterableOf<>(transactions)); } + /** + * Ctor. + * @param id The wallet id. + * @param transactions Transactions. + */ public Fake(final long id, final Iterable transactions) { this.id = id; this.transactions = transactions; @@ -175,6 +190,21 @@ public void pay(final long amt, final long bnf) throws IOException { } @Override + // @todo #16:30min Following transactions should be ignored according + // to the whitepaper: + // a) If the transaction is negative and its signature is not valid, + // it is ignored; + // b) If the transaction makes the balance of the wallet negative, + // it is ignored; + // c) If the transaction is positive and it’s absent in the paying + // wallet (which exists at the node), it’s ignored; If the paying + // wallet doesn’t exist at the node, the transaction is ignored; + // + // @todo: #16:30min Merge method should update transactions + // in wallet's file and return concrete implementation not a fake one. + // Beware that tests should be refactored to take care of file cleanup + // after each case that merges wallets. + // public Wallet merge(final Wallet other) throws IOException { if (other.id() != this.id()) { throw new IOException( @@ -188,16 +218,17 @@ public Wallet merge(final Wallet other) throws IOException { ); } final Iterable ledger = this.ledger(); - final Collection candidates = new ArrayList<>(); + final Collection candidates = new ArrayList<>(0); for (final Transaction remote : other.ledger()) { final Collection filtered = new Filtered<>( input -> new UncheckedScalar<>( new Or( () -> remote.equals(input), - () -> remote.id() == input.id() && - remote.bnf() == input.bnf(), - () -> remote.id() == input.id() && remote.amount() < 0L, + () -> remote.id() == input.id() + && remote.bnf().equals(input.bnf()), + () -> remote.id() == input.id() + && remote.amount() < 0L, () -> remote.prefix().equals(input.prefix()) ) ).value(), @@ -207,10 +238,12 @@ public Wallet merge(final Wallet other) throws IOException { candidates.add(remote); } } - return new Wallet.Fake(this.id(), new Joined<>(ledger, candidates)); + return new Wallet.Fake( + this.id(), + new Joined(ledger, candidates) + ); } - @Override public Iterable ledger() { return new Mapped<>( diff --git a/src/test/java/io/zold/api/WalletTest.java b/src/test/java/io/zold/api/WalletTest.java index a707c5b..d9a88f8 100644 --- a/src/test/java/io/zold/api/WalletTest.java +++ b/src/test/java/io/zold/api/WalletTest.java @@ -46,7 +46,10 @@ * @checkstyle JavadocMethodCheck (500 lines) * @checkstyle JavadocVariableCheck (500 lines) * @checkstyle MagicNumberCheck (500 lines) + * @checkstyle LineLengthCheck (500 lines) + * @checkstyle ClassDataAbstractionCouplingCheck (500 lines) */ +@SuppressWarnings("PMD.TooManyMethods") public final class WalletTest { @Rule @@ -127,7 +130,7 @@ public void doesNotMergeExistingTransactions() throws IOException { final Wallet merged = wallet.merge( new Wallet.Fake( id, - new RtTransaction("003b;2017-07-19T21:25:07Z;ffffffffffa72367;xksQuJa9;98bb82c81735c4ee;For food;QCuLuVr4...") + new RtTransaction("003b;2017-07-19T21:25:07Z;0000000000a72366;xksQuJa9;98bb82c81735c4ee;For food;QCuLuVr4...") ) ); MatcherAssert.assertThat( @@ -143,7 +146,40 @@ public void doesNotMergeTransactionsWithSameIdAndBnf() throws IOException { final Wallet merged = wallet.merge( new Wallet.Fake( id, - new RtTransaction("003b;2017-07-18T21:25:07Z;ffffffffffa72367;xxsQuJa9;98bb82c81735c4ee;For food;QCuLuVr4...") + new RtTransaction("003b;2017-07-18T21:25:07Z;0000000000a72366;xxxxuuuu;98bb82c81735c4ee;For food;QCuLuVr4...") + ) + ); + MatcherAssert.assertThat( + new CollectionOf<>(merged.ledger()).size(), + new IsEqual<>(new CollectionOf<>(wallet.ledger()).size()) + ); + } + + @Test + public void doesNotMergeTransactionsWithSameIdAndNegativeAmount() + throws IOException { + final long id = 5124095577148911L; + final Wallet wallet = new Wallet.File(this.wallet(id)); + final Wallet merged = wallet.merge( + new Wallet.Fake( + id, + new RtTransaction("003b;2017-07-18T21:25:07Z;ffffffffffa72366;xxxxuuuu;98bb82c81735c4ee;For food;QCuLuVr4...") + ) + ); + MatcherAssert.assertThat( + new CollectionOf<>(merged.ledger()).size(), + new IsEqual<>(new CollectionOf<>(wallet.ledger()).size()) + ); + } + + @Test + public void doesNotMergeTransactionsWithSamePrefix() throws IOException { + final long id = 5124095577148911L; + final Wallet wallet = new Wallet.File(this.wallet(id)); + final Wallet merged = wallet.merge( + new Wallet.Fake( + id, + new RtTransaction("0011;2017-07-18T21:25:07Z;0000000000a72366;xksQuJa9;99bb82c81735c4ee;For food;QCuLuVr4...") ) ); MatcherAssert.assertThat( From 4381fda8feb12e3b1938b0b69d721918ac57d377 Mon Sep 17 00:00:00 2001 From: Vatavuk Date: Thu, 9 Aug 2018 07:27:01 +0200 Subject: [PATCH 3/7] #16 rev comments --- src/main/java/io/zold/api/Wallet.java | 38 +++++++++++---------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/zold/api/Wallet.java b/src/main/java/io/zold/api/Wallet.java index d8c952e..70be1a9 100644 --- a/src/main/java/io/zold/api/Wallet.java +++ b/src/main/java/io/zold/api/Wallet.java @@ -27,8 +27,6 @@ import java.io.IOException; import java.io.Writer; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; import org.cactoos.collection.Filtered; import org.cactoos.iterable.IterableOf; import org.cactoos.iterable.Joined; @@ -218,26 +216,22 @@ public Wallet merge(final Wallet other) throws IOException { ); } final Iterable ledger = this.ledger(); - final Collection candidates = new ArrayList<>(0); - for (final Transaction remote : other.ledger()) { - final Collection filtered = - new Filtered<>( - input -> new UncheckedScalar<>( - new Or( - () -> remote.equals(input), - () -> remote.id() == input.id() - && remote.bnf().equals(input.bnf()), - () -> remote.id() == input.id() - && remote.amount() < 0L, - () -> remote.prefix().equals(input.prefix()) - ) - ).value(), - ledger - ); - if (filtered.isEmpty()) { - candidates.add(remote); - } - } + final Iterable candidates = new Filtered<>( + origin -> new Filtered<>( + incoming -> new UncheckedScalar<>( + new Or( + () -> incoming.equals(origin), + () -> incoming.id() == origin.id() + && incoming.bnf().equals(origin.bnf()), + () -> incoming.id() == origin.id() + && incoming.amount() < 0L, + () -> incoming.prefix().equals(origin.prefix()) + ) + ).value(), + ledger + ).isEmpty(), + other.ledger() + ); return new Wallet.Fake( this.id(), new Joined(ledger, candidates) From ec9250b0bd3625e3b069750319d8647044a2f3ed Mon Sep 17 00:00:00 2001 From: Vatavuk Date: Thu, 9 Aug 2018 08:11:54 +0200 Subject: [PATCH 4/7] #16 rev comments in tests --- src/test/java/io/zold/api/WalletTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/zold/api/WalletTest.java b/src/test/java/io/zold/api/WalletTest.java index d9a88f8..c719c89 100644 --- a/src/test/java/io/zold/api/WalletTest.java +++ b/src/test/java/io/zold/api/WalletTest.java @@ -47,7 +47,7 @@ * @checkstyle JavadocVariableCheck (500 lines) * @checkstyle MagicNumberCheck (500 lines) * @checkstyle LineLengthCheck (500 lines) - * @checkstyle ClassDataAbstractionCouplingCheck (500 lines) + * @checkstyle ClassDataAbstractionCouplingCheck (3 lines) */ @SuppressWarnings("PMD.TooManyMethods") public final class WalletTest { From 7f7651e5280519562b4a8715fb9b863c21e39a6e Mon Sep 17 00:00:00 2001 From: Vatavuk Date: Sat, 11 Aug 2018 20:10:37 +0200 Subject: [PATCH 5/7] #16 rev comments --- src/main/java/io/zold/api/Wallet.java | 10 +++++----- src/test/java/io/zold/api/WalletTest.java | 9 +++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/zold/api/Wallet.java b/src/main/java/io/zold/api/Wallet.java index 70be1a9..d4bcfad 100644 --- a/src/main/java/io/zold/api/Wallet.java +++ b/src/main/java/io/zold/api/Wallet.java @@ -199,9 +199,9 @@ public void pay(final long amt, final long bnf) throws IOException { // wallet doesn’t exist at the node, the transaction is ignored; // // @todo: #16:30min Merge method should update transactions - // in wallet's file and return concrete implementation not a fake one. - // Beware that tests should be refactored to take care of file cleanup - // after each case that merges wallets. + // in wallet's file and return concrete implementation not a fake one. + // Beware that tests should be refactored to take care of file cleanup + // after each case that merges wallets. // public Wallet merge(final Wallet other) throws IOException { if (other.id() != this.id()) { @@ -217,8 +217,8 @@ public Wallet merge(final Wallet other) throws IOException { } final Iterable ledger = this.ledger(); final Iterable candidates = new Filtered<>( - origin -> new Filtered<>( - incoming -> new UncheckedScalar<>( + incoming -> new Filtered<>( + origin -> new UncheckedScalar<>( new Or( () -> incoming.equals(origin), () -> incoming.id() == origin.id() diff --git a/src/test/java/io/zold/api/WalletTest.java b/src/test/java/io/zold/api/WalletTest.java index c719c89..513eaba 100644 --- a/src/test/java/io/zold/api/WalletTest.java +++ b/src/test/java/io/zold/api/WalletTest.java @@ -46,7 +46,6 @@ * @checkstyle JavadocMethodCheck (500 lines) * @checkstyle JavadocVariableCheck (500 lines) * @checkstyle MagicNumberCheck (500 lines) - * @checkstyle LineLengthCheck (500 lines) * @checkstyle ClassDataAbstractionCouplingCheck (3 lines) */ @SuppressWarnings("PMD.TooManyMethods") @@ -105,6 +104,7 @@ public void mergesWallets() throws IOException { final Wallet merged = wallet.merge( new Wallet.Fake( id, + //@checkstyle LineLengthCheck (1 lines) new RtTransaction("abcd;2017-07-19T21:25:07Z;0000000000a72366;xxsQuJa9;98bb82c81735c4ee;") ) ); @@ -117,6 +117,7 @@ public void mergesWallets() throws IOException { @Test public void doesNotMergeWalletsWithDifferentId() throws IOException { this.error.expect(IOException.class); + //@checkstyle LineLengthCheck (1 lines) this.error.expectMessage("Wallet ID mismatch, ours is 123, theirs is 5124095577148911"); final long id = 5124095577148911L; final Wallet wallet = new Wallet.File(this.wallet(id)); @@ -130,6 +131,7 @@ public void doesNotMergeExistingTransactions() throws IOException { final Wallet merged = wallet.merge( new Wallet.Fake( id, + //@checkstyle LineLengthCheck (1 lines) new RtTransaction("003b;2017-07-19T21:25:07Z;0000000000a72366;xksQuJa9;98bb82c81735c4ee;For food;QCuLuVr4...") ) ); @@ -146,6 +148,7 @@ public void doesNotMergeTransactionsWithSameIdAndBnf() throws IOException { final Wallet merged = wallet.merge( new Wallet.Fake( id, + //@checkstyle LineLengthCheck (1 lines) new RtTransaction("003b;2017-07-18T21:25:07Z;0000000000a72366;xxxxuuuu;98bb82c81735c4ee;For food;QCuLuVr4...") ) ); @@ -163,7 +166,8 @@ public void doesNotMergeTransactionsWithSameIdAndNegativeAmount() final Wallet merged = wallet.merge( new Wallet.Fake( id, - new RtTransaction("003b;2017-07-18T21:25:07Z;ffffffffffa72366;xxxxuuuu;98bb82c81735c4ee;For food;QCuLuVr4...") + //@checkstyle LineLengthCheck (1 lines) + new RtTransaction("003b;2017-07-18T21:25:07Z;ffffffffffa72366;xxxxuuuu;98bb82c81735c4ff;For food;QCuLuVr4...") ) ); MatcherAssert.assertThat( @@ -179,6 +183,7 @@ public void doesNotMergeTransactionsWithSamePrefix() throws IOException { final Wallet merged = wallet.merge( new Wallet.Fake( id, + //@checkstyle LineLengthCheck (1 lines) new RtTransaction("0011;2017-07-18T21:25:07Z;0000000000a72366;xksQuJa9;99bb82c81735c4ee;For food;QCuLuVr4...") ) ); From 9cc6e8aa2e098e71e4557ca2079d2a384389b8c8 Mon Sep 17 00:00:00 2001 From: Vatavuk Date: Sun, 12 Aug 2018 17:46:20 +0200 Subject: [PATCH 6/7] #16 rearanged puzzles --- src/main/java/io/zold/api/Wallet.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/zold/api/Wallet.java b/src/main/java/io/zold/api/Wallet.java index d4bcfad..ab93c36 100644 --- a/src/main/java/io/zold/api/Wallet.java +++ b/src/main/java/io/zold/api/Wallet.java @@ -44,6 +44,10 @@ /** * Wallet. * @since 0.1 + * @todo: #16:30min Merge method should update transactions + * in wallet's file and return concrete implementation not a fake one. + * Beware that tests should be refactored to take care of file cleanup + * after each case that merges wallets. */ @SuppressWarnings({"PMD.ShortMethodName", "PMD.TooManyMethods"}) public interface Wallet { @@ -187,7 +191,6 @@ public void pay(final long amt, final long bnf) throws IOException { } } - @Override // @todo #16:30min Following transactions should be ignored according // to the whitepaper: // a) If the transaction is negative and its signature is not valid, @@ -197,12 +200,7 @@ public void pay(final long amt, final long bnf) throws IOException { // c) If the transaction is positive and it’s absent in the paying // wallet (which exists at the node), it’s ignored; If the paying // wallet doesn’t exist at the node, the transaction is ignored; - // - // @todo: #16:30min Merge method should update transactions - // in wallet's file and return concrete implementation not a fake one. - // Beware that tests should be refactored to take care of file cleanup - // after each case that merges wallets. - // + @Override public Wallet merge(final Wallet other) throws IOException { if (other.id() != this.id()) { throw new IOException( From 4e9209f6235fbb0880deacc29662319d844a4708 Mon Sep 17 00:00:00 2001 From: Vatavuk Date: Sun, 12 Aug 2018 22:04:53 +0200 Subject: [PATCH 7/7] puzzle format fix --- src/main/java/io/zold/api/Wallet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/zold/api/Wallet.java b/src/main/java/io/zold/api/Wallet.java index ab93c36..b4d6850 100644 --- a/src/main/java/io/zold/api/Wallet.java +++ b/src/main/java/io/zold/api/Wallet.java @@ -44,7 +44,7 @@ /** * Wallet. * @since 0.1 - * @todo: #16:30min Merge method should update transactions + * @todo #16:30min Merge method should update transactions * in wallet's file and return concrete implementation not a fake one. * Beware that tests should be refactored to take care of file cleanup * after each case that merges wallets.