migrant-web/tests/Feature/EmployerMessageApiTest.php

359 lines
11 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
$uploadResponse = $this->postJson('/api/employers/upload-emirates-id', [
'email' => 'alice@example.com',
'emirates_id_file' => $fakeIdFile,
]);
$uploadResponse->assertStatus(200)
->assertJsonStructure([
'success',
'message',
'ocr_extracted_data' => [
'emirates_id_number',
'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 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!';
});
}
}