changes on api worker employer details, review, issue
This commit is contained in:
parent
2f81865175
commit
52b47cc2ac
@ -933,4 +933,155 @@ private function cleanVisaData(array $visaDataInput): array
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get details of an employer profile for worker view.
|
||||||
|
* GET /api/workers/employers/{id}
|
||||||
|
*/
|
||||||
|
public function getEmployerProfile(Request $request, $id)
|
||||||
|
{
|
||||||
|
/** @var Worker $worker */
|
||||||
|
$worker = $request->attributes->get('worker');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Find the employer (role should be 'employer')
|
||||||
|
$employer = \App\Models\User::where('id', $id)->where('role', 'employer')->first();
|
||||||
|
|
||||||
|
if (!$employer) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Employer not found.'
|
||||||
|
], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$empProfile = $employer->employerProfile;
|
||||||
|
|
||||||
|
// 1. Check access permissions for contact details
|
||||||
|
$hasAccess = false;
|
||||||
|
|
||||||
|
// Check if there is an active/previous JobOffer
|
||||||
|
$hasOffer = \App\Models\JobOffer::where('employer_id', $employer->id)
|
||||||
|
->where('worker_id', $worker->id)
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
// Check if the worker has applied to any jobs of this employer
|
||||||
|
$hasApplication = \App\Models\JobApplication::where('worker_id', $worker->id)
|
||||||
|
->whereHas('jobPost', function ($query) use ($employer) {
|
||||||
|
$query->where('employer_id', $employer->id);
|
||||||
|
})
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
// Check if there is an active/previous conversation
|
||||||
|
$hasConversation = \App\Models\Conversation::where('employer_id', $employer->id)
|
||||||
|
->where('worker_id', $worker->id)
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($hasOffer || $hasApplication || $hasConversation) {
|
||||||
|
$hasAccess = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Fetch active job details (if applicable)
|
||||||
|
$activeJobs = \App\Models\JobPost::where('employer_id', $employer->id)
|
||||||
|
->where('status', 'active')
|
||||||
|
->latest()
|
||||||
|
->get()
|
||||||
|
->map(function ($job) {
|
||||||
|
return [
|
||||||
|
'id' => $job->id,
|
||||||
|
'title' => $job->title,
|
||||||
|
'location' => $job->location,
|
||||||
|
'salary' => (int) $job->salary,
|
||||||
|
'job_type' => $job->job_type,
|
||||||
|
'start_date' => $job->start_date ? $job->start_date->format('Y-m-d') : null,
|
||||||
|
'description' => $job->description,
|
||||||
|
'requirements' => $job->requirements,
|
||||||
|
'posted_at' => $job->created_at->toIso8601String(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Overall Rating and Review Summary
|
||||||
|
$reviewsQuery = \App\Models\EmployerReview::where('employer_id', $employer->id);
|
||||||
|
$totalReviews = $reviewsQuery->count();
|
||||||
|
$avgRating = $totalReviews > 0 ? round($reviewsQuery->avg('rating'), 1) : 0.0;
|
||||||
|
|
||||||
|
$starsBreakdown = [
|
||||||
|
'5' => \App\Models\EmployerReview::where('employer_id', $employer->id)->where('rating', 5)->count(),
|
||||||
|
'4' => \App\Models\EmployerReview::where('employer_id', $employer->id)->where('rating', 4)->count(),
|
||||||
|
'3' => \App\Models\EmployerReview::where('employer_id', $employer->id)->where('rating', 3)->count(),
|
||||||
|
'2' => \App\Models\EmployerReview::where('employer_id', $employer->id)->where('rating', 2)->count(),
|
||||||
|
'1' => \App\Models\EmployerReview::where('employer_id', $employer->id)->where('rating', 1)->count(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// 4. Paginated Reviews list
|
||||||
|
$perPage = (int) $request->input('per_page', 10);
|
||||||
|
$reviewsPaginator = \App\Models\EmployerReview::where('employer_id', $employer->id)
|
||||||
|
->with('worker')
|
||||||
|
->latest()
|
||||||
|
->paginate($perPage);
|
||||||
|
|
||||||
|
$reviews = collect($reviewsPaginator->items())->map(function ($rev) {
|
||||||
|
return [
|
||||||
|
'id' => $rev->id,
|
||||||
|
'rating' => $rev->rating,
|
||||||
|
'title' => $rev->title,
|
||||||
|
'comment' => $rev->comment,
|
||||||
|
'created_at' => $rev->created_at->toIso8601String(),
|
||||||
|
'worker' => $rev->worker ? [
|
||||||
|
'id' => $rev->worker->id,
|
||||||
|
'name' => $rev->worker->name,
|
||||||
|
'nationality' => $rev->worker->nationality,
|
||||||
|
] : null,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5. Structure the response
|
||||||
|
$employerData = [
|
||||||
|
'id' => $employer->id,
|
||||||
|
'name' => $employer->name,
|
||||||
|
'email' => $hasAccess ? $employer->email : null,
|
||||||
|
'phone' => $hasAccess ? ($empProfile->phone ?? $employer->phone) : null,
|
||||||
|
'company_name' => $empProfile->company_name ?? 'Private Sponsor',
|
||||||
|
'city' => $empProfile->city ?? null,
|
||||||
|
'district' => $empProfile->district ?? null,
|
||||||
|
'nationality' => $empProfile->nationality ?? null,
|
||||||
|
'address' => $empProfile->address ?? null,
|
||||||
|
'accommodation' => $empProfile->accommodation ?? null,
|
||||||
|
'language' => $empProfile->language ?? null,
|
||||||
|
'profile_photo_url' => $empProfile->profile_photo_url ?? null,
|
||||||
|
'rating' => $avgRating,
|
||||||
|
'reviews_count' => $totalReviews,
|
||||||
|
'review_summary' => [
|
||||||
|
'total' => $totalReviews,
|
||||||
|
'average' => $avgRating,
|
||||||
|
'stars' => $starsBreakdown,
|
||||||
|
],
|
||||||
|
'active_jobs' => $activeJobs,
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => [
|
||||||
|
'employer' => $employerData,
|
||||||
|
'reviews' => [
|
||||||
|
'data' => $reviews,
|
||||||
|
'pagination' => [
|
||||||
|
'total' => $reviewsPaginator->total(),
|
||||||
|
'per_page' => $reviewsPaginator->perPage(),
|
||||||
|
'current_page' => $reviewsPaginator->currentPage(),
|
||||||
|
'last_page' => $reviewsPaginator->lastPage(),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], 200);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
logger()->error('Worker View Employer Profile Failure: ' . $e->getMessage());
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'An error occurred while loading the employer profile.',
|
||||||
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,12 @@ public function addReview(Request $request)
|
|||||||
/** @var Worker $worker */
|
/** @var Worker $worker */
|
||||||
$worker = $request->attributes->get('worker');
|
$worker = $request->attributes->get('worker');
|
||||||
|
|
||||||
|
// Sanitize job_id: if not a valid existing JobPost ID, treat it as null (job id is not needed/optional)
|
||||||
|
$jobIdInput = $request->input('job_id');
|
||||||
|
if (empty($jobIdInput) || !is_numeric($jobIdInput) || !\App\Models\JobPost::where('id', $jobIdInput)->exists()) {
|
||||||
|
$request->merge(['job_id' => null]);
|
||||||
|
}
|
||||||
|
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'employer_id' => 'required|exists:users,id',
|
'employer_id' => 'required|exists:users,id',
|
||||||
'job_id' => 'nullable|exists:job_posts,id',
|
'job_id' => 'nullable|exists:job_posts,id',
|
||||||
|
|||||||
@ -33,6 +33,8 @@ public function handle(Request $request, Closure $next): Response
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
|
session()->forget('user');
|
||||||
|
auth()->logout();
|
||||||
return redirect()->route('employer.login');
|
return redirect()->route('employer.login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7305,6 +7305,161 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/workers/employers/{id}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Worker/Employers"
|
||||||
|
],
|
||||||
|
"summary": "Worker View Employer Profile",
|
||||||
|
"description": "Allows authenticated workers to view the complete profile of a specific employer, including company details, ratings, review summaries, active jobs, and a paginated list of worker-submitted reviews. Contact details are conditionally visible depending on whether the worker has a past/present connection with the employer.",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"description": "The unique database ID of the employer.",
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "per_page",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"description": "Number of reviews to display per page (default: 10).",
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "page",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"description": "The page number of the reviews list to fetch.",
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Employer profile loaded successfully.",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"success": {
|
||||||
|
"type": "boolean",
|
||||||
|
"example": true
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"employer": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "integer", "example": 1 },
|
||||||
|
"name": { "type": "string", "example": "Jane Sponsor" },
|
||||||
|
"email": { "type": "string", "nullable": true, "example": "sponsor@example.com" },
|
||||||
|
"phone": { "type": "string", "nullable": true, "example": "+971501112222" },
|
||||||
|
"company_name": { "type": "string", "example": "Sponsor Co" },
|
||||||
|
"city": { "type": "string", "nullable": true, "example": "Dubai" },
|
||||||
|
"district": { "type": "string", "nullable": true, "example": "Dubai Marina" },
|
||||||
|
"nationality": { "type": "string", "nullable": true, "example": "Emirati" },
|
||||||
|
"address": { "type": "string", "nullable": true, "example": "123 Street" },
|
||||||
|
"accommodation": { "type": "string", "nullable": true, "example": "Provided" },
|
||||||
|
"language": { "type": "string", "nullable": true, "example": "English" },
|
||||||
|
"profile_photo_url": { "type": "string", "nullable": true, "example": "https://example.com/photo.jpg" },
|
||||||
|
"rating": { "type": "number", "format": "float", "example": 4.5 },
|
||||||
|
"reviews_count": { "type": "integer", "example": 12 },
|
||||||
|
"review_summary": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"total": { "type": "integer", "example": 12 },
|
||||||
|
"average": { "type": "number", "example": 4.5 },
|
||||||
|
"stars": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"5": { "type": "integer", "example": 8 },
|
||||||
|
"4": { "type": "integer", "example": 2 },
|
||||||
|
"3": { "type": "integer", "example": 1 },
|
||||||
|
"2": { "type": "integer", "example": 1 },
|
||||||
|
"1": { "type": "integer", "example": 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active_jobs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "integer", "example": 5 },
|
||||||
|
"title": { "type": "string", "example": "Housekeeper Needed" },
|
||||||
|
"location": { "type": "string", "example": "Dubai Marina" },
|
||||||
|
"salary": { "type": "integer", "example": 2500 },
|
||||||
|
"job_type": { "type": "string", "example": "Full Time" },
|
||||||
|
"start_date": { "type": "string", "format": "date", "example": "2026-08-01" },
|
||||||
|
"description": { "type": "string", "example": "Clean house" },
|
||||||
|
"requirements": { "type": "string", "nullable": true, "example": "Experience required" },
|
||||||
|
"posted_at": { "type": "string", "format": "date-time", "example": "2026-07-04T12:00:00Z" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reviews": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "integer", "example": 1 },
|
||||||
|
"rating": { "type": "integer", "example": 5 },
|
||||||
|
"title": { "type": "string", "example": "Great employer" },
|
||||||
|
"comment": { "type": "string", "example": "Highly recommended!" },
|
||||||
|
"created_at": { "type": "string", "format": "date-time", "example": "2026-07-04T12:00:00Z" },
|
||||||
|
"worker": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "integer", "example": 2 },
|
||||||
|
"name": { "type": "string", "example": "Worker Name" },
|
||||||
|
"nationality": { "type": "string", "example": "Filipino" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"total": { "type": "integer", "example": 1 },
|
||||||
|
"per_page": { "type": "integer", "example": 10 },
|
||||||
|
"current_page": { "type": "integer", "example": 1 },
|
||||||
|
"last_page": { "type": "integer", "example": 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Employer not found."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/workers/forgot-password": {
|
"/workers/forgot-password": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|||||||
@ -78,6 +78,7 @@
|
|||||||
Route::get('/workers/dashboard/views', [WorkerProfileController::class, 'getProfileViews']);
|
Route::get('/workers/dashboard/views', [WorkerProfileController::class, 'getProfileViews']);
|
||||||
Route::get('/workers/dashboard', [WorkerProfileController::class, 'getDashboard']);
|
Route::get('/workers/dashboard', [WorkerProfileController::class, 'getDashboard']);
|
||||||
Route::get('/workers/employers', [WorkerProfileController::class, 'getEmployers']);
|
Route::get('/workers/employers', [WorkerProfileController::class, 'getEmployers']);
|
||||||
|
Route::get('/workers/employers/{id}', [WorkerProfileController::class, 'getEmployerProfile']);
|
||||||
Route::post('/workers/change-password', [WorkerProfileController::class, 'changePassword']);
|
Route::post('/workers/change-password', [WorkerProfileController::class, 'changePassword']);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -85,4 +85,31 @@ public function test_employer_upload_emirates_id_ocr_and_purge()
|
|||||||
$this->assertNotNull($profile->emirates_id_expiry);
|
$this->assertNotNull($profile->emirates_id_expiry);
|
||||||
$this->assertEquals('approved', $profile->verification_status);
|
$this->assertEquals('approved', $profile->verification_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a stale session with an invalid user ID does not cause a redirect loop.
|
||||||
|
*/
|
||||||
|
public function test_stale_session_does_not_cause_redirect_loop()
|
||||||
|
{
|
||||||
|
// 1. Visit with a session user object containing a non-existent ID
|
||||||
|
$staleUser = (object)[
|
||||||
|
'id' => 999999, // Doesn't exist
|
||||||
|
'name' => 'Stale User',
|
||||||
|
'email' => 'stale@example.com',
|
||||||
|
'role' => 'employer',
|
||||||
|
'subscription_status' => 'active',
|
||||||
|
'verification_status' => 'approved',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Accessing dashboard should fail auth, clear session, and redirect to login
|
||||||
|
$response = $this->withSession(['user' => $staleUser])
|
||||||
|
->get('/employer/dashboard');
|
||||||
|
|
||||||
|
$response->assertRedirect(route('employer.login'));
|
||||||
|
$this->assertNull(session('user')); // Session must be cleared
|
||||||
|
|
||||||
|
// Subsequent access to login page should render successfully (no redirect)
|
||||||
|
$loginResponse = $this->get('/employer/login');
|
||||||
|
$loginResponse->assertStatus(200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -236,4 +236,172 @@ public function test_worker_employers_list_pagination_and_sorting()
|
|||||||
$this->assertEquals(3, $responsePaginated->json('data.pagination.total'));
|
$this->assertEquals(3, $responsePaginated->json('data.pagination.total'));
|
||||||
$this->assertEquals(2, $responsePaginated->json('data.pagination.per_page'));
|
$this->assertEquals(2, $responsePaginated->json('data.pagination.per_page'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_worker_can_view_employer_profile_with_permissions_and_reviews()
|
||||||
|
{
|
||||||
|
// Create worker
|
||||||
|
$worker = Worker::create([
|
||||||
|
'name' => 'Rahul Sharma',
|
||||||
|
'email' => 'rahul@example.com',
|
||||||
|
'phone' => '+971501234567',
|
||||||
|
'language' => 'HI',
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
'nationality' => 'Indian',
|
||||||
|
'age' => 25,
|
||||||
|
'salary' => 1500,
|
||||||
|
'availability' => 'Immediate',
|
||||||
|
'experience' => 'Not Specified',
|
||||||
|
'religion' => 'Not Specified',
|
||||||
|
'bio' => 'Test',
|
||||||
|
'verified' => true,
|
||||||
|
'status' => 'active',
|
||||||
|
'api_token' => 'worker-test-token',
|
||||||
|
]);
|
||||||
|
|
||||||
|
WorkerDocument::create([
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'type' => 'passport',
|
||||||
|
'number' => '123456',
|
||||||
|
'file_path' => 'passports/test.pdf',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create an employer
|
||||||
|
$employer = User::create([
|
||||||
|
'name' => 'Employer One',
|
||||||
|
'email' => 'emp1@example.com',
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
'role' => 'employer',
|
||||||
|
]);
|
||||||
|
|
||||||
|
EmployerProfile::create([
|
||||||
|
'user_id' => $employer->id,
|
||||||
|
'company_name' => 'Ahmad Tech Ltd',
|
||||||
|
'phone' => '+971500000001',
|
||||||
|
'nationality' => 'UAE',
|
||||||
|
'city' => 'Dubai',
|
||||||
|
'accommodation' => 'Provided',
|
||||||
|
'language' => 'English',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create active job post
|
||||||
|
\App\Models\JobPost::create([
|
||||||
|
'employer_id' => $employer->id,
|
||||||
|
'title' => 'Housekeeper Needed',
|
||||||
|
'location' => 'Dubai Marina',
|
||||||
|
'salary' => 2500,
|
||||||
|
'workers_needed' => 1,
|
||||||
|
'job_type' => 'Full Time',
|
||||||
|
'start_date' => now()->addDays(5)->toDateString(),
|
||||||
|
'description' => 'Clean house',
|
||||||
|
'status' => 'active',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create an accepted job offer (connection exists)
|
||||||
|
JobOffer::create([
|
||||||
|
'employer_id' => $employer->id,
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'work_date' => now()->subDays(10),
|
||||||
|
'location' => 'Dubai',
|
||||||
|
'salary' => 2500,
|
||||||
|
'status' => 'accepted',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create a review submitted by another worker
|
||||||
|
$otherWorker = Worker::create([
|
||||||
|
'name' => 'John Doe',
|
||||||
|
'email' => 'john@example.com',
|
||||||
|
'phone' => '+971501234568',
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
'nationality' => 'Filipino',
|
||||||
|
'age' => 25,
|
||||||
|
'salary' => 1500,
|
||||||
|
'availability' => 'Immediate',
|
||||||
|
'experience' => 'Not Specified',
|
||||||
|
'religion' => 'Not Specified',
|
||||||
|
'bio' => 'Test',
|
||||||
|
'passport_status' => 'valid',
|
||||||
|
]);
|
||||||
|
|
||||||
|
\App\Models\EmployerReview::create([
|
||||||
|
'worker_id' => $otherWorker->id,
|
||||||
|
'employer_id' => $employer->id,
|
||||||
|
'rating' => 5,
|
||||||
|
'title' => 'Excellent sponsor',
|
||||||
|
'comment' => 'Very polite and pays on time.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Hit the new API endpoint
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer worker-test-token',
|
||||||
|
])->getJson("/api/workers/employers/{$employer->id}");
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonPath('success', true);
|
||||||
|
$response->assertJsonPath('data.employer.name', 'Employer One');
|
||||||
|
$response->assertJsonPath('data.employer.email', 'emp1@example.com'); // Contact info visible because of JobOffer connection
|
||||||
|
$response->assertJsonPath('data.employer.phone', '+971500000001');
|
||||||
|
$response->assertJsonPath('data.employer.company_name', 'Ahmad Tech Ltd');
|
||||||
|
$response->assertJsonPath('data.employer.rating', 5);
|
||||||
|
$response->assertJsonPath('data.employer.reviews_count', 1);
|
||||||
|
$response->assertJsonPath('data.employer.review_summary.stars.5', 1);
|
||||||
|
$response->assertJsonCount(1, 'data.employer.active_jobs');
|
||||||
|
$response->assertJsonPath('data.employer.active_jobs.0.title', 'Housekeeper Needed');
|
||||||
|
$response->assertJsonCount(1, 'data.reviews.data');
|
||||||
|
$response->assertJsonPath('data.reviews.data.0.worker.name', 'John Doe');
|
||||||
|
$response->assertJsonPath('data.reviews.data.0.title', 'Excellent sponsor');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_worker_can_view_employer_profile_without_permissions_masks_contact()
|
||||||
|
{
|
||||||
|
// Create worker
|
||||||
|
$worker = Worker::create([
|
||||||
|
'name' => 'Rahul Sharma',
|
||||||
|
'email' => 'rahul@example.com',
|
||||||
|
'phone' => '+971501234567',
|
||||||
|
'language' => 'HI',
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
'nationality' => 'Indian',
|
||||||
|
'age' => 25,
|
||||||
|
'salary' => 1500,
|
||||||
|
'availability' => 'Immediate',
|
||||||
|
'experience' => 'Not Specified',
|
||||||
|
'religion' => 'Not Specified',
|
||||||
|
'bio' => 'Test',
|
||||||
|
'verified' => true,
|
||||||
|
'status' => 'active',
|
||||||
|
'api_token' => 'worker-test-token',
|
||||||
|
]);
|
||||||
|
|
||||||
|
WorkerDocument::create([
|
||||||
|
'worker_id' => $worker->id,
|
||||||
|
'type' => 'passport',
|
||||||
|
'number' => '123456',
|
||||||
|
'file_path' => 'passports/test.pdf',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create an employer
|
||||||
|
$employer = User::create([
|
||||||
|
'name' => 'Employer One',
|
||||||
|
'email' => 'emp1@example.com',
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
'role' => 'employer',
|
||||||
|
]);
|
||||||
|
|
||||||
|
EmployerProfile::create([
|
||||||
|
'user_id' => $employer->id,
|
||||||
|
'company_name' => 'Ahmad Tech Ltd',
|
||||||
|
'phone' => '+971500000001',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Hit the API (no connection exists)
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer worker-test-token',
|
||||||
|
])->getJson("/api/workers/employers/{$employer->id}");
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonPath('success', true);
|
||||||
|
$response->assertJsonPath('data.employer.name', 'Employer One');
|
||||||
|
$response->assertJsonPath('data.employer.email', null); // Masked/null since no connection exists
|
||||||
|
$response->assertJsonPath('data.employer.phone', null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -268,4 +268,37 @@ public function test_view_and_list_reviews()
|
|||||||
$response->assertJsonCount(1, 'data.reviews');
|
$response->assertJsonCount(1, 'data.reviews');
|
||||||
$response->assertJsonPath('data.reviews.0.title', 'Awesome place');
|
$response->assertJsonPath('data.reviews.0.title', 'Awesome place');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_review_with_invalid_or_missing_job_id_passes_and_saves_null()
|
||||||
|
{
|
||||||
|
// Setup job application with hired status and joining_confirmed_at 3.5 months ago
|
||||||
|
JobApplication::create([
|
||||||
|
'job_id' => $this->job->id,
|
||||||
|
'worker_id' => $this->worker->id,
|
||||||
|
'status' => 'hired',
|
||||||
|
'joining_confirmed_at' => now()->subMonths(3)->subDays(15)->toDateString(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Act: Post review with an invalid job_id (e.g. 'invalid-id-or-empty')
|
||||||
|
$response = $this->postJson('/api/workers/reviews', [
|
||||||
|
'employer_id' => $this->employer->id,
|
||||||
|
'job_id' => 'invalid-id-or-empty',
|
||||||
|
'rating' => 4,
|
||||||
|
'title' => 'Fair Employer',
|
||||||
|
'comment' => 'Very good experience overall.',
|
||||||
|
], [
|
||||||
|
'Authorization' => 'Bearer worker_api_token'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertStatus(201);
|
||||||
|
$response->assertJsonPath('success', true);
|
||||||
|
$response->assertJsonPath('data.review.job_id', null); // Resolved to null
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('employer_reviews', [
|
||||||
|
'worker_id' => $this->worker->id,
|
||||||
|
'employer_id' => $this->employer->id,
|
||||||
|
'job_id' => null,
|
||||||
|
'rating' => 4,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user