store/tests/Feature/Admin/Manage/OwnerVerificationTest.php

357 lines
14 KiB
PHP

<?php
use App\Enums\CuttingStatus;
use App\Enums\OwnerVerificationAction;
use App\Enums\OwnerVerificationStatus;
use App\Enums\Permission as PermissionEnum;
use App\Enums\Role as RoleEnum;
use App\Models\Cutting;
use App\Models\OwnerVerificationRequest;
use App\Models\Product;
use App\Models\Purchase;
use App\Models\RawMaterialPrice;
use App\Models\Supplier;
use App\Models\User;
use App\Support\ActivityLog\ModelLabel;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->seed(RolePermissionSeeder::class);
});
function createOwnerVerificationRequestFor(User $submitter, ?Product $product = null): OwnerVerificationRequest
{
$product ??= Product::factory()->create(['is_active' => false]);
return OwnerVerificationRequest::query()->create([
'action' => OwnerVerificationAction::CREATE,
'status' => OwnerVerificationStatus::PENDING,
'subject_type' => Product::class,
'subject_id' => $product->id,
'submitted_by_id' => $submitter->id,
'payload' => ['old' => null, 'new' => ['name' => $product->name]],
]);
}
function createOwnerVerifierUser(): User
{
$user = User::factory()->create();
$user->assignRole(RoleEnum::OWNER->value);
$user->forgetCachedPermissions();
return $user;
}
function createPendingCutting(?User $submitter = null): Cutting
{
$submitter ??= User::factory()->create();
return Cutting::factory()->create([
'status' => CuttingStatus::PENDING_VERIFICATION,
'submitted_by_id' => $submitter->id,
'created_by_id' => $submitter->id,
'description' => 'Cutting test pending',
]);
}
describe('Owner Verification Index', function () {
test('submitter with view permission only sees own verification requests', function () {
$submitter = User::factory()->create();
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
$submitter->forgetCachedPermissions();
$otherUser = User::factory()->create();
$otherUser->assignRole(RoleEnum::ADMIN_TOKO->value);
$otherUser->forgetCachedPermissions();
$ownRequest = createOwnerVerificationRequestFor($submitter);
createOwnerVerificationRequestFor($otherUser);
$response = $this->actingAs($submitter)
->get(route('admin.manage.owner_verifications.index'));
$response->assertOk();
$response->assertInertia(fn ($page) => $page
->component('admin/manage/owner-verifications/Index')
->has('verificationRequests.data', 1)
->where('verificationRequests.data.0.id', $ownRequest->id)
->where('verificationRequests.data.0.source', 'request')
);
});
test('verifier sees all verification requests', function () {
$submitter = User::factory()->create();
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
$submitter->forgetCachedPermissions();
$verifier = createOwnerVerifierUser();
createOwnerVerificationRequestFor($submitter);
createOwnerVerificationRequestFor($submitter);
$response = $this->actingAs($verifier)
->get(route('admin.manage.owner_verifications.index'));
$response->assertOk();
$response->assertInertia(fn ($page) => $page
->component('admin/manage/owner-verifications/Index')
->has('verificationRequests.data', 2)
);
});
test('submitter cannot approve verification request', function () {
$submitter = User::factory()->create();
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
$submitter->forgetCachedPermissions();
$request = createOwnerVerificationRequestFor($submitter);
$this->actingAs($submitter)
->post(route('admin.manage.owner_verifications.approve_request', $request))
->assertForbidden();
});
test('admin toko role has owner verification view permission', function () {
$user = User::factory()->create();
$user->assignRole(RoleEnum::ADMIN_TOKO->value);
$user->forgetCachedPermissions();
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VIEW->value))->toBeTrue();
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VERIFY->value))->toBeFalse();
});
test('admin bahan baku role has owner verification view permission', function () {
$user = User::factory()->create();
$user->assignRole(RoleEnum::ADMIN_BAHAN_BAKU->value);
$user->forgetCachedPermissions();
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VIEW->value))->toBeTrue();
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VERIFY->value))->toBeFalse();
});
});
describe('Owner Verification Unified Cutting List', function () {
test('verifier sees pending cuttings in the same verification table', function () {
$verifier = createOwnerVerifierUser();
$cutting = createPendingCutting();
$request = createOwnerVerificationRequestFor(User::factory()->create());
$response = $this->actingAs($verifier)
->get(route('admin.manage.owner_verifications.index'));
$response->assertOk();
$response->assertInertia(fn ($page) => $page
->component('admin/manage/owner-verifications/Index')
->has('verificationRequests.data', 2)
->where('verificationRequests.data.0.source', 'cutting')
->where('verificationRequests.data.0.id', $cutting->id)
->where('verificationRequests.data.0.action', OwnerVerificationAction::STOCK_VERIFY->value)
->where('verificationRequests.data.0.subject_type', Cutting::class)
->where('verificationRequests.data.1.source', 'request')
->where('verificationRequests.data.1.id', $request->id)
);
});
test('submitter does not see pending cuttings in verification table', function () {
$submitter = User::factory()->create();
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
$submitter->forgetCachedPermissions();
createPendingCutting();
createOwnerVerificationRequestFor($submitter);
$response = $this->actingAs($submitter)
->get(route('admin.manage.owner_verifications.index'));
$response->assertOk();
$response->assertInertia(fn ($page) => $page
->component('admin/manage/owner-verifications/Index')
->has('verificationRequests.data', 1)
->where('verificationRequests.data.0.source', 'request')
);
});
test('filtering by cutting module shows only pending cuttings', function () {
$verifier = createOwnerVerifierUser();
$cutting = createPendingCutting();
createOwnerVerificationRequestFor(User::factory()->create());
$response = $this->actingAs($verifier)
->get(route('admin.manage.owner_verifications.index', [
'subject_type' => Cutting::class,
]));
$response->assertOk();
$response->assertInertia(fn ($page) => $page
->has('verificationRequests.data', 1)
->where('verificationRequests.data.0.source', 'cutting')
->where('verificationRequests.data.0.id', $cutting->id)
);
});
test('filtering by stock verify action shows only pending cuttings', function () {
$verifier = createOwnerVerifierUser();
$cutting = createPendingCutting();
createOwnerVerificationRequestFor(User::factory()->create());
$response = $this->actingAs($verifier)
->get(route('admin.manage.owner_verifications.index', [
'action' => OwnerVerificationAction::STOCK_VERIFY->value,
]));
$response->assertOk();
$response->assertInertia(fn ($page) => $page
->has('verificationRequests.data', 1)
->where('verificationRequests.data.0.source', 'cutting')
->where('verificationRequests.data.0.id', $cutting->id)
);
});
test('filtering by approved status hides pending cuttings', function () {
$verifier = createOwnerVerifierUser();
createPendingCutting();
OwnerVerificationRequest::query()->create([
'action' => OwnerVerificationAction::CREATE,
'status' => OwnerVerificationStatus::APPROVED,
'subject_type' => Product::class,
'subject_id' => Product::factory()->create()->id,
'submitted_by_id' => User::factory()->create()->id,
'payload' => ['old' => null, 'new' => ['name' => 'Approved Product']],
'verified_by_id' => $verifier->id,
'verified_at' => now(),
]);
$response = $this->actingAs($verifier)
->get(route('admin.manage.owner_verifications.index', [
'status' => OwnerVerificationStatus::APPROVED->value,
]));
$response->assertOk();
$response->assertInertia(fn ($page) => $page
->has('verificationRequests.data', 1)
->where('verificationRequests.data.0.source', 'request')
->where('verificationRequests.data.0.status', OwnerVerificationStatus::APPROVED->value)
);
});
test('subject options include cutting when pending cuttings exist', function () {
$verifier = createOwnerVerifierUser();
createPendingCutting();
$response = $this->actingAs($verifier)
->get(route('admin.manage.owner_verifications.index'));
$response->assertOk();
$response->assertInertia(fn ($page) => $page
->where('subjectOptions', fn ($options) => collect($options)->contains(
fn (array $option) => $option['value'] === Cutting::class
&& $option['label'] === ModelLabel::for(Cutting::class),
))
);
});
test('verifier can approve pending cutting from verification page', function () {
$verifier = createOwnerVerifierUser();
$cutting = createPendingCutting();
$this->actingAs($verifier)
->post(route('admin.manage.owner_verifications.approve', $cutting))
->assertRedirect(route('admin.manage.owner_verifications.index'));
expect($cutting->fresh()->status)->toBe(CuttingStatus::VERIFIED);
});
test('verifier can reject pending cutting from verification page', function () {
$verifier = createOwnerVerifierUser();
$cutting = createPendingCutting();
$this->actingAs($verifier)
->post(route('admin.manage.owner_verifications.reject', $cutting), [
'reason' => 'Data tidak sesuai',
])
->assertRedirect(route('admin.manage.owner_verifications.index'));
$cutting->refresh();
expect($cutting->status)->toBe(CuttingStatus::COMPLETED);
expect($cutting->rejection?->reason)->toBe('Data tidak sesuai');
expect($cutting->rejection?->rejected_by_id)->toBe($verifier->id);
});
});
describe('Owner Verification Purchase Module', function () {
test('verifier sees purchase verification requests in index', function () {
$verifier = createOwnerVerifierUser();
$submitter = User::factory()->create();
$purchase = Purchase::factory()->create();
OwnerVerificationRequest::query()->create([
'action' => OwnerVerificationAction::CREATE,
'status' => OwnerVerificationStatus::PENDING,
'subject_type' => Purchase::class,
'subject_id' => $purchase->id,
'submitted_by_id' => $submitter->id,
'payload' => [
'old' => null,
'new' => ['supplier_name' => $purchase->supplier?->name ?? 'Supplier'],
],
]);
$response = $this->actingAs($verifier)
->get(route('admin.manage.owner_verifications.index', [
'subject_type' => Purchase::class,
]));
$response->assertOk();
$response->assertInertia(fn ($page) => $page
->has('verificationRequests.data', 1)
->where('verificationRequests.data.0.source', 'request')
->where('verificationRequests.data.0.subject_type', Purchase::class)
->where('verificationRequests.data.0.subject_id', $purchase->id)
);
});
test('purchase submitter sees only own purchase verification requests', function () {
$submitter = User::factory()->create();
$submitter->assignRole(RoleEnum::ADMIN_BAHAN_BAKU->value);
$submitter->forgetCachedPermissions();
$otherUser = User::factory()->create();
$ownPurchase = Purchase::factory()->create(['created_by_id' => $submitter->id]);
$otherPurchase = Purchase::factory()->create(['created_by_id' => $otherUser->id]);
OwnerVerificationRequest::query()->create([
'action' => OwnerVerificationAction::CREATE,
'status' => OwnerVerificationStatus::PENDING,
'subject_type' => Purchase::class,
'subject_id' => $ownPurchase->id,
'submitted_by_id' => $submitter->id,
'payload' => ['old' => null, 'new' => ['supplier_name' => 'Own']],
]);
OwnerVerificationRequest::query()->create([
'action' => OwnerVerificationAction::CREATE,
'status' => OwnerVerificationStatus::PENDING,
'subject_type' => Purchase::class,
'subject_id' => $otherPurchase->id,
'submitted_by_id' => $otherUser->id,
'payload' => ['old' => null, 'new' => ['supplier_name' => 'Other']],
]);
$response = $this->actingAs($submitter)
->get(route('admin.manage.owner_verifications.index', [
'subject_type' => Purchase::class,
]));
$response->assertOk();
$response->assertInertia(fn ($page) => $page
->has('verificationRequests.data', 1)
->where('verificationRequests.data.0.subject_id', $ownPurchase->id)
);
});
});