/* * Copyright 2012, 2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.matthewmitchell.nubitsj.uri; import com.matthewmitchell.nubitsj.core.Address; import com.matthewmitchell.nubitsj.params.MainNetParams; import com.google.common.collect.ImmutableList; import org.junit.Test; import static com.matthewmitchell.nubitsj.core.Coin.*; import static org.junit.Assert.*; public class NubitsURITest { private NubitsURI testObject = null; private static final String MAINNET_GOOD_ADDRESS = "BiM5wdu2apVnp17h6uZTWcNbbPE93sDeGs"; private static final String MAINNET_BAD_ADDRESS = "mranY19RYUjgJjXY4BJNYp88WXXAg7Pr9T"; @Test public void testConvertToNubitsURI() throws Exception { Address goodAddress = new Address(MainNetParams.get(), MAINNET_GOOD_ADDRESS); // simple example assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello&message=AMessage", NubitsURI.convertToNubitsURI(goodAddress, parseCoin("12.34"), "Hello", "AMessage")); // example with spaces, ampersand and plus assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello%20World&message=Mess%20%26%20age%20%2B%20hope", NubitsURI.convertToNubitsURI(goodAddress, parseCoin("12.34"), "Hello World", "Mess & age + hope")); // no amount, label present, message present assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?label=Hello&message=glory", NubitsURI.convertToNubitsURI(goodAddress, null, "Hello", "glory")); // amount present, no label, message present assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?amount=0.1&message=glory", NubitsURI.convertToNubitsURI(goodAddress, parseCoin("0.1"), null, "glory")); assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?amount=0.1&message=glory", NubitsURI.convertToNubitsURI(goodAddress, parseCoin("0.1"), "", "glory")); // amount present, label present, no message assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello", NubitsURI.convertToNubitsURI(goodAddress, parseCoin("12.34"), "Hello", null)); assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello", NubitsURI.convertToNubitsURI(goodAddress, parseCoin("12.34"), "Hello", "")); // amount present, no label, no message assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?amount=1000", NubitsURI.convertToNubitsURI(goodAddress, parseCoin("1000"), null, null)); assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?amount=1000", NubitsURI.convertToNubitsURI(goodAddress, parseCoin("1000"), "", "")); // no amount, label present, no message assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?label=Hello", NubitsURI.convertToNubitsURI(goodAddress, null, "Hello", null)); // no amount, no label, message present assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?message=Agatha", NubitsURI.convertToNubitsURI(goodAddress, null, null, "Agatha")); assertEquals("Nu:" + MAINNET_GOOD_ADDRESS + "?message=Agatha", NubitsURI.convertToNubitsURI(goodAddress, null, "", "Agatha")); // no amount, no label, no message assertEquals("Nu:" + MAINNET_GOOD_ADDRESS, NubitsURI.convertToNubitsURI(goodAddress, null, null, null)); assertEquals("Nu:" + MAINNET_GOOD_ADDRESS, NubitsURI.convertToNubitsURI(goodAddress, null, "", "")); } @Test public void testGood_Simple() throws NubitsURIParseException { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS); assertNotNull(testObject); assertNull("Unexpected amount", testObject.getAmount()); assertNull("Unexpected label", testObject.getLabel()); assertEquals("Unexpected label", 20, testObject.getAddress().getHash160().length); } /** * Test a broken URI (bad scheme) */ @Test public void testBad_Scheme() { try { testObject = new NubitsURI(MainNetParams.get(), "blimpcoin:" + MAINNET_GOOD_ADDRESS); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { } } /** * Test a broken URI (bad syntax) */ @Test public void testBad_BadSyntax() { // Various illegal characters try { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + "|" + MAINNET_GOOD_ADDRESS); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { assertTrue(e.getMessage().contains("Bad URI syntax")); } try { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "\\"); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { assertTrue(e.getMessage().contains("Bad URI syntax")); } // Separator without field try { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":"); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { assertTrue(e.getMessage().contains("Bad URI syntax")); } } /** * Test a broken URI (missing address) */ @Test public void testBad_Address() { try { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { } } /** * Test a broken URI (bad address type) */ @Test public void testBad_IncorrectAddressType() { try { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_BAD_ADDRESS); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { assertTrue(e.getMessage().contains("Bad address")); } } /** * Handles a simple amount * * @throws NubitsURIParseException * If something goes wrong */ @Test public void testGood_Amount() throws NubitsURIParseException { // Test the decimal parsing testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=6543210.1234"); assertEquals("65432101234", testObject.getAmount().toString()); // Test the decimal parsing testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=.1234"); assertEquals("1234", testObject.getAmount().toString()); // Test the integer parsing testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=65432"); assertEquals("654320000", testObject.getAmount().toString()); } /** * Handles a simple label * * @throws NubitsURIParseException * If something goes wrong */ @Test public void testGood_Label() throws NubitsURIParseException { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=Hello%20World"); assertEquals("Hello World", testObject.getLabel()); } /** * Handles a simple label with an embedded ampersand and plus * * @throws NubitsURIParseException * If something goes wrong */ @Test public void testGood_LabelWithAmpersandAndPlus() throws NubitsURIParseException { String testString = "Hello Earth & Mars + Venus"; String encodedLabel = NubitsURI.encodeURLString(testString); testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=" + encodedLabel); assertEquals(testString, testObject.getLabel()); } /** * Handles a Russian label (Unicode test) * * @throws NubitsURIParseException * If something goes wrong */ @Test public void testGood_LabelWithRussian() throws NubitsURIParseException { // Moscow in Russian in Cyrillic String moscowString = "\u041c\u043e\u0441\u043a\u0432\u0430"; String encodedLabel = NubitsURI.encodeURLString(moscowString); testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=" + encodedLabel); assertEquals(moscowString, testObject.getLabel()); } /** * Handles a simple message * * @throws NubitsURIParseException * If something goes wrong */ @Test public void testGood_Message() throws NubitsURIParseException { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?message=Hello%20World"); assertEquals("Hello World", testObject.getMessage()); } /** * Handles various well-formed combinations * * @throws NubitsURIParseException * If something goes wrong */ @Test public void testGood_Combinations() throws NubitsURIParseException { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=6543210&label=Hello%20World&message=Be%20well"); assertEquals( "NubitsURI['amount'='65432100000','label'='Hello World','message'='Be well','address'='BiM5wdu2apVnp17h6uZTWcNbbPE93sDeGs']", testObject.toString()); } /** * Handles a badly formatted amount field * * @throws NubitsURIParseException * If something goes wrong */ @Test public void testBad_Amount() throws NubitsURIParseException { // Missing try { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount="); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { assertTrue(e.getMessage().contains("amount")); } // Non-decimal (BIP 21) try { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=12X4"); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { assertTrue(e.getMessage().contains("amount")); } } @Test public void testEmpty_Label() throws NubitsURIParseException { assertNull(new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=").getLabel()); } @Test public void testEmpty_Message() throws NubitsURIParseException { assertNull(new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?message=").getMessage()); } /** * Handles duplicated fields (sneaky address overwrite attack) * * @throws NubitsURIParseException * If something goes wrong */ @Test public void testBad_Duplicated() throws NubitsURIParseException { try { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?address=aardvark"); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { assertTrue(e.getMessage().contains("address")); } } @Test public void testGood_ManyEquals() throws NubitsURIParseException { assertEquals("aardvark=zebra", new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=aardvark=zebra").getLabel()); } /** * Handles unknown fields (required and not required) * * @throws NubitsURIParseException * If something goes wrong */ @Test public void testUnknown() throws NubitsURIParseException { // Unknown not required field testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?aardvark=true"); assertEquals("NubitsURI['aardvark'='true','address'='BiM5wdu2apVnp17h6uZTWcNbbPE93sDeGs']", testObject.toString()); assertEquals("true", testObject.getParameterByName("aardvark")); // Unknown not required field (isolated) try { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?aardvark"); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { assertTrue(e.getMessage().contains("no separator")); } // Unknown and required field try { testObject = new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?req-aardvark=true"); fail("Expecting NubitsURIParseException"); } catch (NubitsURIParseException e) { assertTrue(e.getMessage().contains("req-aardvark")); } } @Test public void brokenURIs() throws NubitsURIParseException { // Check we can parse the incorrectly formatted URIs produced by blockchain.info and its iPhone app. String str = "Nu://BiM5wdu2apVnp17h6uZTWcNbbPE93sDeGs?amount=0.01000000"; NubitsURI uri = new NubitsURI(MainNetParams.get(), str); assertEquals("BiM5wdu2apVnp17h6uZTWcNbbPE93sDeGs", uri.getAddress().toString()); assertEquals(CENT, uri.getAmount()); } @Test(expected = NubitsURIParseException.class) public void testBad_AmountTooPrecise() throws NubitsURIParseException { new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=0.123456789"); } @Test(expected = NubitsURIParseException.class) public void testBad_NegativeAmount() throws NubitsURIParseException { new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=-1"); } @Test(expected = NubitsURIParseException.class) public void testBad_TooLargeAmount() throws NubitsURIParseException { new NubitsURI(MainNetParams.get(), NubitsURI.NUBITS_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=2000000000001"); } @Test public void testPaymentProtocolReq() throws Exception { // Non-backwards compatible form ... NubitsURI uri = new NubitsURI(MainNetParams.get(), "Nu:?r=https%3A%2F%2Fnubitscore.org%2F%7Egavin%2Ff.php%3Fh%3Db0f02e7cea67f168e25ec9b9f9d584f9"); assertEquals("https://nubitscore.org/~gavin/f.php?h=b0f02e7cea67f168e25ec9b9f9d584f9", uri.getPaymentRequestUrl()); assertEquals(ImmutableList.of("https://nubitscore.org/~gavin/f.php?h=b0f02e7cea67f168e25ec9b9f9d584f9"), uri.getPaymentRequestUrls()); assertNull(uri.getAddress()); } @Test public void testMultiplePaymentProtocolReq() throws Exception { NubitsURI uri = new NubitsURI(MainNetParams.get(), "Nu:?r=https%3A%2F%2Fnubitscore.org%2F%7Egavin&r1=bt:112233445566"); assertEquals(ImmutableList.of("bt:112233445566", "https://nubitscore.org/~gavin"), uri.getPaymentRequestUrls()); assertEquals("https://nubitscore.org/~gavin", uri.getPaymentRequestUrl()); } @Test public void testNoPaymentProtocolReq() throws Exception { NubitsURI uri = new NubitsURI(MainNetParams.get(), "Nu:" + MAINNET_GOOD_ADDRESS); assertNull(uri.getPaymentRequestUrl()); assertEquals(ImmutableList.of(), uri.getPaymentRequestUrls()); assertNotNull(uri.getAddress()); } @Test public void testUnescapedPaymentProtocolReq() throws Exception { NubitsURI uri = new NubitsURI(MainNetParams.get(), "Nu:?r=https://merchant.com/pay.php?h%3D2a8628fc2fbe"); assertEquals("https://merchant.com/pay.php?h=2a8628fc2fbe", uri.getPaymentRequestUrl()); assertEquals(ImmutableList.of("https://merchant.com/pay.php?h=2a8628fc2fbe"), uri.getPaymentRequestUrls()); assertNull(uri.getAddress()); } }