591 lines
19 KiB
PHP
591 lines
19 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Worker;
|
|
use App\Models\WorkerCategory;
|
|
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 $category;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
// Create standard General Helper category
|
|
\Illuminate\Support\Facades\DB::table('worker_categories')->insert([
|
|
'id' => 7,
|
|
'name' => 'General Helper',
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
$this->category = (object) ['id' => 7];
|
|
}
|
|
|
|
/**
|
|
* 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: 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' => 'India',
|
|
'language' => 'HI',
|
|
'gender' => 'male',
|
|
'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',
|
|
'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' => 'Kenya',
|
|
'age' => 22,
|
|
'salary' => 1200,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Christian',
|
|
'bio' => 'New helper',
|
|
'category_id' => $this->category->id,
|
|
'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,
|
|
'bio' => 'Extremely experienced in Arabic and Western cooking.',
|
|
]);
|
|
|
|
$response->assertStatus(200);
|
|
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $worker->id,
|
|
'experience' => '3 Years Cooking & Childcare',
|
|
'salary' => 1800,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test S2 Profile: Upload Emirates ID & PDPL Security Purge & Verified Badge Awarded.
|
|
*/
|
|
public function test_s2_upload_emirates_id_and_pdpl_secure_purge()
|
|
{
|
|
Storage::fake('local');
|
|
|
|
$worker = Worker::create([
|
|
'name' => 'Juan Dela Cruz',
|
|
'email' => 'juan@example.com',
|
|
'phone' => '+971507777777',
|
|
'language' => 'TL',
|
|
'password' => bcrypt('password'),
|
|
'nationality' => 'Philippines',
|
|
'age' => 28,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Looking for helper job',
|
|
'category_id' => $this->category->id,
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => 'test-bearer-token-456',
|
|
]);
|
|
|
|
$fakeIdFile = UploadedFile::fake()->create('emirates_id.jpg', 800, 'image/jpeg');
|
|
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer test-bearer-token-456',
|
|
])->postJson('/api/workers/profile/upload-emirates-id', [
|
|
'emirates_id_file' => $fakeIdFile,
|
|
]);
|
|
|
|
$response->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'ocr_extracted_data' => [
|
|
'name',
|
|
'emirates_id_number',
|
|
'expiry_date',
|
|
],
|
|
'data' => [
|
|
'worker',
|
|
'document',
|
|
]
|
|
]);
|
|
|
|
// Asserts database states
|
|
$this->assertDatabaseHas('workers', [
|
|
'id' => $worker->id,
|
|
'verified' => true, // Badge awarded!
|
|
]);
|
|
|
|
$this->assertDatabaseHas('worker_documents', [
|
|
'worker_id' => $worker->id,
|
|
'type' => 'emirates_id',
|
|
'file_path' => '[DELETED_FOR_PDPL_COMPLIANCE]', // Image file removed for PDPL
|
|
]);
|
|
|
|
// Ensure physical file is deleted and not stored in storage disk
|
|
Storage::disk('local')->assertDirectoryEmpty('temp/documents');
|
|
}
|
|
|
|
/**
|
|
* 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 Lanka',
|
|
'age' => 32,
|
|
'salary' => 1400,
|
|
'availability' => 'Not Available',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Ready to work',
|
|
'category_id' => $this->category->id,
|
|
'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' => 'India',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Test',
|
|
'category_id' => $this->category->id,
|
|
'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' => 'India',
|
|
'age' => 25,
|
|
'salary' => 1500,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Test',
|
|
'category_id' => $this->category->id,
|
|
'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' => 'Kenya',
|
|
'age' => 30,
|
|
'experience' => '5 Years',
|
|
'in_country' => 'true',
|
|
'visa_status' => 'Tourist Visa',
|
|
'preferred_job_type' => 'full-time',
|
|
'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',
|
|
'gender' => 'male',
|
|
'live_in_out' => 'live_out',
|
|
'preferred_location' => 'Dubai Marina',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 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' => 'Kenya',
|
|
'age' => 22,
|
|
'salary' => 1200,
|
|
'availability' => 'Immediate',
|
|
'experience' => 'None',
|
|
'religion' => 'Christian',
|
|
'bio' => 'New helper',
|
|
'category_id' => $this->category->id,
|
|
'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',
|
|
'country' => 'UAE',
|
|
'city' => 'Abu Dhabi',
|
|
'area' => 'Khalifa City',
|
|
]);
|
|
|
|
$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',
|
|
'country' => 'UAE',
|
|
'city' => 'Abu Dhabi',
|
|
'area' => 'Khalifa City',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 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',
|
|
'name' => 'Indian',
|
|
])
|
|
->assertJsonFragment([
|
|
'code' => 'PH',
|
|
'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',
|
|
'name' => 'Indian'
|
|
]);
|
|
}
|
|
}
|