adds passing tests
This commit is contained in:
@@ -19,14 +19,18 @@ class TransactionFactory extends Factory
|
|||||||
*/
|
*/
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
|
$transaction_type = $this->faker->randomElement(['BUY', 'SELL']);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'symbol' => $this->faker->randomElement(['AAPL', 'GOOGL', 'AMZN']),
|
'symbol' => $this->faker->randomElement(['AAPL', 'GOOG', 'AMZN']),
|
||||||
'transaction_type' => static::$transaction_type = $this->faker->randomElement(['BUY', 'SELL']),
|
'transaction_type' => $transaction_type,
|
||||||
'portfolio_id' => Portfolio::factory()->create(),
|
'portfolio_id' => Portfolio::factory()->create()->id,
|
||||||
'date' => $this->faker->date('Y-m-d'),
|
'date' => $this->faker->date('Y-m-d'),
|
||||||
'quantity' => $this->faker->randomFloat(2, 1, 100),
|
'quantity' => 1,
|
||||||
'cost_basis' => $this->faker->randomFloat(2, 10, 500),
|
'cost_basis' => $transaction_type == 'BUY'
|
||||||
'sale_price' => static::$transaction_type == 'SELL'
|
? $this->faker->randomFloat(2, 10, 500)
|
||||||
|
: null,
|
||||||
|
'sale_price' => $transaction_type == 'SELL'
|
||||||
? $this->faker->randomFloat(2, 10, 500)
|
? $this->faker->randomFloat(2, 10, 500)
|
||||||
: null,
|
: null,
|
||||||
];
|
];
|
||||||
@@ -39,7 +43,7 @@ class TransactionFactory extends Factory
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function portfolios($portfolio_id): static
|
public function portfolio($portfolio_id): static
|
||||||
{
|
{
|
||||||
return $this->state(fn (array $attributes) => [
|
return $this->state(fn (array $attributes) => [
|
||||||
'portfolio_id' => $portfolio_id,
|
'portfolio_id' => $portfolio_id,
|
||||||
@@ -50,6 +54,8 @@ class TransactionFactory extends Factory
|
|||||||
{
|
{
|
||||||
return $this->state(fn (array $attributes) => [
|
return $this->state(fn (array $attributes) => [
|
||||||
'transaction_type' => 'BUY',
|
'transaction_type' => 'BUY',
|
||||||
|
'cost_basis' => $this->faker->randomFloat(2, 10, 500),
|
||||||
|
'sale_price' => null
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +63,8 @@ class TransactionFactory extends Factory
|
|||||||
{
|
{
|
||||||
return $this->state(fn (array $attributes) => [
|
return $this->state(fn (array $attributes) => [
|
||||||
'transaction_type' => 'SELL',
|
'transaction_type' => 'SELL',
|
||||||
|
'sale_price' => $this->faker->randomFloat(2, 10, 500),
|
||||||
|
'cost_basis' => null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,5 +26,6 @@
|
|||||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||||
<env name="SESSION_DRIVER" value="array"/>
|
<env name="SESSION_DRIVER" value="array"/>
|
||||||
<env name="TELESCOPE_ENABLED" value="false"/>
|
<env name="TELESCOPE_ENABLED" value="false"/>
|
||||||
|
<env name="MARKET_DATA_PROVIDER" value="fake"/>
|
||||||
</php>
|
</php>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Portfolio;
|
||||||
|
use App\Models\DailyChange;
|
||||||
|
use App\Models\Transaction;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
class CaptureDailyChangeTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
$this->portfolio = Portfolio::factory()->create();
|
||||||
|
Transaction::factory(5)->buy()->portfolio($this->portfolio->id)->symbol('AAPL')->create();
|
||||||
|
$this->transaction = Transaction::factory()->sell()->portfolio($this->portfolio->id)->symbol('AAPL')->create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public function test_daily_change_for_portfolios()
|
||||||
|
{
|
||||||
|
// Run the command
|
||||||
|
Artisan::call('daily-change:capture');
|
||||||
|
|
||||||
|
// Assert the daily change was captured for the portfolio
|
||||||
|
$this->assertDatabaseHas('daily_change', [
|
||||||
|
'portfolio_id' => $this->portfolio->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$output = Artisan::output();
|
||||||
|
$this->assertStringContainsString('Capturing daily change for', $output);
|
||||||
|
|
||||||
|
$daily_change = DailyChange::where([
|
||||||
|
'portfolio_id' => $this->portfolio->id,
|
||||||
|
])->get();
|
||||||
|
|
||||||
|
$this->assertCount(1, $daily_change);
|
||||||
|
|
||||||
|
$this->assertEqualsWithDelta(
|
||||||
|
$this->transaction->sale_price - $this->transaction->cost_basis,
|
||||||
|
$daily_change->first()->realized_gains,
|
||||||
|
0.01
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Holding;
|
||||||
|
use App\Models\Portfolio;
|
||||||
|
use App\Models\Transaction;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
class DashboardTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public function test_user_has_portfolios(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$this->actingAs($user);
|
||||||
|
|
||||||
|
Portfolio::factory(5)->create();
|
||||||
|
|
||||||
|
$this->assertCount(5, $user->portfolios);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public function test_user_has_transactions(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$this->actingAs($user);
|
||||||
|
|
||||||
|
Transaction::factory(10)->create();
|
||||||
|
|
||||||
|
$this->assertCount(10, $user->transactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public function test_user_has_holdings(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$this->actingAs($user);
|
||||||
|
|
||||||
|
$portfolio = Portfolio::factory()->create();
|
||||||
|
|
||||||
|
Transaction::factory(5)->symbol('AAPL')->portfolio($portfolio->id)->create();
|
||||||
|
|
||||||
|
$this->assertCount(1, $user->holdings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public function test_user_has_dashboard_metrics(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$this->actingAs($user);
|
||||||
|
|
||||||
|
$portfolio = Portfolio::factory()->create();
|
||||||
|
|
||||||
|
Transaction::factory(5)->buy()->portfolio($portfolio->id)->symbol('AAPL')->create();
|
||||||
|
$transaction = Transaction::factory()->sell()->portfolio($portfolio->id)->symbol('AAPL')->create();
|
||||||
|
|
||||||
|
$metrics = Holding::query()
|
||||||
|
->myHoldings()
|
||||||
|
->withPortfolioMetrics()
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$this->assertEqualsWithDelta(
|
||||||
|
$transaction->sale_price - $transaction->cost_basis,
|
||||||
|
$metrics->realized_gain_dollars,
|
||||||
|
0.01
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -11,7 +11,7 @@ abstract class TestCase extends BaseTestCase
|
|||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
Artisan::call('migrate');
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown(): void
|
protected function tearDown(): void
|
||||||
|
|||||||
+60
-54
@@ -2,72 +2,78 @@
|
|||||||
|
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Models\Holding;
|
||||||
use App\Models\Portfolio;
|
use App\Models\Portfolio;
|
||||||
use App\Models\Transaction;
|
use App\Models\Transaction;
|
||||||
use Tests\TestCase;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
class TransactionsTest extends TestCase
|
class TransactionsTest extends TestCase
|
||||||
{
|
{
|
||||||
use RefreshDatabase;
|
use RefreshDatabase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic test example.
|
|
||||||
*/
|
*/
|
||||||
public function test_can_create_a_transaction(): void
|
public function test_can_create_a_transaction(): void
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$this->actingAs($user = User::factory()->create());
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$transactions = Transaction::factory()->create();
|
$transaction = Transaction::factory()->create();
|
||||||
|
|
||||||
|
$this->assertNotNull($transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public function test_sales_calculate_cost_basis(): void
|
||||||
|
{
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
Transaction::factory(5)->buy()->symbol('AAPL')->create();
|
||||||
|
|
||||||
|
$transaction = Transaction::factory()->sell()->symbol('AAPL')->create();
|
||||||
|
|
||||||
|
$this->assertNotNull($transaction->cost_basis);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public function test_purchases_dont_have_sale_price(): void
|
||||||
|
{
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
$transaction = Transaction::factory()->buy()->create();
|
||||||
|
|
||||||
|
$this->assertNull($transaction->sale_price);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public function test_transaction_synced_to_holding(): void
|
||||||
|
{
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
$portfolio = Portfolio::factory()->create();
|
||||||
|
|
||||||
|
Transaction::factory(5)->buy()->portfolio($portfolio->id)->symbol('AAPL')->create();
|
||||||
|
$transaction = Transaction::factory()->sell()->portfolio($portfolio->id)->symbol('AAPL')->create();
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('holdings', [
|
||||||
|
'portfolio_id' => $portfolio->id,
|
||||||
|
'symbol' => 'AAPL',
|
||||||
|
'quantity' => 4
|
||||||
|
]);
|
||||||
|
|
||||||
|
$holding = Holding::where([
|
||||||
|
'portfolio_id' => $portfolio->id,
|
||||||
|
'symbol' => 'AAPL'
|
||||||
|
])->first();
|
||||||
|
|
||||||
|
$this->assertEqualsWithDelta(
|
||||||
|
$holding->realized_gain_dollars,
|
||||||
|
$transaction->sale_price - $transaction->cost_basis,
|
||||||
|
0.01
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// static::saving(function ($transaction) {
|
|
||||||
|
|
||||||
// if ($transaction->transaction_type == 'SELL') {
|
|
||||||
|
|
||||||
// $transaction->ensureCostBasisIsAddedToSale();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// static::saved(function ($transaction) {
|
|
||||||
|
|
||||||
// $transaction->syncToHolding();
|
|
||||||
|
|
||||||
// $transaction->refreshMarketData();
|
|
||||||
|
|
||||||
// cache()->tags(['metrics', $transaction->portfolio_id])->flush();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// public function update()
|
|
||||||
// {
|
|
||||||
|
|
||||||
// $this->transaction->update($this->validate());
|
|
||||||
// // $this->transaction->owner_id = auth()->user()->id;
|
|
||||||
// $this->transaction->save();
|
|
||||||
|
|
||||||
// $this->success(__('Transaction updated'));
|
|
||||||
|
|
||||||
// $this->dispatch('toggle-manage-transaction');
|
|
||||||
// $this->dispatch('transaction-updated');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public function save()
|
|
||||||
// {
|
|
||||||
// $validated = $this->validate();
|
|
||||||
|
|
||||||
// if (!isset($this->portfolio)) {
|
|
||||||
// $this->portfolio = Portfolio::find($this->portfolio_id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// $transaction = $this->portfolio->transactions()->create($validated);
|
|
||||||
// $transaction->save();
|
|
||||||
|
|
||||||
// $this->dispatch('transaction-saved');
|
|
||||||
|
|
||||||
// $this->success(__('Transaction created'), redirectTo: route('holding.show', ['portfolio' => $this->portfolio->id, 'symbol' => $this->symbol]));
|
|
||||||
// }
|
|
||||||
|
|||||||
Reference in New Issue
Block a user