From bb7556075375731fa2581a4d3b7dbd7297584eb7 Mon Sep 17 00:00:00 2001 From: osteel Date: Mon, 29 May 2023 16:24:38 +0100 Subject: [PATCH] 100% test coverage in domain folder --- .../Entities/SharePoolingAssetAcquisition.php | 2 +- .../SharePoolingAssetTransactions.php | 6 ++- .../DisposalProcessor/DisposalProcessor.php | 4 +- .../Exceptions/TaxYearSummaryException.php | 20 --------- .../TaxYear/Projections/TaxYearSummary.php | 7 +-- domain/src/Services/Math/Math.php | 2 +- .../Exceptions/TransferHandlerException.php | 21 --------- .../Handlers/TransferHandler.php | 2 - domain/src/ValueObjects/Fee.php | 2 +- domain/src/ValueObjects/FiatAmount.php | 2 +- .../ValueObjects/Transactions/Acquisition.php | 14 +++--- .../ValueObjects/Transactions/Disposal.php | 14 +++--- domain/src/ValueObjects/Transactions/Swap.php | 18 ++++---- .../ValueObjects/Transactions/Transfer.php | 13 +++--- .../SharePoolingAssetAcquisitionTest.php | 11 +++++ .../SharePoolingAssetAcquisitionsTest.php | 28 ++++++++++-- .../SharePoolingAssetDisposalTest.php | 12 +++++ .../SharePoolingAssetTransactionsTest.php | 1 + .../DisposalProcessorTest.php | 44 +++++++++++++++++++ .../QuantityAdjuster/QuantityAdjusterTest.php | 39 ++++++++++++++++ .../ReversionFinder/ReversionFinderTest.php | 22 ++++++++++ .../TaxYearSummaryProjectorTest.php | 13 ++++++ domain/tests/Services/Math/MathTest.php | 22 ++++++++++ domain/tests/ValueObjects/FeeTest.php | 6 +++ .../Transactions/TransferTest.php | 17 +++++++ 25 files changed, 257 insertions(+), 85 deletions(-) delete mode 100644 domain/src/Aggregates/TaxYear/Projections/Exceptions/TaxYearSummaryException.php delete mode 100644 domain/src/Services/TransactionDispatcher/Handlers/Exceptions/TransferHandlerException.php create mode 100644 domain/tests/Aggregates/SharePoolingAsset/Services/DisposalProcessor/DisposalProcessorTest.php create mode 100644 domain/tests/Aggregates/SharePoolingAsset/Services/QuantityAdjuster/QuantityAdjusterTest.php create mode 100644 domain/tests/Aggregates/SharePoolingAsset/Services/ReversionFinder/ReversionFinderTest.php diff --git a/domain/src/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisition.php b/domain/src/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisition.php index f9d6899..279b475 100644 --- a/domain/src/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisition.php +++ b/domain/src/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisition.php @@ -42,7 +42,7 @@ protected static function newFactory(): SharePoolingAssetAcquisitionFactory return SharePoolingAssetAcquisitionFactory::new(); } - public function averageCostBasisPerUnit(): ?FiatAmount + public function averageCostBasisPerUnit(): FiatAmount { return $this->costBasis->dividedBy($this->quantity); } diff --git a/domain/src/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetTransactions.php b/domain/src/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetTransactions.php index de063b8..cafc531 100644 --- a/domain/src/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetTransactions.php +++ b/domain/src/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetTransactions.php @@ -24,7 +24,11 @@ public static function make(SharePoolingAssetTransaction ...$transactions): self return new self(array_values($transactions)); } - /** @return Traversable */ + /** + * @return Traversable + * + * @codeCoverageIgnore + */ public function getIterator(): Traversable { return new ArrayIterator($this->transactions); diff --git a/domain/src/Aggregates/SharePoolingAsset/Services/DisposalProcessor/DisposalProcessor.php b/domain/src/Aggregates/SharePoolingAsset/Services/DisposalProcessor/DisposalProcessor.php index 6809bf4..710d9d3 100644 --- a/domain/src/Aggregates/SharePoolingAsset/Services/DisposalProcessor/DisposalProcessor.php +++ b/domain/src/Aggregates/SharePoolingAsset/Services/DisposalProcessor/DisposalProcessor.php @@ -166,9 +166,7 @@ private static function processAcquisitionsWithinThirtyDays( $averageCostBasisPerUnit = $acquisition->averageCostBasisPerUnit(); - $costBasis = $averageCostBasisPerUnit - ? $costBasis->plus($averageCostBasisPerUnit->multipliedBy($thirtyDayQuantityToApply)) - : $costBasis; + $costBasis = $costBasis->plus($averageCostBasisPerUnit->multipliedBy($thirtyDayQuantityToApply)); $thirtyDayQuantityAllocation->allocateQuantity($thirtyDayQuantityToApply, $acquisition); $acquisition->increaseThirtyDayQuantity($thirtyDayQuantityToApply); diff --git a/domain/src/Aggregates/TaxYear/Projections/Exceptions/TaxYearSummaryException.php b/domain/src/Aggregates/TaxYear/Projections/Exceptions/TaxYearSummaryException.php deleted file mode 100644 index b972d67..0000000 --- a/domain/src/Aggregates/TaxYear/Projections/Exceptions/TaxYearSummaryException.php +++ /dev/null @@ -1,20 +0,0 @@ -currency), diff --git a/domain/src/Services/Math/Math.php b/domain/src/Services/Math/Math.php index 6bd50ab..3da6baf 100644 --- a/domain/src/Services/Math/Math.php +++ b/domain/src/Services/Math/Math.php @@ -41,7 +41,7 @@ public static function add(string ...$operands): string */ public static function sub(string ...$operands): string { - if (is_null($initial = array_shift($operands))) { + if (strlen($initial = (string) array_shift($operands)) === 0) { return '0'; } diff --git a/domain/src/Services/TransactionDispatcher/Handlers/Exceptions/TransferHandlerException.php b/domain/src/Services/TransactionDispatcher/Handlers/Exceptions/TransferHandlerException.php deleted file mode 100644 index b68cf8b..0000000 --- a/domain/src/Services/TransactionDispatcher/Handlers/Exceptions/TransferHandlerException.php +++ /dev/null @@ -1,21 +0,0 @@ -fee)) { diff --git a/domain/src/ValueObjects/Fee.php b/domain/src/ValueObjects/Fee.php index a67bc0c..66f5f31 100644 --- a/domain/src/ValueObjects/Fee.php +++ b/domain/src/ValueObjects/Fee.php @@ -22,6 +22,6 @@ public function isFiat(): bool public function __toString(): string { - return sprintf('%s %s', (string) $this->currency, $this->quantity); + return sprintf('%s %s (market value: %s)', $this->currency, $this->quantity, $this->marketValue); } } diff --git a/domain/src/ValueObjects/FiatAmount.php b/domain/src/ValueObjects/FiatAmount.php index 5181cf6..df7f79f 100644 --- a/domain/src/ValueObjects/FiatAmount.php +++ b/domain/src/ValueObjects/FiatAmount.php @@ -126,6 +126,6 @@ private function assertCurrenciesMatch(FiatAmount ...$fiatAmounts): void public function __toString(): string { - return sprintf('%s%s', $this->currency->symbol(), (string) $this->quantity); + return sprintf('%s%s', $this->currency->symbol(), $this->quantity); } } diff --git a/domain/src/ValueObjects/Transactions/Acquisition.php b/domain/src/ValueObjects/Transactions/Acquisition.php index cccd208..5a936af 100644 --- a/domain/src/ValueObjects/Transactions/Acquisition.php +++ b/domain/src/ValueObjects/Transactions/Acquisition.php @@ -16,13 +16,15 @@ { /** @throws AcquisitionException */ public function __construct( - public LocalDate $date, + LocalDate $date, public Asset $asset, public Quantity $quantity, public FiatAmount $marketValue, - public ?Fee $fee = null, + ?Fee $fee = null, public bool $isIncome = false, ) { + parent::__construct($date, $fee); + ! $asset->isFiat() || throw AcquisitionException::isFiat($this); } @@ -45,13 +47,13 @@ public function __toString(): string { return sprintf( '%s | acquired: %s | non-fungible asset: %s | quantity: %s | cost basis: %s | income: %s | Fee: %s', - (string) $this->date, - (string) $this->asset, + $this->date, + $this->asset, $this->asset->isNonFungible ? 'yes' : 'no', - (string) $this->quantity, + $this->quantity, (string) $this->marketValue ?: 'N/A', $this->isIncome ? 'yes' : 'no', - (string) $this->fee ?: 'N/A', + $this->fee ?: 'N/A', ); } } diff --git a/domain/src/ValueObjects/Transactions/Disposal.php b/domain/src/ValueObjects/Transactions/Disposal.php index 05379c9..b9ef3f0 100644 --- a/domain/src/ValueObjects/Transactions/Disposal.php +++ b/domain/src/ValueObjects/Transactions/Disposal.php @@ -16,12 +16,14 @@ { /** @throws DisposalException */ public function __construct( - public LocalDate $date, + LocalDate $date, public Asset $asset, public Quantity $quantity, public FiatAmount $marketValue, - public ?Fee $fee = null, + ?Fee $fee = null, ) { + parent::__construct($date, $fee); + ! $asset->isFiat() || throw DisposalException::isFiat($this); } @@ -44,12 +46,12 @@ public function __toString(): string { return sprintf( '%s | disposed of: %s | non-fungible asset: %s | quantity: %s | cost basis: %s | Fee: %s', - (string) $this->date, - (string) $this->asset, + $this->date, + $this->asset, $this->asset->isNonFungible ? 'yes' : 'no', - (string) $this->quantity, + $this->quantity, (string) $this->marketValue ?: 'N/A', - (string) $this->fee ?: 'N/A', + $this->fee ?: 'N/A', ); } } diff --git a/domain/src/ValueObjects/Transactions/Swap.php b/domain/src/ValueObjects/Transactions/Swap.php index 0e595d7..ead437d 100644 --- a/domain/src/ValueObjects/Transactions/Swap.php +++ b/domain/src/ValueObjects/Transactions/Swap.php @@ -16,14 +16,16 @@ { /** @throws SwapException */ public function __construct( - public LocalDate $date, + LocalDate $date, public Asset $disposedOfAsset, public Quantity $disposedOfQuantity, public Asset $acquiredAsset, public Quantity $acquiredQuantity, public FiatAmount $marketValue, - public ?Fee $fee = null, + ?Fee $fee = null, ) { + parent::__construct($date, $fee); + ! $disposedOfAsset->isFiat() || ! $acquiredAsset->isFiat() || throw SwapException::bothSidesAreFiat($this); } @@ -61,15 +63,15 @@ public function __toString(): string { return sprintf( '%s | disposed of: %s | non-fungible asset: %s | quantity: %s | acquired: %s | non-fungible asset: %s | quantity: %s | cost basis: %s | Fee: %s', - (string) $this->date, - (string) $this->disposedOfAsset, + $this->date, + $this->disposedOfAsset, $this->disposedOfAsset->isNonFungible ? 'yes' : 'no', - (string) $this->disposedOfQuantity, - (string) $this->acquiredAsset, + $this->disposedOfQuantity, + $this->acquiredAsset, $this->acquiredAsset->isNonFungible ? 'yes' : 'no', - (string) $this->acquiredQuantity, + $this->acquiredQuantity, (string) $this->marketValue ?: 'N/A', - (string) $this->fee ?: 'N/A', + $this->fee ?: 'N/A', ); } } diff --git a/domain/src/ValueObjects/Transactions/Transfer.php b/domain/src/ValueObjects/Transactions/Transfer.php index fcca7a2..fd2b4ce 100644 --- a/domain/src/ValueObjects/Transactions/Transfer.php +++ b/domain/src/ValueObjects/Transactions/Transfer.php @@ -13,11 +13,12 @@ final readonly class Transfer extends Transaction { public function __construct( - public LocalDate $date, + LocalDate $date, public Asset $asset, public Quantity $quantity, - public ?Fee $fee = null, + ?Fee $fee = null, ) { + parent::__construct($date, $fee); } protected static function newFactory(): TransferFactory @@ -39,11 +40,11 @@ public function __toString(): string { return sprintf( '%s | transferred: %s | non-fungible asset: %s | quantity: %s | Fee: %s', - (string) $this->date, - (string) $this->asset, + $this->date, + $this->asset, $this->asset->isNonFungible ? 'yes' : 'no', - (string) $this->quantity, - (string) $this->fee ?: 'N/A', + $this->quantity, + $this->fee ?: 'N/A', ); } } diff --git a/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisitionTest.php b/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisitionTest.php index d7d7345..c086479 100644 --- a/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisitionTest.php +++ b/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisitionTest.php @@ -167,3 +167,14 @@ expect((string) $acquisition->thirtyDayQuantity())->toBe('50'); }); + +it('can return an acquisition as a string', function () { + /** @var SharePoolingAssetAcquisition */ + $acquisition = SharePoolingAssetAcquisition::factory()->make([ + 'date' => LocalDate::parse('2015-10-21'), + 'quantity' => new Quantity('100'), + 'costBasis' => FiatAmount::GBP('100'), + ]); + + expect((string) $acquisition)->toBe('2015-10-21: acquired 100 tokens for £100'); +}); diff --git a/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisitionsTest.php b/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisitionsTest.php index bf5f9c3..d01e660 100644 --- a/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisitionsTest.php +++ b/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetAcquisitionsTest.php @@ -68,7 +68,7 @@ 'scenario 3' => [['1.12345678', '1.123456789'], '2.246913569'], ]); -it('can return the cost basis of a collection of acquisitions', function (array $costBases, string $total) { +it('can return the cost basis of a collection of acquisitions', function (array $costBases, ?string $total) { $acquisitions = []; foreach ($costBases as $costBasis) { @@ -80,12 +80,13 @@ $acquisitions = SharePoolingAssetAcquisitions::make(...$acquisitions); expect($acquisitions->costBasis()) - ->toBeInstanceOf(FiatAmount::class) - ->toEqual(FiatAmount::GBP($total)); + ->when(empty($costBases), fn ($costBasis) => $costBasis->toBeNull()) + ->unless(empty($costBases), fn ($costBasis) => $costBasis->toBeInstanceOf(FiatAmount::class)->toEqual(FiatAmount::GBP($total))); })->with([ 'scenario 1' => [['10'], '10'], 'scenario 2' => [['4', '10', '11'], '25'], 'scenario 3' => [['1.12345678', '1.123456789'], '2.246913569'], + 'scenario 4' => [[], null], ]); it('can return the average cost basis per unit of a collection of acquisitions', function (array $costBases, string $average) { @@ -171,6 +172,27 @@ ->toEqual(FiatAmount::GBP('160')); }); +it('can return the section 104 pool cost basis of an empty collection of acquisitions', function () { + expect(SharePoolingAssetAcquisitions::make()->section104PoolCostBasis())->toBeNull(); +}); + +it('can return the section 104 pool cost basis of a collection of acquisitions with no section 104 pool quantity', function () { + /** @var list */ + $items = [ + SharePoolingAssetAcquisition::factory()->make([ + 'costBasis' => FiatAmount::GBP('100'), + 'quantity' => new Quantity('100'), + 'sameDayQuantity' => new Quantity('100'), + ]), + ]; + + $acquisitions = SharePoolingAssetAcquisitions::make(...$items); + + expect($acquisitions->section104PoolCostBasis()) + ->toBeInstanceOf(FiatAmount::class) + ->toEqual(FiatAmount::GBP('0')); +}); + it('can return the average section 104 pool cost basis per unit of a collection of acquisitions', function () { /** @var list */ $items = [ diff --git a/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetDisposalTest.php b/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetDisposalTest.php index 6fc1c88..3e6ff3c 100644 --- a/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetDisposalTest.php +++ b/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetDisposalTest.php @@ -119,3 +119,15 @@ expect($disposal->hasThirtyDayQuantityAllocatedTo($qux))->toBeFalse(); expect((string) $disposal->thirtyDayQuantityAllocatedTo($qux))->toBe('0'); }); + +it('can return a disposal as a string', function () { + /** @var SharePoolingAssetDisposal */ + $acquisition = SharePoolingAssetDisposal::factory()->make([ + 'date' => LocalDate::parse('2015-10-21'), + 'quantity' => new Quantity('100'), + 'proceeds' => FiatAmount::GBP('150'), + 'costBasis' => FiatAmount::GBP('100'), + ]); + + expect((string) $acquisition)->toBe('2015-10-21: disposed of 100 tokens for £150 (cost basis: £100)'); +}); diff --git a/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetTransactionsTest.php b/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetTransactionsTest.php index 27b5db3..8c72081 100644 --- a/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetTransactionsTest.php +++ b/domain/tests/Aggregates/SharePoolingAsset/Entities/SharePoolingAssetTransactionsTest.php @@ -203,6 +203,7 @@ 'scenario 4' => ['2015-10-24', '2015-10-25', 2], 'scenario 5' => ['2015-10-25', '2015-10-21', 6], 'scenario 6' => ['2015-10-26', '2015-10-27', 0], + 'scenario 7' => ['2015-10-21', '2015-10-21', 2], ]); it('can return a collection of acquisitions that happened between two dates', function (string $date1, string $date2, int $count) { diff --git a/domain/tests/Aggregates/SharePoolingAsset/Services/DisposalProcessor/DisposalProcessorTest.php b/domain/tests/Aggregates/SharePoolingAsset/Services/DisposalProcessor/DisposalProcessorTest.php new file mode 100644 index 0000000..db6fee4 --- /dev/null +++ b/domain/tests/Aggregates/SharePoolingAsset/Services/DisposalProcessor/DisposalProcessorTest.php @@ -0,0 +1,44 @@ +make()), + ); + + expect($disposal->id)->toBe($id); +}); + +it('does not process any section 104 pool acquisitions when none were made before or on the day of the disposal', function () { + $action = new DisposeOfSharePoolingAsset( + transactionId: $id = SharePoolingAssetTransactionId::fromString('foo'), + asset: new Asset('FOO'), + date: LocalDate::parse('2015-10-21'), + quantity: new Quantity('100'), + proceeds: FiatAmount::GBP('0'), + ); + + $acquisition = SharePoolingAssetAcquisition::factory()->make(['date' => LocalDate::parse('2021-10-22')]); + + $disposal = DisposalProcessor::process($action, SharePoolingAssetTransactions::make($acquisition)); + + expect($disposal->id)->toBe($id); +}); diff --git a/domain/tests/Aggregates/SharePoolingAsset/Services/QuantityAdjuster/QuantityAdjusterTest.php b/domain/tests/Aggregates/SharePoolingAsset/Services/QuantityAdjuster/QuantityAdjusterTest.php new file mode 100644 index 0000000..0a9cccb --- /dev/null +++ b/domain/tests/Aggregates/SharePoolingAsset/Services/QuantityAdjuster/QuantityAdjusterTest.php @@ -0,0 +1,39 @@ +withSameDayQuantity(Quantity::zero(), $id = SharePoolingAssetTransactionId::fromString('foo')) + ->make(); + + expect(fn () => QuantityAdjuster::revertDisposal($disposal, SharePoolingAssetTransactions::make())) + ->toThrow(QuantityAdjusterException::class, QuantityAdjusterException::transactionNotFound($id)->getMessage()); +}); + +it('cannot revert a disposal because a transaction is not an acquisition', function () { + $disposal = SharePoolingAssetDisposal::factory() + ->withSameDayQuantity(Quantity::zero(), $id = SharePoolingAssetTransactionId::fromString('foo')) + ->make(); + + $transactions = SharePoolingAssetTransactions::make(SharePoolingAssetDisposal::factory()->make(['id' => $id])); + + expect(fn () => QuantityAdjuster::revertDisposal($disposal, $transactions)) + ->toThrow(QuantityAdjusterException::class, QuantityAdjusterException::notAnAcquisition($id)->getMessage()); +}); + +it('reverts a disposal despite insufficient same-day quantity', function () { + $disposal = SharePoolingAssetDisposal::factory() + ->withSameDayQuantity(new Quantity('100'), $id = SharePoolingAssetTransactionId::fromString('foo')) + ->make(); + + $transactions = SharePoolingAssetTransactions::make(SharePoolingAssetAcquisition::factory()->make(['id' => $id])); + + expect(QuantityAdjuster::revertDisposal($disposal, $transactions))->not->toThrow(QuantityAdjusterException::class); +}); diff --git a/domain/tests/Aggregates/SharePoolingAsset/Services/ReversionFinder/ReversionFinderTest.php b/domain/tests/Aggregates/SharePoolingAsset/Services/ReversionFinder/ReversionFinderTest.php new file mode 100644 index 0000000..946231d --- /dev/null +++ b/domain/tests/Aggregates/SharePoolingAsset/Services/ReversionFinder/ReversionFinderTest.php @@ -0,0 +1,22 @@ +isEmpty())->toBeTrue(); +}); diff --git a/domain/tests/Aggregates/TaxYear/Projectors/TaxYearSummaryProjectorTest.php b/domain/tests/Aggregates/TaxYear/Projectors/TaxYearSummaryProjectorTest.php index 7cab1cc..cc2d8c0 100644 --- a/domain/tests/Aggregates/TaxYear/Projectors/TaxYearSummaryProjectorTest.php +++ b/domain/tests/Aggregates/TaxYear/Projectors/TaxYearSummaryProjectorTest.php @@ -5,6 +5,7 @@ use Domain\Aggregates\TaxYear\Events\CapitalGainUpdateReverted; use Domain\Aggregates\TaxYear\Events\IncomeUpdated; use Domain\Aggregates\TaxYear\Events\NonAttributableAllowableCostUpdated; +use Domain\Aggregates\TaxYear\Projectors\Exceptions\TaxYearSummaryProjectionException; use Domain\Aggregates\TaxYear\ValueObjects\CapitalGain; use Domain\Tests\Aggregates\TaxYear\Projectors\TaxYearSummaryProjectorTestCase; use Domain\ValueObjects\FiatAmount; @@ -85,3 +86,15 @@ && $nonAttributableAllowableCost === $nonAttributableAllowableCostUpdated->newNonAttributableAllowableCost) ->once()); }); + +it('throws an exception when the aggregate root ID is missing', function () { + $capitalGainUpdated = new CapitalGainUpdated( + date: LocalDate::parse('2015-10-21'), + capitalGainUpdate: new CapitalGain(FiatAmount::GBP('100'), FiatAmount::GBP('100')), + newCapitalGain: new CapitalGain(FiatAmount::GBP('100'), FiatAmount::GBP('100')), + ); + + /** @var MessageConsumerTestCase $this */ + $this->when(new Message($capitalGainUpdated)) + ->expectToFail(TaxYearSummaryProjectionException::missingTaxYearId(CapitalGainUpdated::class)); +}); diff --git a/domain/tests/Services/Math/MathTest.php b/domain/tests/Services/Math/MathTest.php index 8e20cfc..6008343 100644 --- a/domain/tests/Services/Math/MathTest.php +++ b/domain/tests/Services/Math/MathTest.php @@ -1,5 +1,6 @@ [['1.11111119', '1.11111111'], '2.2222223'], ]); +it('can throw an exception when adding', function () { + expect(fn () => Math::add('foo'))->toThrow(MathException::class); +}); + it('can subtract', function (array $operands, string $result) { expect(Math::sub(...$operands))->toBe($result); })->with([ @@ -25,8 +30,13 @@ 'scenario 7' => [['-3.33', '1.11'], '-4.44'], 'scenario 8' => [['2.246913569', '1.123456789'], '1.12345678'], 'scenario 9' => [['2.2222223', '1.11111111'], '1.11111119'], + 'scenario 10' => [[''], '0'], ]); +it('can throw an exception when subtracting', function () { + expect(fn () => Math::sub('foo', 'bar'))->toThrow(MathException::class); +}); + it('can multiply', function (string $multiplicand, string $multiplier, string $result) { expect(Math::mul($multiplicand, $multiplier))->toBe($result); })->with([ @@ -39,6 +49,10 @@ 'scenario 7' => ['0.00000000222351', '0.105473', '0.00000000023452027023'], ]); +it('can throw an exception when multiplying', function () { + expect(fn () => Math::mul('foo', 'bar'))->toThrow(MathException::class); +}); + it('can divide', function (string $dividend, string $divisor, string $result) { expect(Math::div($dividend, $divisor))->toBe($result); })->with([ @@ -51,6 +65,10 @@ 'scenario 7' => ['0.00000000222351', '0.105473', '0.00000002108131938979644079527462'], ]); +it('can throw an exception when dividing', function () { + expect(fn () => Math::div('foo', 'bar'))->toThrow(MathException::class); +}); + it('can round', function (string $number, int $precision, string $rounded) { expect(Math::rnd($number, $precision))->toBe($rounded); })->with([ @@ -113,6 +131,10 @@ 'scenario 11' => ['0.0000000230', '0.0000000231', false, true], ]); +it('can throw an exception when comparing', function (string $method) { + expect(fn () => Math::$method('foo', 'bar'))->toThrow(MathException::class); +})->with(['eq', 'gt', 'gte', 'lt', 'lte']); + it('can return the smallest number', function (string $term1, string $term2, string $result) { expect(Math::min($term1, $term2))->toBe($result); })->with([ diff --git a/domain/tests/ValueObjects/FeeTest.php b/domain/tests/ValueObjects/FeeTest.php index 8b4d2b6..d282774 100644 --- a/domain/tests/ValueObjects/FeeTest.php +++ b/domain/tests/ValueObjects/FeeTest.php @@ -10,3 +10,9 @@ expect((new Fee(new Asset(FiatCurrency::GBP->value), Quantity::zero(), FiatAmount::GBP('0')))->isFiat())->toBe(true); expect((new Fee(new Asset('foo'), Quantity::zero(), FiatAmount::GBP('0')))->isFiat())->toBe(false); }); + +it('can return the fee as a string', function () { + $fee = new Fee(new Asset(FiatCurrency::GBP->value), new Quantity('100'), FiatAmount::GBP('100')); + + expect((string) $fee)->toBe('GBP 100 (market value: £100)'); +}); diff --git a/domain/tests/ValueObjects/Transactions/TransferTest.php b/domain/tests/ValueObjects/Transactions/TransferTest.php index 15b1cc7..312703d 100644 --- a/domain/tests/ValueObjects/Transactions/TransferTest.php +++ b/domain/tests/ValueObjects/Transactions/TransferTest.php @@ -1,6 +1,11 @@ hasSharePoolingAsset())->toBeTrue(); expect($transaction->hasNonFungibleAsset())->toBeFalse(); }); + +it('can return the transfer as a string', function () { + /** @var Transfer */ + $transaction = Transfer::factory()->make([ + 'date' => LocalDate::parse('2015-10-21'), + 'asset' => new Asset('foo'), + 'quantity' => new Quantity('100'), + 'fee' => new Fee(new Asset(FiatCurrency::GBP->value), new Quantity('1'), FiatAmount::GBP('1')), + ]); + + expect((string) $transaction)->toBe('2015-10-21 | transferred: FOO | non-fungible asset: no | quantity: 100 | Fee: GBP 1 (market value: £1)'); +});