migrant-web/tests/Feature/AnnouncementApiTest.php
2026-07-07 01:19:50 +05:30

551 lines
20 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Announcement;
use App\Models\User;
use App\Models\EmployerProfile;
use App\Models\Worker;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class AnnouncementApiTest extends TestCase
{
use RefreshDatabase;
protected $worker;
protected $employer;
protected $workerToken;
protected $employerToken;
protected function setUp(): void
{
parent::setUp();
// Create worker with API token
$this->workerToken = 'worker-token-xyz';
$this->worker = Worker::create([
'name' => 'Jane Helper',
'email' => 'jane@example.com',
'phone' => '+971500000001',
'password' => bcrypt('password'),
'api_token' => $this->workerToken,
'status' => 'Available',
'nationality' => 'Indian',
'age' => 25,
'salary' => 1600.00,
'availability' => 'Immediate',
'experience' => '3 Years',
'religion' => 'Hindu',
'bio' => 'Experienced helper ready to work.',
]);
// Create employer with API token
$this->employerToken = 'employer-token-abc';
$this->employer = User::create([
'name' => 'Employer One',
'email' => 'emp1@example.com',
'password' => bcrypt('password'),
'role' => 'employer',
'api_token' => $this->employerToken,
]);
EmployerProfile::create([
'user_id' => $this->employer->id,
'company_name' => 'Elite Services',
'phone' => '+971500000002',
'country' => 'United Arab Emirates',
'verification_status' => 'approved',
'language' => 'English',
'notifications' => true,
]);
}
/**
* Test worker can retrieve all announcements.
*/
public function test_worker_can_get_announcements()
{
// Create announcement
Announcement::create([
'title' => 'Important Update',
'body' => 'Please keep your profile details updated.',
'type' => 'info',
'employer_id' => $this->employer->id,
'status' => 'approved',
]);
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->workerToken,
])->getJson('/api/workers/announcements');
$response->assertStatus(200)
->assertJsonStructure([
'success',
'data' => [
'announcements' => [
'*' => [
'id',
'title',
'body',
'type',
'employer_name',
'company_name',
'created_at',
'time_ago',
]
]
]
]);
$this->assertEquals('Important Update', $response->json('data.announcements.0.title'));
$this->assertEquals('Elite Services', $response->json('data.announcements.0.company_name'));
}
/**
* Test employer can get only their posted announcements via my-announcements.
*/
public function test_employer_can_get_my_announcements()
{
// Announcement by this employer
Announcement::create([
'title' => 'Employer Ann',
'body' => 'Employer specific message.',
'type' => 'success',
'employer_id' => $this->employer->id,
]);
// Announcement by another employer
$otherEmployer = User::create([
'name' => 'Employer Two',
'email' => 'emp2@example.com',
'password' => bcrypt('password'),
'role' => 'employer',
]);
Announcement::create([
'title' => 'Other Ann',
'body' => 'Other specific message.',
'type' => 'warning',
'employer_id' => $otherEmployer->id,
]);
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->employerToken,
])->getJson('/api/employers/my-announcements');
$response->assertStatus(200);
$this->assertCount(1, $response->json('data.announcements'));
$this->assertEquals('Employer Ann', $response->json('data.announcements.0.title'));
}
/**
* Test employer can get all approved announcements.
*/
public function test_employer_can_get_all_approved_announcements()
{
// Approved announcement by this employer
Announcement::create([
'title' => 'Approved Ann 1',
'body' => 'Message 1',
'type' => 'info',
'employer_id' => $this->employer->id,
'status' => 'approved',
]);
// Pending announcement by this employer
Announcement::create([
'title' => 'Pending Ann 1',
'body' => 'Message 2',
'type' => 'info',
'employer_id' => $this->employer->id,
'status' => 'pending',
]);
// Approved announcement by another sponsor
$sponsor = \App\Models\Sponsor::create([
'full_name' => 'Sponsor Name',
'email' => 'sponsor@example.com',
'mobile' => '+971501112233',
'organization_name' => 'Charity Org',
'address' => 'Dubai',
'role' => 'sponsor',
'password' => bcrypt('password'),
]);
Announcement::create([
'title' => 'Approved Ann 2',
'body' => 'Message 3',
'type' => 'info',
'sponsor_id' => $sponsor->id,
'status' => 'approved',
]);
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->employerToken,
])->getJson('/api/employers/announcements');
$response->assertStatus(200);
$this->assertCount(2, $response->json('data.announcements'));
$titles = collect($response->json('data.announcements'))->pluck('title');
$this->assertTrue($titles->contains('Approved Ann 1'));
$this->assertTrue($titles->contains('Approved Ann 2'));
$this->assertFalse($titles->contains('Pending Ann 1'));
}
/**
* Test employer can create a new announcement.
*/
public function test_employer_can_create_announcement()
{
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->employerToken,
])->postJson('/api/employers/announcements', [
'title' => 'Holiday Notice',
'body' => 'Office will be closed tomorrow.',
'type' => 'warning'
]);
$response->assertStatus(201)
->assertJsonStructure([
'success',
'message',
'data' => [
'announcement' => [
'id',
'title',
'body',
'type',
'created_at',
'time_ago',
]
]
]);
$this->assertDatabaseHas('announcements', [
'title' => 'Holiday Notice',
'employer_id' => $this->employer->id,
'type' => 'warning',
'status' => 'pending',
]);
}
/**
* Test employer can delete their own announcement.
*/
public function test_employer_can_delete_announcement()
{
$announcement = Announcement::create([
'title' => 'Delete Me',
'body' => 'Temporary post.',
'type' => 'info',
'employer_id' => $this->employer->id,
]);
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->employerToken,
])->deleteJson('/api/employers/announcements/' . $announcement->id);
$response->assertStatus(200);
$this->assertDatabaseMissing('announcements', [
'id' => $announcement->id,
]);
}
/**
* Test employer cannot delete another employer's announcement.
*/
public function test_employer_cannot_delete_other_employers_announcement()
{
$otherEmployer = User::create([
'name' => 'Employer Two',
'email' => 'emp2@example.com',
'password' => bcrypt('password'),
'role' => 'employer',
]);
$announcement = Announcement::create([
'title' => 'Other Post',
'body' => 'Do not touch.',
'type' => 'info',
'employer_id' => $otherEmployer->id,
]);
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->employerToken,
])->deleteJson('/api/employers/announcements/' . $announcement->id);
$response->assertStatus(404);
$this->assertDatabaseHas('announcements', [
'id' => $announcement->id,
]);
}
/**
* Test worker cannot see pending announcements until approved.
*/
public function test_worker_cannot_see_pending_announcements_until_approved()
{
$announcement = Announcement::create([
'title' => 'Pending Ann',
'body' => 'Should not be seen.',
'type' => 'info',
'employer_id' => $this->employer->id,
'status' => 'pending',
]);
// Worker fetches announcements
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->workerToken,
])->getJson('/api/workers/announcements');
$response->assertStatus(200);
$this->assertCount(0, $response->json('data.announcements'));
// Admin approves it
$announcement->update(['status' => 'approved']);
// Worker fetches announcements again
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->workerToken,
])->getJson('/api/workers/announcements');
$response->assertStatus(200);
$this->assertCount(1, $response->json('data.announcements'));
$this->assertEquals('Pending Ann', $response->json('data.announcements.0.title'));
}
/**
* Test admin can reject announcement with remarks.
*/
public function test_admin_can_reject_announcement_with_remarks()
{
$admin = User::create([
'name' => 'Admin User',
'email' => 'admin@example.com',
'password' => bcrypt('password'),
'role' => 'admin',
]);
$announcement = Announcement::create([
'title' => 'Pending Ann',
'body' => 'Event description.',
'type' => 'Charity',
'employer_id' => $this->employer->id,
'status' => 'pending',
]);
$response = $this->actingAs($admin)
->withSession(['user' => $admin])
->post('/admin/events/' . $announcement->id . '/reject', [
'remarks' => 'Inappropriate content or location details.',
]);
$response->assertStatus(302); // redirects back
$this->assertDatabaseHas('announcements', [
'id' => $announcement->id,
'status' => 'rejected',
'remarks' => 'Inappropriate content or location details.',
]);
}
/**
* Test employer can post and get charity announcements with contact details.
*/
public function test_employer_can_post_and_get_charity_announcement_with_contact_details()
{
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->employerToken,
])->postJson('/api/employers/announcements', [
'title' => 'Free Dental Checkup Camp',
'content' => 'Emirates Charity is providing free professional dental screening, cleanings, and educational packages for domestic helpers.',
'provided_items' => 'Free Dental screening, cleanings, and wellness kits',
'event_date' => '2026-06-15',
'event_time' => '9:00 AM - 4:00 PM',
'location_details' => 'Al Quoz Community Hall, Dubai',
'location_pin' => 'https://maps.app.goo.gl/xyz',
'contact_person_name' => 'Jane Doe',
'contact_number' => '551234567',
'country_code' => '+971',
]);
$response->assertStatus(201)
->assertJson([
'success' => true,
'message' => 'Announcement posted successfully.',
])
->assertJsonStructure([
'data' => [
'announcement' => [
'id',
'title',
'body',
'type',
'status',
'charity_details' => [
'type',
'provided_items',
'event_date',
'event_time',
'location_details',
'location_pin',
'content',
'contact_person_name',
'contact_number',
'country_code',
]
]
]
]);
$announcementId = $response->json('data.announcement.id');
// Approve it so it shows up in global get endpoint
Announcement::find($announcementId)->update(['status' => 'approved']);
// Test GET /api/employers/announcements
$getResponse = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->employerToken,
])->getJson('/api/employers/announcements');
$getResponse->assertStatus(200);
$announcements = $getResponse->json('data.announcements');
$postedAnn = collect($announcements)->firstWhere('id', $announcementId);
$this->assertNotNull($postedAnn);
$this->assertEquals('Free Dental Checkup Camp', $postedAnn['title']);
$this->assertEquals('Jane Doe', $postedAnn['charity_details']['contact_person_name']);
$this->assertEquals('551234567', $postedAnn['charity_details']['contact_number']);
$this->assertEquals('+971', $postedAnn['charity_details']['country_code']);
// Test GET /api/employers/my-announcements
$getMyResponse = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->employerToken,
])->getJson('/api/employers/my-announcements');
$getMyResponse->assertStatus(200);
$myAnnouncements = $getMyResponse->json('data.announcements');
$myPostedAnn = collect($myAnnouncements)->firstWhere('id', $announcementId);
$this->assertNotNull($myPostedAnn);
$this->assertEquals('Free Dental Checkup Camp', $myPostedAnn['title']);
$this->assertEquals('Jane Doe', $myPostedAnn['charity_details']['contact_person_name']);
$this->assertEquals('551234567', $myPostedAnn['charity_details']['contact_number']);
$this->assertEquals('+971', $myPostedAnn['charity_details']['country_code']);
}
/**
* Test web employer can create charity event with contact details.
*/
public function test_web_employer_can_create_charity_event()
{
$response = $this->actingAs($this->employer)
->post(route('employer.events.store'), [
'title' => 'Web Charity Camp',
'content' => 'Giving away free wellness items and tools.',
'provided_items' => 'Wellness Kits',
'event_date' => '2026-08-20',
'event_time' => '10:00 AM - 3:00 PM',
'location_details' => 'Safa Park, Dubai',
'location_pin' => 'https://maps.app.goo.gl/xyz',
'contact_person_name' => 'John Doe',
'contact_number' => '559876543',
'country_code' => '+971',
]);
$response->assertStatus(302); // Redirect on success
$this->assertDatabaseHas('announcements', [
'title' => 'Web Charity Camp',
'employer_id' => $this->employer->id,
'status' => 'pending',
]);
$announcement = Announcement::where('title', 'Web Charity Camp')->first();
$this->assertNotNull($announcement);
$body = json_decode($announcement->body, true);
$this->assertEquals('John Doe', $body['contact_person_name']);
$this->assertEquals('559876543', $body['contact_number']);
$this->assertEquals('+971', $body['country_code']);
}
/**
* Test web admin can create charity event with contact details.
*/
public function test_web_admin_can_create_charity_event()
{
$admin = User::create([
'name' => 'Admin User',
'email' => 'admin@example.com',
'password' => bcrypt('password'),
'role' => 'admin',
]);
$response = $this->actingAs($admin)
->withSession(['user' => $admin])
->post(route('admin.events.store'), [
'title' => 'Admin Charity Camp',
'content' => 'Giving away free medical checkups.',
'provided_items' => 'Medical Checkups',
'event_date' => '2026-09-10',
'event_time' => '08:00 AM - 05:00 PM',
'location_details' => 'Community Center',
'location_pin' => 'https://maps.app.goo.gl/abc',
'contact_person_name' => 'Admin Coordinator',
'contact_number' => '501234567',
'country_code' => '+971',
]);
$response->assertStatus(302); // Redirect on success
$this->assertDatabaseHas('announcements', [
'title' => 'Admin Charity Camp',
'status' => 'approved', // Admin events are auto-approved
]);
$announcement = Announcement::where('title', 'Admin Charity Camp')->first();
$this->assertNotNull($announcement);
$body = json_decode($announcement->body, true);
$this->assertEquals('Admin Coordinator', $body['contact_person_name']);
$this->assertEquals('501234567', $body['contact_number']);
$this->assertEquals('+971', $body['country_code']);
}
/**
* Test admin approving announcement creates database notification for worker.
*/
public function test_admin_approving_announcement_creates_database_notification()
{
$admin = User::create([
'name' => 'Admin User',
'email' => 'admin@example.com',
'password' => bcrypt('password'),
'role' => 'admin',
]);
$announcement = Announcement::create([
'title' => 'Important Community Event',
'body' => 'Event description.',
'type' => 'Charity',
'employer_id' => $this->employer->id,
'status' => 'pending',
]);
$this->assertEquals(0, $this->worker->notifications()->count());
$response = $this->actingAs($admin)
->withSession(['user' => $admin])
->post(route('admin.events.approve', $announcement->id));
$response->assertStatus(302);
// Verify database notification exists for worker
$this->assertEquals(1, $this->worker->notifications()->count());
$notification = $this->worker->notifications()->first();
$this->assertNotNull($notification);
$this->assertEquals('New Event: Important Community Event', $notification->data['title']);
$this->assertEquals('announcement', $notification->data['type']);
$this->assertEquals($announcement->id, $notification->data['announcement_id']);
}
}