1823 lines
54 KiB
PHP
1823 lines
54 KiB
PHP
<?php
|
||
|
||
use App\Models\Customer;
|
||
use App\Models\Purchase;
|
||
use App\Models\User;
|
||
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.master.customers.index'));
|
||
$response->assertRedirect(route('login'));
|
||
});
|
||
|
||
test('authenticated users can visit the customer index page', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->get(route('admin.master.customers.index'));
|
||
$response->assertOk();
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| INDEX PAGE
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('customer index page displays customers', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
Customer::factory()->count(3)->create();
|
||
|
||
$response = $this->get(route('admin.master.customers.index'));
|
||
$response->assertOk();
|
||
$response->assertInertia(fn (Assert $page) => $page
|
||
->component('admin/master/customer/index')
|
||
->has('customers', 3)
|
||
);
|
||
});
|
||
|
||
test('index page works with zero customers', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->get(route('admin.master.customers.index'));
|
||
$response->assertOk();
|
||
$response->assertInertia(fn (Assert $page) => $page
|
||
->component('admin/master/customer/index')
|
||
->has('customers', 0)
|
||
);
|
||
});
|
||
|
||
test('index page does not display soft-deleted customers', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
Customer::factory()->create(['name' => 'Active Customer']);
|
||
Customer::factory()->create(['name' => 'Deleted Customer'])->delete();
|
||
|
||
$response = $this->get(route('admin.master.customers.index'));
|
||
$response->assertOk();
|
||
$response->assertInertia(fn (Assert $page) => $page
|
||
->component('admin/master/customer/index')
|
||
->has('customers', 1)
|
||
->where('customers.0.name', 'Active Customer')
|
||
);
|
||
});
|
||
|
||
test('index page displays correct count after delete', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
Customer::factory()->count(5)->create();
|
||
$customer = Customer::first();
|
||
|
||
$this->delete(route('admin.master.customers.destroy', $customer));
|
||
|
||
$response = $this->get(route('admin.master.customers.index'));
|
||
$response->assertOk();
|
||
$response->assertInertia(fn (Assert $page) => $page
|
||
->component('admin/master/customer/index')
|
||
->has('customers', 4)
|
||
);
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| CREATE / STORE
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('customer can be created', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Test',
|
||
'phone_number' => '08123456789',
|
||
'address' => 'Jl. Test No. 1',
|
||
]);
|
||
|
||
$response
|
||
->assertSessionHasNoErrors()
|
||
->assertRedirect(route('admin.master.customers.index'));
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer Test',
|
||
'phone_number' => '08123456789',
|
||
'address' => 'Jl. Test No. 1',
|
||
]);
|
||
});
|
||
|
||
test('customer can be created without optional fields', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Minimal',
|
||
]);
|
||
|
||
$response
|
||
->assertSessionHasNoErrors()
|
||
->assertRedirect(route('admin.master.customers.index'));
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer Minimal',
|
||
]);
|
||
});
|
||
|
||
test('customer name is required', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => '',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('customer name must be string', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 12345,
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('customer name must not exceed 200 characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => str_repeat('a', 201),
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('customer name exactly 200 characters passes validation', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => str_repeat('a', 200),
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('customer name exactly 1 character passes validation', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'A',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'A',
|
||
]);
|
||
});
|
||
|
||
test('customer name must be unique', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
Customer::factory()->create(['name' => 'Existing Customer']);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Existing Customer',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('customer name uniqueness is case sensitive', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
Customer::factory()->create(['name' => 'Customer ABC']);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer ABC',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('customer name with only whitespace is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => ' ',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('customer name with special characters is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer & Co. (PT)',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer & Co. (PT)',
|
||
]);
|
||
});
|
||
|
||
test('customer name with HTML tags is accepted as plain text', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => '<script>alert("xss")</script>',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => '<script>alert("xss")</script>',
|
||
]);
|
||
});
|
||
|
||
test('customer name with numbers is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer 123',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer 123',
|
||
]);
|
||
});
|
||
|
||
test('customer name with only numbers is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => '12345',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => '12345',
|
||
]);
|
||
});
|
||
|
||
test('customer name with unicode characters is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Nono',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer Nono',
|
||
]);
|
||
});
|
||
|
||
test('customer name with mixed case is stored as entered', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'customer tEsT',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'customer tEsT',
|
||
]);
|
||
});
|
||
|
||
test('customer name with leading and trailing spaces is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => ' Customer Spasi ',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('customer name with parentheses and brackets is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer (A) [VIP]',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer (A) [VIP]',
|
||
]);
|
||
});
|
||
|
||
test('customer name with multiple spaces is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer dengan spasi banyak',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer dengan spasi banyak',
|
||
]);
|
||
});
|
||
|
||
test('customer can be created with name at exact DB column limit (200 chars)', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$name = str_repeat('a', 200);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => $name,
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => $name,
|
||
]);
|
||
});
|
||
|
||
test('store flashes success toast via Inertia', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Toast',
|
||
]);
|
||
|
||
$response->assertRedirect();
|
||
});
|
||
|
||
test('store creates new customer in database', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Database Check',
|
||
]);
|
||
|
||
$this->assertDatabaseCount('customers', 1);
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Database Check',
|
||
]);
|
||
});
|
||
|
||
test('multiple customers can be created sequentially', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Customer 1']);
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Customer 2']);
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Customer 3']);
|
||
|
||
$this->assertDatabaseCount('customers', 3);
|
||
|
||
$this->assertDatabaseHas('customers', ['name' => 'Customer 1']);
|
||
$this->assertDatabaseHas('customers', ['name' => 'Customer 2']);
|
||
$this->assertDatabaseHas('customers', ['name' => 'Customer 3']);
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| PHONE NUMBER VALIDATION
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('customer phone_number must be numeric', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer With Letters',
|
||
'phone_number' => 'abc123',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number accepts valid numeric string', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Valid Phone',
|
||
'phone_number' => '08123456789012345678',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer Valid Phone',
|
||
'phone_number' => '08123456789012345678',
|
||
]);
|
||
});
|
||
|
||
test('customer phone_number must not exceed 20 digits', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Long Phone',
|
||
'phone_number' => '081234567890123456789',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number exactly 20 digits passes validation', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Max Phone',
|
||
'phone_number' => '12345678901234567890',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('customer phone_number exactly 1 digit passes validation', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Min Phone',
|
||
'phone_number' => '1',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer Min Phone',
|
||
'phone_number' => '1',
|
||
]);
|
||
});
|
||
|
||
test('customer phone_number can be null', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer No Phone',
|
||
'phone_number' => null,
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer No Phone',
|
||
'phone_number' => null,
|
||
]);
|
||
});
|
||
|
||
test('customer phone_number with special characters is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Special Phone',
|
||
'phone_number' => '+62-812-3456-7890',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number with spaces is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Space Phone',
|
||
'phone_number' => '0812 345 678',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number with dashes is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Dash Phone',
|
||
'phone_number' => '0812-3456-7890',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number with dots is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Dot Phone',
|
||
'phone_number' => '0812.3456.7890',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number with parentheses is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Paren Phone',
|
||
'phone_number' => '(0812)3456789',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number with plus sign is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Plus Phone',
|
||
'phone_number' => '+6281234567890',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number with hash is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Hash Phone',
|
||
'phone_number' => '#08123456789',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number with asterisk is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Star Phone',
|
||
'phone_number' => '*08123456789',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number with letters and numbers is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Mixed Phone',
|
||
'phone_number' => '0812abc3456',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number with only letters is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Letter Phone',
|
||
'phone_number' => 'abcdefghij',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer phone_number with empty string is accepted as nullable', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Empty Phone',
|
||
'phone_number' => '',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| ADDRESS VALIDATION
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('customer address can be null', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer No Address',
|
||
'address' => null,
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer No Address',
|
||
'address' => null,
|
||
]);
|
||
});
|
||
|
||
test('customer address accepts long text', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$address = str_repeat('Jl. Panjang No. 123, RT 01/RW 02, Kelurahan Sukamaju, Kecamatan Menteng, Jakarta Pusat. ', 10);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Long Address',
|
||
'address' => $address,
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer Long Address',
|
||
]);
|
||
});
|
||
|
||
test('customer address with special characters is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Special Address',
|
||
'address' => 'Jl. Raya No. 1/2, Lt. 3 (Gedung ABC)',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer Special Address',
|
||
'address' => 'Jl. Raya No. 1/2, Lt. 3 (Gedung ABC)',
|
||
]);
|
||
});
|
||
|
||
test('customer address with unicode is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Unicode Address',
|
||
'address' => 'Jl. Sudirman No. 123, Jakarta',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer Unicode Address',
|
||
'address' => 'Jl. Sudirman No. 123, Jakarta',
|
||
]);
|
||
});
|
||
|
||
test('customer address with newlines is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Multiline Address',
|
||
'address' => "Jl. Raya No. 1\nKota Bandung\nJawa Barat",
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer Multiline Address',
|
||
]);
|
||
});
|
||
|
||
test('customer address with HTML is accepted as plain text', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer HTML Address',
|
||
'address' => '<b>Jl. Raya</b> No. 1',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer HTML Address',
|
||
'address' => '<b>Jl. Raya</b> No. 1',
|
||
]);
|
||
});
|
||
|
||
test('customer address can be empty string', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Empty Address',
|
||
'address' => '',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| UPDATE / PUT
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('customer can be updated', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Customer Updated',
|
||
'phone_number' => '0987654321',
|
||
'address' => 'Jl. Updated No. 2',
|
||
]);
|
||
|
||
$response
|
||
->assertSessionHasNoErrors()
|
||
->assertRedirect(route('admin.master.customers.index'));
|
||
|
||
$customer->refresh();
|
||
expect($customer->name)->toBe('Customer Updated');
|
||
expect($customer->phone_number)->toBe('0987654321');
|
||
expect($customer->address)->toBe('Jl. Updated No. 2');
|
||
});
|
||
|
||
test('customer name can be updated to itself', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create(['name' => 'My Customer']);
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'My Customer',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('customer update name must be unique excluding itself', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create(['name' => 'First']);
|
||
Customer::factory()->create(['name' => 'Second']);
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Second',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('customer update name is required', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => '',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('customer update name must not exceed 200 characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => str_repeat('a', 201),
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('customer update phone_number must be numeric', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Updated Customer',
|
||
'phone_number' => 'abc123',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer update phone_number with special characters is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Updated Customer',
|
||
'phone_number' => '+62-812-3456-7890',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('phone_number');
|
||
});
|
||
|
||
test('customer can be updated with only name', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create(['phone_number' => '08123456789', 'address' => 'Old Address']);
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Updated Name Only',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$customer->refresh();
|
||
expect($customer->name)->toBe('Updated Name Only');
|
||
expect($customer->phone_number)->toBe('08123456789');
|
||
expect($customer->address)->toBe('Old Address');
|
||
});
|
||
|
||
test('customer can be updated multiple times', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create(['name' => 'Version 1']);
|
||
|
||
$this->put(route('admin.master.customers.update', $customer), ['name' => 'Version 2']);
|
||
$customer->refresh();
|
||
expect($customer->name)->toBe('Version 2');
|
||
|
||
$this->put(route('admin.master.customers.update', $customer), ['name' => 'Version 3']);
|
||
$customer->refresh();
|
||
expect($customer->name)->toBe('Version 3');
|
||
});
|
||
|
||
test('update flashes success toast via Inertia', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Updated Name',
|
||
]);
|
||
|
||
$response->assertRedirect();
|
||
});
|
||
|
||
test('customer update with special characters in name is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Customer & Co. (PT)',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$customer->refresh();
|
||
expect($customer->name)->toBe('Customer & Co. (PT)');
|
||
});
|
||
|
||
test('customer update with unicode characters is accepted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create(['name' => 'Original']);
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Customer Nono',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$customer->refresh();
|
||
expect($customer->name)->toBe('Customer Nono');
|
||
});
|
||
|
||
test('customer update with only whitespace name is rejected', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => ' ',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('customer update can clear phone_number to null', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create(['phone_number' => '08123456789']);
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => $customer->name,
|
||
'phone_number' => null,
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$customer->refresh();
|
||
expect($customer->phone_number)->toBeNull();
|
||
});
|
||
|
||
test('customer update can clear address to null', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create(['address' => 'Old Address']);
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => $customer->name,
|
||
'address' => null,
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$customer->refresh();
|
||
expect($customer->address)->toBeNull();
|
||
});
|
||
|
||
test('updating non-existent customer returns 404', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->put(route('admin.master.customers.update', 999999), [
|
||
'name' => 'Ghost Customer',
|
||
]);
|
||
|
||
$response->assertStatus(404);
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| DELETE / DESTROY
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('customer can be deleted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->delete(route('admin.master.customers.destroy', $customer));
|
||
|
||
$response
|
||
->assertSessionHasNoErrors()
|
||
->assertRedirect(route('admin.master.customers.index'));
|
||
|
||
$this->assertSoftDeleted('customers', ['id' => $customer->id]);
|
||
});
|
||
|
||
test('customer is soft-deleted not permanently removed', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$this->delete(route('admin.master.customers.destroy', $customer));
|
||
|
||
$this->assertDatabaseHas('customers', ['id' => $customer->id]);
|
||
$this->assertSoftDeleted('customers', ['id' => $customer->id]);
|
||
});
|
||
|
||
test('soft-deleted customer still exists in database', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$this->delete(route('admin.master.customers.destroy', $customer));
|
||
|
||
$this->assertDatabaseHas('customers', ['id' => $customer->id]);
|
||
});
|
||
|
||
test('soft-deleted customer has deleted_at timestamp', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$this->delete(route('admin.master.customers.destroy', $customer));
|
||
|
||
$customer->refresh();
|
||
expect($customer->deleted_at)->not->toBeNull();
|
||
});
|
||
|
||
test('deleted customer is excluded from getAll query', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
Customer::factory()->create(['name' => 'Active']);
|
||
$deleted = Customer::factory()->create(['name' => 'Deleted']);
|
||
$deleted->delete();
|
||
|
||
$response = $this->get(route('admin.master.customers.index'));
|
||
$response->assertOk();
|
||
$response->assertInertia(fn (Assert $page) => $page
|
||
->component('admin/master/customer/index')
|
||
->has('customers', 1)
|
||
->where('customers.0.name', 'Active')
|
||
);
|
||
});
|
||
|
||
test('delete flashes success toast via Inertia', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->delete(route('admin.master.customers.destroy', $customer));
|
||
|
||
$response->assertRedirect();
|
||
});
|
||
|
||
test('multiple customers can be deleted one by one', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customers = Customer::factory()->count(3)->create();
|
||
|
||
$this->delete(route('admin.master.customers.destroy', $customers[0]));
|
||
$this->delete(route('admin.master.customers.destroy', $customers[1]));
|
||
|
||
$this->assertSoftDeleted('customers', ['id' => $customers[0]->id]);
|
||
$this->assertSoftDeleted('customers', ['id' => $customers[1]->id]);
|
||
$this->assertDatabaseHas('customers', ['id' => $customers[2]->id, 'deleted_at' => null]);
|
||
});
|
||
|
||
test('updating soft-deleted customer via route returns 404', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
$customer->delete();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Should Not Work',
|
||
]);
|
||
|
||
$response->assertStatus(404);
|
||
});
|
||
|
||
test('deleting soft-deleted customer via route returns 404', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
$customer->delete();
|
||
|
||
$response = $this->delete(route('admin.master.customers.destroy', $customer));
|
||
|
||
$response->assertStatus(404);
|
||
});
|
||
|
||
test('deleting non-existent customer returns 404', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->delete(route('admin.master.customers.destroy', 999999));
|
||
|
||
$response->assertStatus(404);
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| CUSTOMER - PURCHASE RELATIONSHIP
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('customer with purchases can be deleted', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
Purchase::factory()->count(3)->create(['customer_id' => $customer->id]);
|
||
|
||
$this->delete(route('admin.master.customers.destroy', $customer));
|
||
|
||
$this->assertSoftDeleted('customers', ['id' => $customer->id]);
|
||
// Purchases still exist because soft delete doesn't trigger cascade
|
||
$this->assertDatabaseCount('purchases', 3);
|
||
});
|
||
|
||
test('index page works correctly with customers that have purchases', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
Purchase::factory()->count(5)->create(['customer_id' => $customer->id]);
|
||
|
||
$response = $this->get(route('admin.master.customers.index'));
|
||
$response->assertOk();
|
||
$response->assertInertia(fn (Assert $page) => $page
|
||
->component('admin/master/customer/index')
|
||
->has('customers', 1)
|
||
);
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| AUTHORIZATION - GUEST CANNOT PERFORM ACTIONS
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('guest cannot create customer', function () {
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Unauthorized',
|
||
]);
|
||
|
||
$response->assertRedirect(route('login'));
|
||
$this->assertDatabaseMissing('customers', ['name' => 'Unauthorized']);
|
||
});
|
||
|
||
test('guest cannot update customer', function () {
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Unauthorized Update',
|
||
]);
|
||
|
||
$response->assertRedirect(route('login'));
|
||
});
|
||
|
||
test('guest cannot delete customer', function () {
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->delete(route('admin.master.customers.destroy', $customer));
|
||
|
||
$response->assertRedirect(route('login'));
|
||
$this->assertDatabaseHas('customers', ['id' => $customer->id, 'deleted_at' => null]);
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| DATA INTEGRITY
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('created customer has correct timestamps', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Timestamp Test',
|
||
]);
|
||
|
||
$customer = Customer::where('name', 'Timestamp Test')->first();
|
||
expect($customer->created_at)->not->toBeNull();
|
||
expect($customer->updated_at)->not->toBeNull();
|
||
});
|
||
|
||
test('updated customer has updated timestamp changed', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
$originalUpdatedAt = $customer->updated_at;
|
||
|
||
sleep(1);
|
||
|
||
$this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Timestamp Changed',
|
||
]);
|
||
|
||
$customer->refresh();
|
||
expect($customer->updated_at->gt($originalUpdatedAt))->toBeTrue();
|
||
});
|
||
|
||
test('customer ID is auto-incremented', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'First']);
|
||
$first = Customer::where('name', 'First')->first();
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Second']);
|
||
$second = Customer::where('name', 'Second')->first();
|
||
|
||
expect($second->id)->toBe($first->id + 1);
|
||
});
|
||
|
||
test('customer factory creates valid customer', function () {
|
||
$customer = Customer::factory()->create();
|
||
|
||
expect($customer->name)->not->toBeEmpty();
|
||
expect($customer->id)->toBeInt();
|
||
});
|
||
|
||
test('customer factory creates unique names', function () {
|
||
$customers = Customer::factory()->count(20)->create();
|
||
|
||
$names = $customers->pluck('name')->toArray();
|
||
expect($names)->toHaveCount(count(array_unique($names)));
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| SOFT DELETE & RESTORE
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('customer can be restored after soft delete', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create(['name' => 'Restored Customer']);
|
||
$customer->delete();
|
||
|
||
$this->assertSoftDeleted('customers', ['id' => $customer->id]);
|
||
|
||
$customer->restore();
|
||
|
||
$this->assertDatabaseHas('customers', ['id' => $customer->id, 'deleted_at' => null]);
|
||
});
|
||
|
||
test('restored customer appears in index again', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create(['name' => 'Restored Customer']);
|
||
$customer->delete();
|
||
|
||
$response = $this->get(route('admin.master.customers.index'));
|
||
$response->assertInertia(fn (Assert $page) => $page
|
||
->component('admin/master/customer/index')
|
||
->has('customers', 0)
|
||
);
|
||
|
||
$customer->restore();
|
||
|
||
$response = $this->get(route('admin.master.customers.index'));
|
||
$response->assertInertia(fn (Assert $page) => $page
|
||
->component('admin/master/customer/index')
|
||
->has('customers', 1)
|
||
->where('customers.0.name', 'Restored Customer')
|
||
);
|
||
});
|
||
|
||
test('restored customer can be updated', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create(['name' => 'Before Restore']);
|
||
$customer->delete();
|
||
$customer->restore();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'After Restore',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$customer->refresh();
|
||
expect($customer->name)->toBe('After Restore');
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| REALISTIC USER SCENARIOS
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
test('user creates customer then immediately edits it', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Draft Customer']);
|
||
$customer = Customer::where('name', 'Draft Customer')->first();
|
||
|
||
$this->put(route('admin.master.customers.update', $customer), ['name' => 'Final Customer']);
|
||
|
||
$customer->refresh();
|
||
expect($customer->name)->toBe('Final Customer');
|
||
});
|
||
|
||
test('user creates multiple customers and deletes one', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Keep']);
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Delete Me']);
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Also Keep']);
|
||
|
||
$toDelete = Customer::where('name', 'Delete Me')->first();
|
||
$this->delete(route('admin.master.customers.destroy', $toDelete));
|
||
|
||
$response = $this->get(route('admin.master.customers.index'));
|
||
$response->assertOk();
|
||
$response->assertInertia(fn (Assert $page) => $page
|
||
->component('admin/master/customer/index')
|
||
->has('customers', 2)
|
||
);
|
||
});
|
||
|
||
test('user renames customer to match another existing customer name', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
Customer::factory()->create(['name' => 'Target Name']);
|
||
$customer = Customer::factory()->create(['name' => 'Source Name']);
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), [
|
||
'name' => 'Target Name',
|
||
]);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('user creates customer with Indonesian characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'PT Maju Jaya Sentosa',
|
||
'phone_number' => '0215551234',
|
||
'address' => 'Jl. Sudirman No. 123, Jakarta Selatan',
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'PT Maju Jaya Sentosa',
|
||
'phone_number' => '0215551234',
|
||
'address' => 'Jl. Sudirman No. 123, Jakarta Selatan',
|
||
]);
|
||
});
|
||
|
||
test('user tries to create customer without submitting any data', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), []);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('user tries to update customer without submitting any data', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$customer = Customer::factory()->create();
|
||
|
||
$response = $this->put(route('admin.master.customers.update', $customer), []);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('user rapidly submits same customer creation twice', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Rapid Submit']);
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'Rapid Submit']);
|
||
|
||
$response->assertSessionHasErrors('name');
|
||
$this->assertDatabaseCount('customers', 1);
|
||
});
|
||
|
||
test('user creates customer with name containing chinese characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => '电子产品供应商']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing japanese characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'サプライヤー']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing korean characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => '공급업체']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing arabic characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'المورد']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing cyrillic characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'Поставщик']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing thai characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'ซัพพลายเออร์']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing hindi characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'आपूर्तिकर्ता']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing greek characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'Προμηθευτής']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing vietnamese characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'Nhà cung cấp']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing mixed unicode scripts', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'Customer 电子产品']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing emoji', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => '🏢 Customer']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with very short name', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'X']);
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'X',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing mixed case and numbers', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'PT123ABC456']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'PT123ABC456',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing only spaces and numbers', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => ' 123 ']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with very long name', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$name = str_repeat('a', 200);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => $name]);
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => $name,
|
||
]);
|
||
});
|
||
|
||
test('user creates customer then creates another with similar name', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Customer ABC']);
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Customer XYZ']);
|
||
|
||
$this->assertDatabaseCount('customers', 2);
|
||
$this->assertDatabaseHas('customers', ['name' => 'Customer ABC']);
|
||
$this->assertDatabaseHas('customers', ['name' => 'Customer XYZ']);
|
||
});
|
||
|
||
test('user creates customer with name containing curly braces', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => '{code}']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => '{code}',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing angle brackets', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => '<html>']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => '<html>',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing semicolon', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'A;B']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'A;B',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing colon', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Time: Now']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Time: Now',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing backtick', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => '`Code`']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => '`Code`',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing tilde', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Caf~']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Caf~',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing caret', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'A^B']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'A^B',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing percentage', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => '100% Natural']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => '100% Natural',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing fullwidth characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'ABCDE']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing fullwidth brackets', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => '()【】']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing hiragana', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'あいうえお']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing katakana', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'アイウエオ']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing hangul', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => '가나다라마']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing hebrew', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'אבגדהו']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing bengali', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'অআইঈউ']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing tamil', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'அஆஇஈஉ']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|
||
|
||
test('user creates customer with name containing email-like pattern', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'user@example.com']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'user@example.com',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with name containing URL-like pattern', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'https://example.com']);
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'https://example.com',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with address containing Indonesian address format', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer Alamat',
|
||
'address' => "Jl. Raya Bogor Km. 30\nRT 01/02\nKel. Sukamaju\nKec. Menteng\nJakarta Pusat 10310",
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer Alamat',
|
||
]);
|
||
});
|
||
|
||
test('user creates customer with phone number in various Indonesian formats', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$phones = [
|
||
'081234567890',
|
||
'6281234567890',
|
||
'0215551234',
|
||
'02155512345',
|
||
];
|
||
|
||
foreach ($phones as $phone) {
|
||
$response = $this->post(route('admin.master.customers.store'), [
|
||
'name' => 'Customer '.$phone,
|
||
'phone_number' => $phone,
|
||
]);
|
||
|
||
$response->assertSessionHasNoErrors();
|
||
|
||
$this->assertDatabaseHas('customers', [
|
||
'name' => 'Customer '.$phone,
|
||
'phone_number' => $phone,
|
||
]);
|
||
}
|
||
});
|
||
|
||
test('user creates customer then deletes it and creates new one with same name', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$this->post(route('admin.master.customers.store'), ['name' => 'Reusable']);
|
||
$customer = Customer::where('name', 'Reusable')->first();
|
||
|
||
$this->delete(route('admin.master.customers.destroy', $customer));
|
||
|
||
// Soft-deleted name still triggers unique validation
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => 'Reusable']);
|
||
$response->assertSessionHasErrors('name');
|
||
});
|
||
|
||
test('user creates customer with name containing regex special characters', function () {
|
||
$user = User::factory()->create();
|
||
$this->actingAs($user);
|
||
|
||
$response = $this->post(route('admin.master.customers.store'), ['name' => '.*+?^${}()|[]\\']);
|
||
$response->assertSessionHasNoErrors();
|
||
});
|