seed(RolePermissionSeeder::class); }); // ─── Helper ─────────────────────────────────────────────── function createUserWithPermission(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('Category Index', function () { test('authenticated user with permission can view category index', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW); Category::factory()->count(3)->create(); $this->actingAs($user) ->get(route('admin.master.categories.index')) ->assertOk(); }); test('guest is redirected to login', function () { $this->get(route('admin.master.categories.index')) ->assertRedirect(route('login')); }); test('user without permission is forbidden', function () { $user = User::factory()->create(); $this->actingAs($user) ->get(route('admin.master.categories.index')) ->assertForbidden(); }); test('index returns categories with pagination', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW); Category::factory()->count(15)->create(); $response = $this->actingAs($user) ->get(route('admin.master.categories.index')); $response->assertOk(); }); test('index can search categories by name', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW); Category::factory()->create(['name' => 'Kaos Polos']); Category::factory()->create(['name' => 'Kemeja Formal']); $this->actingAs($user) ->get(route('admin.master.categories.index', ['search' => 'Kaos'])) ->assertOk(); }); }); // ─── Store ──────────────────────────────────────────────── describe('Category Store', function () { test('authenticated user with permission can create a category', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_CREATE); $this->actingAs($user) ->post(route('admin.master.categories.store'), [ 'name' => 'Kategori Baru', ]) ->assertRedirect(route('admin.master.categories.index')); $this->assertDatabaseHas('categories', [ 'name' => 'Kategori Baru', 'slug' => 'kategori-baru', ]); }); test('guest cannot create a category', function () { $this->post(route('admin.master.categories.store'), [ 'name' => 'Kategori Baru', ])->assertRedirect(route('login')); }); test('user without create permission is forbidden', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW); $this->actingAs($user) ->post(route('admin.master.categories.store'), [ 'name' => 'Kategori Baru', ]) ->assertForbidden(); $this->assertDatabaseCount('categories', 0); }); test('name is required', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_CREATE); $this->actingAs($user) ->post(route('admin.master.categories.store'), [ 'name' => '', ]) ->assertSessionHasErrors('name'); $this->assertDatabaseCount('categories', 0); }); test('name must not exceed 50 characters', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_CREATE); $this->actingAs($user) ->post(route('admin.master.categories.store'), [ 'name' => str_repeat('a', 51), ]) ->assertSessionHasErrors('name'); $this->assertDatabaseCount('categories', 0); }); test('name must be a string', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_CREATE); $this->actingAs($user) ->post(route('admin.master.categories.store'), [ 'name' => ['bukan string'], ]) ->assertSessionHasErrors('name'); $this->assertDatabaseCount('categories', 0); }); test('slug is auto-generated from name', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_CREATE); $this->actingAs($user) ->post(route('admin.master.categories.store'), [ 'name' => 'Bahan Katun', ]); $this->assertDatabaseHas('categories', [ 'name' => 'Bahan Katun', 'slug' => 'bahan-katun', ]); }); }); // ─── Update ─────────────────────────────────────────────── describe('Category Update', function () { test('authenticated user with permission can update a category', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_UPDATE); $category = Category::factory()->create(['name' => 'Nama Lama']); $this->actingAs($user) ->put(route('admin.master.categories.update', $category), [ 'name' => 'Nama Baru', ]) ->assertRedirect(route('admin.master.categories.index')); $this->assertDatabaseHas('categories', [ 'id' => $category->id, 'name' => 'Nama Baru', ]); }); test('guest cannot update a category', function () { $category = Category::factory()->create(); $this->put(route('admin.master.categories.update', $category), [ 'name' => 'Nama Baru', ])->assertRedirect(route('login')); }); test('user without update permission is forbidden', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW); $category = Category::factory()->create(['name' => 'Nama Lama']); $this->actingAs($user) ->put(route('admin.master.categories.update', $category), [ 'name' => 'Nama Baru', ]) ->assertForbidden(); $this->assertDatabaseHas('categories', [ 'id' => $category->id, 'name' => 'Nama Lama', ]); }); test('name is required on update', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_UPDATE); $category = Category::factory()->create(); $this->actingAs($user) ->put(route('admin.master.categories.update', $category), [ 'name' => '', ]) ->assertSessionHasErrors('name'); }); test('name must not exceed 50 characters on update', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_UPDATE); $category = Category::factory()->create(); $this->actingAs($user) ->put(route('admin.master.categories.update', $category), [ 'name' => str_repeat('a', 51), ]) ->assertSessionHasErrors('name'); }); test('slug is updated when name changes', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_UPDATE); $category = Category::factory()->create(['name' => 'Nama Lama']); $this->actingAs($user) ->put(route('admin.master.categories.update', $category), [ 'name' => 'Nama Terbaru', ]); $this->assertDatabaseHas('categories', [ 'id' => $category->id, 'slug' => 'nama-terbaru', ]); }); }); // ─── Destroy ────────────────────────────────────────────── describe('Category Destroy', function () { test('authenticated user with permission can delete a category', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_DELETE); $category = Category::factory()->create(); $this->actingAs($user) ->delete(route('admin.master.categories.destroy', $category)) ->assertRedirect(route('admin.master.categories.index')); $this->assertSoftDeleted('categories', [ 'id' => $category->id, ]); }); test('guest cannot delete a category', function () { $category = Category::factory()->create(); $this->delete(route('admin.master.categories.destroy', $category)) ->assertRedirect(route('login')); $this->assertNotSoftDeleted('categories', [ 'id' => $category->id, ]); }); test('user without delete permission is forbidden', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW); $category = Category::factory()->create(); $this->actingAs($user) ->delete(route('admin.master.categories.destroy', $category)) ->assertForbidden(); $this->assertNotSoftDeleted('categories', [ 'id' => $category->id, ]); }); test('deleted category is soft deleted', function () { $user = createUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_DELETE); $category = Category::factory()->create(); $this->actingAs($user) ->delete(route('admin.master.categories.destroy', $category)); $this->assertSoftDeleted('categories', ['id' => $category->id]); $this->assertDatabaseHas('categories', ['id' => $category->id]); }); }); // ─── Category Model ─────────────────────────────────────── describe('Category Model', function () { test('category has slug auto-generated via attribute', function () { $category = Category::factory()->create(['name' => 'Test Kategori', 'slug' => null]); expect($category->fresh()->slug)->toBe('test-kategori'); }); test('category uses soft deletes', function () { $category = Category::factory()->create(); $category->delete(); expect($category->trashed())->toBeTrue(); }); test('category can have products', function () { $category = Category::factory()->create(); expect($category->products)->toBeInstanceOf(Collection::class); }); });