withoutMiddleware(EnsurePlatformSession::class); config([ 'meet.url' => 'https://meet.test/api/service/v1', 'meet.key' => 'test-care-key', ]); $this->user = User::create([ 'public_id' => 'test-user-meet', 'name' => 'Test User', 'email' => 'meet@example.com', ]); $this->organization = Organization::create([ 'owner_ref' => $this->user->public_id, 'name' => 'Test Clinic', 'slug' => 'test-clinic-meet', 'timezone' => 'UTC', 'settings' => ['onboarded' => true], ]); Member::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'user_ref' => $this->user->public_id, 'role' => 'receptionist', ]); $this->branch = Branch::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'name' => 'Main Branch', 'is_active' => true, ]); Department::create([ 'owner_ref' => $this->user->public_id, 'branch_id' => $this->branch->id, 'name' => 'General Outpatient', 'type' => 'outpatient', 'is_active' => true, ]); $this->patient = Patient::create([ 'uuid' => (string) \Illuminate\Support\Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'LC-2026-00099', 'first_name' => 'Ama', 'last_name' => 'Mensah', 'email' => 'ama@example.com', ]); } public function test_can_schedule_video_visit_for_appointment(): void { Http::fake([ 'meet.test/*' => Http::response([ 'room' => ['uuid' => 'room-uuid-1'], 'join_url' => 'https://meet.test/join/abc', ], 201), ]); $appointment = $this->createAppointment(); $this->actingAs($this->user) ->post(route('care.appointments.schedule-meet', $appointment)) ->assertRedirect(route('care.appointments.show', $appointment)); $appointment->refresh(); $this->assertSame('room-uuid-1', $appointment->meet_room_uuid); $this->assertSame('https://meet.test/join/abc', $appointment->meet_join_url); $this->assertDatabaseHas('care_audit_logs', ['action' => 'appointment.meet_scheduled']); } public function test_can_start_instant_video_visit(): void { Http::fake([ 'meet.test/*' => Http::sequence() ->push([ 'room' => ['uuid' => 'room-uuid-2'], 'join_url' => 'https://meet.test/join/live', ], 201) ->push([ 'join_url' => 'https://meet.test/join/live-now', ], 200), ]); Member::where('user_ref', $this->user->public_id)->update(['role' => 'doctor']); $appointment = $this->createAppointment(Appointment::STATUS_WAITING); $this->actingAs($this->user) ->post(route('care.appointments.start-meet', $appointment)) ->assertRedirect('https://meet.test/join/live-now'); $appointment->refresh(); $this->assertSame('room-uuid-2', $appointment->meet_room_uuid); } public function test_schedule_meet_shows_friendly_error_when_unauthorized(): void { Http::fake([ 'meet.test/*' => Http::response(['error' => 'Unauthorized.'], 401), ]); $appointment = $this->createAppointment(); $this->actingAs($this->user) ->from(route('care.appointments.show', $appointment)) ->post(route('care.appointments.schedule-meet', $appointment)) ->assertRedirect(route('care.appointments.show', $appointment)) ->assertSessionHasErrors('meet'); $appointment->refresh(); $this->assertNull($appointment->meet_room_uuid); } public function test_cancelling_appointment_cancels_linked_meet_room(): void { Http::fake([ 'meet.test/*/rooms/room-uuid-cancel/cancel' => Http::response([ 'room' => ['uuid' => 'room-uuid-cancel', 'status' => 'cancelled'], ], 200), ]); $appointment = $this->createAppointment(); $appointment->update([ 'meet_room_uuid' => 'room-uuid-cancel', 'meet_join_url' => 'https://meet.test/join/cancel-me', ]); $this->actingAs($this->user) ->post(route('care.appointments.cancel', $appointment)) ->assertRedirect(); $appointment->refresh(); $this->assertSame(Appointment::STATUS_CANCELLED, $appointment->status); $this->assertDatabaseHas('care_audit_logs', ['action' => 'appointment.cancelled']); $this->assertDatabaseHas('care_audit_logs', ['action' => 'appointment.meet_cancelled']); Http::assertSent(function ($request) { return $request->method() === 'POST' && str_contains($request->url(), '/rooms/room-uuid-cancel/cancel') && $request['owner_ref'] === $this->user->public_id; }); } public function test_cancelling_appointment_still_succeeds_when_meet_cancel_fails(): void { Http::fake([ 'meet.test/*' => Http::response(['error' => 'Unauthorized.'], 401), ]); $appointment = $this->createAppointment(); $appointment->update([ 'meet_room_uuid' => 'room-uuid-orphan', 'meet_join_url' => 'https://meet.test/join/orphan', ]); $this->actingAs($this->user) ->post(route('care.appointments.cancel', $appointment)) ->assertRedirect() ->assertSessionHas('success'); $appointment->refresh(); $this->assertSame(Appointment::STATUS_CANCELLED, $appointment->status); $this->assertDatabaseHas('care_audit_logs', ['action' => 'appointment.cancelled']); $this->assertDatabaseMissing('care_audit_logs', ['action' => 'appointment.meet_cancelled']); } protected function createAppointment(string $status = Appointment::STATUS_SCHEDULED): Appointment { return Appointment::create([ 'uuid' => (string) \Illuminate\Support\Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'type' => Appointment::TYPE_SCHEDULED, 'status' => $status, 'scheduled_at' => now()->addHour(), 'reason' => 'Follow-up', ]); } }