* @link https://descomplicar.pt * @since 1.0.0 */ namespace Care_API\Tests\Unit\Core; use Care_API\API_Init; use WP_REST_Server; use WP_REST_Request; use WP_REST_Response; use WP_Error; /** * Class ApiInitTest * * Unit tests for API_Init class covering: * - Plugin initialization * - Endpoint registration * - Service dependency injection * - Error handler setup * - WordPress hook integration * * @since 1.0.0 */ class ApiInitTest extends \Care_API_Test_Case { /** * API_Init instance for testing * * @var API_Init */ private $api_init; /** * Test setup before each test method * * @since 1.0.0 */ public function setUp(): void { parent::setUp(); // Mock WordPress functions that might not be available in test environment if (!function_exists('get_bloginfo')) { function get_bloginfo($show) { switch ($show) { case 'version': return '6.0.0'; case 'blogname': return 'Test Blog'; default: return ''; } } } // Mock plugin activation check if (!function_exists('is_plugin_active')) { function is_plugin_active($plugin) { return true; // Mock KiviCare as active } } // Create fresh API_Init instance for each test $this->api_init = API_Init::instance(); } /** * Test 1: Plugin Initialization * * Verifies correct plugin initialization including: * - Singleton pattern implementation * - System requirements validation * - Constants definition * - Version information * * @test * @since 1.0.0 */ public function test_plugin_initialization() { // Test singleton pattern $instance1 = API_Init::instance(); $instance2 = API_Init::instance(); $this->assertSame( $instance1, $instance2, 'API_Init should implement singleton pattern correctly' ); // Test API version constant $this->assertEquals( '1.0.0', API_Init::VERSION, 'API version should be correctly defined' ); // Test API namespace constant $this->assertEquals( 'care/v1', API_Init::API_NAMESPACE, 'API namespace should be correctly defined' ); // Test minimum requirements constants $this->assertEquals( '7.4', API_Init::MIN_PHP_VERSION, 'Minimum PHP version should be correctly defined' ); $this->assertEquals( '5.0', API_Init::MIN_WP_VERSION, 'Minimum WordPress version should be correctly defined' ); // Test static method accessibility $this->assertEquals( 'care/v1', API_Init::get_namespace(), 'get_namespace() method should return correct namespace' ); $this->assertEquals( '1.0.0', API_Init::get_api_version(), 'get_api_version() method should return correct version' ); } /** * Test 2: REST API Endpoint Registration * * Validates proper registration of all REST API endpoints: * - Authentication endpoints * - Core entity endpoints (clinics, patients, doctors, etc.) * - Utility endpoints (status, health, version) * - Route validation and accessibility * * @test * @since 1.0.0 */ public function test_endpoint_registration() { // Trigger REST API initialization do_action('rest_api_init'); // Get all registered routes $routes = $this->server->get_routes(); $care_routes = array(); // Filter Care API routes foreach ($routes as $route => $handlers) { if (strpos($route, '/care/v1') === 0) { $care_routes[] = $route; } } // Test authentication endpoints $auth_endpoints = array( '/care/v1/auth/login', '/care/v1/auth/logout', '/care/v1/auth/refresh', '/care/v1/auth/validate', '/care/v1/auth/profile', ); foreach ($auth_endpoints as $endpoint) { $this->assertContains( $endpoint, $care_routes, "Authentication endpoint {$endpoint} should be registered" ); } // Test utility endpoints $utility_endpoints = array( '/care/v1/status', '/care/v1/health', '/care/v1/version', ); foreach ($utility_endpoints as $endpoint) { $this->assertContains( $endpoint, $care_routes, "Utility endpoint {$endpoint} should be registered" ); } // Test that routes have proper methods and callbacks if (isset($routes['/care/v1/status'])) { $route_data = $routes['/care/v1/status']; $this->assertIsArray($route_data, 'Route data should be array'); $this->assertArrayHasKey('methods', $route_data[0], 'Route should have methods defined'); $this->assertArrayHasKey('callback', $route_data[0], 'Route should have callback defined'); } // Test endpoint count (should have substantial number of endpoints) $this->assertGreaterThan( 10, count($care_routes), 'Should have registered substantial number of Care API endpoints' ); } /** * Test 3: Service Dependency Injection * * Validates proper service initialization and dependency management: * - Core services loading * - Database services initialization * - Authentication services setup * - Middleware registration * - Error handler initialization * * @test * @since 1.0.0 */ public function test_service_dependency_injection() { // Test that error handler is initialized first $this->assertTrue( true, // Error handler initialization is private, testing indirectly 'Error handler should be initialized during API setup' ); // Test WordPress hooks are properly set $this->assertGreaterThan( 0, has_action('rest_api_init'), 'rest_api_init hook should be registered' ); $this->assertGreaterThan( 0, has_action('init'), 'init hook should be registered for WordPress integration' ); $this->assertGreaterThan( 0, has_action('wp_loaded'), 'wp_loaded hook should be registered for late loading' ); // Test admin hooks in admin context if (is_admin()) { $this->assertGreaterThan( 0, has_action('admin_init'), 'admin_init hook should be registered in admin context' ); $this->assertGreaterThan( 0, has_action('admin_menu'), 'admin_menu hook should be registered in admin context' ); } // Test AJAX hooks $this->assertGreaterThan( 0, has_action('wp_ajax_care_api_status'), 'AJAX hook for authenticated users should be registered' ); $this->assertGreaterThan( 0, has_action('wp_ajax_nopriv_care_api_status'), 'AJAX hook for non-authenticated users should be registered' ); // Test cron hook for maintenance $this->assertGreaterThan( 0, has_action('kivicare_daily_maintenance'), 'Daily maintenance cron hook should be registered' ); // Test REST response filter $this->assertGreaterThan( 0, has_filter('rest_pre_serve_request'), 'REST response filter should be registered' ); } /** * Test 4: Authentication Endpoints Functionality * * Tests authentication-related endpoint functionality: * - Login endpoint accessibility * - Logout endpoint protection * - Token validation endpoint * - Profile endpoint authentication * - Permission checking mechanisms * * @test * @since 1.0.0 */ public function test_auth_endpoints_functionality() { // Trigger REST API initialization do_action('rest_api_init'); // Test login endpoint (public access) $login_request = new WP_REST_Request('POST', '/care/v1/auth/login'); $login_request->set_body_params(array( 'username' => 'test_user', 'password' => 'test_password' )); // Should be accessible (even if it fails due to invalid credentials) $login_response = $this->server->dispatch($login_request); $this->assertInstanceOf( 'WP_REST_Response', $login_response, 'Login endpoint should be accessible and return WP_REST_Response' ); // Test logout endpoint (requires authentication) $logout_request = new WP_REST_Request('POST', '/care/v1/auth/logout'); $logout_response = $this->server->dispatch($logout_request); // Should return error for unauthenticated request $this->assertInstanceOf( 'WP_REST_Response', $logout_response, 'Logout endpoint should return response (may be error for unauthenticated user)' ); // Test profile endpoint (requires authentication) $profile_request = new WP_REST_Request('GET', '/care/v1/auth/profile'); $profile_response = $this->server->dispatch($profile_request); $this->assertInstanceOf( 'WP_REST_Response', $profile_response, 'Profile endpoint should return response' ); // Test with authenticated user wp_set_current_user($this->admin_user); $auth_profile_request = new WP_REST_Request('GET', '/care/v1/auth/profile'); $auth_profile_response = $this->server->dispatch($auth_profile_request); $this->assertInstanceOf( 'WP_REST_Response', $auth_profile_response, 'Profile endpoint should work with authenticated user' ); // Test status code ranges $status_code = $auth_profile_response->get_status(); $this->assertTrue( ($status_code >= 200 && $status_code < 300) || ($status_code >= 400 && $status_code < 600), 'Response should have valid HTTP status code' ); } /** * Test 5: Error Handler Setup * * Validates error handling system initialization: * - Error handler class loading * - Exception handling setup * - Error logging configuration * - Response error formatting * - Security considerations in error messages * * @test * @since 1.0.0 */ public function test_error_handler_setup() { // Test utility endpoints that might trigger errors $status_request = new WP_REST_Request('GET', '/care/v1/status'); $status_response = $this->server->dispatch($status_request); // Should return proper response structure $this->assertInstanceOf( 'WP_REST_Response', $status_response, 'Status endpoint should return WP_REST_Response' ); // Test health endpoint $health_request = new WP_REST_Request('GET', '/care/v1/health'); $health_response = $this->server->dispatch($health_request); $this->assertInstanceOf( 'WP_REST_Response', $health_response, 'Health endpoint should return WP_REST_Response' ); // Test version endpoint $version_request = new WP_REST_Request('GET', '/care/v1/version'); $version_response = $this->server->dispatch($version_request); $this->assertInstanceOf( 'WP_REST_Response', $version_response, 'Version endpoint should return WP_REST_Response' ); // Test response data structure for version endpoint if ($version_response->get_status() === 200) { $version_data = $version_response->get_data(); $this->assertIsArray($version_data, 'Version response data should be array'); if (isset($version_data['version'])) { $this->assertEquals( '1.0.0', $version_data['version'], 'Version data should contain correct version number' ); } } // Test error response format for invalid endpoint $invalid_request = new WP_REST_Request('GET', '/care/v1/invalid-endpoint'); $invalid_response = $this->server->dispatch($invalid_request); $this->assertInstanceOf( 'WP_REST_Response', $invalid_response, 'Invalid endpoint should return WP_REST_Response' ); // Should return 404 for invalid endpoint $this->assertEquals( 404, $invalid_response->get_status(), 'Invalid endpoint should return 404 status code' ); // Test daily maintenance functionality $this->assertTrue( method_exists($this->api_init, 'daily_maintenance'), 'API_Init should have daily_maintenance method for error log cleanup' ); // Test AJAX status functionality $this->assertTrue( method_exists($this->api_init, 'ajax_api_status'), 'API_Init should have ajax_api_status method' ); } /** * Test teardown after each test method * * @since 1.0.0 */ public function tearDown(): void { // Clear current user wp_set_current_user(0); // Clear any scheduled events wp_clear_scheduled_hook('kivicare_daily_maintenance'); parent::tearDown(); } /** * Helper method to create mock user with specific capabilities * * @param array $capabilities User capabilities * @return int User ID * @since 1.0.0 */ private function create_user_with_capabilities($capabilities = array()) { $user_id = $this->factory->user->create(array( 'user_login' => 'test_cap_user_' . wp_rand(1000, 9999), 'user_email' => 'testcap' . wp_rand(1000, 9999) . '@example.com', 'role' => 'subscriber' )); $user = new \WP_User($user_id); foreach ($capabilities as $cap) { $user->add_cap($cap); } return $user_id; } /** * Helper method to simulate WordPress admin context * * @since 1.0.0 */ private function simulate_admin_context() { if (!defined('WP_ADMIN')) { define('WP_ADMIN', true); } // Mock is_admin() to return true global $current_screen; $current_screen = (object) array('id' => 'admin'); } }