Files
care-api/tests/contract/test-appointment-endpoints.php
Emanuel Almeida 4a7b232f68 chore: add spec-kit and standardize signatures
- Added GitHub spec-kit for development workflow
- Standardized file signatures to Descomplicar® format
- Updated development configuration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-12 01:27:29 +01:00

316 lines
11 KiB
PHP

/**
* Descomplicar® Crescimento Digital
* https://descomplicar.pt
*/
<?php
/**
* Contract tests for Appointment endpoints.
*
* These tests define the API contract and MUST FAIL initially (TDD RED phase).
*
* @package KiviCare_API\Tests\Contract
*/
/**
* Appointment endpoints contract tests.
*/
class Test_Appointment_Endpoints_Contract extends KiviCare_API_Test_Case {
/**
* Test GET /wp-json/kivicare/v1/appointments endpoint contract.
*
* @test
*/
public function test_get_appointments_endpoint_contract() {
// This test will fail initially as the endpoint doesn't exist yet
$this->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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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/kivicare/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'] );
}
}
}