diff --git a/src/php/Client/Cloud_API.php b/src/php/Client/Cloud_API.php index db6b94dd..d3697158 100644 --- a/src/php/Client/Cloud_API.php +++ b/src/php/Client/Cloud_API.php @@ -162,9 +162,9 @@ private function unpack_request_json( $response ): ?array { $json = json_decode( $body, true ); - return is_array( $json ) && isset( $json['data'] ) - ? $json['data'] - : null; + // Return the whole decoded envelope; each caller extracts the key it needs (search reads + // `snippets`/`meta`/`available_filters`, single reads `snippet`, taxonomy reads `data`). + return is_array( $json ) ? $json : null; } /** @@ -346,7 +346,7 @@ public function get_single_snippet_from_cloud( int $cloud_id ): Cloud_Snippet { $url = sprintf( '%s/public/getsnippet/%s', $this->get_cloud_api_url(), $cloud_id ); $response = wp_remote_get( $url ); $cloud_snippet = self::unpack_request_json( $response ); - return new Cloud_Snippet( $cloud_snippet['snippet'] ); + return new Cloud_Snippet( is_array( $cloud_snippet ) ? ( $cloud_snippet['snippet'] ?? [] ) : [] ); } /** diff --git a/tests/phpunit/test-cloud-api-snippet.php b/tests/phpunit/test-cloud-api-snippet.php new file mode 100644 index 00000000..b12d31c3 --- /dev/null +++ b/tests/phpunit/test-cloud-api-snippet.php @@ -0,0 +1,145 @@ +mock_response = null; + $this->api = new Cloud_API(); + + add_filter( 'pre_http_request', [ $this, 'mock_request' ], 10, 3 ); + } + + /** + * Tear down after each test. + * + * @return void + */ + public function tear_down() { + remove_filter( 'pre_http_request', [ $this, 'mock_request' ], 10 ); + + parent::tear_down(); + } + + /** + * Intercept getsnippet / getsnippetrevision requests and return the mock response. + * + * @param mixed $preempt Short-circuit value. + * @param array $parsed_args Request arguments. + * @param string $url Request URL. + * + * @return array|WP_Error|mixed + */ + public function mock_request( $preempt, array $parsed_args, string $url ) { + if ( false === strpos( $url, 'public/getsnippet' ) ) { + return $preempt; + } + + return null !== $this->mock_response ? $this->mock_response : $preempt; + } + + /** + * Wrap a JSON body in a mock HTTP response array. + * + * @param array $body Response body to encode. + * + * @return array + */ + private function json_response( array $body ): array { + return [ + 'body' => wp_json_encode( $body ), + 'response' => [ 'code' => 200 ], + ]; + } + + /** + * A single snippet response (`{ snippet: {...}, success: true }`) is parsed into a Cloud_Snippet. + * + * @return void + */ + public function test_get_single_snippet_parses_snippet(): void { + $this->mock_response = $this->json_response( + [ + 'snippet' => [ + 'id' => 5, + 'name' => 'Test Snippet', + 'code' => ' true, + ] + ); + + $result = $this->api->get_single_snippet_from_cloud( 5 ); + + $this->assertInstanceOf( Cloud_Snippet::class, $result ); + $this->assertSame( 'Test Snippet', $result->name ); + } + + /** + * A failed/empty single-snippet request returns an empty Cloud_Snippet without erroring. + * + * @return void + */ + public function test_get_single_snippet_handles_empty_response(): void { + $this->mock_response = new WP_Error( 'http_request_failed', 'down' ); + + $result = $this->api->get_single_snippet_from_cloud( 5 ); + + $this->assertInstanceOf( Cloud_Snippet::class, $result ); + $this->assertSame( '', $result->name ); + } + + /** + * A revision response (`{ snippet_revision: N }`) returns the revision string. + * + * @return void + */ + public function test_get_cloud_snippet_revision_returns_revision(): void { + $this->mock_response = $this->json_response( [ 'snippet_revision' => 7 ] ); + + $this->assertSame( '7', $this->api->get_cloud_snippet_revision( '5' ) ); + } + + /** + * A failed/empty revision request returns null without erroring. + * + * @return void + */ + public function test_get_cloud_snippet_revision_handles_empty_response(): void { + $this->mock_response = new WP_Error( 'http_request_failed', 'down' ); + + $this->assertNull( $this->api->get_cloud_snippet_revision( '5' ) ); + } +}