From 01a13a4bf4eed5d3b652066a99dc12c10081d0c6 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Thu, 30 Apr 2026 08:31:35 +0700 Subject: [PATCH] test: add feature tests for expense module CRUD operations and authorization policies --- tests/Feature/Admin/Finance/ExpenseTest.php | 148 ++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 tests/Feature/Admin/Finance/ExpenseTest.php diff --git a/tests/Feature/Admin/Finance/ExpenseTest.php b/tests/Feature/Admin/Finance/ExpenseTest.php new file mode 100644 index 0000000..a46175e --- /dev/null +++ b/tests/Feature/Admin/Finance/ExpenseTest.php @@ -0,0 +1,148 @@ +assertRedirect(route('login')); + }); + + it('returns 403 when user has no permission to view expenses', function () { + actingAs(createUnauthorizedUser()) + ->get(route('expense.index')) + ->assertStatus(403); + }); +}); + +describe('Expense Module - Authorized Actions', function () { + beforeEach(function () { + $user = createAuthorizedUser([ + 'View:Expense', + 'Create:Expense', + 'Edit:Expense', + 'Delete:Expense', + 'DeleteAny:Expense', + ]); + actingAs($user); + }); + + it('can access expense index page', function () { + get(route('expense.index')) + ->assertOk() + ->assertInertia(fn ($page) => $page + ->component('admin/finance/expense/index') + ->has('expenses') + ); + }); + + it('can store a new expense with proof image', function () { + Storage::fake('public'); + + $data = [ + 'name' => 'Beli Alat Tulis', + 'amount' => 50000, + 'image' => UploadedFile::fake()->image('receipt.jpg'), + ]; + + postJson(route('expense.store'), $data) + ->assertRedirect() + ->assertSessionHas('success'); + + assertDatabaseHas('expenses', [ + 'name' => 'Beli Alat Tulis', + 'amount' => 50000, + 'user_id' => auth()->id(), + ]); + + $expense = Expense::where('name', 'Beli Alat Tulis')->first(); + expect($expense->getFirstMediaUrl('proof'))->not->toBeEmpty(); + }); + + it('validates expense creation', function () { + postJson(route('expense.store'), []) + ->assertStatus(422) + ->assertJsonValidationErrors(['name', 'amount']); + }); + + it('can update an expense', function () { + $expense = Expense::factory()->create(['name' => 'Old Expense', 'amount' => 10000]); + + $newData = [ + 'name' => 'Updated Expense', + 'amount' => 20000, + ]; + + patchJson(route('expense.update', $expense), $newData) + ->assertRedirect() + ->assertSessionHas('success'); + + assertDatabaseHas('expenses', [ + 'id' => $expense->id, + 'name' => 'Updated Expense', + 'amount' => 20000, + ]); + }); + + it('can delete an expense', function () { + $expense = Expense::factory()->create(); + + deleteJson(route('expense.destroy', $expense)) + ->assertRedirect() + ->assertSessionHas('success'); + + assertSoftDeleted('expenses', ['id' => $expense->id]); + }); + + it('can delete expenses in bulk', function () { + $expenses = Expense::factory()->count(3)->create(); + $ids = $expenses->pluck('id')->toArray(); + + deleteJson(route('expense.bulkDestroy'), ['ids' => $ids]) + ->assertRedirect() + ->assertSessionHas('success'); + + foreach ($ids as $id) { + assertSoftDeleted('expenses', ['id' => $id]); + } + }); +}); + +describe('Expense Module - Unauthorized Actions', function () { + beforeEach(function () { + actingAs(createUnauthorizedUser()); + }); + + it('cannot store an expense without permission', function () { + postJson(route('expense.store'), ['name' => 'Unauthorized']) + ->assertStatus(403); + }); + + it('cannot update an expense without permission', function () { + $expense = Expense::factory()->create(); + patchJson(route('expense.update', $expense), ['name' => 'Unauthorized']) + ->assertStatus(403); + }); + + it('cannot delete an expense without permission', function () { + $expense = Expense::factory()->create(); + deleteJson(route('expense.destroy', $expense)) + ->assertStatus(403); + }); +});