433 lines
14 KiB
PHP
433 lines
14 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Conversation;
|
|
use App\Models\Message;
|
|
use App\Models\User;
|
|
use App\Models\EmployerProfile;
|
|
use App\Models\Worker;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class EmployerMessageApiTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected $worker;
|
|
protected $employer;
|
|
protected $token;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
|
|
// Create a test worker
|
|
$this->worker = Worker::create([
|
|
'name' => 'Jane Smith',
|
|
'email' => 'jane@example.com',
|
|
'phone' => '+971500000001',
|
|
'password' => bcrypt('password'),
|
|
'status' => 'Available',
|
|
'nationality' => 'Ethiopian',
|
|
'age' => 28,
|
|
'salary' => 1500.00,
|
|
'availability' => 'Immediate',
|
|
'experience' => '2 Years',
|
|
'religion' => 'Christian',
|
|
'bio' => 'Experienced housemaid seeking employment.',
|
|
]);
|
|
|
|
// Create an employer user with token
|
|
$this->token = 'employer-test-token-12345';
|
|
$this->employer = User::create([
|
|
'name' => 'John Employer',
|
|
'email' => 'employer_test@example.com',
|
|
'password' => bcrypt('password'),
|
|
'role' => 'employer',
|
|
'api_token' => $this->token,
|
|
]);
|
|
|
|
// Create employer profile
|
|
EmployerProfile::create([
|
|
'user_id' => $this->employer->id,
|
|
'company_name' => 'John Ltd',
|
|
'phone' => '+971500000002',
|
|
'country' => 'United Arab Emirates',
|
|
'verification_status' => 'approved',
|
|
'language' => 'English',
|
|
'notifications' => true,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test employer can login via API.
|
|
*/
|
|
public function test_employer_can_login()
|
|
{
|
|
$response = $this->postJson('/api/employers/login', [
|
|
'email' => 'employer_test@example.com',
|
|
'password' => 'password',
|
|
]);
|
|
|
|
$response->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'data' => [
|
|
'employer',
|
|
'token'
|
|
]
|
|
]);
|
|
|
|
$this->assertNotNull($response->json('data.token'));
|
|
}
|
|
|
|
/**
|
|
* Test employer can register via API.
|
|
*/
|
|
public function test_employer_can_register()
|
|
{
|
|
\Illuminate\Support\Facades\Storage::fake('local');
|
|
$fakeIdFile = \Illuminate\Http\UploadedFile::fake()->create('emirates_id.jpg', 800, 'image/jpeg');
|
|
|
|
// Step 1: Register
|
|
$response = $this->postJson('/api/employers/register', [
|
|
'company_name' => 'New Company Corp',
|
|
'name' => 'Alice Employer',
|
|
'email' => 'alice@example.com',
|
|
'phone' => '+971500000003',
|
|
'password' => 'Password@123',
|
|
'password_confirmation' => 'Password@123',
|
|
]);
|
|
|
|
$response->assertStatus(201)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'email'
|
|
]);
|
|
|
|
$this->assertDatabaseHas('users', [
|
|
'email' => 'alice@example.com',
|
|
'role' => 'employer',
|
|
]);
|
|
|
|
$this->assertDatabaseHas('employer_profiles', [
|
|
'company_name' => 'Alice Employer Household',
|
|
]);
|
|
|
|
// Step 2: Upload Emirates ID Front
|
|
$uploadFrontResponse = $this->postJson('/api/employers/register/emirates-front', [
|
|
'email' => 'alice@example.com',
|
|
'emirates_id_front' => $fakeIdFile,
|
|
]);
|
|
|
|
$uploadFrontResponse->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'ocr_extracted_data' => [
|
|
'emirates_id_number',
|
|
],
|
|
'email'
|
|
]);
|
|
|
|
// Step 3: Upload Emirates ID Back
|
|
$uploadBackResponse = $this->postJson('/api/employers/register/emirates-back', [
|
|
'email' => 'alice@example.com',
|
|
'emirates_id_back' => $fakeIdFile,
|
|
]);
|
|
|
|
$uploadBackResponse->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'ocr_extracted_data' => [
|
|
'expiry_date',
|
|
],
|
|
'email'
|
|
]);
|
|
|
|
$user = User::where('email', 'alice@example.com')->first();
|
|
$profile = EmployerProfile::where('user_id', $user->id)->first();
|
|
|
|
$this->assertNull($profile->emirates_id_front);
|
|
$this->assertNotNull($profile->emirates_id_number);
|
|
$this->assertNotNull($profile->emirates_id_expiry);
|
|
}
|
|
|
|
/**
|
|
* Test employer can upload front and back side Emirates ID separately via distinct API endpoints.
|
|
*/
|
|
public function test_employer_can_upload_separate_emirates_id_front_and_back()
|
|
{
|
|
\Illuminate\Support\Facades\Storage::fake('local');
|
|
$frontFile = \Illuminate\Http\UploadedFile::fake()->create('front.jpg', 500, 'image/jpeg');
|
|
$backFile = \Illuminate\Http\UploadedFile::fake()->create('back.jpg', 500, 'image/jpeg');
|
|
|
|
// Create an employer user to upload for
|
|
$user = User::create([
|
|
'name' => 'Charlie Employer',
|
|
'email' => 'charlie@example.com',
|
|
'password' => bcrypt('password'),
|
|
'role' => 'employer',
|
|
]);
|
|
|
|
// Upload Front
|
|
$responseFront = $this->postJson('/api/employers/register/emirates-front', [
|
|
'email' => 'charlie@example.com',
|
|
'emirates_id_front' => $frontFile,
|
|
]);
|
|
|
|
$responseFront->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'ocr_extracted_data' => [
|
|
'emirates_id_number',
|
|
],
|
|
'email'
|
|
]);
|
|
|
|
$this->assertEquals('784-1988-5310327-2', $responseFront->json('ocr_extracted_data.emirates_id_number'));
|
|
|
|
// Upload Back
|
|
$responseBack = $this->postJson('/api/employers/register/emirates-back', [
|
|
'email' => 'charlie@example.com',
|
|
'emirates_id_back' => $backFile,
|
|
]);
|
|
|
|
$responseBack->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'ocr_extracted_data' => [
|
|
'expiry_date',
|
|
],
|
|
'email'
|
|
]);
|
|
|
|
$this->assertEquals('2028-04-11', $responseBack->json('ocr_extracted_data.expiry_date'));
|
|
|
|
$profile = EmployerProfile::where('user_id', $user->id)->first();
|
|
$this->assertNotNull($profile);
|
|
$this->assertEquals('784-1988-5310327-2', $profile->emirates_id_number);
|
|
$this->assertEquals('2028-04-11', $profile->emirates_id_expiry);
|
|
}
|
|
|
|
/**
|
|
* Test retrieving conversation list for an employer.
|
|
*/
|
|
public function test_employer_can_get_conversations()
|
|
{
|
|
// Create conversation
|
|
$conversation = Conversation::create([
|
|
'employer_id' => $this->employer->id,
|
|
'worker_id' => $this->worker->id,
|
|
]);
|
|
|
|
// Create a message from worker
|
|
Message::create([
|
|
'conversation_id' => $conversation->id,
|
|
'sender_type' => 'worker',
|
|
'sender_id' => $this->worker->id,
|
|
'text' => 'Hello from candidate!',
|
|
]);
|
|
|
|
// Request API
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer ' . $this->token,
|
|
])->getJson('/api/employers/conversations');
|
|
|
|
$response->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'data' => [
|
|
'conversations' => [
|
|
'*' => [
|
|
'id',
|
|
'worker_id',
|
|
'worker_name',
|
|
'nationality',
|
|
'salary',
|
|
'last_message',
|
|
'unread_count',
|
|
'sent_at',
|
|
'updated_at',
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals('Hello from candidate!', $response->json('data.conversations.0.last_message'));
|
|
$this->assertEquals(1, $response->json('data.conversations.0.unread_count'));
|
|
}
|
|
|
|
/**
|
|
* Test retrieving messages in a conversation and marking them as read.
|
|
*/
|
|
public function test_employer_can_get_messages_and_mark_as_read()
|
|
{
|
|
// Create conversation
|
|
$conversation = Conversation::create([
|
|
'employer_id' => $this->employer->id,
|
|
'worker_id' => $this->worker->id,
|
|
]);
|
|
|
|
// Create message
|
|
$message = Message::create([
|
|
'conversation_id' => $conversation->id,
|
|
'sender_type' => 'worker',
|
|
'sender_id' => $this->worker->id,
|
|
'text' => 'Unread message from candidate',
|
|
'read_at' => null,
|
|
]);
|
|
|
|
// Request API
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer ' . $this->token,
|
|
])->getJson('/api/employers/conversations/' . $conversation->id . '/messages');
|
|
|
|
$response->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'data' => [
|
|
'conversation' => [
|
|
'id',
|
|
'worker_name',
|
|
'nationality',
|
|
'salary',
|
|
],
|
|
'messages' => [
|
|
'*' => [
|
|
'id',
|
|
'sender_type',
|
|
'text',
|
|
'time',
|
|
'read_at',
|
|
'created_at',
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
|
|
// Verify message marked as read in database
|
|
$this->assertNotNull($message->fresh()->read_at);
|
|
}
|
|
|
|
/**
|
|
* Test employer can send reply.
|
|
*/
|
|
public function test_employer_can_send_reply()
|
|
{
|
|
// Create conversation
|
|
$conversation = Conversation::create([
|
|
'employer_id' => $this->employer->id,
|
|
'worker_id' => $this->worker->id,
|
|
]);
|
|
|
|
// Reply request
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer ' . $this->token,
|
|
])->postJson('/api/employers/conversations/' . $conversation->id . '/messages', [
|
|
'text' => 'Hello Candidate, when are you free for an interview?'
|
|
]);
|
|
|
|
$response->assertStatus(201)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'data' => [
|
|
'message' => [
|
|
'id',
|
|
'sender_type',
|
|
'text',
|
|
'time',
|
|
'created_at',
|
|
]
|
|
]
|
|
]);
|
|
|
|
// Verify message created in database
|
|
$this->assertDatabaseHas('messages', [
|
|
'conversation_id' => $conversation->id,
|
|
'sender_type' => 'employer',
|
|
'sender_id' => $this->employer->id,
|
|
'text' => 'Hello Candidate, when are you free for an interview?',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test employer can start conversation.
|
|
*/
|
|
public function test_employer_can_start_conversation()
|
|
{
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer ' . $this->token,
|
|
])->postJson('/api/employers/conversations/start', [
|
|
'worker_id' => $this->worker->id,
|
|
]);
|
|
|
|
$response->assertStatus(200)
|
|
->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'data' => [
|
|
'conversation_id'
|
|
]
|
|
]);
|
|
|
|
$this->assertDatabaseHas('conversations', [
|
|
'employer_id' => $this->employer->id,
|
|
'worker_id' => $this->worker->id,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test request fails without authorization token.
|
|
*/
|
|
public function test_unauthenticated_requests_are_blocked()
|
|
{
|
|
$response = $this->getJson('/api/employers/conversations');
|
|
$response->assertStatus(401);
|
|
}
|
|
|
|
/**
|
|
* Test employer message triggers push notification.
|
|
*/
|
|
public function test_employer_message_triggers_push_notification()
|
|
{
|
|
\Illuminate\Support\Facades\Http::fake([
|
|
'https://oauth2.googleapis.com/token' => \Illuminate\Support\Facades\Http::response(['access_token' => 'mocked-fcm-token'], 200),
|
|
'https://fcm.googleapis.com/*' => \Illuminate\Support\Facades\Http::response(['name' => 'projects/migrant-fe5a7/messages/mock-id'], 200),
|
|
]);
|
|
|
|
// Set fcm_token on worker
|
|
$this->worker->update(['fcm_token' => 'test-device-fcm-token-xyz']);
|
|
|
|
// Create conversation
|
|
$conversation = Conversation::create([
|
|
'employer_id' => $this->employer->id,
|
|
'worker_id' => $this->worker->id,
|
|
]);
|
|
|
|
// Reply request
|
|
$response = $this->withHeaders([
|
|
'Authorization' => 'Bearer ' . $this->token,
|
|
])->postJson('/api/employers/conversations/' . $conversation->id . '/messages', [
|
|
'text' => 'Testing push notifications!'
|
|
]);
|
|
|
|
$response->assertStatus(201);
|
|
|
|
// Assert Http call was made to FCM endpoint
|
|
\Illuminate\Support\Facades\Http::assertSent(function ($request) {
|
|
return str_contains($request->url(), 'fcm.googleapis.com') &&
|
|
$request['message']['token'] === 'test-device-fcm-token-xyz' &&
|
|
$request['message']['notification']['body'] === 'Testing push notifications!';
|
|
});
|
|
}
|
|
}
|