Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions tests/WP_Ultimo/API_Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php
/**
* Tests for API class.
*
* @package WP_Ultimo\Tests
*/

namespace WP_Ultimo;

use WP_UnitTestCase;

/**
* Test class for API.
*/
class API_Test extends WP_UnitTestCase {

/**
* @var API
*/
private API $api;

/**
* Set up test fixtures.
*/
public function set_up(): void {

parent::set_up();
$this->api = API::get_instance();
}

/**
* Test singleton returns correct instance.
*/
public function test_singleton_returns_correct_instance(): void {

$this->assertInstanceOf(API::class, $this->api);
}

/**
* Test singleton returns same instance.
*/
public function test_singleton_returns_same_instance(): void {

$this->assertSame(API::get_instance(), API::get_instance());
}

/**
* Test init registers hooks.
*/
public function test_init_registers_hooks(): void {

$this->api->init();

$this->assertGreaterThan(0, has_action('init', [$this->api, 'add_settings']));
$this->assertGreaterThan(0, has_action('rest_api_init', [$this->api, 'register_routes']));
$this->assertGreaterThan(0, has_filter('rest_request_after_callbacks', [$this->api, 'log_api_errors']));
$this->assertGreaterThan(0, has_filter('rest_authentication_errors', [$this->api, 'maybe_bypass_wp_auth']));
}

/**
* Test maybe_bypass_wp_auth with true returns true.
*/
public function test_maybe_bypass_wp_auth_already_bypassed(): void {

$result = $this->api->maybe_bypass_wp_auth(true);

$this->assertTrue($result);
}

/**
* Test maybe_bypass_wp_auth with null (no other handler).
*/
public function test_maybe_bypass_wp_auth_null(): void {

$result = $this->api->maybe_bypass_wp_auth(null);

// Should return null or a value (depends on request URI).
$this->assertNotTrue($result);
}

/**
* Test maybe_bypass_wp_auth with WP_Error.
*/
public function test_maybe_bypass_wp_auth_wp_error(): void {

$error = new \WP_Error('test', 'test error');
$result = $this->api->maybe_bypass_wp_auth($error);

// Should pass through or handle the error.
$this->assertNotTrue($result);
}
}
242 changes: 242 additions & 0 deletions tests/WP_Ultimo/Addon_Repository_Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
<?php
/**
* Tests for the Addon_Repository class.
*/

namespace WP_Ultimo;

use WP_UnitTestCase;

