diff --git a/src/main/java/io/zold/api/CpTransaction.java b/src/main/java/io/zold/api/CpTransaction.java index 1b85b9e..12fb129 100644 --- a/src/main/java/io/zold/api/CpTransaction.java +++ b/src/main/java/io/zold/api/CpTransaction.java @@ -27,9 +27,10 @@ * Computed Transaction. * * @since 1.0 - * @todo #29:30min Implement the computation of the transaction string - * based on the white paper. The unit test should also be updated to - * ensure it works as expected. + * @todo #54:30min Implement the computation of the transaction string + * based on the white paper. The unit tests should also be updated to + * ensure it works as expected and test for + * returnSignatureForNegativeTransaction must be implemented. */ public final class CpTransaction extends TransactionEnvelope { diff --git a/src/main/java/io/zold/api/Network.java b/src/main/java/io/zold/api/Network.java index 7e3364d..c28f126 100644 --- a/src/main/java/io/zold/api/Network.java +++ b/src/main/java/io/zold/api/Network.java @@ -31,9 +31,9 @@ * @since 0.1 */ public interface Network extends Iterable { + /** - * Push the wallet to the network. The network will select the - * remote node with the highest score (with a minimum of {@code 16}). + * Push the wallet to the network. * @param wallet The wallet */ void push(Wallet wallet); diff --git a/src/main/java/io/zold/api/Remote.java b/src/main/java/io/zold/api/Remote.java index 9065dd5..4fddd08 100644 --- a/src/main/java/io/zold/api/Remote.java +++ b/src/main/java/io/zold/api/Remote.java @@ -24,6 +24,9 @@ package io.zold.api; +import org.cactoos.iterable.Repeated; +import org.cactoos.text.RandomText; + /** * Remote node. * @@ -50,18 +53,28 @@ public interface Remote { Wallet pull(long id); /** - * Fake Remote. + * A Fake {@link Remote}. */ final class Fake implements Remote { /** - * Score. + * The remote's score. */ private final Score score; /** * Ctor. - * @param score Score + * @param val The remote's score value + */ + public Fake(final int val) { + this(new RtScore( + new Repeated<>(val, new RandomText()) + )); + } + + /** + * Ctor. + * @param score The remote's score */ public Fake(final Score score) { this.score = score; diff --git a/src/main/java/io/zold/api/RtNetwork.java b/src/main/java/io/zold/api/RtNetwork.java index c1fc783..108670b 100644 --- a/src/main/java/io/zold/api/RtNetwork.java +++ b/src/main/java/io/zold/api/RtNetwork.java @@ -54,10 +54,6 @@ public final class RtNetwork implements Network { this.nodes = remotes; } - // @todo #5:30min Implement scoring algorithm when paying taxes. Scoring - // algorithm must select the node with the highest score and with score - // >= 16. There are some tests for the scoring algorithm in NetworkTest: - // remove ignore tag from them after algorithm implementation. @Override public void push(final Wallet wallet) { this.nodes.forEach( diff --git a/src/main/java/io/zold/api/RtTransaction.java b/src/main/java/io/zold/api/RtTransaction.java index c672525..62ba285 100644 --- a/src/main/java/io/zold/api/RtTransaction.java +++ b/src/main/java/io/zold/api/RtTransaction.java @@ -54,9 +54,9 @@ final class RtTransaction implements Transaction { ); /** - * Pattern for amount String. + * Pattern for 16 symbol hex string. */ - private static final Pattern AMT = Pattern.compile("[A-Fa-f0-9]{16}"); + private static final Pattern HEX = Pattern.compile("[A-Fa-f0-9]{16}"); /** * Pattern for parsing Signature. @@ -138,7 +138,7 @@ public long amount() throws IOException { ) ).value() ).asString(); - if (!RtTransaction.AMT.matcher(amnt).matches()) { + if (!RtTransaction.HEX.matcher(amnt).matches()) { throw new IOException( new UncheckedText( new FormattedText( @@ -183,12 +183,27 @@ public String prefix() throws IOException { } } - // @todo #15:30min Implement bnf() by parsing the string representation - // of transaction according to the pattern, described in the white - // paper. Replace relevant test case with actual tests. @Override - public long bnf() { - throw new UnsupportedOperationException("bnf() not yet implemented"); + public String bnf() throws IOException { + final String bnf = + new IoCheckedScalar<>( + () -> new ItemAt<>( + // @checkstyle MagicNumberCheck (1 line) + 4, new SplitText(this.transaction, ";") + ).value().asString() + ).value(); + if (!RtTransaction.HEX.matcher(bnf).matches()) { + throw new IOException( + new UncheckedText( + new FormattedText( + // @checkstyle LineLength (1 line) + "Invalid bnf string '%s', expecting hex string with 16 symbols", + bnf + ) + ).asString() + ); + } + return bnf; } @Override diff --git a/src/main/java/io/zold/api/TaxBeneficiaries.java b/src/main/java/io/zold/api/TaxBeneficiaries.java new file mode 100644 index 0000000..d806994 --- /dev/null +++ b/src/main/java/io/zold/api/TaxBeneficiaries.java @@ -0,0 +1,54 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Yegor Bugayenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package io.zold.api; + +import java.util.Comparator; +import org.cactoos.iterable.Filtered; +import org.cactoos.iterable.IterableEnvelope; +import org.cactoos.iterable.LengthOf; +import org.cactoos.iterable.Sorted; + +/** + * {@link Remote} nodes that should receive taxes. + * + * @since 1.0 + */ +public final class TaxBeneficiaries extends IterableEnvelope { + + /** + * Ctor. + * + * @param nodes Remote nodes to select from. + */ + public TaxBeneficiaries(final Iterable nodes) { + super(() -> new Sorted<>( + Comparator.comparing(Remote::score), + new Filtered<>( + // @checkstyle MagicNumberCheck (1 line) + n -> new LengthOf(n.score().suffixes()).intValue() >= 16, + nodes + ) + )); + } +} diff --git a/src/main/java/io/zold/api/Taxes.java b/src/main/java/io/zold/api/Taxes.java new file mode 100644 index 0000000..74fdea7 --- /dev/null +++ b/src/main/java/io/zold/api/Taxes.java @@ -0,0 +1,69 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Yegor Bugayenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package io.zold.api; + +import org.cactoos.Proc; +import org.cactoos.text.FormattedText; +import org.cactoos.text.UncheckedText; + +/** + * Taxes payment algorithm. + * + * @since 1.0 + * @todo #40:30min Implement tax payment to remote nodes. + * Payment should happen only if the wallet is in debt of more than + * 1 Zold. Debt is difference between current taxes that should have + * been paid by wallet (see whitepaper for formula) and how much it + * already paid in the past. A first algorithm could pay the + * max to each node until there is nothing else to pay. The test + * must also be updated. + */ +public final class Taxes implements Proc { + + /** + * The beneficiary nodes. + */ + private final Iterable bnfs; + + /** + * Ctor. + * + * @param nodes Remote nodes. + */ + public Taxes(final Iterable nodes) { + this.bnfs = new TaxBeneficiaries(nodes); + } + + @Override + public void exec(final Wallet wallet) { + throw new UnsupportedOperationException( + new UncheckedText( + new FormattedText( + "paying taxes to %s not yet supported", + this.bnfs + ) + ).asString() + ); + } +} diff --git a/src/main/java/io/zold/api/Transaction.java b/src/main/java/io/zold/api/Transaction.java index fd423f5..763fa40 100644 --- a/src/main/java/io/zold/api/Transaction.java +++ b/src/main/java/io/zold/api/Transaction.java @@ -68,8 +68,9 @@ public interface Transaction { /** * Beneficiary. * @return Beneficiary + * @throws IOException When something goes wrong */ - long bnf(); + String bnf() throws IOException; /** * Details. diff --git a/src/main/java/io/zold/api/Wallet.java b/src/main/java/io/zold/api/Wallet.java index e6e4ab9..2e6b7da 100644 --- a/src/main/java/io/zold/api/Wallet.java +++ b/src/main/java/io/zold/api/Wallet.java @@ -72,6 +72,12 @@ public interface Wallet { */ Iterable ledger(); + /** + * This wallet's RSA key. + * @return This wallet's RSA key. + */ + String key(); + /** * A Fake {@link Wallet}. * @@ -112,6 +118,11 @@ public Wallet merge(final Wallet other) { public Iterable ledger() { return new IterableOf<>(); } + + @Override + public String key() { + return Long.toString(this.id); + } } /** @@ -185,5 +196,15 @@ public Iterable ledger() { ) ); } + + // @todo #54:30min Implement key method. This should return the + // public RSA key of the wallet owner in Base64. Also add a unit test + // to replace WalletTest.keyIsNotYetImplemented(). + @Override + public String key() { + throw new UnsupportedOperationException( + "key() not yet supported" + ); + } } } diff --git a/src/main/java/io/zold/api/Wallets.java b/src/main/java/io/zold/api/Wallets.java index f38fbd2..1ffd451 100644 --- a/src/main/java/io/zold/api/Wallets.java +++ b/src/main/java/io/zold/api/Wallets.java @@ -24,6 +24,8 @@ package io.zold.api; +import java.io.IOException; + /** * Wallets. * @@ -32,7 +34,8 @@ public interface Wallets extends Iterable { /** * Create a wallet. - * @return The new wallet + * @return The new wallet. + * @throws IOException If an error occurs. */ - Wallet create(); + Wallet create() throws IOException; } diff --git a/src/main/java/io/zold/api/WalletsIn.java b/src/main/java/io/zold/api/WalletsIn.java index 3568423..4ce5351 100644 --- a/src/main/java/io/zold/api/WalletsIn.java +++ b/src/main/java/io/zold/api/WalletsIn.java @@ -25,20 +25,23 @@ import java.io.IOException; import java.nio.file.FileSystems; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Iterator; +import java.util.Random; import org.cactoos.Scalar; import org.cactoos.func.IoCheckedFunc; import org.cactoos.io.Directory; import org.cactoos.iterable.Filtered; import org.cactoos.iterable.Mapped; import org.cactoos.scalar.IoCheckedScalar; -import org.cactoos.scalar.StickyScalar; -import org.cactoos.scalar.SyncScalar; +import org.cactoos.scalar.SolidScalar; +import org.cactoos.text.FormattedText; +import org.cactoos.text.JoinedText; +import org.cactoos.text.UncheckedText; /** * Wallets in path. - * * @since 0.1 * @checkstyle ClassDataAbstractionCoupling (2 lines) */ @@ -54,6 +57,16 @@ public final class WalletsIn implements Wallets { */ private final IoCheckedFunc filter; + /** + * Wallets file extension. + */ + private final String ext; + + /** + * Randomizer. + */ + private final Random random; + /** * Ctor. * @param pth Path with wallets @@ -61,40 +74,72 @@ public final class WalletsIn implements Wallets { public WalletsIn(final Path pth) { this( () -> pth, - "z" + "z", + new Random() + ); + } + + /** + * Ctor. + * @param pth Path with wallets + * @param random Randomizer + */ + public WalletsIn(final Path pth, final Random random) { + this( + () -> pth, + "z", + random ); } /** * Ctor. * @param pth Path with wallets - * @param ext File extension to match + * @param random Randomizer + * @param ext Wallets file extension */ - public WalletsIn(final Scalar pth, final String ext) { + public WalletsIn(final Scalar pth, final String ext, + final Random random) { this.path = new IoCheckedScalar<>( - new SyncScalar<>( - new StickyScalar<>(pth) - ) + new SolidScalar<>(pth) ); this.filter = new IoCheckedFunc( (file) -> file.toFile().isFile() && FileSystems.getDefault() - .getPathMatcher(String.format("glob:**.%s", ext)) - .matches(file) + .getPathMatcher(String.format("glob:**.%s", ext)) + .matches(file) ); + this.ext = ext; + this.random = random; } - // @todo #4:30min Return the new instance of the Wallet, that will - // be created in the path with all wallets. Should be taken care of - // after Wallet interface will have implementations. Cover with tests and - // remove irrelevant test case. + // @todo #12:30min Create the new wallet in the path with all wallets. + // It should contain the correct content according to the + // white paper. Also add a the test to validate everything is ok. @Override - public Wallet create() { - throw new UnsupportedOperationException("create() not yet supported"); + public Wallet create() throws IOException { + final Path wpth = this.path.value().resolve( + new JoinedText( + ".", + Long.toHexString(this.random.nextLong()), + this.ext + ).asString() + ); + if (wpth.toFile().exists()) { + throw new IOException( + new UncheckedText( + new FormattedText( + "Wallet in path %s already exists", + wpth.toUri().getPath() + ) + ).asString() + ); + } + Files.createFile(wpth); + return new Wallet.File(wpth); } @Override - @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public Iterator iterator() { try { return new Mapped( diff --git a/src/test/java/io/zold/api/CpTransactionTest.java b/src/test/java/io/zold/api/CpTransactionTest.java index 228b7c5..633dc78 100644 --- a/src/test/java/io/zold/api/CpTransactionTest.java +++ b/src/test/java/io/zold/api/CpTransactionTest.java @@ -24,6 +24,10 @@ package io.zold.api; import java.io.IOException; +import org.hamcrest.MatcherAssert; +import org.hamcrest.core.IsEqual; +import org.hamcrest.core.StringContains; +import org.junit.Ignore; import org.junit.Test; /** @@ -35,8 +39,57 @@ * @checkstyle MagicNumberCheck (500 lines) */ public final class CpTransactionTest { - @Test(expected = IOException.class) - public void cpTransactionIsNotYetImplemented() throws IOException { - new CpTransaction(1, 1234).amount(); + + @Test + @Ignore + public void returnAmount() throws IOException { + final long amount = 256; + MatcherAssert.assertThat( + "Cannot return amount", + new CpTransaction(amount, 1024).amount(), + new IsEqual<>(256) + ); + } + + @Test + @Ignore + public void returnSignatureForPositiveTransaction() throws IOException { + final long id = 1024; + MatcherAssert.assertThat( + "Cannot return signature", + new CpTransaction(256, id).signature(), + new IsEqual<>("1024") + ); + } + + @Test(expected = UnsupportedOperationException.class) + public void returnSignatureForNegativeTransaction() throws IOException { + throw new UnsupportedOperationException( + "returnSignatureForNegativeTransaction() not yet implemented" + ); + } + + @Test + @Ignore + public void returnPrefix() throws IOException { + final long id = 1024; + final Wallet wallet = new Wallet.Fake(id); + MatcherAssert.assertThat( + "Cannot return prefix", + wallet.key(), + new StringContains(new CpTransaction(256, id).prefix()) + ); + } + + @Test + @Ignore + public void returnBeneficiary() throws IOException { + final long id = 1024; + final Wallet wallet = new Wallet.Fake(id); + MatcherAssert.assertThat( + "Cannot return beneficiary", + new CpTransaction(256, id).bnf(), + new IsEqual<>(wallet.key()) + ); } } diff --git a/src/test/java/io/zold/api/NetworkTest.java b/src/test/java/io/zold/api/NetworkTest.java index 797a110..c5a4b67 100644 --- a/src/test/java/io/zold/api/NetworkTest.java +++ b/src/test/java/io/zold/api/NetworkTest.java @@ -25,12 +25,9 @@ import java.io.IOException; import org.cactoos.iterable.IterableOf; -import org.cactoos.iterable.Repeated; -import org.cactoos.text.RandomText; import org.cactoos.text.TextOf; import org.hamcrest.MatcherAssert; import org.hamcrest.core.IsEqual; -import org.junit.Ignore; import org.junit.Test; import org.mockito.Mockito; @@ -48,37 +45,6 @@ */ public final class NetworkTest { - @Test - @Ignore - public void pushWalletToRightRemote() { - final Remote highremote = Mockito.mock(Remote.class); - final Score highscore = Mockito.mock(Score.class); - Mockito.when(highscore.suffixes()).thenReturn( - new Repeated<>(20, new RandomText()) - ); - Mockito.when(highremote.score()).thenReturn(highscore); - final Remote lowremote = Mockito.mock(Remote.class); - final Score lowscore = Mockito.mock(Score.class); - Mockito.when(lowscore.suffixes()).thenReturn( - new Repeated<>(18, new RandomText()) - ); - Mockito.when(lowremote.score()).thenReturn(lowscore); - final Wallet wallet = Mockito.mock(Wallet.class); - new RtNetwork( - new IterableOf( - highremote, lowremote - ) - ).push(wallet); - Mockito.verify( - highremote, - Mockito.times(1) - ).push(Mockito.any(Wallet.class)); - Mockito.verify( - lowremote, - Mockito.never() - ).push(Mockito.any(Wallet.class)); - } - @Test public void pushWalletToAllRemotes() { final Remote highremote = Mockito.mock(Remote.class); @@ -99,25 +65,6 @@ public void pushWalletToAllRemotes() { ).push(Mockito.any(Wallet.class)); } - @Test - @Ignore - public void filtersUnqualifiedRemotesFromPush() { - final Remote remote = Mockito.mock(Remote.class); - final Score score = Mockito.mock(Score.class); - Mockito.when(score.suffixes()).thenReturn( - new Repeated<>(15, new RandomText()) - ); - Mockito.when(remote.score()).thenReturn(score); - final Wallet wallet = Mockito.mock(Wallet.class); - new RtNetwork( - new IterableOf(remote) - ).push(wallet); - Mockito.verify( - remote, - Mockito.never() - ).push(Mockito.any(Wallet.class)); - } - @Test public void pullsWalletWithTheRightId() throws IOException { final long id = 1L; diff --git a/src/test/java/io/zold/api/RtTransactionTest.java b/src/test/java/io/zold/api/RtTransactionTest.java index 7c709f0..51a8599 100644 --- a/src/test/java/io/zold/api/RtTransactionTest.java +++ b/src/test/java/io/zold/api/RtTransactionTest.java @@ -247,9 +247,58 @@ public void invalidTransactionString() throws IOException { ).prefix(); } - @Test(expected = UnsupportedOperationException.class) - public void bnfIsNotYetImplemented() { - new RtTransaction("bnf()").bnf(); + @Test + public void returnsBnf() throws IOException { + MatcherAssert.assertThat( + new RtTransaction( + "003b;2017-07-19T21:25:07Z;0000000000a72366;xksQuJa9;000000C81735c4ee;For food;QCuLuVr4..." + ).bnf(), + new IsEqual<>("000000C81735c4ee") + ); + } + + @Test + public void bnfSizeTooLong() throws IOException { + this.thrown.expect(IOException.class); + this.thrown.expectMessage( + Matchers.startsWith("Invalid bnf string '000000C81735c4eef'") + ); + new RtTransaction( + "003b;2017-07-19T21:25:07Z;0000000000a72366;xksQuJa9;000000C81735c4eef;For food;QCuLuVr4..." + ).bnf(); + } + + @Test + public void bnfSizeTooShort() throws IOException { + this.thrown.expect(IOException.class); + this.thrown.expectMessage( + Matchers.startsWith("Invalid bnf string '000000C81735c4e'") + ); + new RtTransaction( + "003b;2017-07-19T21:25:07Z;0000000000a72366;xksQuJa9;000000C81735c4e;For food;QCuLuVr4..." + ).bnf(); + } + + @Test + public void invalidBnfCharacter() throws IOException { + this.thrown.expect(IOException.class); + this.thrown.expectMessage( + Matchers.startsWith("Invalid bnf string '000000C81735X4ee'") + ); + new RtTransaction( + "003b;2017-07-19T21:25:07Z;0000000000a72366;xksQuJa9;000000C81735X4ee;For food;QCuLuVr4..." + ).bnf(); + } + + @Test + public void bnfNotPresent() throws IOException { + this.thrown.expect(IOException.class); + this.thrown.expectMessage( + Matchers.startsWith("The iterable doesn't have the position #4") + ); + new RtTransaction( + "003b;2017-07-19T21:25:07Z;0000000000a72366;xksQuJa9;" + ).bnf(); } @Test diff --git a/src/test/java/io/zold/api/TaxBeneficiariesTest.java b/src/test/java/io/zold/api/TaxBeneficiariesTest.java new file mode 100644 index 0000000..6e6b33d --- /dev/null +++ b/src/test/java/io/zold/api/TaxBeneficiariesTest.java @@ -0,0 +1,76 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Yegor Bugayenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package io.zold.api; + +import org.cactoos.iterable.IterableOf; +import org.cactoos.list.ListOf; +import org.hamcrest.MatcherAssert; +import org.hamcrest.collection.IsIterableContainingInOrder; +import org.hamcrest.core.IsEqual; +import org.junit.Test; + +/** + * Test case for {@link TaxBeneficiaries}. + * + * @since 1.0 + * @checkstyle JavadocMethodCheck (500 lines) + * @checkstyle MagicNumberCheck (500 lines) + */ +public final class TaxBeneficiariesTest { + + @Test + @SuppressWarnings("unchecked") + public void sorts() { + final Remote highremote = new Remote.Fake(20); + final Remote lowremote = new Remote.Fake(18); + MatcherAssert.assertThat( + "Can't sort", + new TaxBeneficiaries( + new IterableOf<>(lowremote, highremote) + ), + new IsIterableContainingInOrder<>( + new ListOf<>( + new IsEqual<>(highremote), + new IsEqual<>(lowremote) + ) + ) + ); + } + + @Test + @SuppressWarnings("unchecked") + public void filters() { + final Remote highremote = new Remote.Fake(20); + final Remote vrylowremote = new Remote.Fake(14); + MatcherAssert.assertThat( + "Can't filter", + new TaxBeneficiaries( + new IterableOf<>(vrylowremote, highremote) + ), + new IsIterableContainingInOrder<>( + new ListOf<>(new IsEqual<>(highremote)) + ) + ); + } +} diff --git a/src/test/java/io/zold/api/TaxesTest.java b/src/test/java/io/zold/api/TaxesTest.java new file mode 100644 index 0000000..792856d --- /dev/null +++ b/src/test/java/io/zold/api/TaxesTest.java @@ -0,0 +1,43 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Yegor Bugayenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package io.zold.api; + +import org.cactoos.iterable.IterableOf; +import org.junit.Test; +import org.mockito.Mockito; + +/** + * Test case for {@link Taxes}. + * + * @since 1.0 + * @checkstyle JavadocMethodCheck (500 lines) + */ +public final class TaxesTest { + + @Test(expected = UnsupportedOperationException.class) + public void payNotYetSupported() throws Exception { + new Taxes(new IterableOf<>()).exec(Mockito.mock(Wallet.class)); + } + +} diff --git a/src/test/java/io/zold/api/WalletTest.java b/src/test/java/io/zold/api/WalletTest.java index 3601045..cb1ad9d 100644 --- a/src/test/java/io/zold/api/WalletTest.java +++ b/src/test/java/io/zold/api/WalletTest.java @@ -110,6 +110,11 @@ public void walletShouldBeAbleToReturnLedger() throws Exception { ); } + @Test(expected = UnsupportedOperationException.class) + public void keyIsNotYetImplemented() throws IOException { + new Wallet.File(this.folder.newFile().toPath()).key(); + } + private Path wallet(final long id) { return this.wallet(Long.toHexString(id)); } diff --git a/src/test/java/io/zold/api/WalletsInTest.java b/src/test/java/io/zold/api/WalletsInTest.java index 51926c4..5e53c4e 100644 --- a/src/test/java/io/zold/api/WalletsInTest.java +++ b/src/test/java/io/zold/api/WalletsInTest.java @@ -24,9 +24,12 @@ package io.zold.api; import java.io.IOException; +import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Random; import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; +import org.hamcrest.collection.IsIterableWithSize; +import org.hamcrest.core.IsEqual; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -52,20 +55,69 @@ public void iteratesWallets() { MatcherAssert.assertThat( new WalletsIn(Paths.get("src/test/resources/walletsIn")), // @checkstyle MagicNumber (1 line) - Matchers.iterableWithSize(5) + new IsIterableWithSize<>(new IsEqual<>(5)) ); } @Test - public void createIsNotYetImplemented() throws IOException { - this.thrown.expect(UnsupportedOperationException.class); - this.thrown.expectMessage( - Matchers.is( - "create() not yet supported" - ) + public void createsWalletInWallets() throws IOException { + final Wallets wallets = new WalletsIn(this.folder.newFolder().toPath()); + wallets.create(); + MatcherAssert.assertThat( + "Can't create wallet in wallets", + wallets, + new IsIterableWithSize<>(new IsEqual<>(1)) + ); + } + + @Test + public void createsWalletInFolder() throws IOException { + final Path path = this.folder.newFolder().toPath(); + new WalletsIn(path).create(); + MatcherAssert.assertThat( + "Can't create wallet in folder", + new WalletsIn(path), + new IsIterableWithSize<>(new IsEqual<>(1)) ); - new WalletsIn( - this.folder.newFolder().toPath() - ).create(); + } + + @Test + public void doesNotOverwriteExistingWallet() throws Exception { + final Path path = this.folder.newFolder().toPath(); + final Random random = new FkRandom(16725L); + new WalletsIn(path, random).create(); + this.thrown.expect(IOException.class); + this.thrown.expectMessage("already exists"); + new WalletsIn(path, random).create(); + } + + /** + * Fake randomizer that returns the same value each time. + */ + private static class FkRandom extends Random { + + /** + * Serial version. + */ + private static final long serialVersionUID = 2905348968220129619L; + + /** + * Value that represents a random number. + */ + private final long value; + + /** + * Ctor. + * @param val Value that represents a random number. + */ + FkRandom(final long val) { + super(); + this.value = val; + } + + @Override + public long nextLong() { + return this.value; + } } }