/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ markTestIncomplete( 'Appointments GET endpoint not implemented yet - TDD RED phase' ); // ARRANGE: Authenticated doctor wp_set_current_user( $this->doctor_user ); // ACT: Make GET request to appointments endpoint $response = $this->make_request( '/wp-json/care/v1/appointments' ); // ASSERT: Response contract $this->assertRestResponse( $response, 200 ); $data = $response->get_data(); $this->assertIsArray( $data ); // Validate pagination structure if ( ! empty( $data ) ) { $this->assertArrayHasKey( 'data', $data ); $this->assertArrayHasKey( 'total', $data ); $this->assertArrayHasKey( 'page', $data ); $this->assertArrayHasKey( 'per_page', $data ); // Validate appointment data structure $appointment = $data['data'][0]; $this->assertAppointmentStructure( $appointment ); } } /** * Test POST /wp-json/care/v1/appointments endpoint contract. * * @test */ public function test_create_appointment_endpoint_contract() { // This test will fail initially as the endpoint doesn't exist yet $this->markTestIncomplete( 'Appointments POST endpoint not implemented yet - TDD RED phase' ); // ARRANGE: Valid appointment data $clinic_id = $this->create_test_clinic(); $appointment_data = array( 'appointment_start_date' => gmdate( 'Y-m-d', strtotime( '+1 day' ) ), 'appointment_start_time' => '14:30:00', 'appointment_end_date' => gmdate( 'Y-m-d', strtotime( '+1 day' ) ), 'appointment_end_time' => '15:00:00', 'doctor_id' => $this->doctor_user, 'patient_id' => $this->patient_user, 'clinic_id' => $clinic_id, 'visit_type' => 'consultation', 'description' => 'Consulta de rotina', ); // ACT: Make POST request as receptionist $response = $this->make_request( '/wp-json/care/v1/appointments', 'POST', $appointment_data, $this->receptionist_user ); // ASSERT: Response contract $this->assertRestResponse( $response, 201 ); $data = $response->get_data(); $this->assertAppointmentStructure( $data ); $this->assertEquals( $appointment_data['doctor_id'], $data['doctor_id'] ); $this->assertEquals( $appointment_data['patient_id'], $data['patient_id'] ); $this->assertIsInt( $data['id'] ); $this->assertGreaterThan( 0, $data['id'] ); } /** * Test POST /wp-json/care/v1/appointments with scheduling conflict. * * @test */ public function test_create_appointment_time_conflict() { // This test will fail initially as the endpoint doesn't exist yet $this->markTestIncomplete( 'Appointment time conflict validation not implemented yet - TDD RED phase' ); // ARRANGE: Existing appointment and conflicting data $clinic_id = $this->create_test_clinic(); $existing_appointment = $this->create_test_appointment( $clinic_id, $this->doctor_user, $this->patient_user ); $conflicting_data = array( 'appointment_start_date' => gmdate( 'Y-m-d', strtotime( '+1 day' ) ), 'appointment_start_time' => '14:45:00', // Conflicts with existing 14:30-15:00 'appointment_end_date' => gmdate( 'Y-m-d', strtotime( '+1 day' ) ), 'appointment_end_time' => '15:15:00', 'doctor_id' => $this->doctor_user, 'patient_id' => $this->factory->user->create( array( 'role' => 'patient' ) ), 'clinic_id' => $clinic_id, 'visit_type' => 'consultation', ); // ACT: Make POST request with conflicting time $response = $this->make_request( '/wp-json/care/v1/appointments', 'POST', $conflicting_data, $this->receptionist_user ); // ASSERT: Time conflict error contract $this->assertRestResponse( $response, 409 ); $data = $response->get_data(); $this->assertArrayHasKey( 'code', $data ); $this->assertEquals( 'appointment_time_conflict', $data['code'] ); } /** * Test GET /wp-json/care/v1/appointments/{id} endpoint contract. * * @test */ public function test_get_appointment_by_id_endpoint_contract() { // This test will fail initially as the endpoint doesn't exist yet $this->markTestIncomplete( 'Appointment by ID endpoint not implemented yet - TDD RED phase' ); // ARRANGE: Existing appointment $clinic_id = $this->create_test_clinic(); $appointment_id = $this->create_test_appointment( $clinic_id, $this->doctor_user, $this->patient_user ); // ACT: Make GET request for specific appointment $response = $this->make_request( "/wp-json/care/v1/appointments/{$appointment_id}", 'GET', array(), $this->doctor_user ); // ASSERT: Response contract $this->assertRestResponse( $response, 200 ); $data = $response->get_data(); $this->assertAppointmentStructure( $data ); $this->assertEquals( $appointment_id, $data['id'] ); } /** * Test PUT /wp-json/care/v1/appointments/{id} endpoint contract. * * @test */ public function test_update_appointment_endpoint_contract() { // This test will fail initially as the endpoint doesn't exist yet $this->markTestIncomplete( 'Appointment PUT endpoint not implemented yet - TDD RED phase' ); // ARRANGE: Existing appointment and update data $clinic_id = $this->create_test_clinic(); $appointment_id = $this->create_test_appointment( $clinic_id, $this->doctor_user, $this->patient_user ); $update_data = array( 'appointment_start_time' => '15:30:00', 'appointment_end_time' => '16:00:00', 'description' => 'Updated appointment description', ); // ACT: Make PUT request to update appointment $response = $this->make_request( "/wp-json/care/v1/appointments/{$appointment_id}", 'PUT', $update_data, $this->receptionist_user ); // ASSERT: Response contract $this->assertRestResponse( $response, 200 ); $data = $response->get_data(); $this->assertAppointmentStructure( $data ); $this->assertEquals( $update_data['appointment_start_time'], $data['appointment_start_time'] ); $this->assertEquals( $update_data['description'], $data['description'] ); } /** * Test DELETE /wp-json/care/v1/appointments/{id} endpoint contract. * * @test */ public function test_delete_appointment_endpoint_contract() { // This test will fail initially as the endpoint doesn't exist yet $this->markTestIncomplete( 'Appointment DELETE endpoint not implemented yet - TDD RED phase' ); // ARRANGE: Existing appointment $clinic_id = $this->create_test_clinic(); $appointment_id = $this->create_test_appointment( $clinic_id, $this->doctor_user, $this->patient_user ); // ACT: Make DELETE request $response = $this->make_request( "/wp-json/care/v1/appointments/{$appointment_id}", 'DELETE', array(), $this->receptionist_user ); // ASSERT: Response contract $this->assertRestResponse( $response, 200 ); $data = $response->get_data(); $this->assertArrayHasKey( 'deleted', $data ); $this->assertTrue( $data['deleted'] ); $this->assertEquals( $appointment_id, $data['id'] ); } /** * Test GET /wp-json/care/v1/appointments/available-slots endpoint contract. * * @test */ public function test_get_available_slots_contract() { // This test will fail initially as the endpoint doesn't exist yet $this->markTestIncomplete( 'Available slots endpoint not implemented yet - TDD RED phase' ); // ARRANGE: Query parameters for slot availability $query_params = array( 'doctor_id' => $this->doctor_user, 'date' => gmdate( 'Y-m-d', strtotime( '+1 day' ) ), 'clinic_id' => $this->create_test_clinic(), ); // ACT: Make GET request for available slots $response = $this->make_request( '/wp-json/care/v1/appointments/available-slots', 'GET', $query_params ); // ASSERT: Response contract $this->assertRestResponse( $response, 200 ); $data = $response->get_data(); $this->assertIsArray( $data ); $this->assertArrayHasKey( 'date', $data ); $this->assertArrayHasKey( 'doctor_id', $data ); $this->assertArrayHasKey( 'available_slots', $data ); // Validate slot structure if ( ! empty( $data['available_slots'] ) ) { $slot = $data['available_slots'][0]; $this->assertArrayHasKey( 'start_time', $slot ); $this->assertArrayHasKey( 'end_time', $slot ); $this->assertArrayHasKey( 'available', $slot ); } } /** * Test appointment filtering and search capabilities. * * @test */ public function test_appointment_filtering_contract() { // This test will fail initially as filtering isn't implemented $this->markTestIncomplete( 'Appointment filtering not implemented yet - TDD RED phase' ); // ARRANGE: Multiple appointments with different attributes $clinic_id = $this->create_test_clinic(); // ACT: Test date filtering $filter_params = array( 'start_date' => gmdate( 'Y-m-d' ), 'end_date' => gmdate( 'Y-m-d', strtotime( '+7 days' ) ), ); $response = $this->make_request( '/wp-json/care/v1/appointments', 'GET', $filter_params, $this->doctor_user ); // ASSERT: Filtered response contract $this->assertRestResponse( $response, 200 ); // ACT: Test doctor filtering $filter_params = array( 'doctor_id' => $this->doctor_user ); $response = $this->make_request( '/wp-json/care/v1/appointments', 'GET', $filter_params, $this->admin_user ); // ASSERT: Doctor-filtered response contract $this->assertRestResponse( $response, 200 ); } /** * Helper method to assert appointment data structure. * * @param array $appointment Appointment data to validate. */ private function assertAppointmentStructure( $appointment ) { $this->assertIsArray( $appointment ); // Required fields $expected_fields = array( 'id', 'appointment_start_date', 'appointment_start_time', 'appointment_end_date', 'appointment_end_time', 'doctor_id', 'patient_id', 'clinic_id', 'status', 'visit_type', 'created_at' ); foreach ( $expected_fields as $field ) { $this->assertArrayHasKey( $field, $appointment ); } // Data type validations $this->assertIsInt( $appointment['id'] ); $this->assertIsInt( $appointment['doctor_id'] ); $this->assertIsInt( $appointment['patient_id'] ); $this->assertIsInt( $appointment['clinic_id'] ); $this->assertIsInt( $appointment['status'] ); // Date/time format validations $this->assertMatchesRegularExpression( '/^\d{4}-\d{2}-\d{2}$/', $appointment['appointment_start_date'] ); $this->assertMatchesRegularExpression( '/^\d{2}:\d{2}:\d{2}$/', $appointment['appointment_start_time'] ); // Optional fields that might be present $optional_fields = array( 'description', 'patient', 'doctor', 'clinic' ); // If expanded data is included, validate structure if ( isset( $appointment['patient'] ) ) { $this->assertIsArray( $appointment['patient'] ); $this->assertArrayHasKey( 'display_name', $appointment['patient'] ); } if ( isset( $appointment['doctor'] ) ) { $this->assertIsArray( $appointment['doctor'] ); $this->assertArrayHasKey( 'display_name', $appointment['doctor'] ); } } }