dstpabuaran.com/tests/Feature/Admin/Settings/AdminSettingsTest.php

907 lines
33 KiB
PHP

<?php
use App\Models\User;
use App\Settings\HomepageSettings;
use App\Settings\HRSettings;
use App\Settings\MarketplaceSettings;
use App\Settings\SocialMediaSettings;
use App\Settings\SystemSettings;
use App\Support\Marketplace\MarketplaceFeeRule;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Inertia\Testing\AssertableInertia as Assert;
uses(RefreshDatabase::class);
/*
|--------------------------------------------------------------------------
| AUTHENTICATION
|--------------------------------------------------------------------------
*/
test('guests are redirected to the login page', function () {
$response = $this->get(route('admin.settings.index'));
$response->assertRedirect(route('login'));
});
test('guest cannot update system settings', function () {
$response = $this->put(route('admin.settings.update-system'), [
'app_name' => 'Unauthorized',
]);
$response->assertRedirect(route('login'));
});
test('guest cannot update homepage settings', function () {
$response = $this->put(route('admin.settings.update-homepage'), [
'hero_image_key' => 'unauthorized/image.jpg',
]);
$response->assertRedirect(route('login'));
});
test('guest cannot update social media settings', function () {
$response = $this->put(route('admin.settings.update-social-media'), [
'instagram_url' => 'https://unauthorized.com',
]);
$response->assertRedirect(route('login'));
});
test('guest cannot update marketplace settings', function () {
$response = $this->put(route('admin.settings.update-marketplace'), [
'tiktok_shop_platform_commission' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 100],
]);
$response->assertRedirect(route('login'));
});
test('guest cannot update hr settings', function () {
$response = $this->put(route('admin.settings.update-hr'), [
'scheduled_check_in_time' => '09:00',
]);
$response->assertRedirect(route('login'));
});
/*
|--------------------------------------------------------------------------
| INDEX PAGE
|--------------------------------------------------------------------------
*/
test('authenticated users can visit the settings page', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->get(route('admin.settings.index'));
$response->assertOk();
});
test('settings page renders with correct inertia component', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->get(route('admin.settings.index'));
$response->assertInertia(fn (Assert $page) => $page
->component('admin/settings/index')
->has('system')
->has('homepage')
->has('socialMedia')
->has('marketplace')
->has('hr')
);
});
test('settings page contains system data', function () {
$user = User::factory()->create();
$this->actingAs($user);
$system = app(SystemSettings::class);
$response = $this->get(route('admin.settings.index'));
$response->assertInertia(fn (Assert $page) => $page
->where('system.app_name', $system->app_name)
->where('system.email', $system->email)
->where('system.phone', $system->phone)
);
});
test('settings page contains marketplace data', function () {
$user = User::factory()->create();
$this->actingAs($user);
$marketplace = app(MarketplaceSettings::class);
$response = $this->get(route('admin.settings.index'));
$response->assertInertia(fn (Assert $page) => $page
->where('marketplace.tiktok_shop.platform_commission.base', $marketplace->tiktok_shop_platform_commission->base)
->where('marketplace.tiktok_shop.platform_commission.type', $marketplace->tiktok_shop_platform_commission->type)
->where('marketplace.tiktok_shop.platform_commission.value', json_decode(json_encode($marketplace->tiktok_shop_platform_commission->value)))
->where('marketplace.shopee.admin_fee.base', $marketplace->shopee_admin_fee->base)
);
});
test('settings page contains hr data', function () {
$user = User::factory()->create();
$this->actingAs($user);
$hr = app(HRSettings::class);
$response = $this->get(route('admin.settings.index'));
$response->assertInertia(fn (Assert $page) => $page
->where('hr.scheduled_check_in_time', $hr->scheduled_check_in_time)
->where('hr.scheduled_check_out_time', $hr->scheduled_check_out_time)
->where('hr.late_penalty_amount', $hr->late_penalty_amount)
->where('hr.absent_penalty_amount', $hr->absent_penalty_amount)
);
});
/*
|--------------------------------------------------------------------------
| UPDATE SYSTEM
|--------------------------------------------------------------------------
*/
test('user can update system settings with valid data', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-system'), [
'app_name' => 'DST Collection Updated',
'about_app' => 'Tentang DST Collection',
'address' => 'Jl. Test No. 123',
'email' => 'test@dst.com',
'phone' => '081234567890',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('admin.settings.index'));
$settings = app(SystemSettings::class);
expect($settings->app_name)->toBe('DST Collection Updated');
expect($settings->about_app)->toBe('Tentang DST Collection');
expect($settings->address)->toBe('Jl. Test No. 123');
expect($settings->email)->toBe('test@dst.com');
expect($settings->phone)->toBe('081234567890');
});
test('system app_name is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-system'), [
'app_name' => '',
]);
$response->assertSessionHasErrors('app_name');
});
test('system email must be valid email format', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-system'), [
'app_name' => 'DST',
'email' => 'not-an-email',
]);
$response->assertSessionHasErrors('email');
});
test('system app_name must not exceed 255 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-system'), [
'app_name' => str_repeat('a', 256),
]);
$response->assertSessionHasErrors('app_name');
});
test('system about_app must not exceed 2000 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-system'), [
'app_name' => 'DST',
'about_app' => str_repeat('a', 2001),
]);
$response->assertSessionHasErrors('about_app');
});
test('system address must not exceed 500 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-system'), [
'app_name' => 'DST',
'address' => str_repeat('a', 501),
]);
$response->assertSessionHasErrors('address');
});
test('system phone must not exceed 20 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-system'), [
'app_name' => 'DST',
'phone' => str_repeat('1', 21),
]);
$response->assertSessionHasErrors('phone');
});
test('system nullable fields can be empty', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-system'), [
'app_name' => 'DST',
'about_app' => '',
'address' => '',
'email' => '',
'phone' => '',
]);
$response->assertSessionHasNoErrors();
});
/*
|--------------------------------------------------------------------------
| UPDATE HOMEPAGE
|--------------------------------------------------------------------------
*/
test('user can update homepage settings with valid data', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-homepage'), [
'hero_image_key' => 'homepage/hero/2026/07/31/abc123/hero.jpg',
'about_image_key' => 'homepage/about/2026/07/31/def456/about.jpg',
'gallery_image_keys' => ['homepage/gallery/2026/07/31/g1/img.jpg', 'homepage/gallery/2026/07/31/g2/img.jpg'],
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('admin.settings.index'));
$settings = app(HomepageSettings::class);
expect($settings->hero_image_url)->toBe('homepage/hero/2026/07/31/abc123/hero.jpg');
expect($settings->about_image_url)->toBe('homepage/about/2026/07/31/def456/about.jpg');
expect($settings->gallery_images)->toBe(['homepage/gallery/2026/07/31/g1/img.jpg', 'homepage/gallery/2026/07/31/g2/img.jpg']);
});
test('homepage fields are nullable', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-homepage'), [
'hero_image_key' => null,
'about_image_key' => null,
]);
$response->assertSessionHasNoErrors();
});
test('homepage hero_image_key must not exceed 2000 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-homepage'), [
'hero_image_key' => str_repeat('a', 2001),
]);
$response->assertSessionHasErrors('hero_image_key');
});
test('homepage gallery_image_keys must be array', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-homepage'), [
'gallery_image_keys' => 'not-an-array',
]);
$response->assertSessionHasErrors('gallery_image_keys');
});
test('homepage gallery_image_keys items must not exceed 2000 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-homepage'), [
'gallery_image_keys' => [str_repeat('a', 2001)],
]);
$response->assertSessionHasErrors('gallery_image_keys.0');
});
/*
|--------------------------------------------------------------------------
| UPDATE SOCIAL MEDIA
|--------------------------------------------------------------------------
*/
test('user can update social media settings with valid data', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-social-media'), [
'instagram_url' => 'https://instagram.com/dst',
'facebook_url' => 'https://facebook.com/dst',
'tiktok_url' => 'https://tiktok.com/@dst',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('admin.settings.index'));
$settings = app(SocialMediaSettings::class);
expect($settings->instagram_url)->toBe('https://instagram.com/dst');
expect($settings->facebook_url)->toBe('https://facebook.com/dst');
expect($settings->tiktok_url)->toBe('https://tiktok.com/@dst');
});
test('social media fields are nullable', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-social-media'), [
'instagram_url' => null,
'facebook_url' => null,
'tiktok_url' => null,
]);
$response->assertSessionHasNoErrors();
});
test('social media instagram_url must not exceed 500 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-social-media'), [
'instagram_url' => str_repeat('a', 501),
]);
$response->assertSessionHasErrors('instagram_url');
});
test('social media facebook_url must not exceed 500 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-social-media'), [
'facebook_url' => str_repeat('a', 501),
]);
$response->assertSessionHasErrors('facebook_url');
});
test('social media tiktok_url must not exceed 500 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-social-media'), [
'tiktok_url' => str_repeat('a', 501),
]);
$response->assertSessionHasErrors('tiktok_url');
});
/*
|--------------------------------------------------------------------------
| UPDATE MARKETPLACE
|--------------------------------------------------------------------------
*/
test('user can update marketplace settings with valid data', function () {
$user = User::factory()->create();
$this->actingAs($user);
$feeRule = ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 5000];
$response = $this->put(route('admin.settings.update-marketplace'), [
'tiktok_shop_platform_commission' => $feeRule,
'tiktok_shop_logistics_service_fee' => $feeRule,
'tiktok_shop_dynamic_commission' => $feeRule,
'tiktok_shop_order_processing_fee' => $feeRule,
'tiktok_shop_affiliate' => $feeRule,
'tiktok_shop_pre_order_service_fee' => $feeRule,
'shopee_admin_fee' => $feeRule,
'shopee_program_fee' => $feeRule,
'shopee_shipping_savings' => $feeRule,
'shopee_premium' => $feeRule,
'shopee_service_fee' => $feeRule,
'shopee_order_processing_fee' => $feeRule,
'shopee_ams_commission_fee' => $feeRule,
'shopee_pre_order' => $feeRule,
'shopee_live_extra' => $feeRule,
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('admin.settings.index'));
$settings = app(MarketplaceSettings::class);
expect($settings->tiktok_shop_platform_commission->base)->toBe('per_transaksi');
expect($settings->tiktok_shop_platform_commission->type)->toBe('flat');
expect($settings->tiktok_shop_platform_commission->value)->toBe(5000.0);
expect($settings->shopee_admin_fee->base)->toBe('per_transaksi');
expect($settings->shopee_admin_fee->type)->toBe('flat');
expect($settings->shopee_admin_fee->value)->toBe(5000.0);
});
test('marketplace accepts percentage type', function () {
$user = User::factory()->create();
$this->actingAs($user);
$feeRule = ['base' => 'per_produk', 'type' => 'persentase', 'value' => 2.5];
$response = $this->put(route('admin.settings.update-marketplace'), [
'tiktok_shop_platform_commission' => $feeRule,
'tiktok_shop_logistics_service_fee' => $feeRule,
'tiktok_shop_dynamic_commission' => $feeRule,
'tiktok_shop_order_processing_fee' => $feeRule,
'tiktok_shop_affiliate' => $feeRule,
'tiktok_shop_pre_order_service_fee' => $feeRule,
'shopee_admin_fee' => $feeRule,
'shopee_program_fee' => $feeRule,
'shopee_shipping_savings' => $feeRule,
'shopee_premium' => $feeRule,
'shopee_service_fee' => $feeRule,
'shopee_order_processing_fee' => $feeRule,
'shopee_ams_commission_fee' => $feeRule,
'shopee_pre_order' => $feeRule,
'shopee_live_extra' => $feeRule,
]);
$response->assertSessionHasNoErrors();
$settings = app(MarketplaceSettings::class);
expect($settings->tiktok_shop_platform_commission->base)->toBe('per_produk');
expect($settings->tiktok_shop_platform_commission->type)->toBe('persentase');
expect($settings->tiktok_shop_platform_commission->value)->toBe(2.5);
});
test('marketplace base field must be valid', function () {
$user = User::factory()->create();
$this->actingAs($user);
$feeRule = ['base' => 'invalid', 'type' => 'flat', 'value' => 0];
$response = $this->put(route('admin.settings.update-marketplace'), [
'tiktok_shop_platform_commission' => $feeRule,
'tiktok_shop_logistics_service_fee' => $feeRule,
'tiktok_shop_dynamic_commission' => $feeRule,
'tiktok_shop_order_processing_fee' => $feeRule,
'tiktok_shop_affiliate' => $feeRule,
'tiktok_shop_pre_order_service_fee' => $feeRule,
'shopee_admin_fee' => $feeRule,
'shopee_program_fee' => $feeRule,
'shopee_shipping_savings' => $feeRule,
'shopee_premium' => $feeRule,
'shopee_service_fee' => $feeRule,
'shopee_order_processing_fee' => $feeRule,
'shopee_ams_commission_fee' => $feeRule,
'shopee_pre_order' => $feeRule,
'shopee_live_extra' => $feeRule,
]);
$response->assertSessionHasErrors('tiktok_shop_platform_commission.base');
});
test('marketplace type field must be valid', function () {
$user = User::factory()->create();
$this->actingAs($user);
$feeRule = ['base' => 'per_transaksi', 'type' => 'invalid', 'value' => 0];
$response = $this->put(route('admin.settings.update-marketplace'), [
'tiktok_shop_platform_commission' => $feeRule,
'tiktok_shop_logistics_service_fee' => $feeRule,
'tiktok_shop_dynamic_commission' => $feeRule,
'tiktok_shop_order_processing_fee' => $feeRule,
'tiktok_shop_affiliate' => $feeRule,
'tiktok_shop_pre_order_service_fee' => $feeRule,
'shopee_admin_fee' => $feeRule,
'shopee_program_fee' => $feeRule,
'shopee_shipping_savings' => $feeRule,
'shopee_premium' => $feeRule,
'shopee_service_fee' => $feeRule,
'shopee_order_processing_fee' => $feeRule,
'shopee_ams_commission_fee' => $feeRule,
'shopee_pre_order' => $feeRule,
'shopee_live_extra' => $feeRule,
]);
$response->assertSessionHasErrors('tiktok_shop_platform_commission.type');
});
test('marketplace value must be numeric', function () {
$user = User::factory()->create();
$this->actingAs($user);
$feeRule = ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 'not-a-number'];
$response = $this->put(route('admin.settings.update-marketplace'), [
'tiktok_shop_platform_commission' => $feeRule,
'tiktok_shop_logistics_service_fee' => $feeRule,
'tiktok_shop_dynamic_commission' => $feeRule,
'tiktok_shop_order_processing_fee' => $feeRule,
'tiktok_shop_affiliate' => $feeRule,
'tiktok_shop_pre_order_service_fee' => $feeRule,
'shopee_admin_fee' => $feeRule,
'shopee_program_fee' => $feeRule,
'shopee_shipping_savings' => $feeRule,
'shopee_premium' => $feeRule,
'shopee_service_fee' => $feeRule,
'shopee_order_processing_fee' => $feeRule,
'shopee_ams_commission_fee' => $feeRule,
'shopee_pre_order' => $feeRule,
'shopee_live_extra' => $feeRule,
]);
$response->assertSessionHasErrors('tiktok_shop_platform_commission.value');
});
test('marketplace value must be at least 0', function () {
$user = User::factory()->create();
$this->actingAs($user);
$feeRule = ['base' => 'per_transaksi', 'type' => 'flat', 'value' => -1];
$response = $this->put(route('admin.settings.update-marketplace'), [
'tiktok_shop_platform_commission' => $feeRule,
'tiktok_shop_logistics_service_fee' => $feeRule,
'tiktok_shop_dynamic_commission' => $feeRule,
'tiktok_shop_order_processing_fee' => $feeRule,
'tiktok_shop_affiliate' => $feeRule,
'tiktok_shop_pre_order_service_fee' => $feeRule,
'shopee_admin_fee' => $feeRule,
'shopee_program_fee' => $feeRule,
'shopee_shipping_savings' => $feeRule,
'shopee_premium' => $feeRule,
'shopee_service_fee' => $feeRule,
'shopee_order_processing_fee' => $feeRule,
'shopee_ams_commission_fee' => $feeRule,
'shopee_pre_order' => $feeRule,
'shopee_live_extra' => $feeRule,
]);
$response->assertSessionHasErrors('tiktok_shop_platform_commission.value');
});
test('marketplace fee rule array is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-marketplace'), [
'tiktok_shop_platform_commission' => 'not-an-array',
'tiktok_shop_logistics_service_fee' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'tiktok_shop_dynamic_commission' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'tiktok_shop_order_processing_fee' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'tiktok_shop_affiliate' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'tiktok_shop_pre_order_service_fee' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'shopee_admin_fee' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'shopee_program_fee' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'shopee_shipping_savings' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'shopee_premium' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'shopee_service_fee' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'shopee_order_processing_fee' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'shopee_ams_commission_fee' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'shopee_pre_order' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
'shopee_live_extra' => ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0],
]);
$response->assertSessionHasErrors('tiktok_shop_platform_commission');
});
test('marketplace each fee key must be provided', function () {
$user = User::factory()->create();
$this->actingAs($user);
$feeRule = ['base' => 'per_transaksi', 'type' => 'flat', 'value' => 0];
$response = $this->put(route('admin.settings.update-marketplace'), [
'tiktok_shop_platform_commission' => $feeRule,
// Missing other required keys
]);
$response->assertSessionHasErrors([
'tiktok_shop_logistics_service_fee',
'tiktok_shop_dynamic_commission',
'tiktok_shop_order_processing_fee',
'tiktok_shop_affiliate',
'tiktok_shop_pre_order_service_fee',
'shopee_admin_fee',
'shopee_program_fee',
'shopee_shipping_savings',
'shopee_premium',
'shopee_service_fee',
'shopee_order_processing_fee',
'shopee_ams_commission_fee',
'shopee_pre_order',
'shopee_live_extra',
]);
});
/*
|--------------------------------------------------------------------------
| UPDATE HR
|--------------------------------------------------------------------------
*/
test('user can update hr settings with valid data', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-hr'), [
'scheduled_check_in_time' => '09:00',
'scheduled_check_out_time' => '18:00',
'late_penalty_amount' => 10000,
'absent_penalty_amount' => 50000,
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('admin.settings.index'));
$settings = app(HRSettings::class);
expect($settings->scheduled_check_in_time)->toBe('09:00');
expect($settings->scheduled_check_out_time)->toBe('18:00');
expect($settings->late_penalty_amount)->toBe(10000);
expect($settings->absent_penalty_amount)->toBe(50000);
});
test('hr scheduled_check_in_time is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-hr'), [
'scheduled_check_in_time' => '',
'scheduled_check_out_time' => '18:00',
'late_penalty_amount' => 0,
'absent_penalty_amount' => 0,
]);
$response->assertSessionHasErrors('scheduled_check_in_time');
});
test('hr scheduled_check_out_time is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-hr'), [
'scheduled_check_in_time' => '09:00',
'scheduled_check_out_time' => '',
'late_penalty_amount' => 0,
'absent_penalty_amount' => 0,
]);
$response->assertSessionHasErrors('scheduled_check_out_time');
});
test('hr late_penalty_amount is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-hr'), [
'scheduled_check_in_time' => '09:00',
'scheduled_check_out_time' => '18:00',
'late_penalty_amount' => '',
'absent_penalty_amount' => 0,
]);
$response->assertSessionHasErrors('late_penalty_amount');
});
test('hr absent_penalty_amount is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-hr'), [
'scheduled_check_in_time' => '09:00',
'scheduled_check_out_time' => '18:00',
'late_penalty_amount' => 0,
'absent_penalty_amount' => '',
]);
$response->assertSessionHasErrors('absent_penalty_amount');
});
test('hr penalty amounts must be at least 0', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-hr'), [
'scheduled_check_in_time' => '09:00',
'scheduled_check_out_time' => '18:00',
'late_penalty_amount' => -1,
'absent_penalty_amount' => -1,
]);
$response->assertSessionHasErrors(['late_penalty_amount', 'absent_penalty_amount']);
});
test('hr penalty amounts must be integers', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-hr'), [
'scheduled_check_in_time' => '09:00',
'scheduled_check_out_time' => '18:00',
'late_penalty_amount' => 1000.50,
'absent_penalty_amount' => 2000.75,
]);
$response->assertSessionHasErrors(['late_penalty_amount', 'absent_penalty_amount']);
});
test('hr time fields must not exceed 5 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.settings.update-hr'), [
'scheduled_check_in_time' => '09:000',
'scheduled_check_out_time' => '18:000',
'late_penalty_amount' => 0,
'absent_penalty_amount' => 0,
]);
$response->assertSessionHasErrors(['scheduled_check_in_time', 'scheduled_check_out_time']);
});
/*
|--------------------------------------------------------------------------
| EDGE CASES & DATA INTEGRITY
|--------------------------------------------------------------------------
*/
test('system settings default values exist', function () {
$settings = app(SystemSettings::class);
expect($settings->app_name)->not->toBeEmpty();
expect($settings->about_app)->not->toBeNull();
expect($settings->address)->not->toBeNull();
expect($settings->email)->not->toBeNull();
expect($settings->phone)->not->toBeNull();
});
test('hr settings default values exist', function () {
$settings = app(HRSettings::class);
expect($settings->scheduled_check_in_time)->not->toBeEmpty();
expect($settings->scheduled_check_out_time)->not->toBeEmpty();
expect($settings->late_penalty_amount)->toBeInt();
expect($settings->absent_penalty_amount)->toBeInt();
});
test('marketplace settings default values are MarketplaceFeeRule', function () {
$settings = app(MarketplaceSettings::class);
expect($settings->tiktok_shop_platform_commission)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->tiktok_shop_logistics_service_fee)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->tiktok_shop_dynamic_commission)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->tiktok_shop_order_processing_fee)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->tiktok_shop_affiliate)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->tiktok_shop_pre_order_service_fee)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->shopee_admin_fee)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->shopee_program_fee)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->shopee_shipping_savings)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->shopee_premium)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->shopee_service_fee)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->shopee_order_processing_fee)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->shopee_ams_commission_fee)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->shopee_pre_order)->toBeInstanceOf(MarketplaceFeeRule::class);
expect($settings->shopee_live_extra)->toBeInstanceOf(MarketplaceFeeRule::class);
});
test('marketplace fee rule to array contains correct keys', function () {
$rule = MarketplaceFeeRule::defaultPercent(0.0);
$array = $rule->toArray();
expect($array)->toHaveKeys(['base', 'type', 'value']);
expect($array['base'])->toBe('per_transaksi');
expect($array['type'])->toBe('persentase');
expect($array['value'])->toBe(0.0);
});
test('updating one setting group does not affect others', function () {
$user = User::factory()->create();
$this->actingAs($user);
$systemBefore = app(SystemSettings::class);
$hrBefore = app(HRSettings::class);
$this->put(route('admin.settings.update-system'), [
'app_name' => 'Changed Name',
'about_app' => $systemBefore->about_app,
'address' => $systemBefore->address,
'email' => $systemBefore->email,
'phone' => $systemBefore->phone,
]);
$systemAfter = app(SystemSettings::class);
$hrAfter = app(HRSettings::class);
expect($systemAfter->app_name)->toBe('Changed Name');
expect($hrAfter->scheduled_check_in_time)->toBe($hrBefore->scheduled_check_in_time);
expect($hrAfter->late_penalty_amount)->toBe($hrBefore->late_penalty_amount);
});
/*
|--------------------------------------------------------------------------
| REALISTIC USER SCENARIOS
|--------------------------------------------------------------------------
*/
test('user updates all settings groups in sequence', function () {
$user = User::factory()->create();
$this->actingAs($user);
// Update system
$this->put(route('admin.settings.update-system'), [
'app_name' => 'DST Updated',
'about_app' => 'About Updated',
'address' => 'Address Updated',
'email' => 'updated@dst.com',
'phone' => '089999999999',
])->assertSessionHasNoErrors();
// Update homepage
$this->put(route('admin.settings.update-homepage'), [
'hero_image_key' => 'homepage/hero/2026/07/31/new-hero.jpg',
'about_image_key' => 'homepage/about/2026/07/31/new-about.jpg',
])->assertSessionHasNoErrors();
// Update social media
$this->put(route('admin.settings.update-social-media'), [
'instagram_url' => 'https://instagram.com/newdst',
'facebook_url' => 'https://facebook.com/newdst',
'tiktok_url' => 'https://tiktok.com/@newdst',
])->assertSessionHasNoErrors();
// Update marketplace
$feeRule = ['base' => 'per_transaksi', 'type' => 'persentase', 'value' => 3.0];
$this->put(route('admin.settings.update-marketplace'), [
'tiktok_shop_platform_commission' => $feeRule,
'tiktok_shop_logistics_service_fee' => $feeRule,
'tiktok_shop_dynamic_commission' => $feeRule,
'tiktok_shop_order_processing_fee' => $feeRule,
'tiktok_shop_affiliate' => $feeRule,
'tiktok_shop_pre_order_service_fee' => $feeRule,
'shopee_admin_fee' => $feeRule,
'shopee_program_fee' => $feeRule,
'shopee_shipping_savings' => $feeRule,
'shopee_premium' => $feeRule,
'shopee_service_fee' => $feeRule,
'shopee_order_processing_fee' => $feeRule,
'shopee_ams_commission_fee' => $feeRule,
'shopee_pre_order' => $feeRule,
'shopee_live_extra' => $feeRule,
])->assertSessionHasNoErrors();
// Update HR
$this->put(route('admin.settings.update-hr'), [
'scheduled_check_in_time' => '08:30',
'scheduled_check_out_time' => '17:30',
'late_penalty_amount' => 15000,
'absent_penalty_amount' => 75000,
])->assertSessionHasNoErrors();
// Verify all settings persisted
$system = app(SystemSettings::class);
$homepage = app(HomepageSettings::class);
$socialMedia = app(SocialMediaSettings::class);
$marketplace = app(MarketplaceSettings::class);
$hr = app(HRSettings::class);
expect($system->app_name)->toBe('DST Updated');
expect($homepage->hero_image_url)->toBe('homepage/hero/2026/07/31/new-hero.jpg');
expect($socialMedia->instagram_url)->toBe('https://instagram.com/newdst');
expect($marketplace->tiktok_shop_platform_commission->value)->toBe(3.0);
expect($hr->scheduled_check_in_time)->toBe('08:30');
expect($hr->late_penalty_amount)->toBe(15000);
});