From 99c83e08f8f6569911e250cad8d1e2f618767a6e Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Fri, 2 Jan 2026 20:54:45 +0700 Subject: [PATCH] test: Add comprehensive feature tests for purchase management, including item cart operations, purchase creation, stock updates, and permission checks. --- tests/Feature/Studio/Manage/PurchaseTest.php | 285 +++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 tests/Feature/Studio/Manage/PurchaseTest.php diff --git a/tests/Feature/Studio/Manage/PurchaseTest.php b/tests/Feature/Studio/Manage/PurchaseTest.php new file mode 100644 index 0000000..d4cb359 --- /dev/null +++ b/tests/Feature/Studio/Manage/PurchaseTest.php @@ -0,0 +1,285 @@ +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(); +});