- 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>
316 lines
11 KiB
PHP
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'] );
|
|
}
|
|
}
|
|
} |