seed(RolePermissionSeeder::class); }); // ─── Helper ─────────────────────────────────────────────── function createSupplierUserWithPermission(PermissionEnum ...$permissions): User { $user = User::factory()->create(); $user->givePermissionTo( array_merge( [PermissionEnum::DASHBOARD_VIEW->value], array_map(fn (PermissionEnum $p) => $p->value, $permissions) ) ); $user->forgetCachedPermissions(); return $user; } // ─── Index ──────────────────────────────────────────────── describe('Supplier Index', function () { test('authenticated user with permission can view supplier index', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW); Supplier::factory()->count(3)->create(); $this->actingAs($user) ->get(route('admin.master.suppliers.index')) ->assertOk(); }); test('guest is redirected to login', function () { $this->get(route('admin.master.suppliers.index')) ->assertRedirect(route('login')); }); test('user without permission is forbidden', function () { $user = User::factory()->create(); $this->actingAs($user) ->get(route('admin.master.suppliers.index')) ->assertForbidden(); }); test('index returns suppliers with pagination', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW); Supplier::factory()->count(15)->create(); $this->actingAs($user) ->get(route('admin.master.suppliers.index')) ->assertOk(); }); test('index can search suppliers by name', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW); Supplier::factory()->create(['name' => 'PT Maju Jaya']); Supplier::factory()->create(['name' => 'CV Berkah Tekstil']); $this->actingAs($user) ->get(route('admin.master.suppliers.index', ['search' => 'Maju'])) ->assertOk(); }); }); // ─── Store ──────────────────────────────────────────────── describe('Supplier Store', function () { test('authenticated user with permission can create a supplier', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_CREATE); $this->actingAs($user) ->post(route('admin.master.suppliers.store'), [ 'name' => 'PT Supplier Baru', 'phone_number' => '081234567890', 'address' => 'Jl. Merdeka No. 1', ]) ->assertRedirect(route('admin.master.suppliers.index')); $this->assertDatabaseHas('suppliers', [ 'name' => 'PT Supplier Baru', 'phone_number' => '081234567890', 'address' => 'Jl. Merdeka No. 1', ]); }); test('guest cannot create a supplier', function () { $this->post(route('admin.master.suppliers.store'), [ 'name' => 'PT Supplier Baru', ])->assertRedirect(route('login')); }); test('user without create permission is forbidden', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW); $this->actingAs($user) ->post(route('admin.master.suppliers.store'), [ 'name' => 'PT Supplier Baru', ]) ->assertForbidden(); $this->assertDatabaseCount('suppliers', 0); }); test('name is required', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_CREATE); $this->actingAs($user) ->post(route('admin.master.suppliers.store'), [ 'name' => '', ]) ->assertSessionHasErrors('name'); $this->assertDatabaseCount('suppliers', 0); }); test('name must not exceed 200 characters', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_CREATE); $this->actingAs($user) ->post(route('admin.master.suppliers.store'), [ 'name' => str_repeat('a', 201), ]) ->assertSessionHasErrors('name'); $this->assertDatabaseCount('suppliers', 0); }); test('phone number must start with 08', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_CREATE); $this->actingAs($user) ->post(route('admin.master.suppliers.store'), [ 'name' => 'PT Supplier Baru', 'phone_number' => '0211234567', ]) ->assertSessionHasErrors('phone_number'); $this->assertDatabaseCount('suppliers', 0); }); test('phone number can be null', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_CREATE); $this->actingAs($user) ->post(route('admin.master.suppliers.store'), [ 'name' => 'PT Supplier Baru', 'phone_number' => null, ]) ->assertRedirect(route('admin.master.suppliers.index')); $this->assertDatabaseHas('suppliers', [ 'name' => 'PT Supplier Baru', 'phone_number' => null, ]); }); test('address can be null', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_CREATE); $this->actingAs($user) ->post(route('admin.master.suppliers.store'), [ 'name' => 'PT Supplier Baru', 'address' => null, ]) ->assertRedirect(route('admin.master.suppliers.index')); $this->assertDatabaseHas('suppliers', [ 'name' => 'PT Supplier Baru', 'address' => null, ]); }); }); // ─── Update ─────────────────────────────────────────────── describe('Supplier Update', function () { test('authenticated user with permission can update a supplier', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_UPDATE); $supplier = Supplier::factory()->create(['name' => 'Nama Lama']); $this->actingAs($user) ->put(route('admin.master.suppliers.update', $supplier), [ 'name' => 'Nama Baru', 'phone_number' => '089876543210', 'address' => 'Jl. Baru No. 2', ]) ->assertRedirect(route('admin.master.suppliers.index')); $this->assertDatabaseHas('suppliers', [ 'id' => $supplier->id, 'name' => 'Nama Baru', 'phone_number' => '089876543210', 'address' => 'Jl. Baru No. 2', ]); }); test('guest cannot update a supplier', function () { $supplier = Supplier::factory()->create(); $this->put(route('admin.master.suppliers.update', $supplier), [ 'name' => 'Nama Baru', ])->assertRedirect(route('login')); }); test('user without update permission is forbidden', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW); $supplier = Supplier::factory()->create(['name' => 'Nama Lama']); $this->actingAs($user) ->put(route('admin.master.suppliers.update', $supplier), [ 'name' => 'Nama Baru', ]) ->assertForbidden(); $this->assertDatabaseHas('suppliers', [ 'id' => $supplier->id, 'name' => 'Nama Lama', ]); }); test('name is required on update', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_UPDATE); $supplier = Supplier::factory()->create(); $this->actingAs($user) ->put(route('admin.master.suppliers.update', $supplier), [ 'name' => '', ]) ->assertSessionHasErrors('name'); }); test('name must not exceed 200 characters on update', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_UPDATE); $supplier = Supplier::factory()->create(); $this->actingAs($user) ->put(route('admin.master.suppliers.update', $supplier), [ 'name' => str_repeat('a', 201), ]) ->assertSessionHasErrors('name'); }); test('phone number validation on update', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_UPDATE); $supplier = Supplier::factory()->create(); $this->actingAs($user) ->put(route('admin.master.suppliers.update', $supplier), [ 'name' => 'Nama Valid', 'phone_number' => '123invalid', ]) ->assertSessionHasErrors('phone_number'); }); }); // ─── Destroy ────────────────────────────────────────────── describe('Supplier Destroy', function () { test('authenticated user with permission can delete a supplier', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_DELETE); $supplier = Supplier::factory()->create(); $this->actingAs($user) ->delete(route('admin.master.suppliers.destroy', $supplier)) ->assertRedirect(route('admin.master.suppliers.index')); $this->assertSoftDeleted('suppliers', [ 'id' => $supplier->id, ]); }); test('guest cannot delete a supplier', function () { $supplier = Supplier::factory()->create(); $this->delete(route('admin.master.suppliers.destroy', $supplier)) ->assertRedirect(route('login')); $this->assertNotSoftDeleted('suppliers', [ 'id' => $supplier->id, ]); }); test('user without delete permission is forbidden', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW); $supplier = Supplier::factory()->create(); $this->actingAs($user) ->delete(route('admin.master.suppliers.destroy', $supplier)) ->assertForbidden(); $this->assertNotSoftDeleted('suppliers', [ 'id' => $supplier->id, ]); }); test('deleted supplier is soft deleted', function () { $user = createSupplierUserWithPermission(PermissionEnum::SUPPLIERS_VIEW, PermissionEnum::SUPPLIERS_DELETE); $supplier = Supplier::factory()->create(); $this->actingAs($user) ->delete(route('admin.master.suppliers.destroy', $supplier)); $this->assertSoftDeleted('suppliers', ['id' => $supplier->id]); $this->assertDatabaseHas('suppliers', ['id' => $supplier->id]); }); }); // ─── Supplier Model ─────────────────────────────────────── describe('Supplier Model', function () { test('supplier uses soft deletes', function () { $supplier = Supplier::factory()->create(); $supplier->delete(); expect($supplier->trashed())->toBeTrue(); }); test('supplier can have purchases', function () { $supplier = Supplier::factory()->create(); expect($supplier->purchases)->toBeInstanceOf(Collection::class); }); });