-
Notifications
You must be signed in to change notification settings - Fork 3
test: REST PHPUnit tests #212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Kallyan01
wants to merge
16
commits into
main
Choose a base branch
from
test/rest-phpunit-tests
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
4e009dd
tests: added tests for Abstract_REST_Controller
Kallyan01 8a8b05b
tests: added tests for Basic_Options_Controller
Kallyan01 f3c8e78
tests: added tests for Governing_Data_Controller
Kallyan01 b86d5ea
tests: added tests for Governing_Data_Handler
Kallyan01 cbe7e46
tests: added tests for Search_Controller
Kallyan01 03ba681
tests: enhanced testcases and fixed bugs
Kallyan01 779b27c
chore: fixed testcase failures and added suggestions
Kallyan01 32192aa
tests: rename test methods for clarity and update URL formatting
Kallyan01 7750b9b
Merge branch 'main' into test/rest-phpunit-tests
Kallyan01 9e2ca65
Merge branch 'main' into test/rest-phpunit-tests
justlevine bd6262b
chore: removed unwanted comments for cleaner test code
Kallyan01 fdd963d
chore: add CoversClass annotation for Abstract_REST_Controller in tes…
Kallyan01 8c5cd4e
chore: refactor REST controller tests to use WP_REST_Server and strea…
Kallyan01 b7a642e
chore: add unit tests for Governing_Data_Controller and Search_Contro…
Kallyan01 a7a61de
test: mock outbound HTTP via pre_http_request filter
Kallyan01 332e021
Merge branch 'main' into test/rest-phpunit-tests
Kallyan01 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
351 changes: 351 additions & 0 deletions
351
tests/php/Unit/Modules/Rest/Basic_Options_ControllerTest.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,351 @@ | ||
| <?php | ||
| /** | ||
| * Tests for Basic_Options_Controller. | ||
| * | ||
| * @package OneSearch\Tests\Unit\Modules\Rest | ||
| */ | ||
|
|
||
| declare( strict_types = 1 ); | ||
|
|
||
| namespace OneSearch\Tests\Unit\Modules\Rest; | ||
|
|
||
| use OneSearch\Modules\Rest\Abstract_REST_Controller; | ||
| use OneSearch\Modules\Rest\Basic_Options_Controller; | ||
| use OneSearch\Modules\Settings\Settings; | ||
| use OneSearch\Tests\TestCase; | ||
| use PHPUnit\Framework\Attributes\CoversClass; | ||
| use WP_REST_Request; | ||
|
|
||
| /** | ||
| * Tests for the basic options REST endpoints. | ||
| */ | ||
| #[CoversClass( Basic_Options_Controller::class )] | ||
| #[CoversClass( Abstract_REST_Controller::class )] | ||
| class Basic_Options_ControllerTest extends TestCase { | ||
| /** | ||
| * REST server. | ||
| */ | ||
| private ?\WP_REST_Server $server; | ||
|
|
||
| /** | ||
| * {@inheritDoc} | ||
| */ | ||
| public function set_up(): void { | ||
| parent::set_up(); | ||
|
|
||
| global $wp_rest_server; | ||
| $wp_rest_server = new \WP_REST_Server(); | ||
| $this->server = $wp_rest_server; | ||
|
|
||
| // Register the controller's routes on rest_api_init. | ||
| ( new Basic_Options_Controller() )->register_hooks(); | ||
| do_action( 'rest_api_init' ); | ||
|
|
||
| // Most endpoints require manage_options; authenticate as an admin. | ||
| $admin_id = self::factory()->user->create( [ 'role' => 'administrator' ] ); | ||
| wp_set_current_user( $admin_id ); | ||
| } | ||
|
Kallyan01 marked this conversation as resolved.
|
||
|
|
||
| /** | ||
| * {@inheritDoc} | ||
| */ | ||
| public function tear_down(): void { | ||
| global $wp_rest_server; | ||
| $wp_rest_server = null; | ||
|
|
||
| parent::tear_down(); | ||
| } | ||
|
|
||
| /** | ||
| * Verify all expected routes are registered. | ||
| */ | ||
| public function test_register_routes_registers_expected_endpoints(): void { | ||
| $routes = $this->server->get_routes(); | ||
| $ns = '/' . Basic_Options_Controller::NAMESPACE; | ||
|
|
||
| $this->assertArrayHasKey( $ns . '/site-type', $routes ); | ||
| $this->assertArrayHasKey( $ns . '/shared-sites', $routes ); | ||
| $this->assertArrayHasKey( $ns . '/health-check', $routes ); | ||
| $this->assertArrayHasKey( $ns . '/secret-key', $routes ); | ||
| $this->assertArrayHasKey( $ns . '/governing-site', $routes ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns governing site type when configured. | ||
| */ | ||
| public function test_get_site_type_returns_governing(): void { | ||
| update_option( Settings::OPTION_SITE_TYPE, Settings::SITE_TYPE_GOVERNING ); | ||
|
|
||
| $request = new WP_REST_Request( 'GET', '/onesearch/v1/site-type' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertSame( 200, $response->get_status() ); | ||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertSame( Settings::SITE_TYPE_GOVERNING, $data['site_type'] ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns consumer site type when configured. | ||
| */ | ||
| public function test_get_site_type_returns_consumer(): void { | ||
| update_option( Settings::OPTION_SITE_TYPE, Settings::SITE_TYPE_CONSUMER ); | ||
|
|
||
| $request = new WP_REST_Request( 'GET', '/onesearch/v1/site-type' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertSame( Settings::SITE_TYPE_CONSUMER, $data['site_type'] ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns null when site type is not set. | ||
| */ | ||
| public function test_get_site_type_returns_null_when_unset(): void { | ||
| delete_option( Settings::OPTION_SITE_TYPE ); | ||
|
|
||
| $request = new WP_REST_Request( 'GET', '/onesearch/v1/site-type' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertNull( $data['site_type'] ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns empty array when no shared sites are configured. | ||
| */ | ||
| public function test_get_shared_sites_returns_empty_when_unset(): void { | ||
| delete_option( Settings::OPTION_GOVERNING_SHARED_SITES ); | ||
|
|
||
| $request = new WP_REST_Request( 'GET', '/onesearch/v1/shared-sites' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertSame( [], $data['shared_sites'] ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns stored shared sites as a numerically-indexed array. | ||
| */ | ||
| public function test_get_shared_sites_returns_indexed_array(): void { | ||
| update_option( Settings::OPTION_SITE_TYPE, Settings::SITE_TYPE_GOVERNING ); | ||
| Settings::set_shared_sites( | ||
| [ | ||
| [ | ||
| 'name' => 'Brand A', | ||
| 'url' => 'https://brand-a.example.com', | ||
| 'api_key' => 'key-a', | ||
| ], | ||
| [ | ||
| 'name' => 'Brand B', | ||
| 'url' => 'https://brand-b.example.com', | ||
| 'api_key' => 'key-b', | ||
| ], | ||
| ] | ||
| ); | ||
|
|
||
| $request = new WP_REST_Request( 'GET', '/onesearch/v1/shared-sites' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertCount( 2, $data['shared_sites'] ); | ||
| $this->assertSame( 'Brand A', $data['shared_sites'][0]['name'] ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns 400 when duplicate site URLs exist. | ||
| */ | ||
| public function test_set_shared_sites_rejects_duplicate_urls(): void { | ||
| $request = new WP_REST_Request( 'POST', '/onesearch/v1/shared-sites' ); | ||
| $request->set_body( | ||
| wp_json_encode( | ||
| [ | ||
| 'sites_data' => [ | ||
| [ | ||
| 'url' => 'https://dup.example.com', | ||
| 'name' => 'Dup 1', | ||
| 'api_key' => 'k1', | ||
| ], | ||
| [ | ||
| 'url' => 'https://dup.example.com', | ||
| 'name' => 'Dup 2', | ||
| 'api_key' => 'k2', | ||
| ], | ||
| ], | ||
| ] | ||
| ) | ||
| ); | ||
| $request->set_header( 'Content-Type', 'application/json' ); | ||
|
|
||
| $response = $this->server->dispatch( $request ); | ||
|
|
||
| $this->assertSame( 400, $response->get_status() ); | ||
| $this->assertSame( 'duplicate_site_url', $response->get_data()['code'] ); | ||
| } | ||
|
|
||
| /** | ||
| * Saves valid sites data successfully. | ||
| */ | ||
| public function test_set_shared_sites_returns_success_for_valid_data(): void { | ||
| $request = new WP_REST_Request( 'POST', '/onesearch/v1/shared-sites' ); | ||
| $request->set_body( | ||
| wp_json_encode( | ||
| [ | ||
| 'sites_data' => [ | ||
| [ | ||
| 'url' => 'https://site-a.example.com', | ||
| 'name' => 'Site A', | ||
| 'api_key' => 'ka', | ||
| ], | ||
| [ | ||
| 'url' => 'https://site-b.example.com', | ||
| 'name' => 'Site B', | ||
| 'api_key' => 'kb', | ||
| ], | ||
| ], | ||
| ] | ||
| ) | ||
| ); | ||
| $request->set_header( 'Content-Type', 'application/json' ); | ||
|
|
||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertSame( 200, $response->get_status() ); | ||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertCount( 2, $data['shared_sites'] ); | ||
|
|
||
| // Verify data was actually persisted to the database. | ||
| $stored = Settings::get_shared_sites(); | ||
| $this->assertCount( 2, $stored ); | ||
| } | ||
|
|
||
| /** | ||
| * Handles empty sites_data gracefully. | ||
| */ | ||
| public function test_set_shared_sites_allows_empty_sites_data(): void { | ||
| $request = new WP_REST_Request( 'POST', '/onesearch/v1/shared-sites' ); | ||
| $request->set_body( wp_json_encode( [ 'sites_data' => [] ] ) ); | ||
| $request->set_header( 'Content-Type', 'application/json' ); | ||
|
|
||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertSame( [], $data['shared_sites'] ); | ||
| } | ||
|
|
||
| /** | ||
| * Handles missing sites_data key gracefully. | ||
| */ | ||
| public function test_set_shared_sites_handles_missing_sites_data_key(): void { | ||
| $request = new WP_REST_Request( 'POST', '/onesearch/v1/shared-sites' ); | ||
| $request->set_body( wp_json_encode( [ 'other' => 'data' ] ) ); | ||
| $request->set_header( 'Content-Type', 'application/json' ); | ||
|
|
||
| $response = $this->server->dispatch( $request ); | ||
|
|
||
| // sites_data is required by the route schema → request fails validation. | ||
| $this->assertSame( 400, $response->get_status() ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns success response. | ||
| */ | ||
| public function test_health_check_returns_success(): void { | ||
| $request = new WP_REST_Request( 'GET', '/onesearch/v1/health-check' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertArrayHasKey( 'message', $data ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns stored governing site URL. | ||
| */ | ||
| public function test_get_governing_site_returns_stored_url(): void { | ||
| Settings::set_parent_site_url( 'https://governing.example.com' ); | ||
|
|
||
| $request = new WP_REST_Request( 'GET', '/onesearch/v1/governing-site' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertSame( 'https://governing.example.com', $data['governing_site_url'] ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns null when governing site not configured. | ||
| */ | ||
| public function test_get_governing_site_returns_null_when_unset(): void { | ||
| delete_option( Settings::OPTION_CONSUMER_PARENT_SITE_URL ); | ||
|
|
||
| $request = new WP_REST_Request( 'GET', '/onesearch/v1/governing-site' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertNull( $data['governing_site_url'] ); | ||
| } | ||
|
|
||
| /** | ||
| * Removes the governing site option and returns success. | ||
| */ | ||
| public function test_remove_governing_site_deletes_option(): void { | ||
| Settings::set_parent_site_url( 'https://governing.example.com' ); | ||
|
|
||
| $request = new WP_REST_Request( 'DELETE', '/onesearch/v1/governing-site' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertNull( Settings::get_parent_site_url() ); | ||
| } | ||
|
|
||
| /** | ||
| * Succeeds even when no governing site was previously set. | ||
| */ | ||
| public function test_remove_governing_site_succeeds_when_already_unset(): void { | ||
| delete_option( Settings::OPTION_CONSUMER_PARENT_SITE_URL ); | ||
|
|
||
| $request = new WP_REST_Request( 'DELETE', '/onesearch/v1/governing-site' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| } | ||
|
|
||
| /** | ||
| * GET secret-key returns a non-empty key (auto-generated if absent). | ||
| */ | ||
| public function test_get_secret_key_returns_key(): void { | ||
| $request = new WP_REST_Request( 'GET', '/onesearch/v1/secret-key' ); | ||
| $response = $this->server->dispatch( $request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertNotEmpty( $data['secret_key'] ); | ||
| } | ||
|
|
||
| /** | ||
| * PUT secret-key regenerates and returns a new key. | ||
| */ | ||
| public function test_regenerate_secret_key_returns_new_key(): void { | ||
| // Get the current key first. | ||
| $get_request = new WP_REST_Request( 'GET', '/onesearch/v1/secret-key' ); | ||
| $old_key = $this->server->dispatch( $get_request )->get_data()['secret_key']; | ||
|
|
||
| // Regenerate via PUT. | ||
| $put_request = new WP_REST_Request( 'PUT', '/onesearch/v1/secret-key' ); | ||
| $response = $this->server->dispatch( $put_request ); | ||
| $data = $response->get_data(); | ||
|
|
||
| $this->assertTrue( $data['success'] ); | ||
| $this->assertNotEmpty( $data['secret_key'] ); | ||
| $this->assertNotSame( $old_key, $data['secret_key'] ); | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.