migrant-web/tests/Feature/NewRemindersAndSettingsTest.php

370 lines
13 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\User;
use App\Models\Worker;
use App\Models\JobPost;
use App\Models\JobApplication;
use App\Models\JobOffer;
use App\Models\Conversation;
use App\Models\Message;
use App\Models\Review;
use App\Models\EmployerReview;
use App\Notifications\WorkerNoResponseNotification;
use App\Notifications\WorkerReviewReminderNotification;
use App\Notifications\EmployerReviewReminderNotification;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Artisan;
use Tests\TestCase;
class NewRemindersAndSettingsTest extends TestCase
{
use RefreshDatabase;
private function createTestWorker(array $overrides = []): Worker
{
return Worker::create(array_merge([
'name' => 'John Worker',
'email' => 'worker@example.com',
'password' => bcrypt('password'),
'phone' => '971500000001',
'nationality' => 'Indian',
'status' => 'active',
'api_token' => 'worker_api_token',
'age' => 25,
'salary' => 2000,
'availability' => 'Immediate',
'experience' => '2 Years',
'religion' => 'Christian',
'bio' => 'Helper info',
'passport_status' => 'valid',
], $overrides));
}
private function createTestEmployer(array $overrides = []): User
{
return User::create(array_merge([
'name' => 'Alice Employer',
'email' => 'employer@example.com',
'password' => bcrypt('password'),
'role' => 'employer',
'api_token' => 'employer_api_token',
'subscription_status' => 'active',
], $overrides));
}
public function test_worker_no_response_reminders()
{
Notification::fake();
$worker = $this->createTestWorker();
$employer = $this->createTestEmployer();
// 1. Create a pending direct Job Offer created exactly 14 days ago
$offer = JobOffer::create([
'employer_id' => $employer->id,
'worker_id' => $worker->id,
'work_date' => now()->addDays(5)->toDateString(),
'location' => 'Dubai Marina',
'salary' => 3000,
'status' => 'pending',
]);
// Force created_at to 14 days ago
$offer->created_at = now()->subDays(14);
$offer->save();
// 2. Create a conversation where the last message is from employer, sent 14 days ago
$conversation = Conversation::create([
'employer_id' => $employer->id,
'worker_id' => $worker->id,
]);
$message = Message::create([
'conversation_id' => $conversation->id,
'sender_type' => 'App\Models\User',
'sender_id' => $employer->id,
'text' => 'Hello worker, are you interested?',
]);
$message->created_at = now()->subDays(14);
$message->save();
// Run scheduler
Artisan::call('app:send-reminders');
// Assert Worker got WorkerNoResponseNotification for both
Notification::assertSentTo(
$worker,
WorkerNoResponseNotification::class,
function ($notification) use ($offer) {
return $notification->type === 'worker_no_response_offer' &&
$notification->daysElapsed === 14 &&
$notification->entityId === $offer->id;
}
);
Notification::assertSentTo(
$worker,
WorkerNoResponseNotification::class,
function ($notification) use ($message) {
return $notification->type === 'worker_no_response_chat' &&
$notification->daysElapsed === 14 &&
$notification->entityId === $message->id;
}
);
// Assert reminder logs were created
$this->assertDatabaseHas('reminder_logs', [
'user_type' => get_class($worker),
'user_id' => $worker->id,
'event_type' => 'worker_no_response_offer',
'reminder_level' => '14_days',
]);
$this->assertDatabaseHas('reminder_logs', [
'user_type' => get_class($worker),
'user_id' => $worker->id,
'event_type' => 'worker_no_response_chat',
'reminder_level' => '14_days',
]);
// Run again to verify duplicate prevention
Notification::fake();
Artisan::call('app:send-reminders');
Notification::assertNothingSent();
}
public function test_review_reminders_after_joining_date()
{
Notification::fake();
$worker = $this->createTestWorker();
$employer = $this->createTestEmployer();
$job = JobPost::create([
'employer_id' => $employer->id,
'title' => 'Nanny',
'location' => 'Dubai',
'salary' => 2500,
'workers_needed' => 1,
'job_type' => 'Full Time',
'start_date' => now()->subMonths(4)->toDateString(),
'description' => 'Test job',
'status' => 'active',
]);
// JobApplication with joining_confirmed_at exactly 3 months ago
$app = JobApplication::create([
'job_id' => $job->id,
'worker_id' => $worker->id,
'status' => 'hired',
'joining_confirmed_at' => now()->subMonths(3)->toDateString(),
]);
// Run scheduler
Artisan::call('app:send-reminders');
// Assert Worker got review reminder for employer
Notification::assertSentTo(
$worker,
WorkerReviewReminderNotification::class,
function ($notification) use ($app) {
return $notification->employerName === 'Alice Employer' &&
$notification->applicationId === $app->id &&
$notification->reviewLevel === '3_months';
}
);
// Assert Employer got review reminder for worker
Notification::assertSentTo(
$employer,
EmployerReviewReminderNotification::class,
function ($notification) use ($app) {
return $notification->workerName === 'John Worker' &&
$notification->applicationId === $app->id &&
$notification->reviewLevel === '3_months';
}
);
// Assert reminder logs were created
$this->assertDatabaseHas('reminder_logs', [
'user_type' => get_class($worker),
'user_id' => $worker->id,
'event_type' => 'review_employer_reminder',
'reminder_level' => '3_months',
]);
$this->assertDatabaseHas('reminder_logs', [
'user_type' => get_class($employer),
'user_id' => $employer->id,
'event_type' => 'review_worker_reminder',
'reminder_level' => '3_months',
]);
// Run again to verify duplicate prevention
Notification::fake();
Artisan::call('app:send-reminders');
Notification::assertNothingSent();
}
public function test_worker_review_controller_eligibility_and_edit_window()
{
$worker = $this->createTestWorker();
$employer = $this->createTestEmployer();
$job = JobPost::create([
'employer_id' => $employer->id,
'title' => 'Test Job',
'location' => 'Dubai',
'salary' => 2500,
'workers_needed' => 1,
'job_type' => 'Full Time',
'start_date' => now()->subMonths(4)->toDateString(),
'description' => 'Test Description',
'status' => 'active',
]);
// 1. Cannot review before eligibility period (set eligibility to 3 months)
$app = JobApplication::create([
'job_id' => $job->id,
'worker_id' => $worker->id,
'status' => 'hired',
'joining_confirmed_at' => now()->subMonths(2)->toDateString(),
]);
$response = $this->postJson('/api/workers/reviews', [
'employer_id' => $employer->id,
'job_id' => $job->id,
'rating' => 5,
'comment' => 'Great employer!',
], [
'Authorization' => 'Bearer worker_api_token'
]);
$response->assertStatus(403);
$response->assertJsonPath('success', false);
$response->assertJsonPath('message', 'You can write a review only after 3 months from the joining date.');
// 2. Can review after eligibility period
$app->joining_confirmed_at = now()->subMonths(3)->subDays(5)->toDateString();
$app->save();
$response = $this->postJson('/api/workers/reviews', [
'employer_id' => $employer->id,
'job_id' => $job->id,
'rating' => 5,
'comment' => 'Great employer!',
], [
'Authorization' => 'Bearer worker_api_token'
]);
$response->assertStatus(201);
$response->assertJsonPath('success', true);
$reviewId = $response->json('data.review.id');
// 3. Edit review within 7 days
$response = $this->putJson("/api/workers/reviews/{$reviewId}", [
'rating' => 4,
'comment' => 'Good employer indeed.',
], [
'Authorization' => 'Bearer worker_api_token'
]);
$response->assertStatus(200);
// 4. Cannot edit review after 7 days
$review = EmployerReview::find($reviewId);
$review->created_at = now()->subDays(8);
$review->save();
$response = $this->putJson("/api/workers/reviews/{$reviewId}", [
'rating' => 3,
'comment' => 'Okay employer.',
], [
'Authorization' => 'Bearer worker_api_token'
]);
$response->assertStatus(403);
$response->assertJsonPath('success', false);
$response->assertJsonPath('message', 'The 7-day edit window for this review has expired.');
}
public function test_employer_review_controller_eligibility_and_edit_window()
{
$worker = $this->createTestWorker();
$employer = $this->createTestEmployer();
$job = JobPost::create([
'employer_id' => $employer->id,
'title' => 'Test Job',
'location' => 'Dubai',
'salary' => 2500,
'workers_needed' => 1,
'job_type' => 'Full Time',
'start_date' => now()->subMonths(4)->toDateString(),
'description' => 'Test Description',
'status' => 'active',
]);
// 1. Cannot review before eligibility period (set eligibility to 3 months)
$app = JobApplication::create([
'job_id' => $job->id,
'worker_id' => $worker->id,
'status' => 'hired',
'joining_confirmed_at' => now()->subMonths(2)->toDateString(),
]);
$response = $this->postJson('/api/employers/reviews', [
'worker_id' => $worker->id,
'rating' => 5,
'comment' => 'Great worker!',
], [
'Authorization' => 'Bearer employer_api_token'
]);
$response->assertStatus(403);
$response->assertJsonPath('success', false);
$response->assertJsonPath('message', 'You can write a review only after 3 months from the joining date.');
// 2. Can review after eligibility period
$app->joining_confirmed_at = now()->subMonths(3)->subDays(5)->toDateString();
$app->save();
$response = $this->postJson('/api/employers/reviews', [
'worker_id' => $worker->id,
'rating' => 5,
'comment' => 'Great worker!',
], [
'Authorization' => 'Bearer employer_api_token'
]);
$response->assertStatus(201);
$response->assertJsonPath('success', true);
$reviewId = $response->json('data.review.id');
// 3. Edit review within 7 days
$response = $this->putJson("/api/employers/reviews/{$reviewId}", [
'rating' => 4,
'comment' => 'Good worker indeed.',
], [
'Authorization' => 'Bearer employer_api_token'
]);
$response->assertStatus(200);
// 4. Cannot edit review after 7 days
$review = Review::find($reviewId);
$review->created_at = now()->subDays(8);
$review->save();
$response = $this->putJson("/api/employers/reviews/{$reviewId}", [
'rating' => 3,
'comment' => 'Okay worker.',
], [
'Authorization' => 'Bearer employer_api_token'
]);
$response->assertStatus(403);
$response->assertJsonPath('success', false);
$response->assertJsonPath('message', 'The 7-day edit window for this review has expired.');
}
}