1603 lines
56 KiB
PHP
1603 lines
56 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Worker;
|
|
use App\Models\WorkerDocument;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Tests\TestCase;
|
|
|
|
class WorkerJourneyApiTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->seed(\Database\Seeders\NationalitySeeder::class);
|
|
}
|
|
|
|
/**
|
|
* Test S1 Onboard: Get Config and Supported Languages.
|
|
*/
|
|
public function test_s1_get_languages_config()
|
|
{
|
|
$response = $this->getJson('/api/workers/config');
|
|
|
|
$response->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'languages' => [
|
|
['code' => 'HI', 'name' => 'Hindi (हिन्दी)'],
|
|
['code' => 'SW', 'name' => 'Swahili (Kiswahili)'],
|
|
['code' => 'TL', 'name' => 'Tagalog (Wikang Tagalog)'],
|
|
['code' => 'TA', 'name' => 'Tamil (தமிழ்)'],
|
|
]
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test Get Skills Master API.
|
|
*/
|
|
public function test_get_skills_master_api()
|
|
{
|
|
// Seed some master skills
|
|
\Illuminate\Support\Facades\DB::table('skills')->insert([
|
|
['name' => 'Cooking', 'created_at' => now(), 'updated_at' => now()],
|
|
['name' => 'Cleaning', 'created_at' => now(), 'updated_at' => now()],
|
|
]);
|
|
|
|
$response = $this->getJson('/api/workers/skills');
|
|
|
|
$response->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
])
|
|
->assertJsonStructure([
|
|
'success',
|
|
'skills' => [
|
|
'*' => ['id', 'name']
|
|
]
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test S1 Onboard: Send and Verify OTP.
|
|
*/
|
|
public function test_s1_otp_flow()
|
|
{
|
|
$phone = '+971501234567';
|
|
|
|
// 1. Send OTP
|
|
$sendResponse = $this->postJson('/api/workers/send-otp', [
|
|
'phone' => $phone,
|
|
]);
|
|
|
|
$sendResponse->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'identifier' => $phone,
|
|
]);
|
|
|
|
// Verify OTP is stored in cache
|
|
$cached = Cache::get('worker_otp_' . $phone);
|
|
$this->assertNotNull($cached);
|
|
$this->assertEquals('111111', $cached['code']); // Dev default OTP
|
|
|
|
// 2. Verify OTP
|
|
$verifyResponse = $this->postJson('/api/workers/verify-otp', [
|
|
'phone' => $phone,
|
|
'otp' => '111111',
|
|
]);
|
|
|
|
$verifyResponse->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'identifier' => $phone,
|
|
]);
|
|
|
|
// Verify the gate cache is present
|
|
$this->assertTrue(Cache::get('worker_otp_verified_' . $phone));
|
|
}
|
|
|
|
/**
|
|
* Test S1 Onboard: Send OTP fails if mobile number already exists.
|
|
*/
|
|
public function test_s1_otp_flow_fails_if_phone_already_exists()
|
|
{
|
|
Worker::create([
|
|
'name' => 'Existing Worker',
|
|
'email' => 'existing@example.com',
|
|
'phone' => '+971501234567',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'status' => 'active',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Test worker',
|
|
]);
|
|
|
|
$response = $this->postJson('/api/workers/send-otp', [
|
|
'phone' => '+971501234567',
|
|
]);
|
|
|
|
$response->assertStatus(422)
|
|
->assertJson([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
])
|
|
->assertJsonValidationErrors(['phone']);
|
|
}
|
|
|
|
/**
|
|
* Test S1 Onboard: Complete Basic Profile Setup.
|
|
*/
|
|
public function test_s1_complete_basic_profile_setup()
|
|
{
|
|
$phone = '+971501234567';
|
|
|
|
// Bypass OTP by seeding the cache gate
|
|
Cache::put('worker_otp_verified_' . $phone, true, 3600);
|
|
|
|
$response = $this->postJson('/api/workers/setup-profile', [
|
|
'phone' => $phone,
|
|
'name' => 'Rahul Sharma',
|
|
'nationality' => 'Indian',
|
|
'language' => 'HI',
|
|
'gender' => 'male',
|
|
'main_profession' => 'Nanny',
|
|
'skills' => [],
|
|
]);
|
|
|
|
$response->assertStatus(201)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'data' => [
|
|
'worker' => [
|
|
'id',
|
|
'name',
|
|
'email',
|
|
'phone',
|
|
'language',
|
|
'verified',
|
|
'status',
|
|
],
|
|
'token',
|
|
]
|
|
]);
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'name' => 'Rahul Sharma',
|
|
'phone' => $phone,
|
|
'language' => 'HI',
|
|
'gender' => 'male',
|
|
'main_profession' => 'Nanny',
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
]);
|
|
|
|
// Gate cache should be purged
|
|
$this->assertNull(Cache::get('worker_otp_verified_' . $phone));
|
|
}
|
|
|
|
/**
|
|
* Test S2 Profile: Add Experience & Preferences.
|
|
*/
|
|
public function test_s2_update_experience_and_preferences()
|
|
{
|
|
$worker = Worker::create([
|
|
'name' => 'John Swahili',
|
|
'email' => 'john@example.com',
|
|
'phone' => '+971509999999',
|
|
'language' => 'SW',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Kenyan',
|
|
'age' => 22,
|
|
'salary' => 1200,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Christian',
|
|
'bio' => 'New helper',
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => 'test-bearer-token-123',
|
|
]);
|
|
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer test-bearer-token-123',
|
|
])->postJson('/api/workers/profile/update', [
|
|
'experience' => '3 Years Cooking & Childcare',
|
|
'salary' => 1800,
|
|
]);
|
|
|
|
$response->assertStatus(200);
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $worker->id,
|
|
'experience' => '3 Years Cooking & Childcare',
|
|
'salary' => 1800,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
|
|
|
|
/**
|
|
* Test S3 Go Live: Status set to Active.
|
|
*/
|
|
public function test_s3_go_live()
|
|
{
|
|
$worker = Worker::create([
|
|
'name' => 'Selvi Tamil',
|
|
'email' => 'selvi@example.com',
|
|
'phone' => '+971508888888',
|
|
'language' => 'TA',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Sri Lankan',
|
|
'age' => 32,
|
|
'salary' => 1400,
|
|
'availability' => 'Not Available',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Ready to work',
|
|
'verified' => true,
|
|
'status' => 'inactive',
|
|
'api_token' => 'test-bearer-token-789',
|
|
]);
|
|
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer test-bearer-token-789',
|
|
])->postJson('/api/workers/go-live');
|
|
|
|
$response->assertStatus(200)
|
|
->assertJsonPath('data.worker.status', 'active')
|
|
->assertJsonPath('data.worker.availability', 'Immediate');
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $worker->id,
|
|
'status' => 'active',
|
|
'availability' => 'Immediate',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test S5 Avail?: Toggle availability (Still looking?).
|
|
*/
|
|
public function test_s5_toggle_availability_still_looking()
|
|
{
|
|
$worker = Worker::create([
|
|
'name' => 'Rahul Sharma',
|
|
'email' => 'rahul@example.com',
|
|
'phone' => '+971501234567',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Test',
|
|
'verified' => true,
|
|
'status' => 'active',
|
|
'api_token' => 'test-bearer-token-abc',
|
|
]);
|
|
|
|
// Toggle still_looking = false (NO -> Hidden)
|
|
$response1 = $this->withHeaders([
|
|
'Authorization' => 'Bearer test-bearer-token-abc',
|
|
])->postJson('/api/workers/profile/toggle-availability', [
|
|
'still_looking' => false,
|
|
]);
|
|
|
|
$response1->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'still_looking' => false,
|
|
'status' => 'hidden',
|
|
]);
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $worker->id,
|
|
'status' => 'hidden',
|
|
]);
|
|
|
|
// Toggle still_looking = true (YES -> Visible/Active)
|
|
$response2 = $this->withHeaders([
|
|
'Authorization' => 'Bearer test-bearer-token-abc',
|
|
])->postJson('/api/workers/profile/toggle-availability', [
|
|
'still_looking' => true,
|
|
]);
|
|
|
|
$response2->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'still_looking' => true,
|
|
'status' => 'active',
|
|
]);
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $worker->id,
|
|
'status' => 'active',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test S6 Outcome: Marked as Hired (Profile removed from search - auto-hidden).
|
|
*/
|
|
public function test_s6_outcome_mark_hired()
|
|
{
|
|
$worker = Worker::create([
|
|
'name' => 'Rahul Sharma',
|
|
'email' => 'rahul@example.com',
|
|
'phone' => '+971501234567',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Test',
|
|
'verified' => true,
|
|
'status' => 'active',
|
|
'api_token' => 'test-bearer-token-abc',
|
|
]);
|
|
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer test-bearer-token-abc',
|
|
])->postJson('/api/workers/profile/mark-hired');
|
|
|
|
$response->assertStatus(200)
|
|
->assertJsonPath('data.worker.status', 'Hired');
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $worker->id,
|
|
'status' => 'Hired',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test registration with new API fields.
|
|
*/
|
|
public function test_register_with_new_api_fields()
|
|
{
|
|
$fakePassport = UploadedFile::fake()->create('passport.pdf', 500, 'application/pdf');
|
|
|
|
$response = $this->postJson('/api/workers/register', [
|
|
'name' => 'John New Worker',
|
|
'phone' => '+971501111112',
|
|
'salary' => 2200.50,
|
|
'password' => 'secret123',
|
|
'passport_file' => $fakePassport,
|
|
'language' => 'HI',
|
|
'nationality' => 'Kenyan',
|
|
'age' => 30,
|
|
'experience' => '5 Years',
|
|
'in_country' => 'true',
|
|
'visa_status' => 'Tourist Visa',
|
|
'preferred_job_type' => 'full-time',
|
|
'main_profession' => 'Housemaid',
|
|
'gender' => 'male',
|
|
'live_in_out' => 'live_out',
|
|
'preferred_location' => 'Dubai Marina',
|
|
]);
|
|
|
|
$response->assertStatus(201);
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'name' => 'John New Worker',
|
|
'phone' => '+971501111112',
|
|
'in_country' => true,
|
|
'visa_status' => 'Tourist Visa',
|
|
'preferred_job_type' => 'full-time',
|
|
'main_profession' => 'Housemaid',
|
|
'gender' => 'male',
|
|
'live_in_out' => 'live_out',
|
|
'preferred_location' => 'Dubai Marina',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test worker registration utilizing OCR extracted fields.
|
|
*/
|
|
public function test_register_with_ocr_extraction()
|
|
{
|
|
// Consolidated registration with inline passport payload
|
|
$response = $this->postJson('/api/workers/register', [
|
|
'name' => 'Temp Name',
|
|
'phone' => '+971509999999',
|
|
'salary' => 2500,
|
|
'password' => 'secret123',
|
|
'language' => 'HI',
|
|
'passport' => [
|
|
'date_of_birth' => '07/11/1990',
|
|
'date_of_expiry' => '06/09/2030',
|
|
'date_of_issue' => '06/09/2020',
|
|
'given_names' => 'MATAR ALI KHARBASH',
|
|
'surname' => 'ALSAEDI',
|
|
'nationality' => 'ARE',
|
|
'passport_number' => 'SQ0000151',
|
|
]
|
|
]);
|
|
|
|
$response->assertStatus(201);
|
|
$workerId = $response->json('data.worker.id');
|
|
|
|
$expectedAge = now()->diff(new \DateTime('1990-11-07'))->y;
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $workerId,
|
|
'name' => 'MATAR ALI KHARBASH ALSAEDI',
|
|
'phone' => '+971509999999',
|
|
'nationality' => 'Emirati',
|
|
'age' => $expectedAge,
|
|
'verified' => true,
|
|
]);
|
|
|
|
$this->assertDatabaseHas('worker_documents', [
|
|
'worker_id' => $workerId,
|
|
'type' => 'passport',
|
|
'number' => 'SQ0000151',
|
|
'expiry_date' => '2030-09-06',
|
|
'issue_date' => '2020-09-06',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test worker passport and visa registration with direct JSON payload from mobile app.
|
|
*/
|
|
public function test_register_passport_and_visa_with_direct_json_payload()
|
|
{
|
|
$response = $this->postJson('/api/workers/register', [
|
|
'name' => 'Temp Name',
|
|
'phone' => '+971508888888',
|
|
'salary' => 2000,
|
|
'password' => 'secret123',
|
|
'language' => 'HI',
|
|
'passport' => [
|
|
'authority' => 'Issuing Authority',
|
|
'date_of_birth' => '14/05/1990',
|
|
'date_of_expiry' => '05/07/2028',
|
|
'date_of_issue' => '05/07/2018',
|
|
'document_type' => 'P',
|
|
'given_names' => 'Jane',
|
|
'issuing_country' => 'PHL',
|
|
'nationality' => 'PHL',
|
|
'passport_number' => 'Y34B6790',
|
|
'personal_number' => '28',
|
|
'place_of_birth' => 'Manila',
|
|
'sex' => 'F',
|
|
'surname' => 'Doe',
|
|
],
|
|
'visa' => [
|
|
'accompanied_by' => '9',
|
|
'expiry_date' => '2027-02-15',
|
|
'file_number' => '201/2021/2840233',
|
|
'id_number' => '784198839607839',
|
|
'issue_date' => '2025-02-16',
|
|
'name' => 'Jane Doe',
|
|
'passport_number' => 'Y34B6790',
|
|
'place_of_issue' => 'Dubai',
|
|
'profession' => 'CLEANER',
|
|
'sponsor' => 'Sponsor Name',
|
|
]
|
|
]);
|
|
|
|
$response->assertStatus(201);
|
|
$workerId = $response->json('data.worker.id');
|
|
|
|
$expectedAge = now()->diff(new \DateTime('1990-05-14'))->y;
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $workerId,
|
|
'name' => 'Jane Doe',
|
|
'phone' => '+971508888888',
|
|
'nationality' => 'Filipino',
|
|
'age' => $expectedAge,
|
|
'verified' => true,
|
|
'visa_status' => 'Tourist Visa',
|
|
]);
|
|
|
|
$this->assertDatabaseHas('worker_documents', [
|
|
'worker_id' => $workerId,
|
|
'type' => 'passport',
|
|
'number' => 'Y34B6790',
|
|
'expiry_date' => '2028-07-05',
|
|
'issue_date' => '2018-07-05',
|
|
]);
|
|
|
|
$this->assertDatabaseHas('worker_documents', [
|
|
'worker_id' => $workerId,
|
|
'type' => 'visa',
|
|
'number' => '201/2021/2840233',
|
|
'expiry_date' => '2027-02-15',
|
|
'issue_date' => '2025-02-16',
|
|
]);
|
|
|
|
$passportDoc = \App\Models\WorkerDocument::where('worker_id', $workerId)->where('type', 'passport')->first();
|
|
$this->assertNotNull($passportDoc->ocr_data);
|
|
$this->assertEquals('PHL', $passportDoc->ocr_data['nationality']);
|
|
$this->assertEquals('Jane', $passportDoc->ocr_data['given_names']);
|
|
$this->assertEquals('Doe', $passportDoc->ocr_data['surname']);
|
|
$this->assertEquals('Issuing Authority', $passportDoc->ocr_data['authority']);
|
|
|
|
$visaDoc = \App\Models\WorkerDocument::where('worker_id', $workerId)->where('type', 'visa')->first();
|
|
$this->assertNotNull($visaDoc->ocr_data);
|
|
$this->assertEquals('784198839607839', $visaDoc->ocr_data['uid_no']);
|
|
$this->assertEquals('CLEANER', $visaDoc->ocr_data['profession']);
|
|
$this->assertEquals([
|
|
'name' => 'Sponsor Name',
|
|
'address' => '',
|
|
'phone' => '',
|
|
], $visaDoc->ocr_data['sponsor']);
|
|
}
|
|
|
|
/**
|
|
* Test worker registration with the specific passport fields and ocr_accuracy.
|
|
*/
|
|
public function test_register_passport_with_full_ocr_fields()
|
|
{
|
|
$response = $this->postJson('/api/workers/register', [
|
|
'name' => 'Temp Name',
|
|
'phone' => '+971507777777',
|
|
'salary' => 2000,
|
|
'password' => 'secret123',
|
|
'language' => 'HI',
|
|
'passport' => [
|
|
'document_type' => 'P',
|
|
'issuing_country' => 'HAS',
|
|
'surname' => 'KHALED',
|
|
'given_names' => 'KHALED AL',
|
|
'passport_number' => 'Y34B67890',
|
|
'nationality' => 'RE1',
|
|
'date_of_birth' => '1990-05-14',
|
|
'date_of_expiry' => '2022-05-07',
|
|
'place_of_birth' => 'SHARJAH',
|
|
'authority' => 'MINISTRY OF INTERIOR U.S. 21 is ,,,, is',
|
|
'sex' => 'M',
|
|
'date_of_issue' => '2017-07-05',
|
|
'ocr_accuracy' => 98.7,
|
|
]
|
|
]);
|
|
|
|
$response->assertStatus(201);
|
|
$workerId = $response->json('data.worker.id');
|
|
|
|
$this->assertDatabaseHas('worker_documents', [
|
|
'worker_id' => $workerId,
|
|
'type' => 'passport',
|
|
'number' => 'Y34B67890',
|
|
'expiry_date' => '2022-05-07',
|
|
'issue_date' => '2017-07-05',
|
|
'ocr_accuracy' => 98.7,
|
|
]);
|
|
|
|
$passportDoc = \App\Models\WorkerDocument::where('worker_id', $workerId)->where('type', 'passport')->first();
|
|
$this->assertNotNull($passportDoc->ocr_data);
|
|
$this->assertEquals('KHALED AL', $passportDoc->ocr_data['given_names']);
|
|
$this->assertEquals('KHALED', $passportDoc->ocr_data['surname']);
|
|
$this->assertEquals('MINISTRY OF INTERIOR U.S. 21 is ,,,, is', $passportDoc->ocr_data['authority']);
|
|
$this->assertEquals(98.7, $passportDoc->ocr_data['ocr_accuracy']);
|
|
}
|
|
|
|
/**
|
|
* Test worker registration with the specific visa fields and ocr_accuracy.
|
|
*/
|
|
public function test_register_visa_with_full_ocr_fields()
|
|
{
|
|
$response = $this->postJson('/api/workers/register', [
|
|
'name' => 'Temp Name',
|
|
'phone' => '+971507777778',
|
|
'salary' => 2000,
|
|
'password' => 'secret123',
|
|
'language' => 'HI',
|
|
'visa' => [
|
|
'document_type' => 'uae_visa',
|
|
'country' => 'United Arab Emirates',
|
|
'visa_type' => 'ENTRY PERMIT',
|
|
'entry_permit_no' => '77003098 / 2019 / 204',
|
|
'issue_date' => '',
|
|
'valid_until' => '04-MAR-2019',
|
|
'uid_no' => '207404887',
|
|
'full_name' => 'Mr.MUHAMMAD NADEEM RASHEED',
|
|
'nationality' => 'PAKISTAN',
|
|
'place_of_birth' => 'SIALKOT PAK',
|
|
'date_of_birth' => '25-JUN-1999',
|
|
'passport_no' => 'EN9458281',
|
|
'profession' => 'SALES REPRESENTATIVE',
|
|
'sponsor_name' => '',
|
|
'ocr_accuracy' => 97.4,
|
|
]
|
|
]);
|
|
|
|
$response->assertStatus(201);
|
|
$workerId = $response->json('data.worker.id');
|
|
|
|
$this->assertDatabaseHas('worker_documents', [
|
|
'worker_id' => $workerId,
|
|
'type' => 'visa',
|
|
'number' => '77003098 / 2019 / 204',
|
|
'expiry_date' => '2019-03-04',
|
|
'ocr_accuracy' => 97.4,
|
|
]);
|
|
|
|
$visaDoc = \App\Models\WorkerDocument::where('worker_id', $workerId)->where('type', 'visa')->first();
|
|
$this->assertNotNull($visaDoc->ocr_data);
|
|
$this->assertEquals('uae_visa', $visaDoc->ocr_data['document_type']);
|
|
$this->assertEquals('Tourist Visa', $visaDoc->ocr_data['visa_type']);
|
|
$this->assertEquals('Mr.MUHAMMAD NADEEM RASHEED', $visaDoc->ocr_data['full_name']);
|
|
$this->assertEquals(97.4, $visaDoc->ocr_accuracy);
|
|
}
|
|
|
|
/**
|
|
* Test worker passport and visa registration when they are passed as JSON strings.
|
|
*/
|
|
public function test_register_passport_and_visa_with_json_encoded_strings()
|
|
{
|
|
$response = $this->postJson('/api/workers/register', [
|
|
'name' => 'JSON String Worker',
|
|
'phone' => '+971509999999',
|
|
'salary' => 3000,
|
|
'password' => 'secret123',
|
|
'language' => 'EN',
|
|
'passport' => json_encode([
|
|
'authority' => 'Authority',
|
|
'date_of_birth' => '14/05/1990',
|
|
'date_of_expiry' => '05/07/2028',
|
|
'date_of_issue' => '05/07/2018',
|
|
'document_type' => 'P',
|
|
'given_names' => 'Johnny',
|
|
'issuing_country' => 'PHL',
|
|
'nationality' => 'PHL',
|
|
'passport_number' => 'Z99B1234',
|
|
'personal_number' => '28',
|
|
'place_of_birth' => 'Manila',
|
|
'sex' => 'M',
|
|
'surname' => 'Test',
|
|
]),
|
|
'visa' => json_encode([
|
|
'accompanied_by' => '0',
|
|
'expiry_date' => '2027-02-15',
|
|
'file_number' => '202/2022/2840234',
|
|
'id_number' => '784198839607840',
|
|
'issue_date' => '2025-02-16',
|
|
'name' => 'Johnny Test',
|
|
'passport_number' => 'Z99B1234',
|
|
'place_of_issue' => 'Abu Dhabi',
|
|
'profession' => 'HELPER',
|
|
'sponsor' => 'Sponsor Name',
|
|
])
|
|
]);
|
|
|
|
$response->assertStatus(201);
|
|
$workerId = $response->json('data.worker.id');
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $workerId,
|
|
'name' => 'Johnny Test',
|
|
'phone' => '+971509999999',
|
|
'verified' => true,
|
|
'visa_status' => 'Tourist Visa',
|
|
]);
|
|
|
|
$this->assertDatabaseHas('worker_documents', [
|
|
'worker_id' => $workerId,
|
|
'type' => 'passport',
|
|
'number' => 'Z99B1234',
|
|
]);
|
|
|
|
$this->assertDatabaseHas('worker_documents', [
|
|
'worker_id' => $workerId,
|
|
'type' => 'visa',
|
|
'number' => '202/2022/2840234',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test updating profile with new API fields.
|
|
*/
|
|
public function test_update_profile_with_new_api_fields()
|
|
{
|
|
$worker = Worker::create([
|
|
'name' => 'Original Worker',
|
|
'email' => 'orig@example.com',
|
|
'phone' => '+971502222223',
|
|
'language' => 'SW',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Kenyan',
|
|
'age' => 22,
|
|
'salary' => 1200,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Christian',
|
|
'bio' => 'New helper',
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => 'bearer-update-token',
|
|
]);
|
|
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer bearer-update-token',
|
|
])->postJson('/api/workers/profile/update', [
|
|
'in_country' => false,
|
|
'visa_status' => null,
|
|
'preferred_job_type' => 'part-time',
|
|
'gender' => 'female',
|
|
'live_in_out' => 'live_in',
|
|
'preferred_location' => 'Abu Dhabi',
|
|
]);
|
|
|
|
$response->assertStatus(200);
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $worker->id,
|
|
'in_country' => false,
|
|
'visa_status' => null,
|
|
'preferred_job_type' => 'part-time',
|
|
'gender' => 'female',
|
|
'live_in_out' => 'live_in',
|
|
'preferred_location' => 'Abu Dhabi',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test nationalities list API with translations.
|
|
*/
|
|
public function test_nationalities_list_api()
|
|
{
|
|
// 1. Default (English)
|
|
$responseEn = $this->getJson('/api/nationalities?per_page=200');
|
|
$responseEn->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
])
|
|
->assertJsonFragment([
|
|
'code' => 'IN',
|
|
'country_code' => 'IN',
|
|
'phone_code' => '+91',
|
|
'dial_code' => '+91',
|
|
'calling_code' => '+91',
|
|
'name' => 'Indian',
|
|
])
|
|
->assertJsonFragment([
|
|
'code' => 'PH',
|
|
'country_code' => 'PH',
|
|
'phone_code' => '+63',
|
|
'dial_code' => '+63',
|
|
'calling_code' => '+63',
|
|
'name' => 'Filipino',
|
|
]);
|
|
|
|
// 2. Hindi
|
|
$responseHi = $this->getJson('/api/nationalities?lang=hindi&per_page=200');
|
|
$responseHi->assertStatus(200)
|
|
->assertJsonFragment([
|
|
'code' => 'IN',
|
|
'name' => 'भारतीय',
|
|
]);
|
|
|
|
// 3. Swahili via header
|
|
$responseSw = $this->withHeaders([
|
|
'Accept-Language' => 'sw',
|
|
])->getJson('/api/nationalities?per_page=200');
|
|
$responseSw->assertStatus(200)
|
|
->assertJsonFragment([
|
|
'code' => 'IN',
|
|
'name' => 'Kihindi',
|
|
]);
|
|
|
|
// 4. Tagalog (taglo)
|
|
$responseTl = $this->getJson('/api/nationalities?lang=taglo&per_page=200');
|
|
$responseTl->assertStatus(200)
|
|
->assertJsonFragment([
|
|
'code' => 'IN',
|
|
'name' => 'Kano/Indian',
|
|
]);
|
|
|
|
// 5. Tamil
|
|
$responseTa = $this->getJson('/api/nationalities?lang=tamil&per_page=200');
|
|
$responseTa->assertStatus(200)
|
|
->assertJsonFragment([
|
|
'code' => 'IN',
|
|
'name' => 'இந்தியன்',
|
|
]);
|
|
|
|
// 6. Test Pagination structure
|
|
$this->flushHeaders();
|
|
$responsePaginated = $this->getJson('/api/nationalities?page=2&per_page=5');
|
|
$responsePaginated->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'data' => [
|
|
'nationalities',
|
|
'pagination' => [
|
|
'total',
|
|
'per_page',
|
|
'current_page',
|
|
'last_page'
|
|
]
|
|
]
|
|
]);
|
|
|
|
$this->assertCount(5, $responsePaginated->json('data.nationalities'));
|
|
$this->assertEquals(2, $responsePaginated->json('data.pagination.current_page'));
|
|
|
|
// 7. Test Search functionality
|
|
$this->flushHeaders();
|
|
$responseSearch = $this->getJson('/api/nationalities?search=Indian');
|
|
$responseSearch->assertStatus(200);
|
|
$this->assertGreaterThanOrEqual(1, count($responseSearch->json('data.nationalities')));
|
|
$responseSearch->assertJsonFragment([
|
|
'code' => 'IN',
|
|
'country_code' => 'IN',
|
|
'phone_code' => '+91',
|
|
'dial_code' => '+91',
|
|
'calling_code' => '+91',
|
|
'name' => 'Indian'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test languages list API with translations.
|
|
*/
|
|
public function test_languages_list_api()
|
|
{
|
|
// 1. Default (English)
|
|
$responseEn = $this->getJson('/api/languages?per_page=200');
|
|
$responseEn->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
])
|
|
->assertJsonFragment([
|
|
'code' => 'en',
|
|
'name' => 'English',
|
|
])
|
|
->assertJsonFragment([
|
|
'code' => 'hi',
|
|
'name' => 'Hindi',
|
|
]);
|
|
|
|
// 2. Hindi
|
|
$responseHi = $this->getJson('/api/languages?lang=hindi&per_page=200');
|
|
$responseHi->assertStatus(200)
|
|
->assertJsonFragment([
|
|
'code' => 'hi',
|
|
'name' => 'हिन्दी',
|
|
]);
|
|
|
|
// 3. Swahili via header
|
|
$responseSw = $this->withHeaders([
|
|
'Accept-Language' => 'sw',
|
|
])->getJson('/api/languages?per_page=200');
|
|
$responseSw->assertStatus(200)
|
|
->assertJsonFragment([
|
|
'code' => 'sw',
|
|
'name' => 'Kiswahili',
|
|
]);
|
|
|
|
// 4. Tagalog (taglo)
|
|
$responseTl = $this->getJson('/api/languages?lang=taglo&per_page=200');
|
|
$responseTl->assertStatus(200)
|
|
->assertJsonFragment([
|
|
'code' => 'tl',
|
|
'name' => 'Tagalog',
|
|
]);
|
|
|
|
// 5. Tamil
|
|
$responseTa = $this->getJson('/api/languages?lang=tamil&per_page=200');
|
|
$responseTa->assertStatus(200)
|
|
->assertJsonFragment([
|
|
'code' => 'ta',
|
|
'name' => 'தமிழ்',
|
|
]);
|
|
|
|
// 6. Test Pagination structure
|
|
$this->flushHeaders();
|
|
$responsePaginated = $this->getJson('/api/languages?page=2&per_page=5');
|
|
$responsePaginated->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'data' => [
|
|
'languages',
|
|
'pagination' => [
|
|
'total',
|
|
'per_page',
|
|
'current_page',
|
|
'last_page'
|
|
]
|
|
]
|
|
]);
|
|
|
|
$this->assertCount(5, $responsePaginated->json('data.languages'));
|
|
$this->assertEquals(2, $responsePaginated->json('data.pagination.current_page'));
|
|
|
|
// 7. Test Search functionality
|
|
$this->flushHeaders();
|
|
$responseSearch = $this->getJson('/api/languages?search=Hindi');
|
|
$responseSearch->assertStatus(200);
|
|
$this->assertGreaterThanOrEqual(1, count($responseSearch->json('data.languages')));
|
|
$responseSearch->assertJsonFragment([
|
|
'code' => 'hi',
|
|
'name' => 'Hindi'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test worker dashboard stats including employer_contacted and profile_viewed unique counts.
|
|
*/
|
|
public function test_worker_dashboard_stats()
|
|
{
|
|
// 1. Create a worker
|
|
$worker = Worker::create([
|
|
'name' => 'Rahul Sharma',
|
|
'email' => 'rahul@example.com',
|
|
'phone' => '+971501234567',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Test',
|
|
'verified' => true,
|
|
'status' => 'active',
|
|
'api_token' => 'worker-test-token-dashboard',
|
|
]);
|
|
|
|
// Create passport document to prevent "Passport Pending" 404
|
|
WorkerDocument::create([
|
|
'worker_id' => $worker->id,
|
|
'type' => 'passport',
|
|
'number' => '123456',
|
|
'file_path' => 'passports/test.pdf',
|
|
]);
|
|
|
|
// 2. Create two employers
|
|
$employer1 = \App\Models\User::create([
|
|
'name' => 'Employer One',
|
|
'email' => 'emp1@example.com',
|
|
'password' => bcrypt('password'),
|
|
'role' => 'employer',
|
|
'api_token' => 'emp1-token',
|
|
]);
|
|
|
|
$employer2 = \App\Models\User::create([
|
|
'name' => 'Employer Two',
|
|
'email' => 'emp2@example.com',
|
|
'password' => bcrypt('password'),
|
|
'role' => 'employer',
|
|
'api_token' => 'emp2-token',
|
|
]);
|
|
|
|
// 3. Employer 1 views worker\'s profile twice (should count as 1)
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer emp1-token',
|
|
])->getJson('/api/employers/workers/' . $worker->id);
|
|
$response->assertStatus(200);
|
|
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer emp1-token',
|
|
])->getJson('/api/employers/workers/' . $worker->id);
|
|
$response->assertStatus(200);
|
|
|
|
// 4. Employer 2 views worker\'s profile once
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer emp2-token',
|
|
])->getJson('/api/employers/workers/' . $worker->id);
|
|
$response->assertStatus(200);
|
|
|
|
// Assert profile_views database has 2 unique views
|
|
$this->assertEquals(2, \App\Models\ProfileView::where('worker_id', $worker->id)->count());
|
|
|
|
// 5. Create a conversation (contact) from Employer 1
|
|
\App\Models\Conversation::create([
|
|
'employer_id' => $employer1->id,
|
|
'worker_id' => $worker->id,
|
|
]);
|
|
|
|
// 6. Hit the worker dashboard API
|
|
$dashboardResponse = $this->withHeaders([
|
|
'Authorization' => 'Bearer worker-test-token-dashboard',
|
|
])->getJson('/api/workers/dashboard');
|
|
|
|
$dashboardResponse->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'data' => [
|
|
'active_status' => 'active',
|
|
'profile_viewed' => 2,
|
|
'employer_contacted' => 1,
|
|
]
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test getting new unviewed announcements and marking them viewed.
|
|
*/
|
|
public function test_worker_new_announcements_flow()
|
|
{
|
|
// 1. Create a worker
|
|
$worker = Worker::create([
|
|
'name' => 'Rahul Sharma',
|
|
'email' => 'rahul@example.com',
|
|
'phone' => '+971501234567',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Test',
|
|
'verified' => true,
|
|
'status' => 'active',
|
|
'api_token' => 'worker-test-token-announcements',
|
|
]);
|
|
|
|
// Create passport document to prevent 404/any checks
|
|
WorkerDocument::create([
|
|
'worker_id' => $worker->id,
|
|
'type' => 'passport',
|
|
'number' => '123456',
|
|
'file_path' => 'passports/test.pdf',
|
|
]);
|
|
|
|
// 2. Create 4 approved announcements
|
|
$ann1 = \App\Models\Announcement::create([
|
|
'title' => 'Event 1',
|
|
'body' => 'Details 1',
|
|
'type' => 'charity',
|
|
'status' => 'approved',
|
|
]);
|
|
|
|
$ann2 = \App\Models\Announcement::create([
|
|
'title' => 'Event 2',
|
|
'body' => 'Details 2',
|
|
'type' => 'charity',
|
|
'status' => 'approved',
|
|
]);
|
|
|
|
$ann3 = \App\Models\Announcement::create([
|
|
'title' => 'Event 3',
|
|
'body' => 'Details 3',
|
|
'type' => 'charity',
|
|
'status' => 'approved',
|
|
]);
|
|
|
|
$ann4 = \App\Models\Announcement::create([
|
|
'title' => 'Event 4',
|
|
'body' => 'Details 4',
|
|
'type' => 'charity',
|
|
'status' => 'approved',
|
|
]);
|
|
|
|
// 3. Get new announcements (should return max 3 events, latest first: Event 4, Event 3, Event 2)
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer worker-test-token-announcements',
|
|
])->getJson('/api/workers/announcements/new');
|
|
|
|
$response->assertStatus(200);
|
|
$data = $response->json('data.announcements');
|
|
$this->assertCount(3, $data);
|
|
$this->assertEquals('Event 4', $data[0]['title']);
|
|
$this->assertEquals('Event 3', $data[1]['title']);
|
|
$this->assertEquals('Event 2', $data[2]['title']);
|
|
|
|
// Assert that they are now marked as viewed in the DB
|
|
$this->assertDatabaseHas('announcement_views', ['worker_id' => $worker->id, 'announcement_id' => $ann4->id]);
|
|
$this->assertDatabaseHas('announcement_views', ['worker_id' => $worker->id, 'announcement_id' => $ann3->id]);
|
|
$this->assertDatabaseHas('announcement_views', ['worker_id' => $worker->id, 'announcement_id' => $ann2->id]);
|
|
|
|
// 4. Get new announcements again (should return the remaining 1: Event 1)
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer worker-test-token-announcements',
|
|
])->getJson('/api/workers/announcements/new');
|
|
|
|
$response->assertStatus(200);
|
|
$data = $response->json('data.announcements');
|
|
$this->assertCount(1, $data);
|
|
$this->assertEquals('Event 1', $data[0]['title']);
|
|
|
|
// 5. Get new announcements again (should return 0)
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer worker-test-token-announcements',
|
|
])->getJson('/api/workers/announcements/new');
|
|
|
|
$response->assertStatus(200);
|
|
$this->assertCount(0, $response->json('data.announcements'));
|
|
|
|
// 6. Create a 5th approved announcement
|
|
$ann5 = \App\Models\Announcement::create([
|
|
'title' => 'Event 5',
|
|
'body' => 'Details 5',
|
|
'type' => 'charity',
|
|
'status' => 'approved',
|
|
]);
|
|
|
|
// 7. Manually mark Event 5 as viewed
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer worker-test-token-announcements',
|
|
])->postJson("/api/workers/announcements/{$ann5->id}/view");
|
|
$response->assertStatus(200);
|
|
|
|
// 8. Get new announcements (should return 0 because Event 5 is already viewed)
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer worker-test-token-announcements',
|
|
])->getJson('/api/workers/announcements/new');
|
|
|
|
$response->assertStatus(200);
|
|
$this->assertCount(0, $response->json('data.announcements'));
|
|
}
|
|
|
|
/**
|
|
* Test localized preferred locations endpoint.
|
|
*/
|
|
public function test_get_preferred_locations_localized()
|
|
{
|
|
// 1. Get English locations
|
|
$response = $this->getJson('/api/preferred-locations?lang=en');
|
|
$response->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'data' => [
|
|
'preferred_locations' => [
|
|
['code' => 'dubai', 'name' => 'Dubai'],
|
|
['code' => 'abu dhabi', 'name' => 'Abu Dhabi'],
|
|
['code' => 'oman', 'name' => 'Oman'],
|
|
]
|
|
]
|
|
]);
|
|
|
|
// 2. Get Hindi locations
|
|
$response = $this->getJson('/api/preferred-locations?lang=hi');
|
|
$response->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'data' => [
|
|
'preferred_locations' => [
|
|
['code' => 'dubai', 'name' => 'दुबई'],
|
|
['code' => 'abu dhabi', 'name' => 'अबू धाबी'],
|
|
['code' => 'oman', 'name' => 'ओमान'],
|
|
]
|
|
]
|
|
]);
|
|
|
|
// 3. Test pagination
|
|
$response = $this->getJson('/api/preferred-locations?per_page=1&page=2');
|
|
$response->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'data' => [
|
|
'preferred_locations' => [
|
|
['code' => 'abu dhabi', 'name' => 'Abu Dhabi'],
|
|
],
|
|
'pagination' => [
|
|
'total' => 3,
|
|
'per_page' => 1,
|
|
'current_page' => 2,
|
|
'last_page' => 3
|
|
]
|
|
]
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test localized preferred location areas endpoint.
|
|
*/
|
|
public function test_get_preferred_location_areas_localized()
|
|
{
|
|
// 1. Get English areas for Dubai (using path parameter)
|
|
$response = $this->getJson('/api/preferred-locations/dubai/areas?lang=en');
|
|
$response->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'data' => [
|
|
'location' => 'dubai',
|
|
]
|
|
]);
|
|
$this->assertGreaterThan(5, count($response->json('data.areas')));
|
|
$this->assertEquals('Dubai Marina', $response->json('data.areas.1.name'));
|
|
|
|
// 2. Get Hindi areas for Dubai (using query parameter)
|
|
$response = $this->getJson('/api/preferred-locations/areas?city=dubai&lang=hi');
|
|
$response->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'data' => [
|
|
'location' => 'dubai',
|
|
]
|
|
]);
|
|
$this->assertEquals('दुबई मरीना', $response->json('data.areas.1.name'));
|
|
|
|
// 3. Test pagination
|
|
$response = $this->getJson('/api/preferred-locations/oman/areas?per_page=2&page=2');
|
|
$response->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'data' => [
|
|
'location' => 'oman',
|
|
'pagination' => [
|
|
'total' => 8,
|
|
'per_page' => 2,
|
|
'current_page' => 2,
|
|
'last_page' => 4
|
|
]
|
|
]
|
|
]);
|
|
$this->assertCount(2, $response->json('data.areas'));
|
|
|
|
// 4. Test not found location
|
|
$response = $this->getJson('/api/preferred-locations/sharjah/areas');
|
|
$response->assertStatus(404)
|
|
->assertJson([
|
|
'success' => false,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test worker registration fails validation if the phone number already exists.
|
|
*/
|
|
public function test_register_validation_fails_if_phone_number_already_exists()
|
|
{
|
|
// 1. Create a worker with a specific phone number
|
|
Worker::create([
|
|
'name' => 'Existing Worker',
|
|
'email' => 'existing@example.com',
|
|
'phone' => '+971501234567',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'status' => 'active',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Test worker',
|
|
]);
|
|
|
|
// 2. Attempt to register another worker with the same phone number
|
|
$response = $this->postJson('/api/workers/register', [
|
|
'name' => 'New Worker',
|
|
'phone' => '+971501234567',
|
|
'salary' => 2000,
|
|
'password' => 'secret123',
|
|
'language' => 'HI',
|
|
]);
|
|
|
|
$response->assertStatus(422)
|
|
->assertJson([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
])
|
|
->assertJsonValidationErrors(['phone']);
|
|
|
|
$this->assertEquals(
|
|
'This mobile number is already registered.',
|
|
$response->json('errors.phone.0')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test worker registration fails validation if the passport number already exists.
|
|
*/
|
|
public function test_register_validation_fails_if_passport_number_already_exists()
|
|
{
|
|
// 1. Create a worker and their passport document
|
|
$worker = Worker::create([
|
|
'name' => 'First Worker',
|
|
'email' => 'first@example.com',
|
|
'phone' => '+971501111111',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'status' => 'active',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Test worker',
|
|
]);
|
|
|
|
$worker->documents()->create([
|
|
'type' => 'passport',
|
|
'number' => 'SQ1234567',
|
|
]);
|
|
|
|
// 2. Attempt to register another worker with the same passport number
|
|
$response = $this->postJson('/api/workers/register', [
|
|
'name' => 'Second Worker',
|
|
'phone' => '+971502222222',
|
|
'salary' => 2000,
|
|
'password' => 'secret123',
|
|
'language' => 'HI',
|
|
'passport' => [
|
|
'passport_number' => 'SQ1234567',
|
|
]
|
|
]);
|
|
|
|
$response->assertStatus(422)
|
|
->assertJson([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
])
|
|
->assertJsonValidationErrors(['passport.passport_number']);
|
|
|
|
$this->assertEquals(
|
|
'This passport number is already registered.',
|
|
$response->json('errors')['passport.passport_number'][0]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test profile update fails if updating a passport document with a different passport number.
|
|
*/
|
|
public function test_update_profile_fails_if_passport_number_does_not_match_existing()
|
|
{
|
|
$worker = Worker::create([
|
|
'name' => 'Test Worker',
|
|
'email' => 'test@example.com',
|
|
'phone' => '+971503333333',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'New helper',
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => 'bearer-test-token-passport-mismatch',
|
|
]);
|
|
|
|
$worker->documents()->create([
|
|
'type' => 'passport',
|
|
'number' => 'ABC123456',
|
|
'issue_date' => '2020-01-01',
|
|
'expiry_date' => '2030-01-01',
|
|
'ocr_accuracy' => 95.0,
|
|
]);
|
|
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer bearer-test-token-passport-mismatch',
|
|
])->postJson('/api/workers/profile/update', [
|
|
'passport' => [
|
|
'passport_number' => 'XYZ987654', // Different passport number
|
|
]
|
|
]);
|
|
|
|
$response->assertStatus(422)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'errors' => [
|
|
'passport.passport_number'
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals(
|
|
'The passport number cannot be changed. It must match the previously verified passport number.',
|
|
$response->json('errors')['passport.passport_number'][0]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test profile update succeeds if passport number matches existing.
|
|
*/
|
|
public function test_update_profile_succeeds_if_passport_number_matches_existing()
|
|
{
|
|
$worker = Worker::create([
|
|
'name' => 'Test Worker',
|
|
'email' => 'test2@example.com',
|
|
'phone' => '+971503333334',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'New helper',
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => 'bearer-test-token-passport-match',
|
|
]);
|
|
|
|
$worker->documents()->create([
|
|
'type' => 'passport',
|
|
'number' => 'ABC123456',
|
|
'issue_date' => '2020-01-01',
|
|
'expiry_date' => '2030-01-01',
|
|
'ocr_accuracy' => 95.0,
|
|
]);
|
|
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer bearer-test-token-passport-match',
|
|
])->postJson('/api/workers/profile/update', [
|
|
'passport' => [
|
|
'passport_number' => 'ABC123456', // Same passport number
|
|
'date_of_expiry' => '2032-01-01', // Updating expiry date
|
|
]
|
|
]);
|
|
|
|
$response->assertStatus(200);
|
|
|
|
$this->assertDatabaseHas('worker_documents', [
|
|
'worker_id' => $worker->id,
|
|
'type' => 'passport',
|
|
'number' => 'ABC123456',
|
|
'expiry_date' => '2032-01-01',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test unique mobile number validation on profile update.
|
|
*/
|
|
public function test_update_profile_fails_if_phone_already_exists()
|
|
{
|
|
// 1. Create original worker with phone +971504444444
|
|
Worker::create([
|
|
'name' => 'Other Worker',
|
|
'email' => 'other@example.com',
|
|
'phone' => '+971504444444',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'New helper',
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
]);
|
|
|
|
// 2. Create current worker with phone +971505555555
|
|
$worker = Worker::create([
|
|
'name' => 'Current Worker',
|
|
'email' => 'current@example.com',
|
|
'phone' => '+971505555555',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'New helper',
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => 'bearer-test-token-phone-unique',
|
|
]);
|
|
|
|
// 3. Attempt to update current worker's phone to the other worker's phone
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer bearer-test-token-phone-unique',
|
|
])->postJson('/api/workers/profile/update', [
|
|
'phone' => '+971504444444',
|
|
]);
|
|
|
|
$response->assertStatus(422)
|
|
->assertJsonValidationErrors(['phone']);
|
|
|
|
$this->assertEquals(
|
|
'This mobile number is already registered.',
|
|
$response->json('errors')['phone'][0]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test deprecated fields are hidden from JSON response.
|
|
*/
|
|
public function test_profile_response_does_not_contain_deprecated_fields()
|
|
{
|
|
$worker = Worker::create([
|
|
'name' => 'Test Worker',
|
|
'email' => 'test_dep@example.com',
|
|
'phone' => '+971503333335',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Indian',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Christian',
|
|
'bio' => 'Some Bio',
|
|
'country' => 'UAE',
|
|
'city' => 'Dubai',
|
|
'area' => 'JLT',
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => 'bearer-test-token-deprecated-fields',
|
|
]);
|
|
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer bearer-test-token-deprecated-fields',
|
|
])->getJson('/api/workers/profile');
|
|
|
|
$response->assertStatus(200);
|
|
$json = $response->json('data.worker');
|
|
|
|
$this->assertArrayNotHasKey('email', $json);
|
|
$this->assertArrayNotHasKey('religion', $json);
|
|
$this->assertArrayNotHasKey('availability', $json);
|
|
$this->assertArrayNotHasKey('bio', $json);
|
|
$this->assertArrayNotHasKey('country', $json);
|
|
$this->assertArrayNotHasKey('city', $json);
|
|
$this->assertArrayNotHasKey('area', $json);
|
|
}
|
|
|
|
/**
|
|
* Test worker can change password successfully.
|
|
*/
|
|
public function test_worker_can_change_password()
|
|
{
|
|
$worker = Worker::create([
|
|
'name' => 'Password Changer',
|
|
'email' => 'changer@example.com',
|
|
'phone' => '+971509990099',
|
|
'language' => 'HI',
|
|
'password' => bcrypt('OldPassword@123'),
|
|
'nationality' => 'Indian',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Christian',
|
|
'bio' => 'Some Bio',
|
|
'api_token' => 'changer-token',
|
|
]);
|
|
|
|
// 1. Invalid current password
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer changer-token',
|
|
])->postJson('/api/workers/change-password', [
|
|
'current_password' => 'WrongPassword',
|
|
'new_password' => 'NewPassword@123',
|
|
'new_password_confirmation' => 'NewPassword@123',
|
|
]);
|
|
|
|
$response->assertStatus(422)
|
|
->assertJsonValidationErrors(['current_password']);
|
|
|
|
// 2. Successful change
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer changer-token',
|
|
])->postJson('/api/workers/change-password', [
|
|
'current_password' => 'OldPassword@123',
|
|
'new_password' => 'NewPassword@123',
|
|
'new_password_confirmation' => 'NewPassword@123',
|
|
]);
|
|
|
|
$response->assertStatus(200)
|
|
->assertJson([
|
|
'success' => true,
|
|
'message' => 'Password changed successfully.'
|
|
]);
|
|
|
|
$this->assertTrue(\Illuminate\Support\Facades\Hash::check('NewPassword@123', $worker->fresh()->password));
|
|
}
|
|
}
|