forked from Solo-Web-Works/Projects-Portfolio
Add GitHub provider adapter with full test coverage
This commit is contained in:
+5
-67
@@ -8,75 +8,13 @@ if ( ! file_exists( $autoload ) ) {
|
||||
require_once $autoload;
|
||||
|
||||
// Brain\Monkey sets up minimal function stubs (apply_filters, plugin_dir_path, etc.).
|
||||
// Must run BEFORE the WP function stubs in wp-stubs.php are loaded, because Patchwork
|
||||
// (used by Brain\Monkey) cannot redefine functions defined in the same file/import
|
||||
// chain that loaded Patchwork itself. Loading the stubs in a separate file imported
|
||||
// after Brain\Monkey\setUp() lets Patchwork track them properly.
|
||||
\Brain\Monkey\setUp();
|
||||
|
||||
// Define WP constants the helpers rely on.
|
||||
if ( ! defined( 'HOUR_IN_SECONDS' ) ) {
|
||||
define( 'HOUR_IN_SECONDS', 3600 );
|
||||
}
|
||||
if ( ! defined( 'WPINC' ) ) {
|
||||
define( 'WPINC', 'wp-includes' );
|
||||
}
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
define( 'ABSPATH', __DIR__ . '/' );
|
||||
}
|
||||
|
||||
// Provide minimal WordPress function stubs used by the providers under test.
|
||||
if ( ! function_exists( 'get_option' ) ) {
|
||||
function get_option( $key, $default = false ) { return $default; }
|
||||
}
|
||||
if ( ! function_exists( 'get_transient' ) ) {
|
||||
function get_transient( $key ) { return false; }
|
||||
}
|
||||
if ( ! function_exists( 'set_transient' ) ) {
|
||||
function set_transient( $key, $value, $expiration ) { return true; }
|
||||
}
|
||||
if ( ! function_exists( 'delete_transient' ) ) {
|
||||
function delete_transient( $key ) { return true; }
|
||||
}
|
||||
if ( ! function_exists( 'wp_remote_get' ) ) {
|
||||
function wp_remote_get( $url, $args = [] ) { return new \WP_Error( 'no_http', 'no http' ); }
|
||||
}
|
||||
if ( ! function_exists( 'is_wp_error' ) ) {
|
||||
function is_wp_error( $thing ) { return $thing instanceof \WP_Error; }
|
||||
}
|
||||
if ( ! function_exists( 'wp_remote_retrieve_response_code' ) ) {
|
||||
function wp_remote_retrieve_response_code( $response ) {
|
||||
if ( $response instanceof \WP_Error ) { return 0; }
|
||||
return $response['response']['code'] ?? 0;
|
||||
}
|
||||
}
|
||||
if ( ! function_exists( 'wp_remote_retrieve_body' ) ) {
|
||||
function wp_remote_retrieve_body( $response ) {
|
||||
if ( $response instanceof \WP_Error ) { return ''; }
|
||||
return $response['body'] ?? '';
|
||||
}
|
||||
}
|
||||
if ( ! function_exists( 'wp_remote_retrieve_header' ) ) {
|
||||
function wp_remote_retrieve_header( $response, $header ) {
|
||||
if ( $response instanceof \WP_Error ) { return ''; }
|
||||
$headers = $response['headers'] ?? [];
|
||||
$key = strtolower( $header );
|
||||
return $headers[ $key ] ?? '';
|
||||
}
|
||||
}
|
||||
if ( ! function_exists( 'error_log' ) ) {
|
||||
function error_log( $message ) { /* swallow during tests */ }
|
||||
}
|
||||
|
||||
// Minimal WP_Error stub if Brain\Monkey doesn't supply one.
|
||||
if ( ! class_exists( 'WP_Error' ) ) {
|
||||
class WP_Error {
|
||||
private $code;
|
||||
private $message;
|
||||
public function __construct( $code = '', $message = '' ) {
|
||||
$this->code = $code;
|
||||
$this->message = $message;
|
||||
}
|
||||
public function get_error_code() { return $this->code; }
|
||||
public function get_error_message() { return $this->message; }
|
||||
}
|
||||
}
|
||||
require_once __DIR__ . '/wp-stubs.php';
|
||||
|
||||
register_shutdown_function( function () {
|
||||
\Brain\Monkey\tearDown();
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/providers/interface-repository-provider.php';
|
||||
require_once __DIR__ . '/../includes/providers/class-github-provider.php';
|
||||
|
||||
/**
|
||||
* @covers \GitHub_Provider
|
||||
*/
|
||||
class GitHub_Provider_Test extends \PHPUnit\Framework\TestCase {
|
||||
|
||||
protected function setUp(): void {
|
||||
\Brain\Monkey\setUp();
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
\Brain\Monkey\tearDown();
|
||||
}
|
||||
|
||||
private function make( string $token = '' ): GitHub_Provider {
|
||||
return new GitHub_Provider( 'https://github.com/owner/repo', $token );
|
||||
}
|
||||
|
||||
public function test_get_id_and_label(): void {
|
||||
$p = $this->make();
|
||||
$this->assertSame( 'github', $p->get_id() );
|
||||
$this->assertSame( 'GitHub', $p->get_label() );
|
||||
}
|
||||
|
||||
public function test_get_repo_data_normalizes_response(): void {
|
||||
\Brain\Monkey\Functions\expect( 'wp_remote_get' )
|
||||
->once()
|
||||
->with( 'https://api.github.com/repos/owner/repo', \Brain\Monkey\Actions\anyArgs() )
|
||||
->andReturn( [
|
||||
'response' => [ 'code' => 200 ],
|
||||
'headers' => [],
|
||||
'body' => json_encode( [
|
||||
'owner' => [
|
||||
'avatar_url' => 'https://avatars.example/owner.png',
|
||||
'login' => 'owner',
|
||||
'html_url' => 'https://github.com/owner',
|
||||
],
|
||||
'updated_at' => '2026-01-02T03:04:05Z',
|
||||
'language' => 'PHP',
|
||||
'license' => [ 'name' => 'MIT' ],
|
||||
'stargazers_count' => 42,
|
||||
'forks_count' => 7,
|
||||
'open_issues_count' => 3,
|
||||
] ),
|
||||
] );
|
||||
|
||||
$data = $this->make()->get_repo_data();
|
||||
|
||||
$this->assertSame( 'owner', $data['owner']['login'] );
|
||||
$this->assertSame( 42, $data['stargazers_count'] );
|
||||
$this->assertSame( 'MIT', $data['license']['name'] );
|
||||
}
|
||||
|
||||
public function test_release_url_prefers_zip_asset(): void {
|
||||
$release_body = json_encode( [
|
||||
'tag_name' => 'v1.2.3',
|
||||
'assets' => [
|
||||
[ 'name' => 'project.tar.gz', 'browser_download_url' => 'https://example/x.tar.gz' ],
|
||||
[ 'name' => 'project.zip', 'browser_download_url' => 'https://example/x.zip' ],
|
||||
],
|
||||
] );
|
||||
|
||||
\Brain\Monkey\Functions\expect( 'wp_remote_get' )
|
||||
->twice()
|
||||
->with( 'https://api.github.com/repos/owner/repo/releases/latest', \Brain\Monkey\Actions\anyArgs() )
|
||||
->andReturn( [
|
||||
'response' => [ 'code' => 200 ],
|
||||
'headers' => [],
|
||||
'body' => $release_body,
|
||||
] );
|
||||
|
||||
$this->assertSame( 'https://example/x.zip', $this->make()->get_release_url() );
|
||||
$this->assertSame( 'v1.2.3', $this->make()->get_latest_version() );
|
||||
}
|
||||
|
||||
public function test_release_url_falls_back_to_zipball(): void {
|
||||
$release_body = json_encode( [
|
||||
'tag_name' => 'v0.0.1',
|
||||
'zipball_url' => 'https://api.github.com/repos/owner/repo/zipball/v0.0.1',
|
||||
'assets' => [],
|
||||
] );
|
||||
|
||||
\Brain\Monkey\Functions\expect( 'wp_remote_get' )
|
||||
->once()
|
||||
->andReturn( [
|
||||
'response' => [ 'code' => 200 ],
|
||||
'headers' => [],
|
||||
'body' => $release_body,
|
||||
] );
|
||||
|
||||
$this->assertSame(
|
||||
'https://api.github.com/repos/owner/repo/zipball/v0.0.1',
|
||||
$this->make()->get_release_url()
|
||||
);
|
||||
}
|
||||
|
||||
public function test_repo_browse_url_returns_input(): void {
|
||||
$this->assertSame(
|
||||
'https://github.com/owner/repo',
|
||||
$this->make()->get_repo_browse_url()
|
||||
);
|
||||
}
|
||||
|
||||
public function test_http_failure_returns_null(): void {
|
||||
\Brain\Monkey\Functions\expect( 'wp_remote_get' )
|
||||
->once()
|
||||
->andReturn( new \WP_Error( 'http_error', 'boom' ) );
|
||||
|
||||
$this->assertNull( $this->make()->get_repo_data() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
namespace Brain\Monkey\Actions {
|
||||
if ( ! function_exists( __NAMESPACE__ . '\\anyArgs' ) && function_exists( 'anyArgs' ) ) {
|
||||
function anyArgs( ...$args ) { return \anyArgs( ...$args ); }
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Shim: ensure Mockery's anyArgs() helper is loaded.
|
||||
if ( ! function_exists( 'anyArgs' ) ) {
|
||||
require_once dirname( __DIR__ ) . '/vendor/mockery/mockery/library/helpers.php';
|
||||
}
|
||||
|
||||
// Define WP constants the helpers rely on.
|
||||
if ( ! defined( 'HOUR_IN_SECONDS' ) ) {
|
||||
define( 'HOUR_IN_SECONDS', 3600 );
|
||||
}
|
||||
if ( ! defined( 'WPINC' ) ) {
|
||||
define( 'WPINC', 'wp-includes' );
|
||||
}
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
define( 'ABSPATH', __DIR__ . '/' );
|
||||
}
|
||||
|
||||
// Provide minimal WordPress function stubs used by the providers under test.
|
||||
if ( ! function_exists( 'get_option' ) ) {
|
||||
function get_option( $key, $default = false ) { return $default; }
|
||||
}
|
||||
if ( ! function_exists( 'get_transient' ) ) {
|
||||
function get_transient( $key ) { return false; }
|
||||
}
|
||||
if ( ! function_exists( 'set_transient' ) ) {
|
||||
function set_transient( $key, $value, $expiration ) { return true; }
|
||||
}
|
||||
if ( ! function_exists( 'delete_transient' ) ) {
|
||||
function delete_transient( $key ) { return true; }
|
||||
}
|
||||
if ( ! function_exists( 'wp_remote_get' ) ) {
|
||||
function wp_remote_get( $url, $args = [] ) { return new \WP_Error( 'no_http', 'no http' ); }
|
||||
}
|
||||
if ( ! function_exists( 'is_wp_error' ) ) {
|
||||
function is_wp_error( $thing ) { return $thing instanceof \WP_Error; }
|
||||
}
|
||||
if ( ! function_exists( 'wp_remote_retrieve_response_code' ) ) {
|
||||
function wp_remote_retrieve_response_code( $response ) {
|
||||
if ( $response instanceof \WP_Error ) { return 0; }
|
||||
return $response['response']['code'] ?? 0;
|
||||
}
|
||||
}
|
||||
if ( ! function_exists( 'wp_remote_retrieve_body' ) ) {
|
||||
function wp_remote_retrieve_body( $response ) {
|
||||
if ( $response instanceof \WP_Error ) { return ''; }
|
||||
return $response['body'] ?? '';
|
||||
}
|
||||
}
|
||||
if ( ! function_exists( 'wp_remote_retrieve_header' ) ) {
|
||||
function wp_remote_retrieve_header( $response, $header ) {
|
||||
if ( $response instanceof \WP_Error ) { return ''; }
|
||||
$headers = $response['headers'] ?? [];
|
||||
$key = strtolower( $header );
|
||||
return $headers[ $key ] ?? '';
|
||||
}
|
||||
}
|
||||
if ( ! function_exists( 'error_log' ) ) {
|
||||
function error_log( $message ) { /* swallow during tests */ }
|
||||
}
|
||||
|
||||
// Minimal WP_Error stub if Brain\Monkey doesn't supply one.
|
||||
if ( ! class_exists( 'WP_Error' ) ) {
|
||||
class WP_Error {
|
||||
private $code;
|
||||
private $message;
|
||||
public function __construct( $code = '', $message = '' ) {
|
||||
$this->code = $code;
|
||||
$this->message = $message;
|
||||
}
|
||||
public function get_error_code() { return $this->code; }
|
||||
public function get_error_message() { return $this->message; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user