/**
* @group addon-repository
*/
class Addon_Repository_Test extends WP_UnitTestCase {

/**
* @var Addon_Repository
*/
private $repo;

public function set_up() {
parent::set_up();
$this->repo = new Addon_Repository();
}

public function tear_down() {
remove_all_filters('upgrader_pre_download');
remove_all_filters('http_request_args');
parent::tear_down();
}

// ------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------

public function test_constructor_creates_instance() {
$this->assertInstanceOf(Addon_Repository::class, $this->repo);
}

// ------------------------------------------------------------------
// init
// ------------------------------------------------------------------

public function test_init_registers_upgrader_pre_download_filter() {
$this->repo->init();

$this->assertNotFalse(has_filter('upgrader_pre_download', [$this->repo, 'upgrader_pre_download']));
}

// ------------------------------------------------------------------
// get_access_token
// ------------------------------------------------------------------

public function test_get_access_token_returns_empty_string_without_refresh_token() {
// No refresh token saved
wu_save_option('wu-refresh-token', '');

$token = $this->repo->get_access_token();
$this->assertIsString($token);
$this->assertEmpty($token);
}

public function test_get_access_token_returns_string() {
$token = $this->repo->get_access_token();
$this->assertIsString($token);
}

// ------------------------------------------------------------------
// set_update_download_headers
// ------------------------------------------------------------------

public function test_set_update_download_headers_adds_auth_for_matching_url() {
// Set the authorization header via reflection
$ref = new \ReflectionProperty(Addon_Repository::class, 'authorization_header');

if (PHP_VERSION_ID < 80100) {
$ref->setAccessible(true);
}

$ref->setValue($this->repo, 'Bearer test_token_123');

$parsed_args = ['headers' => []];
$url = MULTISITE_ULTIMATE_UPDATE_URL . 'some/path';

Check warning on line 82 in tests/WP_Ultimo/Addon_Repository_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Equals sign not aligned with surrounding assignments; expected 9 spaces but found 1 space

$result = $this->repo->set_update_download_headers($parsed_args, $url);

$this->assertEquals('Bearer test_token_123', $result['headers']['Authorization']);
}

public function test_set_update_download_headers_ignores_non_matching_url() {
$ref = new \ReflectionProperty(Addon_Repository::class, 'authorization_header');

if (PHP_VERSION_ID < 80100) {
$ref->setAccessible(true);
}

$ref->setValue($this->repo, 'Bearer test_token_123');

$parsed_args = ['headers' => []];
$url = 'https://example.com/other/path';

$result = $this->repo->set_update_download_headers($parsed_args, $url);

$this->assertArrayNotHasKey('Authorization', $result['headers']);
}

public function test_set_update_download_headers_ignores_empty_auth_header() {
$parsed_args = ['headers' => []];
$url = MULTISITE_ULTIMATE_UPDATE_URL . 'some/path';

$result = $this->repo->set_update_download_headers($parsed_args, $url);

$this->assertArrayNotHasKey('Authorization', $result['headers']);
}

// ------------------------------------------------------------------
// delete_tokens
// ------------------------------------------------------------------

public function test_delete_tokens_clears_all_token_data() {
// Set some token data first
wu_save_option('wu-refresh-token', 'test_refresh');
set_transient('wu-access-token', 'test_access', 3600);
set_site_transient('wu-addons-list', ['addon1'], 3600);
set_site_transient('wu_has_addon_purchase', '1', 3600);

$this->repo->delete_tokens();

$this->assertEmpty(wu_get_option('wu-refresh-token'));
$this->assertFalse(get_transient('wu-access-token'));
$this->assertFalse(get_site_transient('wu-addons-list'));
$this->assertFalse(get_site_transient('wu_has_addon_purchase'));
}

// ------------------------------------------------------------------
// clear_addon_purchase_cache
// ------------------------------------------------------------------

public function test_clear_addon_purchase_cache_deletes_transient() {
set_site_transient('wu_has_addon_purchase', '1', 3600);

Addon_Repository::clear_addon_purchase_cache();

$this->assertFalse(get_site_transient('wu_has_addon_purchase'));
}

// ------------------------------------------------------------------
// has_addon_purchase
// ------------------------------------------------------------------

public function test_has_addon_purchase_returns_false_without_access_token() {
// No refresh token = no access token
wu_save_option('wu-refresh-token', '');
delete_site_transient('wu_has_addon_purchase');

$result = $this->repo->has_addon_purchase();
$this->assertFalse($result);
}

public function test_has_addon_purchase_returns_cached_true() {
set_site_transient('wu_has_addon_purchase', '1', 3600);

$result = $this->repo->has_addon_purchase();
$this->assertTrue($result);
}

public function test_has_addon_purchase_returns_cached_false() {
set_site_transient('wu_has_addon_purchase', '0', 3600);

$result = $this->repo->has_addon_purchase();
$this->assertFalse($result);
}

public function test_has_addon_purchase_returns_bool() {
$result = $this->repo->has_addon_purchase();
$this->assertIsBool($result);
}

// ------------------------------------------------------------------
// get_oauth_url
// ------------------------------------------------------------------

public function test_get_oauth_url_returns_string() {
$url = $this->repo->get_oauth_url();
$this->assertIsString($url);
}

public function test_get_oauth_url_contains_base_url() {
$url = $this->repo->get_oauth_url();
$this->assertStringContainsString(MULTISITE_ULTIMATE_UPDATE_URL, $url);
}

public function test_get_oauth_url_contains_oauth_authorize() {
$url = $this->repo->get_oauth_url();
$this->assertStringContainsString('oauth/authorize', $url);
}

public function test_get_oauth_url_contains_response_type() {
$url = $this->repo->get_oauth_url();
$this->assertStringContainsString('response_type=code', $url);
}

public function test_get_oauth_url_contains_client_id() {
$url = $this->repo->get_oauth_url();
$this->assertStringContainsString('client_id=', $url);
}

public function test_get_oauth_url_contains_redirect_uri() {
$url = $this->repo->get_oauth_url();
$this->assertStringContainsString('redirect_uri=', $url);
}

// ------------------------------------------------------------------
// get_user_data
// ------------------------------------------------------------------

public function test_get_user_data_returns_empty_array_without_token() {
wu_save_option('wu-refresh-token', '');
delete_transient('wu-access-token');

$data = $this->repo->get_user_data();
$this->assertIsArray($data);
$this->assertEmpty($data);
}

// ------------------------------------------------------------------
// decrypt_value (private, tested via reflection)
// ------------------------------------------------------------------

public function test_decrypt_value_returns_string() {
$method = new \ReflectionMethod(Addon_Repository::class, 'decrypt_value');

if (PHP_VERSION_ID < 80100) {
$method->setAccessible(true);
}

// Provide data with enough bytes for IV (16 bytes) + some cipher text
$fake_data = str_repeat('A', 32); // 16 bytes IV + 16 bytes cipher text
$result = $method->invoke($this->repo, base64_encode($fake_data));
// Result will be empty string or decrypted string (likely empty since data is invalid)
$this->assertIsString($result);
}
}
Loading
Loading