From 22771fdeea8d73c77237cfdf007956c430b63f20 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Thu, 30 Apr 2026 08:34:25 +0700 Subject: [PATCH] test: add feature tests for order management authorization, CRUD operations, and cart functionality --- tests/Feature/Admin/Manage/OrderTest.php | 277 +++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 tests/Feature/Admin/Manage/OrderTest.php diff --git a/tests/Feature/Admin/Manage/OrderTest.php b/tests/Feature/Admin/Manage/OrderTest.php new file mode 100644 index 0000000..e6a8f52 --- /dev/null +++ b/tests/Feature/Admin/Manage/OrderTest.php @@ -0,0 +1,277 @@ +assertRedirect(route('login')); + }); + + it('returns 403 when user has no permission to view orders', function () { + actingAs(createUnauthorizedUser()) + ->get(route('order.index')) + ->assertStatus(403); + }); +}); + +describe('Order Module - Authorized Actions', function () { + beforeEach(function () { + $user = createAuthorizedUser([ + 'View:Order', + 'Create:Order', + 'Edit:Order', + 'Delete:Order', + 'DeleteAny:Order', + ]); + actingAs($user); + }); + + it('can access order index page', function () { + get(route('order.index')) + ->assertOk() + ->assertInertia(fn ($page) => $page + ->component('admin/manage/order/index') + ->has('orders') + ); + }); + + it('can access order create page', function () { + get(route('order.create')) + ->assertOk() + ->assertInertia(fn ($page) => $page + ->component('admin/manage/order/create') + ->has('products') + ->has('cartItems') + ); + }); + + it('can manage cart items', function () { + $product = Product::factory()->create(['stock' => 10]); + + // 1. Add to cart + postJson(route('order.addToCart'), [ + 'product_id' => $product->id, + 'qty' => 2, + 'price' => 10000, + 'price_type' => PriceType::RETAIL->value, + ])->assertRedirect(); + + assertDatabaseHas('order_items', [ + 'user_id' => auth()->id(), + 'order_id' => null, + 'product_id' => $product->id, + 'qty' => 2, + ]); + + $cartItem = OrderItem::where('product_id', $product->id)->whereNull('order_id')->first(); + + // 2. Update cart item + patchJson(route('order.updateCartItem', $cartItem), [ + 'product_id' => $product->id, + 'qty' => 5, + 'price' => 10000, + 'price_type' => PriceType::RETAIL->value, + ])->assertRedirect(); + + expect($cartItem->fresh()->qty)->toBe(5); + + // 3. Remove from cart + deleteJson(route('order.removeFromCart', $cartItem))->assertRedirect(); + assertSoftDeleted('order_items', ['id' => $cartItem->id]); + }); + + it('can store a new order', function () { + $product = Product::factory()->create(['stock' => 10]); + + // Add a price for PURCHASE to avoid errors in COGS calculation if applicable + $product->prices()->create([ + 'price_type' => PriceType::PURCHASE->value, + 'price' => 8000, + ]); + + $data = [ + 'customer_name' => 'John Doe', + 'subtotal' => 20000, + 'discount' => 2000, + 'payment' => 18000, + 'payment_method' => PaymentMethod::CASH->value, + 'order_status' => OrderStatus::PENDING->value, + 'order_channel' => OrderChannel::STORE->value, + 'items' => [ + [ + 'product_id' => $product->id, + 'qty' => 2, + 'price' => 10000, + 'total' => 20000, + 'price_type' => PriceType::RETAIL->value, + ], + ], + ]; + + postJson(route('order.store'), $data) + ->assertRedirect(route('order.index')) + ->assertSessionHas('success'); + + assertDatabaseHas('orders', [ + 'customer_name' => 'John Doe', + 'total' => 18000, + ]); + + // Check stock decrement + expect($product->fresh()->stock)->toBe(8); + + // Check order items + assertDatabaseHas('order_items', [ + 'product_id' => $product->id, + 'qty' => 2, + 'price' => 10000, + ]); + }); + + it('validates order creation', function () { + postJson(route('order.store'), []) + ->assertStatus(422) + ->assertJsonValidationErrors(['customer_name', 'items']); + }); + + it('can update an order', function () { + $product1 = Product::factory()->create(['stock' => 10]); + $product2 = Product::factory()->create(['stock' => 10]); + + $order = Order::create([ + 'customer_name' => 'Old Customer', + 'cogs' => 0, + 'subtotal' => 10000, + 'discount' => 0, + 'payment' => 10000, + 'total' => 10000, + 'payment_method' => PaymentMethod::CASH->value, + 'order_status' => OrderStatus::PENDING->value, + 'order_channel' => OrderChannel::STORE->value, + ]); + + $order->items()->create([ + 'user_id' => auth()->id(), + 'product_id' => $product1->id, + 'qty' => 1, + 'price' => 10000, + 'total' => 10000, + 'price_type' => PriceType::RETAIL->value, + ]); + + // Initial stock check + $product1->decrement('stock', 1); + + $newData = [ + 'customer_name' => 'Updated Customer', + 'subtotal' => 15000, + 'discount' => 0, + 'payment' => 15000, + 'payment_method' => PaymentMethod::CASH->value, + 'order_status' => OrderStatus::PROCESSING->value, + 'order_channel' => OrderChannel::STORE->value, + 'items' => [ + [ + 'product_id' => $product2->id, + 'qty' => 3, + 'price' => 5000, + 'total' => 15000, + 'price_type' => PriceType::RETAIL->value, + ], + ], + ]; + + patchJson(route('order.update', $order), $newData) + ->assertRedirect(route('order.index')) + ->assertSessionHas('success'); + + // Check data updated + assertDatabaseHas('orders', [ + 'id' => $order->id, + 'customer_name' => 'Updated Customer', + ]); + + // Check stock: product1 should be restored (10), product2 should be decremented (7) + expect($product1->fresh()->stock)->toBe(10); + expect($product2->fresh()->stock)->toBe(7); + }); + + it('can delete an order and restore stock', function () { + $product = Product::factory()->create(['stock' => 5]); + $order = Order::factory()->create(); + $order->items()->create([ + 'user_id' => auth()->id(), + 'product_id' => $product->id, + 'qty' => 2, + 'price' => 1000, + 'total' => 2000, + 'price_type' => PriceType::RETAIL->value, + ]); + + deleteJson(route('order.destroy', $order)) + ->assertRedirect() + ->assertSessionHas('success'); + + assertSoftDeleted('orders', ['id' => $order->id]); + + // Stock should be restored (5 + 2 = 7) + expect($product->fresh()->stock)->toBe(7); + }); + + it('can delete orders in bulk', function () { + $orders = Order::factory()->count(3)->create(); + $ids = $orders->pluck('id')->toArray(); + + deleteJson(route('order.bulkDestroy'), ['ids' => $ids]) + ->assertRedirect() + ->assertSessionHas('success'); + + foreach ($ids as $id) { + assertSoftDeleted('orders', ['id' => $id]); + } + }); +}); + +describe('Order Module - Unauthorized Actions', function () { + beforeEach(function () { + actingAs(createUnauthorizedUser()); + }); + + it('cannot store an order without permission', function () { + postJson(route('order.store'), ['customer_name' => 'Unauthorized']) + ->assertStatus(403); + }); + + it('cannot update an order without permission', function () { + $order = Order::factory()->create(); + patchJson(route('order.update', $order), ['customer_name' => 'Unauthorized']) + ->assertStatus(403); + }); + + it('cannot delete an order without permission', function () { + $order = Order::factory()->create(); + deleteJson(route('order.destroy', $order)) + ->assertStatus(403); + }); +});