test: Add comprehensive feature tests for purchase management, including item cart operations, purchase creation, stock updates, and permission checks.

This commit is contained in:
Yoga Pangestu 2026-01-02 20:54:45 +07:00
parent 7873701015
commit 99c83e08f8

View File

@ -0,0 +1,285 @@
<?php
use App\Livewire\Studio\Manage\Purchase\Create;
use App\Livewire\Studio\Manage\Purchase\Index;
use App\Models\Bottle;
use App\Models\Perfume;
use App\Models\Product;
use App\Models\Purchase;
use App\Models\PurchaseItem;
use App\Models\Warehouse;
use Illuminate\Support\Facades\Storage;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
$this->setupUser();
$role = Role::firstOrCreate(['name' => 'Owner']);
Permission::firstOrCreate(['name' => 'view purchase']);
Permission::firstOrCreate(['name' => 'create purchase']);
Permission::firstOrCreate(['name' => 'delete purchase']);
$role->givePermissionTo(['view purchase', 'create purchase', 'delete purchase']);
$this->user->assignRole($role);
$this->warehouse = Warehouse::factory()->create();
});
it('renders the purchase index page', function () {
$this->actingAs($this->user)
->get(route('studio.manage.purchase.index'))
->assertOk()
->assertSeeLivewire(Index::class);
});
it('renders the purchase create page', function () {
$this->actingAs($this->user)
->get(route('studio.manage.purchase.create'))
->assertOk()
->assertSeeLivewire(Create::class);
});
it('can add a perfume to the cart', function () {
$perfume = Perfume::factory()->create(['cost_price' => 50000]);
Livewire::actingAs($this->user)
->test(Create::class)
->set('form.perfume_id', $perfume->id)
->set('form.quantity_perfume', '10')
->call('addItem', 'parfum')
->assertHasNoErrors();
$this->assertDatabaseHas('purchase_items', [
'user_id' => $this->user->id,
'purchasable_type' => Perfume::class,
'purchasable_id' => $perfume->id,
'quantity' => 10,
'unit_price' => 50000,
'purchase_id' => null,
]);
});
it('can add a product to the cart', function () {
$product = Product::factory()->create(['cost_price' => 30000]);
Livewire::actingAs($this->user)
->test(Create::class)
->set('form.product_id', $product->id)
->set('form.quantity_product', '5')
->call('addItem', 'produk')
->assertHasNoErrors();
$this->assertDatabaseHas('purchase_items', [
'user_id' => $this->user->id,
'purchasable_type' => Product::class,
'purchasable_id' => $product->id,
'quantity' => 5,
'unit_price' => 30000,
'purchase_id' => null,
]);
});
it('can add a bottle to the cart', function () {
$bottle = Bottle::factory()->create(['cost_price' => 5000]);
Livewire::actingAs($this->user)
->test(Create::class)
->set('form.bottle_id', $bottle->id)
->set('form.quantity_bottle', '100')
->call('addItem', 'botol')
->assertHasNoErrors();
$this->assertDatabaseHas('purchase_items', [
'user_id' => $this->user->id,
'purchasable_type' => Bottle::class,
'purchasable_id' => $bottle->id,
'quantity' => 100,
'unit_price' => 5000,
'purchase_id' => null,
]);
});
it('can update item quantity in the cart', function () {
$perfume = Perfume::factory()->create(['cost_price' => 50000]);
$item = PurchaseItem::factory()->create([
'user_id' => $this->user->id,
'purchase_id' => null,
'purchasable_type' => Perfume::class,
'purchasable_id' => $perfume->id,
'quantity' => 10,
'unit_price' => 50000,
'total_price' => 500000,
]);
Livewire::actingAs($this->user)
->test(Create::class)
->set('form.item_id', $item->id)
->set('form.quantity_edit', '15')
->call('updateItem')
->assertHasNoErrors();
$this->assertDatabaseHas('purchase_items', [
'id' => $item->id,
'quantity' => 15,
'total_price' => 750000,
]);
});
it('can delete an item from the cart', function () {
$perfume = Perfume::factory()->create();
$item = PurchaseItem::factory()->create([
'user_id' => $this->user->id,
'purchase_id' => null,
'purchasable_type' => Perfume::class,
'purchasable_id' => $perfume->id,
]);
Livewire::actingAs($this->user)
->test(Create::class)
->call('deleteItem', $item->id)
->assertHasNoErrors();
$this->assertSoftDeleted('purchase_items', ['id' => $item->id]);
});
it('can store a purchase and update warehouse stock', function () {
$perfume = Perfume::factory()->create(['cost_price' => 100000]);
PurchaseItem::factory()->create([
'user_id' => $this->user->id,
'purchase_id' => null,
'purchasable_type' => Perfume::class,
'purchasable_id' => $perfume->id,
'quantity' => 10,
'unit_price' => 100000,
'total_price' => 1000000,
]);
Livewire::actingAs($this->user)
->test(Create::class)
->set('form.warehouse_id', $this->warehouse->id)
->set('form.purchase_date', now()->format('Y-m-d'))
->set('form.invoice_number', 'INV-001')
->call('save')
->assertHasNoErrors()
->assertRedirect(route('studio.manage.purchase.index', [
'notification' => 'Belanja berhasil ditambahkan.',
]));
$this->assertDatabaseHas('purchases', [
'warehouse_id' => $this->warehouse->id,
'invoice_number' => 'INV-001',
'total' => 1000000,
]);
// Check stock update
$this->assertDatabaseHas('perfume_warehouse', [
'warehouse_id' => $this->warehouse->id,
'perfume_id' => $perfume->id,
'stock' => 10,
]);
});
it('can store a purchase with image', function () {
$perfume = Perfume::factory()->create(['cost_price' => 100000]);
PurchaseItem::factory()->create([
'user_id' => $this->user->id,
'purchase_id' => null,
'purchasable_type' => Perfume::class,
'purchasable_id' => $perfume->id,
'quantity' => 10,
'unit_price' => 100000,
'total_price' => 1000000,
]);
Storage::fake('public');
$image = \Illuminate\Http\UploadedFile::fake()->image('invoice.jpg');
Livewire::actingAs($this->user)
->test(Create::class)
->set('form.warehouse_id', $this->warehouse->id)
->set('form.purchase_date', now()->format('Y-m-d'))
->set('form.image', [[
'name' => 'invoice.jpg',
'path' => Storage::disk('public')->path($image->store('tmp', 'public')),
'size' => $image->getSize(),
'type' => $image->getMimeType(),
]])
->call('save')
->assertHasNoErrors();
$purchase = Purchase::latest()->first();
expect($purchase->getFirstMediaUrl('image'))->not->toBeEmpty();
});
it('cannot store a purchase with empty cart', function () {
Livewire::actingAs($this->user)
->test(Create::class)
->set('form.warehouse_id', $this->warehouse->id)
->set('form.purchase_date', now()->format('Y-m-d'))
->call('save')
->assertHasNoErrors(); // It shows a toast, doesn't redirect
$this->assertDatabaseCount('purchases', 0);
});
it('validates required fields', function () {
$perfume = Perfume::factory()->create();
PurchaseItem::factory()->create([
'user_id' => $this->user->id,
'purchase_id' => null,
'purchasable_type' => Perfume::class,
'purchasable_id' => $perfume->id,
]);
Livewire::actingAs($this->user)
->test(Create::class)
->call('save')
->assertHasErrors([
'form.warehouse_id' => 'required',
'form.purchase_date' => 'required',
]);
});
it('can delete a purchase', function () {
$perfume = Perfume::factory()->create();
$purchase = Purchase::factory()->create([
'warehouse_id' => $this->warehouse->id,
]);
$item = PurchaseItem::factory()->create([
'purchase_id' => $purchase->id,
'purchasable_type' => Perfume::class,
'purchasable_id' => $perfume->id,
'quantity' => 10,
]);
// Initial stock (assuming it was added when purchase was created)
$this->warehouse->perfumes()->attach($perfume->id, ['stock' => 10]);
Livewire::actingAs($this->user)
->test(Index::class)
->call('delete', $purchase->id)
->assertHasNoErrors();
$this->assertSoftDeleted('purchases', ['id' => $purchase->id]);
// Check stock was decreased
$this->assertDatabaseMissing('perfume_warehouse', [
'warehouse_id' => $this->warehouse->id,
'perfume_id' => $perfume->id,
'stock' => 10,
]);
});
it('cannot access purchase without permission', function () {
$this->user->roles()->detach();
$this->user->permissions()->detach();
$this->actingAs($this->user)
->get(route('studio.manage.purchase.index'))
->assertForbidden();
$this->actingAs($this->user)
->get(route('studio.manage.purchase.create'))
->assertForbidden();
});