Skip to content

Commit

Permalink
Adding custom exception handling && Adding missing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mgcodeur committed Jan 30, 2024
1 parent f06896d commit 9148833
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 26 deletions.
12 changes: 7 additions & 5 deletions src/CurrencyConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Mgcodeur\CurrencyConverter;

use Exception;
use Mgcodeur\CurrencyConverter\Exceptions\NetworkException;
use Mgcodeur\CurrencyConverter\Services\CurrencyService;
use Mgcodeur\CurrencyConverter\Traits\CurrencyConverterManager;

Expand All @@ -25,7 +25,7 @@ public function __construct(private CurrencyService $currencyService)
/**
* @return $this
*/
public function convert(float $amount): static
public function convert(float $amount = 0): static
{
$this->amount = $amount;

Expand All @@ -35,7 +35,7 @@ public function convert(float $amount): static
/**
* @return $this
*/
public function amount(float $amount): static
public function amount(float $amount = 0): static
{
$this->amount = $amount;

Expand Down Expand Up @@ -63,15 +63,17 @@ public function to(string $to): static
}

/**
* @throws Exception
* @return $this
*
* @throws NetworkException
*/
public function currencies(): static
{
$response = $this->currencyService->fetchAllCurrencies();
$result = $response->json();

if (! $result) {
throw new Exception('Something went wrong, please try again later');
throw new NetworkException();
}

$this->currencies = array_change_key_case($result, CASE_UPPER);
Expand Down
10 changes: 10 additions & 0 deletions src/Exceptions/MissingAmountException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Mgcodeur\CurrencyConverter\Exceptions;

class MissingAmountException extends \Exception
{
protected $message = 'Amount is required, please use convert() or amount() method before getting the result.';

protected $code = 400;
}
10 changes: 10 additions & 0 deletions src/Exceptions/MissingCurrencyException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Mgcodeur\CurrencyConverter\Exceptions;

class MissingCurrencyException extends \Exception
{
protected $message = '`From` is required, please specify currency using from() method before getting the result.';

protected $code = 400;
}
10 changes: 10 additions & 0 deletions src/Exceptions/NetworkException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Mgcodeur\CurrencyConverter\Exceptions;

class NetworkException extends \Exception
{
protected $message = 'Something went wrong with the network, please try again later.';

protected $code = 400;
}
8 changes: 2 additions & 6 deletions src/Services/CurrencyService.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,8 @@ public function fetchAllCurrencies(): Response
return Http::get($this->baseUrl.'.json');
}

public function runConversionFrom(string $from, ?string $to = null): Response
public function runConversionFrom(string $from, ?string $to = ''): Response
{
if (! $to) {
return Http::get($this->baseUrl."/{$from}.json");
}

return Http::get($this->baseUrl."/{$from}/{$to}.json");
return $to ? Http::get($this->baseUrl."/{$from}/{$to}.json") : Http::get($this->baseUrl."/{$from}.json");
}
}
16 changes: 9 additions & 7 deletions src/Traits/CurrencyConverterManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

namespace Mgcodeur\CurrencyConverter\Traits;

use Exception;
use Mgcodeur\CurrencyConverter\Exceptions\MissingAmountException;
use Mgcodeur\CurrencyConverter\Exceptions\MissingCurrencyException;
use Mgcodeur\CurrencyConverter\Exceptions\NetworkException;

trait CurrencyConverterManager
{
/**
* @throws Exception
* @throws NetworkException | MissingAmountException | MissingCurrencyException
*/
public function get($format = false): float|int|array|string
{
Expand All @@ -23,7 +25,7 @@ public function get($format = false): float|int|array|string
);

if ($response->failed() || ! $response->json()) {
throw new Exception('Something went wrong, please try again later');
throw new NetworkException();
}

$result = $response->json();
Expand All @@ -50,23 +52,23 @@ public function get($format = false): float|int|array|string
}

/**
* @throws Exception
* @throws NetworkException | MissingAmountException | MissingCurrencyException
*/
public function format(): float|array|int|string
{
return $this->get(true);
}

/**
* @throws Exception
* @throws MissingAmountException | MissingCurrencyException
*/
private function verifyDataBeforeGettingResults(): void
{
if (! $this->amount && ! $this->currencies) {
throw new Exception('Amount is required, please use convert() or amount() method before getting the result');
throw new MissingAmountException();
}
if (! $this->from && ! $this->currencies) {
throw new Exception('From currency is required, please specify currency using from() method before getting the result');
throw new MissingCurrencyException();
}
}
}
111 changes: 103 additions & 8 deletions tests/Unit/CurrencyConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,122 @@
expect($currencyConverter->to('USD'))->toBeInstanceOf(CurrencyConverter::class);
});

