diff --git a/Makefile b/Makefile
index 6e0868ef..c3d61c37 100644
--- a/Makefile
+++ b/Makefile
@@ -6,4 +6,4 @@ test:
php ./bin/phpunit -c build/
phantomjs:
- ./node_modules/.bin/phantomjs --webdriver=4444 --ssl-protocol=TLSv1 --ignore-ssl-errors=yes &
+ phantomjs --webdriver=8643 --ignore-ssl-errors=yes &
diff --git a/behat.yml b/behat.yml
new file mode 100644
index 00000000..6c86f17d
--- /dev/null
+++ b/behat.yml
@@ -0,0 +1,32 @@
+# behat.yml
+
+default:
+ context:
+ parameters:
+ user:
+ password:
+ base_url:
+ driver: 'selenium2'
+ paths:
+ features: tests/integrations
+ bootstrap: %behat.paths.features%/bootstrap
+ extensions:
+ Behat\MinkExtension\Extension:
+ goutte: ~
+ selenium2: ~
+
+phantomjs:
+ context:
+ parameters:
+ user:
+ password:
+ base_url:
+ driver: 'phantomjs'
+ paths:
+ features: tests/integrations
+ bootstrap: %behat.paths.features%/bootstrap
+ extensions:
+ Behat\MinkExtension\Extension:
+ goutte: ~
+ selenium2:
+ wd_host: "http://localhost:8643/wd/hub"
diff --git a/composer.json b/composer.json
index 05e58cd9..014f2b49 100644
--- a/composer.json
+++ b/composer.json
@@ -38,8 +38,12 @@
"symfony/dependency-injection": "~2.3"
},
"require-dev": {
- "behat/mink": "1.6.*@stable",
- "behat/mink-selenium2-driver": "~1.1",
+ "behat/behat": "2.5.*@stable",
+ "behat/mink": "1.6.*-dev",
+ "behat/mink-extension": "1.3.*-dev",
+ "behat/mink-selenium2-driver": "1.2.*-dev",
+ "fabpot/goutte": "~1.0.4",
+ "behat/mink-goutte-driver": "1.*",
"phpmd/phpmd": "~2.1.3",
"phpunit/phpunit": "~4.3.1",
"fzaninotto/faker": "~1.4.0",
diff --git a/docs/testing.rst b/docs/testing.rst
index 62d860f5..fec067b9 100644
--- a/docs/testing.rst
+++ b/docs/testing.rst
@@ -91,3 +91,57 @@ please try to use phpmd. You can see the rules in ``build/rulesets/phpmd.xml``.
.. code-block:: bash
php bin/phpmd src/ text build/rulesets/phpmd.xml
+
+Mink/Behat
+=================
+
+This will run tests verifying that the client is able to perform it's core functionality.
+
+To run mink/behat integration tests, use the command:
+
+.. code-block:: bash
+
+ source ./integration_tests.sh
+
+URL, email, and password can be passed in as arguments to integration_tests.sh like so:
+
+.. code-block::
+
+ source ./integration_tests.sh 'https://bobert.bp:8090' bobert@gmail.com 'abc123%^&@ac'
+
+You can configure which instance of bitpay.com this will test with to by changing the url
+in the behat.yml file. Make sure you replace username and password with the credentials
+used to log into the bitpay site you are testing with.
+
+You can also run specific tests from a command like so:
+
+.. code-block:: bash
+
+ php bin/behat tests/integrations/invoice_create.feature
+
+And you can run specific lines from these tests by:
+
+.. code-block:: bash
+
+ php bin/behat tests/integrations/invoice_create.feature:20
+
+.. note::
+
+ Tests run individually require you to set environment variables for your bitpay
+ credentials or they must be set in the behat.yml file.
+
+ Also keep in mind that rate limiters may hinder some tests and they need to be
+ reset every so often.
+
+.. note::
+
+ pairing.feature's test "the client has a bad port configuration to an incorrect port"
+ requires ports that vary from computer to computer so you may need to manually change
+ these in order to avoid error.
+
+ A timing issue will occasionally occur saying:
+ "Notice: Undefined variable: response in
+ tests/integrations/bootstrap/FeatureContext.php line 368"
+ and there will be a message from BitPay saying:
+ "This endpoint does not support the `public` facade"
+ This is likely a timing issue and the tests will likely pass when run again.
\ No newline at end of file
diff --git a/env.dist b/env.dist
deleted file mode 100644
index 146abb97..00000000
--- a/env.dist
+++ /dev/null
@@ -1,17 +0,0 @@
-####
-#
-# This file is for integration testing. You MUST have these environment variables
-# set or all integration tests will be skipped.
-#
-# To setup, you will need to export this variables like so:
-#
-# export BITPAY_EMAIL='josh@bitpay.com'
-# export BITPAY_PASSWORD='FluffyKitty420'
-#
-# NOTE: Running this on the command line has the potential for them to show up
-# in your history. Please make sure you do this one a secure system.
-#
-####
-
-BITPAY_EMAIL=''
-BITPAY_PASSWORD=''
diff --git a/integration_tests.sh b/integration_tests.sh
new file mode 100755
index 00000000..4762d4c3
--- /dev/null
+++ b/integration_tests.sh
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+set_variables ()
+{
+ while true
+ do
+ read -p "Input Email: " EMAIL
+ regex="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$";
+ if [[ "$EMAIL" =~ $regex ]]
+ then
+ export BITPAY_EMAIL=$EMAIL
+ break
+ else
+ echo "Please input a valid email"
+ fi
+ done
+ while true
+ do
+ read -p "Input Password: " PASSWORD
+ read -p "Password Confirmation: " PASSWORD2
+ if [ "$PASSWORD" = "$PASSWORD2" ]
+ then
+ break
+ else
+ echo "Please input a valid password"
+ fi
+ done
+ while true
+ do
+ read -p "Input URL: " URL
+ if [ -z $URL ]
+ then
+ echo "Please input a valid URL"
+ else
+ break
+ fi
+ done
+}
+
+if [ -z "$1" ]
+then
+ echo "No parameters passed so using Environment Variables"
+ if [ -z "$BITPAY_EMAIL" ] || [ -z "$BITPAY_PASSWORD"]
+ then
+ echo "ERROR: No Email or password are set."
+ echo "set BITPAY_EMAIL and BITPAY_PASSWORD as environment variables"
+ echo "or pass them as arguments when running this script"
+ while true; do
+ read -p "Do you wish to set your environment variables here? " yn
+ case $yn in
+ [Yy]* ) set_variables; break;;
+ [Nn]* ) echo "Closing script"; exit;;
+ * ) echo "Please answer yes or no.";;
+ esac
+ done
+ else
+ echo "Environment Variables already exist for BITPAY."
+ fi
+else
+ echo "Username $1 and Password $2 passed from command line"
+ URL=$1
+ EMAIL=$2
+ PASSWORD=$3
+ echo "Setting user and Password to new environment variables..."
+
+fi
+
+export BITPAY_EMAIL=$EMAIL
+export BITPAY_PASSWORD=$PASSWORD
+export BITPAY_URL=$URL
+echo "Using Email: $EMAIL"
+echo "Using URL: $URL"
+
+echo "Removing old keys..."
+if [ -e /tmp/bitpay.pub ]
+then
+ rm -rf /tmp/bitpay.pub
+ rm -rf /tmp/bitpay.pri
+ rm -rf /tmp/token.json
+fi
+
+echo "Checking if Selenium exists..."
+if [ ! -f selenium-server-standalone-2.44.0.jar ]
+then
+ echo "Downloading Selenium"
+ curl -O http://selenium-release.storage.googleapis.com/2.44/selenium-server-standalone-2.44.0.jar
+fi
+
+echo "Running Selenium and the tests"
+php bin/behat tests/integrations
\ No newline at end of file
diff --git a/src/Bitpay/Client/Adapter/CurlAdapter.php b/src/Bitpay/Client/Adapter/CurlAdapter.php
index 833a7254..8d6327c2 100644
--- a/src/Bitpay/Client/Adapter/CurlAdapter.php
+++ b/src/Bitpay/Client/Adapter/CurlAdapter.php
@@ -74,7 +74,7 @@ public function sendRequest(RequestInterface $request)
if (false === $raw) {
$errorMessage = curl_error($curl);
curl_close($curl);
- throw new \Exception($errorMessage);
+ throw new \Bitpay\Client\ConnectionException($errorMessage);
}
/** @var ResponseInterface */
diff --git a/src/Bitpay/Client/ArgumentException.php b/src/Bitpay/Client/ArgumentException.php
new file mode 100644
index 00000000..3f1aa746
--- /dev/null
+++ b/src/Bitpay/Client/ArgumentException.php
@@ -0,0 +1,8 @@
+request = $this->createNewRequest();
$this->request->setMethod(Request::METHOD_POST);
$this->request->setPath('tokens');
@@ -478,7 +484,7 @@ public function createToken(array $payload = array())
$body = json_decode($this->response->getBody(), true);
if (isset($body['error'])) {
- throw new \Exception($body['error']);
+ throw new \Bitpay\Client\BitpayException($this->response->getStatusCode().": ".$body['error']);
}
$tkn = $body['data'][0];
@@ -590,7 +596,7 @@ protected function addSignatureHeader(RequestInterface $request)
throw new \Exception('Please set your Private Key');
}
- if (isset($this->network->isPortRequiredInUrl)) {
+ if (true == property_exists($this->network, 'isPortRequiredInUrl')) {
if ($this->network->isPortRequiredInUrl === true) {
$url = $request->getUriWithPort();
}
diff --git a/src/Bitpay/Client/ConnectionException.php b/src/Bitpay/Client/ConnectionException.php
new file mode 100644
index 00000000..ca4fc580
--- /dev/null
+++ b/src/Bitpay/Client/ConnectionException.php
@@ -0,0 +1,8 @@
+price = $price;
return $this;
diff --git a/tests/Bitpay/Client/Adapter/CurlAdapterTest.php b/tests/Bitpay/Client/Adapter/CurlAdapterTest.php
index 0e769403..b511ab31 100644
--- a/tests/Bitpay/Client/Adapter/CurlAdapterTest.php
+++ b/tests/Bitpay/Client/Adapter/CurlAdapterTest.php
@@ -31,8 +31,7 @@ public function testGetCurlOptions()
public function testSendRequestWithException()
{
- $http = $this->getMock('HttpRequest');
- $this->setExpectedException('Exception');
+ $this->setExpectedException('Bitpay\Client\ConnectionException');
$curl_options = array(
CURLOPT_URL => "www.example.com",
@@ -46,8 +45,6 @@ public function testSendRequestWithException()
public function testSendRequestWithoutException()
{
- $http = $this->getMock('HttpRequest');
-
$curl_options = array(
CURLOPT_URL => "www.bitpay.com",
CURLOPT_SSL_VERIFYPEER => 1,
diff --git a/tests/integrations/CreatePairingCodeTest.php b/tests/integrations/CreatePairingCodeTest.php
deleted file mode 100644
index 37af29b1..00000000
--- a/tests/integrations/CreatePairingCodeTest.php
+++ /dev/null
@@ -1,131 +0,0 @@
-markTestSkipped('Environment Variables need to be set.');
- return;
- }
-
- $selenium2driver = new Selenium2Driver('firefox', null, 'http://127.0.0.1:4444');
- $this->mink = new Mink(
- array(
- 'selenium2' => new Session($selenium2driver),
- )
- );
- $this->mink->setDefaultSessionName('selenium2');
- }
-
- protected function tearDown()
- {
- $this->mink->getSession()->reset();
- }
-
- public function testPairingCode()
- {
- /**
- * Login and create a pairing code
- */
- $this->login();
- $pairingCode = $this->createPairingCode();
-
- /**
- * Generate some new keys
- */
- list($pri, $pub, $sin) = $this->generateKeys();
-
- /**
- * Make request to to pair keys and obtain a token
- */
- $this->client = new \Bitpay\Client\Client();
- $this->client->setNetwork(new \Bitpay\Network\Testnet());
- $this->client->setPublicKey($pub);
- $this->client->setPrivateKey($pri);
- $this->client->setAdapter(new \Bitpay\Client\Adapter\CurlAdapter());
- $token = $this->client->createToken(
- array(
- 'id' => (string) $sin,
- 'pairingCode' => $pairingCode,
- 'label' => 'Integration Test',
- )
- );
- /**
- * The token is the what will be needed for all requests in the future
- * and so to inject it into the client, you would add the code
- *
- * $client->setToken($token);
- *
- */
- $this->assertInstanceOf('Bitpay\TokenInterface', $token);
- }
-
- private function login()
- {
- $this->mink->getSession()->visit('https://test.bitpay.com/merchant-login');
- $this->mink->getSession()->wait(2500);
- $this->mink->getSession()->getPage()->fillField('email', getenv('BITPAY_EMAIL'));
- $this->mink->getSession()->getPage()->fillField('password', getenv('BITPAY_PASSWORD'));
- $this->mink->getSession()->getPage()->findById('loginForm')->submit();
- $this->mink->getSession()->wait(2500);
- $assert = $this->mink->assertSession();
- $assert->pageTextContains('Dashboard');
- }
-
- private function createPairingCode()
- {
- $this->mink->getSession()->visit('https://test.bitpay.com/api-tokens');
- $session = $this->mink->getSession();
- $page = $session->getPage();
- // Click the `Add New Token` button
- $page->find('xpath', '//*[@id="apiTokensList"]/div/div[3]/div[2]/div')->click();
- $this->mink->getSession()->wait(500);
- $assert = $this->mink->assertSession();
- $assert->pageTextContains('Add Token');
- // Click `Add Token` button
- $page->find('xpath', '//*[@id="token-new-form"]/button')->click();
- $this->mink->getSession()->wait(2500);
- $pairingCode = $page->find('xpath', '//*[@id="my-token-access-wrapper"]/div[1]/div[2]/div/div')->getText();
- $this->assertNotNull($pairingCode);
-
- return $pairingCode;
- }
-
- private function generateKeys()
- {
- $privateKey = new \Bitpay\PrivateKey();
- $privateKey->generate();
- $publicKey = new \Bitpay\PublicKey();
- $publicKey
- ->setPrivateKey($privateKey)
- ->generate();
- $sin = new \Bitpay\SinKey();
- $sin
- ->setPublicKey($publicKey)
- ->generate();
-
- return array(
- $privateKey,
- $publicKey,
- $sin,
- );
- }
-}
diff --git a/tests/integrations/LoginTest.php b/tests/integrations/LoginTest.php
deleted file mode 100644
index 81a56872..00000000
--- a/tests/integrations/LoginTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-markTestSkipped('Environment Variables need to be set.');
- return;
- }
-
- $selenium2driver = new Selenium2Driver('firefox', null, 'http://127.0.0.1:4444');
- $this->mink = new Mink(
- array(
- 'selenium2' => new Session($selenium2driver),
- )
- );
- $this->mink->setDefaultSessionName('selenium2');
- }
-
- protected function tearDown()
- {
- $this->mink->getSession()->reset();
- }
-
- public function testLogin()
- {
- $this->mink->getSession()->visit('https://test.bitpay.com/merchant-login');
- $this->mink->getSession()->getPage()->fillField('email', getenv('BITPAY_EMAIL'));
- $this->mink->getSession()->getPage()->fillField('password', getenv('BITPAY_PASSWORD'));
- $this->mink->getSession()->getPage()->findById('loginForm')->submit();
- $this->mink->getSession()->wait(2500); // wait 2.5 seconds for page to load
- $assert = $this->mink->assertSession();
- $assert->pageTextContains('Dashboard');
- }
-}
diff --git a/tests/integrations/bootstrap/FeatureContext.php b/tests/integrations/bootstrap/FeatureContext.php
new file mode 100644
index 00000000..2719c925
--- /dev/null
+++ b/tests/integrations/bootstrap/FeatureContext.php
@@ -0,0 +1,373 @@
+params = $parameters;
+
+ if (null == getenv('BITPAY_EMAIL')) {
+ $this->email = $this->params['user'];
+ } else {
+ $this->email = getenv('BITPAY_EMAIL');
+ }
+ if (null == getenv('BITPAY_PASSWORD')) {
+ $this->password = $this->params['password'];
+ } else {
+ $this->password = getenv('BITPAY_PASSWORD');
+ }
+
+ if (null == getenv('BITPAY_URL')) {
+ $this->base_url = $this->params['base_url'];
+ } else {
+ $this->base_url = getenv('BITPAY_URL');
+ }
+
+ $port = parse_url($this->base_url, PHP_URL_PORT);
+ if (true == is_null(parse_url($this->base_url, PHP_URL_PORT))) {
+ $this->port = 443;
+ $this->port_required_in_url = false;
+ } else {
+ $this->port = parse_url($this->base_url, PHP_URL_PORT);
+ $this->port_required_in_url = true;
+ }
+
+ if (parse_url($this->base_url, PHP_URL_HOST) == 'test.bitpay.com') {
+ $this->network = new \Bitpay\Network\Testnet();
+ } else {
+ $url = parse_url($this->base_url, PHP_URL_HOST);
+ $this->network = new \Bitpay\Network\Customnet($url, $this->port, $this->port_required_in_url);
+ }
+
+ if ((null == $this->email) || (null == $this->password)) {
+ throw new Exception("Your email or password are not configured.");
+ return;
+ }
+
+ if (null == $this->base_url) {
+ throw new Exception("Your url is not configured.");
+ return;
+ }
+
+ $phantomjsDriver = new \Behat\Mink\Driver\Selenium2Driver('phantomJS', null, 'http://127.0.0.1:8643');
+ $selenium2Driver = new \Behat\Mink\Driver\Selenium2Driver('firefox');
+
+ $this->mink = new \Behat\Mink\Mink(
+ array(
+ 'phantomjs' => new \Behat\Mink\Session($phantomjsDriver),
+ 'selenium2' => new \Behat\Mink\Session($selenium2Driver),
+ )
+ );
+
+ $this->mink->setDefaultSessionName($this->params['driver']);
+ }
+
+ /**
+ * @Given /^the user is authenticated with BitPay$/
+ */
+ public function theUserIsAuthenticatedWithBitpay()
+ {
+ if(true == !file_exists('/tmp/token.json') || true == !file_exists('/tmp/bitpay.pri') || true == !file_exists('/tmp/bitpay.pub')){
+ $this->theUserPairsWithBitpayWithAValidPairingCode();
+ $this->theUserIsPairedWithBitpay();
+ }
+ }
+
+ /**
+ * @When /^the user creates an invoice for "([^"]*)" "([^"]*)"$/
+ */
+ public function theUserCreatesAnInvoiceFor($price, $currency)
+ {
+ try {
+ // Load keys
+ list($privateKey, $publicKey, $token_id) = loadKeys();
+
+ $network = $this->network;
+ $client = createClient($network, $privateKey, $publicKey);
+
+ $token = new \Bitpay\Token();
+ $token->setToken($token_id);
+ $client->setToken($token);
+
+ $invoice = new \Bitpay\Invoice();
+
+ $item = new \Bitpay\Item();
+ $item
+ ->setCode('skuNumber')
+ ->setDescription('General Description of Item')
+ ->setPrice($price);
+ $invoice->setItem($item);
+
+ $invoice->setCurrency(new \Bitpay\Currency($currency));
+ $client->createInvoice($invoice);
+ $this->response = $client->getResponse();
+ $body = $this->response->getBody();
+ $json = json_decode($body, true);
+ $this->invoiceId = $json['data']['id'];
+ } catch (\Exception $e) {
+ $this->error = $e;
+ } finally {
+ return true;
+ }
+ }
+
+ /**
+ * @Then /^they should recieve an invoice in response for "([^"]*)" "([^"]*)"$/
+ */
+ public function theyShouldRecieveAnInvoiceInResponseFor($price, $currency)
+ {
+ $body = $this->response->getBody();
+ $json = json_decode($body, true);
+ $responsePrice = (string) $json['data']['price'];
+ $responseCurrency = $json['data']['currency'];
+ if ($responsePrice !== $price){
+ throw new Exception("Error: Price is different", 1);
+ }
+ if ($responseCurrency !== $currency){
+ throw new Exception("Error: Currency is different", 1);
+ }
+ }
+
+ /**
+ * @Given /^the user pairs with BitPay with a valid pairing code$/
+ */
+ public function theUserPairsWithBitpayWithAValidPairingCode()
+ {
+ // Login
+
+ $this->mink->getSession()->visit($this->base_url.'/merchant-login');
+
+ $this->mink->getSession()->wait(1500);
+ $this->mink->getSession()->getPage()->fillField('email', $this->email);
+ $this->mink->getSession()->getPage()->fillField('password', $this->password);
+
+ $value = $this->mink->getSession()->getPage()->pressButton('loginButton');
+
+ $this->mink->getSession()->wait(2500);
+
+ $assert = $this->mink->assertSession();
+ $assert->pageTextContains('Dashboard');
+
+ // Navigate to tokens
+ $this->mink->getSession()->wait(1500);
+ $this->mink->getSession()->getPage()->clickLink('My Account');
+ $this->mink->getSession()->wait(1500);
+ $this->mink->getSession()->getPage()->clickLink('API Tokens');
+ $this->mink->getSession()->wait(1500);
+
+ // Create and set pairing code
+ $cssSelector = ".icon-plus";
+ $element = $this->mink->getSession()->getPage()->find(
+ 'xpath',
+ $this->mink->getSession()->getSelectorsHandler()->selectorToXpath('css', $cssSelector) // just changed xpath to css
+ );
+ if (null === $element) {
+ throw new \InvalidArgumentException(sprintf('Could not evaluate CSS Selector: "%s"', $cssSelector));
+ }
+ $element->press();
+
+ $this->mink->getSession()->wait(1500);
+ $this->mink->getSession()->getPage()->pressButton("Add Token");
+ $this->mink->getSession()->wait(5000);
+
+ $this->validPairingCode = $this->mink->getSession()->getPage()->find('xpath', '//*[@id="my-token-access-wrapper"]/div[1]/div[2]/div/div')->getText();
+ }
+
+ /**
+ * @Then /^the user is paired with BitPay$/
+ */
+ public function theUserIsPairedWithBitpay()
+ {
+ // Create Keys
+ list($privateKey, $publicKey, $sinKey) = generateAndPersistKeys();
+
+ //Set Client
+ $network = $this->network;
+ $client = createClient($network, $privateKey, $publicKey);
+
+ $pairingCode = $this->validPairingCode;
+
+ // Pair
+ try {
+ $token = $client->createToken(
+ array(
+ 'pairingCode' => $pairingCode,
+ 'label' => 'Integrations Testing',
+ 'id' => (string) $sinKey,
+ )
+ );
+ $token_file = fopen('/tmp/token.json', 'w');
+ fwrite($token_file, $token);
+ fclose($token_file);
+ } catch (\Exception $e) {
+ $request = $client->getRequest();
+ $response = $client->getResponse();
+ echo (string) $request.PHP_EOL.PHP_EOL.PHP_EOL;
+ echo (string) $response.PHP_EOL.PHP_EOL;
+ exit(1);
+ }
+
+ }
+
+ /**
+ * @Given /^the user fails to pair with a semantically (?:in|)valid code "([^"]*)"$/
+ */
+ public function theUserFailsToPairWithASemanticallyValidCode($pairingCode)
+ {
+ try {
+ // Stupid rate limiters
+ $this->mink->getSession()->wait(1500);
+
+ // Create Keys
+ list($privateKey, $publicKey, $sinKey) = generateAndPersistKeys();
+
+ //Set Client
+ $network = $this->network;
+ $client = createClient($network, $privateKey, $publicKey);
+
+ // Pair
+ $token = $client->createToken(
+ array(
+ 'pairingCode' => $pairingCode,
+ 'label' => 'Integrations Testing',
+ 'id' => (string) $sinKey,
+ )
+ );
+ } catch (\Exception $e) {
+ $this->error = $e;
+ } finally {
+ return true;
+ }
+
+ }
+
+ /**
+ * @Then /^they will receive a "([^"]*)" matching \'([^\']*)\'$/
+ */
+ public function theyWillReceiveAnErrorMatching($expectedErrorName, $expectedErrorMessage)
+ {
+ $curlError = $this->error;
+ $curlErrorMessage = $this->error->getMessage();
+ if (strpos($curlErrorMessage, $expectedErrorMessage) === false) {
+ throw new Exception("Error message incorrect: ".$curlErrorMessage, 1);
+ }
+ if (strpos(get_class($curlError), $expectedErrorName) === false) {
+ throw new Exception("Error name incorrect: ".get_class($curlError), 1);
+ }
+ }
+
+ /**
+ * @When /^the client fails to pair with BitPay because (open|closed) port ([0-9]+) is an incorrect port$/
+ */
+ public function theClientFailsToPairWithBitpayBecauseOfAnIncorrectPort($status, $port)
+ {
+ try {
+ // Stupid rate limiters
+ $this->mink->getSession()->wait(1500);
+
+ // Create Keys
+ list($privateKey, $publicKey, $sinKey) = generateAndPersistKeys();
+
+ //Set Client
+ $url = parse_url($this->base_url, PHP_URL_HOST);
+ $network = new \Bitpay\Network\Customnet($url, $port, true);
+ $curl_options = array(
+ CURLOPT_SSL_VERIFYPEER => false,
+ CURLOPT_SSL_VERIFYHOST => false,
+ CURLOPT_TIMEOUT => 5,
+ );
+ $client = createClient($network, $privateKey, $publicKey, $curl_options);
+
+ // Pair
+ $token = $client->createToken(
+ array(
+ 'pairingCode' => 'aaaaaaa',
+ 'label' => 'Integrations Testing',
+ 'id' => (string) $sinKey,
+ )
+ );
+ } catch (\Exception $e) {
+ $this->error = $e;
+ } finally {
+ return true;
+ }
+ }
+
+ /**
+ * @Given /^that a user knows an invoice id$/
+ */
+ public function thatAUserKnowsAnInvoiceId()
+ {
+ if(true == !file_exists('/tmp/token.json') || true == !file_exists('/tmp/bitpay.pri') || true == !file_exists('/tmp/bitpay.pub')){
+ $this->theUserPairsWithBitpayWithAValidPairingCode();
+ $this->theUserIsPairedWithBitpay();
+ }
+ $this->theUserCreatesAnInvoiceFor(1.99, 'USD');
+ }
+
+ /**
+ * @Then /^they can retrieve that invoice$/
+ */
+ public function theyCanRetrieveThatInvoice()
+ {
+ try
+ {
+ $network = $this->network;
+ $client = createClient($network);
+ $response = $client->getInvoice($this->invoiceId);
+ } catch (Exception $e){
+ var_dump($e->getMessage());
+ }
+ $responseInvoiceId = $response->getId();
+ if($responseInvoiceId !== $this->invoiceId){
+ throw new Exception("Invoice ids don't match");
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/integrations/bootstrap/StepHelper.php b/tests/integrations/bootstrap/StepHelper.php
new file mode 100644
index 00000000..9f9bd000
--- /dev/null
+++ b/tests/integrations/bootstrap/StepHelper.php
@@ -0,0 +1,54 @@
+generate();
+ $publicKey = new \Bitpay\PublicKey('/tmp/bitpay.pub');
+ $publicKey->setPrivateKey($privateKey);
+ $publicKey->generate();
+ $sinKey = new \Bitpay\SinKey('/tmp/sin.key');
+ $sinKey->setPublicKey($publicKey);
+ $sinKey->generate();
+
+ //Persist Keys
+ $storageEngine = new \Bitpay\Storage\EncryptedFilesystemStorage('YourTopSecretPassword');
+ $storageEngine->persist($privateKey);
+ $storageEngine->persist($publicKey);
+
+ return array($privateKey, $publicKey, $sinKey);
+}
+
+function loadKeys()
+{
+ $storageEngine = new \Bitpay\Storage\EncryptedFilesystemStorage('YourTopSecretPassword');
+ $privateKey = $storageEngine->load('/tmp/bitpay.pri');
+ $publicKey = $storageEngine->load('/tmp/bitpay.pub');
+ $token_id = file_get_contents('/tmp/token.json');
+
+ return array($privateKey, $publicKey, $token_id);
+}
+
+function createClient($network, $privateKey = null, $publicKey = null, $curl_options = null)
+{
+ if(true === is_null($curl_options)) {
+ $curl_options = array(
+ CURLOPT_SSL_VERIFYPEER => false,
+ CURLOPT_SSL_VERIFYHOST => false,
+ );
+ }
+ $adapter = new \Bitpay\Client\Adapter\CurlAdapter($curl_options);
+ $client = new \Bitpay\Client\Client();
+
+ if(true === !is_null($privateKey)) {
+ $client->setPrivateKey($privateKey);
+ }
+ if(true === !is_null($publicKey)) {
+ $client->setPublicKey($publicKey);
+ }
+
+ $client->setNetwork($network);
+ $client->setAdapter($adapter);
+
+ return $client;
+}
\ No newline at end of file
diff --git a/tests/integrations/invoice_create.feature b/tests/integrations/invoice_create.feature
new file mode 100644
index 00000000..31d477be
--- /dev/null
+++ b/tests/integrations/invoice_create.feature
@@ -0,0 +1,29 @@
+#create_invoices.feature
+Feature: creating an invoice
+ The user won't get any money
+ If they can't
+ Create Invoices
+
+ Background:
+ Given the user is authenticated with BitPay
+
+ @javascript
+ Scenario Outline: The request is correct
+ When the user creates an invoice for
+ Then they should recieve an invoice in response for
+ Examples:
+ | price | currency |
+ | "1.01" | "USD" |
+ | "1.01" | "EUR" |
+
+ @javascript
+ Scenario Outline: The invoice contains illegal characters
+ When the user creates an invoice for
+ Then they will receive a "Bitpay\Client\ArgumentException" matching
+ Examples:
+ | price | currency | message |
+ | "1,023" | "USD" | 'Price must be formatted as a float' |
+ | "1.21" | "EaUR" | 'The currency code "EaUR" is not supported.' |
+ | "" | "USD" | 'Price must be formatted as a float' |
+ | "Ten" | "USD" | 'Price must be formatted as a float' |
+ | "1" | "" | 'The currency code "" is not supported.' |
diff --git a/tests/integrations/invoice_retrieve.feature b/tests/integrations/invoice_retrieve.feature
new file mode 100644
index 00000000..199fb8a6
--- /dev/null
+++ b/tests/integrations/invoice_retrieve.feature
@@ -0,0 +1,9 @@
+#retrieve_invoices.feature
+Feature: retrieving an invoice
+ The user may want to retrieve invoices
+ So that they can view them
+
+ @javascript
+ Scenario: The request is correct
+ Given that a user knows an invoice id
+ Then they can retrieve that invoice
\ No newline at end of file
diff --git a/tests/integrations/pairing.feature b/tests/integrations/pairing.feature
new file mode 100644
index 00000000..e61af273
--- /dev/null
+++ b/tests/integrations/pairing.feature
@@ -0,0 +1,28 @@
+# pairing.feature
+Feature: pairing with bitpay
+ In order to access bitpay
+ It is required that the library
+ Is able to pair successfully
+
+ @javascript
+ Scenario: the client has a correct pairing code
+ Given the user pairs with BitPay with a valid pairing code
+ Then the user is paired with BitPay
+
+ @javascript
+ Scenario Outline: the client has a bad pairing code
+ Given the user fails to pair with a semantically code
+ Then they will receive a matching
+ Examples:
+ | valid | code | error | message |
+ | valid | "a1b2c3d" | "Bitpay\Client\BitpayException" | '500: Unable to create token' |
+ | invalid | "a1b2c3d4" | "Bitpay\Client\ArgumentException" | 'pairing code is not legal' |
+
+ @javascript
+ Scenario Outline: the client has a bad port configuration to an incorrect port
+ When the client fails to pair with BitPay because port is an incorrect port
+ Then they will receive a matching
+ Examples:
+ | status | port | error | message |
+ | open | 444 | "Bitpay\Client\ConnectionException" | 'timed out' |
+ | closed | 8444 | "Bitpay\Client\ConnectionException" | 'Connection refused' |
\ No newline at end of file