test('get conversion without setting amount throws exception', function () {
test('throws MissingAmountException when getting conversion without setting amount', function () {
$currencyConverter = app(CurrencyConverter::class);

$currencyConverter->get();
})->throws(Exception::class);
})->throws(\Mgcodeur\CurrencyConverter\Exceptions\MissingAmountException::class);

test('throw MissingCurrencyException if we don\'t specify from currency', function () {
$currencyConverter = app(CurrencyConverter::class);

$currencyConverter
->convert(10)
->to('usd')
->get();
})->throws(\Mgcodeur\CurrencyConverter\Exceptions\MissingCurrencyException::class);

test('convert currency from USD to EUR', function () {
$mockResponse = new \Illuminate\Http\Client\Response(
new \GuzzleHttp\Psr7\Response(200, [], json_encode(['eur' => 2000.0]))
// example of value of 1 USD in EUR
$dataToMock = [
'eur' => 0.92326769,
];

$amountToConvert = 100;

$convertedAmount = 0.92326769 * $amountToConvert;

$mockedResponse = new \Illuminate\Http\Client\Response(
new \GuzzleHttp\Psr7\Response(200, [], json_encode($dataToMock))
);

$this->currencyServiceMock
->shouldReceive('runConversionFrom')
->with('usd', 'eur')
->andReturn($mockResponse);
->andReturn($mockedResponse);

$currencyConverter = app(CurrencyConverter::class);
$convertedAmount = $currencyConverter->from('usd')
->to('eur')
->convert($amountToConvert)
->get();

expect($convertedAmount)->toBeFloat()->toEqual($convertedAmount);
});

test('convert currency from USD to all supported currencies', function () {
// example of value of 1 USD in [eur, mga, btc]
$dataToMock = [
'eur' => 0.92326769,
'mga' => 4511.5880329,
'btc' => 0.0000229836,
];

$amountToConvert = 100;

$convertedAmount = array_map(fn ($value) => $value * $amountToConvert, $dataToMock);

$mockedResponse = new \Illuminate\Http\Client\Response(
new \GuzzleHttp\Psr7\Response(200, [], json_encode($dataToMock))
);

$this->currencyServiceMock
->shouldReceive('runConversionFrom')
->with('usd', Mockery::any())
->andReturn($mockedResponse);

$this->currencyServiceMock
->shouldReceive('convertAllCurrency')
->with(
100,
'usd',
$dataToMock,
false
)
->andReturn($convertedAmount);

$currencyConverter = app(CurrencyConverter::class);
$convertedAmount = $currencyConverter->from('USD')
->to('EUR')

$testResult = $currencyConverter->from('usd')
->convert(100)
->get();

expect($convertedAmount)->toBeFloat()->toEqual(2000.0 * 100);
expect($testResult)->toBeArray()->toEqual($convertedAmount);
});

test('fetch a list of all supported currencies', function () {
$dataToMock = [
'1000SATS' => '',
'1INCH' => '1inch',
'AAVE' => 'Aave',
'MGA' => 'Malagasy Ariary',
];

$mockedResponse = new \Illuminate\Http\Client\Response(
new \GuzzleHttp\Psr7\Response(200, [], json_encode($dataToMock))
);

$this->currencyServiceMock
->shouldReceive('fetchAllCurrencies')
->andReturn($mockedResponse);

$currencyConverter = app(CurrencyConverter::class);

$testResult = $currencyConverter->currencies()->get();

expect($testResult)->toBeArray()->toEqual($dataToMock);
});

test('throw NetworkException when fetching a list of all supported currencies has no response or fail', function () {
$mockedResponse = new \Illuminate\Http\Client\Response(
new \GuzzleHttp\Psr7\Response(500, [], '')
);

$this->currencyServiceMock
->shouldReceive('fetchAllCurrencies')
->andReturn($mockedResponse);

$currencyConverter = app(CurrencyConverter::class);

$currencyConverter->currencies()->get();
})->throws(\Mgcodeur\CurrencyConverter\Exceptions\NetworkException::class);

0 comments on commit 9148833

Please sign in to comment.