From b40a4aaeab907b2170c8c750c1527881fc939774 Mon Sep 17 00:00:00 2001 From: David Stone Date: Fri, 7 Feb 2025 11:36:39 -0700 Subject: [PATCH] remove unneccessary dependencies --- .../class-dashboard-admin-page.php | 12 +- .../class-membership-edit-admin-page.php | 4 +- inc/deprecated/deprecated.php | 2 +- inc/domain-mapping/class-helper.php | 155 +++++++++++----- inc/functions/date.php | 12 +- inc/installers/class-core-installer.php | 2 +- inc/installers/class-migrator.php | 2 +- inc/managers/class-site-manager.php | 4 +- inc/models/class-discount-code.php | 4 +- inc/models/class-membership.php | 2 +- inc/tax/class-dashboard-taxes-tab.php | 12 +- .../Admin_Pages/Dashboard_Admin_Page_Test.php | 45 +++++ .../Membership_Edit_Admin_Page_Test.php | 63 +++++++ tests/WP_Ultimo/Date_Functions_Test.php | 11 ++ tests/WP_Ultimo/Models/Discount_Code_Test.php | 128 +++++++++++++ tests/WP_Ultimo/Models/Domain_Test.php | 44 +++++ tests/WP_Ultimo/Models/Membership_Test.php | 173 ++++++++++++++++++ .../Tax/Dashboard_Taxes_Tab_Test.php | 34 ++++ 18 files changed, 630 insertions(+), 79 deletions(-) create mode 100644 tests/Admin_Pages/Dashboard_Admin_Page_Test.php create mode 100644 tests/Admin_Pages/Membership_Edit_Admin_Page_Test.php create mode 100644 tests/WP_Ultimo/Date_Functions_Test.php create mode 100644 tests/WP_Ultimo/Models/Discount_Code_Test.php create mode 100644 tests/WP_Ultimo/Models/Domain_Test.php create mode 100644 tests/WP_Ultimo/Models/Membership_Test.php create mode 100644 tests/WP_Ultimo/Tax/Dashboard_Taxes_Tab_Test.php diff --git a/inc/admin-pages/class-dashboard-admin-page.php b/inc/admin-pages/class-dashboard-admin-page.php index f81602a..d49fd01 100644 --- a/inc/admin-pages/class-dashboard-admin-page.php +++ b/inc/admin-pages/class-dashboard-admin-page.php @@ -488,15 +488,11 @@ class Dashboard_Admin_Page extends Base_Admin_Page { $month_list = array(); - $start = date_i18n('Y-m-d 00:00:00', strtotime('first day of january this year')); + $current_year = date_i18n('Y'); - for ($i = 0; $i < 12; $i++) { - - $start_date = wu_date($start); - - $month_list[] = date_i18n('M y', $start_date->addMonths($i)->format('U')); - - } // end for; + for ($i = 1; $i <= 12; $i++) { + $month_list[] = date_i18n('M y', mktime(0, 0,0,$i,1, $current_year)); + } $statistics = new Dashboard_Statistics(array( 'start_date' => $this->start_date, diff --git a/inc/admin-pages/class-membership-edit-admin-page.php b/inc/admin-pages/class-membership-edit-admin-page.php index 82d91c2..801ca87 100644 --- a/inc/admin-pages/class-membership-edit-admin-page.php +++ b/inc/admin-pages/class-membership-edit-admin-page.php @@ -128,7 +128,7 @@ class Membership_Edit_Admin_Page extends Edit_Admin_Page { ), ); - $date = wu_date($swap_order->scheduled_date); + $date = new \DateTime($swap_order->scheduled_date); // translators: %s is the date, using the site format options $message = sprintf(__('There is a change scheduled to take place on this membership in %s. You can preview the changes here. Scheduled changes are usually created by downgrades.', 'wp-ultimo'), $date->format(get_option('date_format'))); @@ -1078,7 +1078,7 @@ class Membership_Edit_Admin_Page extends Edit_Admin_Page { ), ); - $date = wu_date($swap_order->scheduled_date); + $date = new \DateTime($swap_order->scheduled_date); // translators: %s is the date, using the site format options $message = sprintf(__('This is a preview. This page displays the final stage of the membership after the changes scheduled for %s. Saving here will persist these changes, so be careful.', 'wp-ultimo'), $date->format(get_option('date_format'))); diff --git a/inc/deprecated/deprecated.php b/inc/deprecated/deprecated.php index b044355..12997bb 100644 --- a/inc/deprecated/deprecated.php +++ b/inc/deprecated/deprecated.php @@ -1255,7 +1255,7 @@ class WU_Transactions { _deprecated_function(__CLASS__, '2.0.0', 'wu_date()'); - $date = wu_date(); + $date = new \DateTime(); return $type === 'mysql' ? $date->format('Y-m-d H:i:s') : $date->format('U'); diff --git a/inc/domain-mapping/class-helper.php b/inc/domain-mapping/class-helper.php index 830b7b6..c506c79 100644 --- a/inc/domain-mapping/class-helper.php +++ b/inc/domain-mapping/class-helper.php @@ -9,11 +9,10 @@ namespace WP_Ultimo\Domain_Mapping; -use Spatie\SslCertificate\SslCertificate; use Psr\Log\LogLevel; // Exit if accessed directly -defined('ABSPATH') || exit; +defined( 'ABSPATH' ) || exit; /** * Helper class for domain mapping functionality. @@ -49,7 +48,7 @@ class Helper { $site_url = site_url(); - $is_development_mode = preg_match('#(localhost|staging.*\.|\.local|\.test)#', $site_url); + $is_development_mode = preg_match( '#(localhost|staging.*\.|\.local|\.test)#', $site_url ); /** * Allow plugin developers to add additional tests @@ -61,21 +60,19 @@ class Helper { * @param string $site_url The site URL. * @return bool */ - return apply_filters('wu_is_development_mode', $is_development_mode, $site_url); - + return apply_filters( 'wu_is_development_mode', $is_development_mode, $site_url ); } // end is_development_mode; - /** - * Gets the local IP address of the network. - * - * Sometimes, this will be the same address as the public one, but we need different methods. - * - * @since 2.0.0 - * @return string|bool - */ - public static function get_local_network_ip() { - - return isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : false; + /** + * Gets the local IP address of the network. + * + * Sometimes, this will be the same address as the public one, but we need different methods. + * + * @since 2.0.0 + * @return string|bool + */ + public static function get_local_network_ip() { + return isset( $_SERVER['SERVER_ADDR'] ) ? $_SERVER['SERVER_ADDR'] : false; } // end get_local_network_ip; /** @@ -90,43 +87,38 @@ class Helper { */ public static function get_network_public_ip() { - if (self::is_development_mode()) { - + if ( self::is_development_mode() ) { $local_ip = self::get_local_network_ip(); /** * See more about this filter below, on this same method. */ - return apply_filters('wu_get_network_public_ip', $local_ip, true); - + return apply_filters( 'wu_get_network_public_ip', $local_ip, true ); } // end if; - $_ip_address = get_site_transient('wu_public_network_ip'); - - if (!$_ip_address) { + $_ip_address = get_site_transient( 'wu_public_network_ip' ); + if ( ! $_ip_address ) { $ip_address = false; - foreach (self::$providers as $provider_url) { + foreach ( self::$providers as $provider_url ) { + $response = wp_remote_get( + $provider_url, + array( + 'timeout' => 5, + ) + ); - $response = wp_remote_get($provider_url, array( - 'timeout' => 5, - )); - - if (!is_wp_error($response)) { - - $ip_address = trim(wp_remote_retrieve_body($response)); + if ( ! is_wp_error( $response ) ) { + $ip_address = trim( wp_remote_retrieve_body( $response ) ); continue; - } // end if; - } // end foreach; - set_site_transient('wu_public_network_ip', $ip_address, 10 * DAY_IN_SECONDS); + set_site_transient( 'wu_public_network_ip', $ip_address, 10 * DAY_IN_SECONDS ); $_ip_address = $ip_address; - } // end if; /** @@ -143,10 +135,8 @@ class Helper { * @param bool $local True if this is a local network (localhost, .dev, etc.), false otherwise. * @return string The new IP address. */ - return apply_filters('wu_get_network_public_ip', $_ip_address, false); - + return apply_filters( 'wu_get_network_public_ip', $_ip_address, false ); } // end get_network_public_ip; - /** * Checks if a given domain name has a valid associated SSL certificate. * @@ -156,24 +146,93 @@ class Helper { * @return boolean */ public static function has_valid_ssl_certificate($domain = '') { - $is_valid = false; + // Ensure the domain is not empty. + if (empty($domain)) { + return $is_valid; + } + + // Add 'https://' if not already present to use SSL context properly. + $domain = strpos($domain, 'https://') === 0 ? $domain : 'https://' . $domain; + try { + // Create SSL context to fetch the certificate. + $context = stream_context_create( + array( + 'ssl' => array( + 'capture_peer_cert' => true, + ), + ) + ); - $certificate = SslCertificate::createForHostName($domain); + // Open a stream to the domain over SSL. + $stream = @stream_socket_client( + 'ssl://' . parse_url($domain, PHP_URL_HOST) . ':443', + $errno, + $errstr, + 10, + STREAM_CLIENT_CONNECT, + $context + ); - $is_valid = $certificate->isValid($domain); // returns bool; + // If stream could not be established, SSL is invalid. + if (!$stream) { + throw new \Exception($errstr); + } + // Retrieve the certificate and parse its details. + $options = stream_context_get_options($context); + + if (isset($options['ssl']['peer_certificate'])) { + $cert = openssl_x509_parse($options['ssl']['peer_certificate']); + + if ($cert) { + // Verify the certificate's validity period. + $current_time = time(); + $valid_from = $cert['validFrom_time_t'] ?? 0; + $valid_to = $cert['validTo_time_t'] ?? 0; + + // Check if the certificate is currently valid. + if ($current_time >= $valid_from && $current_time <= $valid_to) { + $host = parse_url($domain, PHP_URL_HOST); + + // Check that the domain matches the certificate. + $common_name = $cert['subject']['CN'] ?? ''; // Common Name (CN) + $alt_names = $cert['extensions']['subjectAltName'] ?? ''; // Subject Alternative Names (SAN) + + // Parse SAN into an array if present. + $alt_names_array = array_filter(array_map('trim', explode(',', str_replace('DNS:', '', $alt_names)))); + $alt_names_array[] = $common_name; + // Check if the host matches either the CN, any SAN entry, or supports a wildcard match. + if ( + $host === $common_name || + in_array( $host, $alt_names_array, true ) + ) { + $is_valid = true; + } else { + foreach ($alt_names_array as $alt_name) { + if ( strpos($alt_name, '*.') === 0 && str_ends_with( $host, substr($alt_name, 1) )) { + $is_valid = true; + break; + } + } + } + } + } + } + + // Close the stream after processing. + fclose($stream); } catch (\Exception $e) { - - // translators: %s is the error message returned by the checker. - wu_log_add('domain-ssl-checks', sprintf(__('Certificate Invalid: %s', 'wp-ultimo'), $e->getMessage()), LogLevel::ERROR); - - } // end try; + // Log the error message. + wu_log_add( + 'domain-ssl-checks', + sprintf(__('Certificate Invalid: %s', 'wp-ultimo'), $e->getMessage()), + LogLevel::ERROR + ); + } return $is_valid; - } // end has_valid_ssl_certificate; - } // end class Helper; diff --git a/inc/functions/date.php b/inc/functions/date.php index 32c2577..e101e3a 100644 --- a/inc/functions/date.php +++ b/inc/functions/date.php @@ -55,17 +55,15 @@ function wu_validate_date($date, $format = 'Y-m-d H:i:s') { * @see https://carbon.nesbot.com/docs/ * * @param string|false $date Parsable date string. - * @return \Carbon\Carbon + * @return \DateTime */ function wu_date($date = false) { if (!wu_validate_date($date)) { - $date = date_i18n('Y-m-d H:i:s'); + } - } // end if; - - return \Carbon\Carbon::parse($date); + return \DateTime::createFromFormat('Y-m-d H:i:s', $date); } // end wu_date; @@ -86,7 +84,9 @@ function wu_get_days_ago($date_1, $date_2 = false) { $datetime_2 = wu_date($date_2); - return - $datetime_1->diffInDays($datetime_2, false); + $dateIntervar = $datetime_1->diff($datetime_2, false); + + return - $dateIntervar->days; } // end wu_get_days_ago; diff --git a/inc/installers/class-core-installer.php b/inc/installers/class-core-installer.php index 5951391..2f67b68 100644 --- a/inc/installers/class-core-installer.php +++ b/inc/installers/class-core-installer.php @@ -40,7 +40,7 @@ class Core_Installer extends Base_Installer { if (!(defined('SUNRISE') && SUNRISE)) { // translators: %s is a URL to a documentation link. - $closte_message = sprintf(__('You are using Closte and they prevent the wp-config.php file from being written to. Follow these instructions to do it manually.'), wu_get_documentation_url('wp-ultimo-closte-config')); + $closte_message = sprintf(__('You are using Closte and they prevent the wp-config.php file from being written to. Follow these instructions to do it manually.', 'wp-ultimo' ), wu_get_documentation_url('wp-ultimo-closte-config')); throw new \Exception($closte_message); diff --git a/inc/installers/class-migrator.php b/inc/installers/class-migrator.php index f1d68fb..cf9e43b 100644 --- a/inc/installers/class-migrator.php +++ b/inc/installers/class-migrator.php @@ -1851,7 +1851,7 @@ class Migrator extends Base_Installer { */ $subscription_creation_date = wu_date($subscription->created_at); - $trial_end_date = $subscription_creation_date->addDays($subscription->trial)->endOfDay(); + $trial_end_date = $subscription_creation_date->add( new \DateInterval( 'P' . $subscription->trial . 'D' ) )->setTime( 23, 59, 59 ); $date_trial_end = $trial_end_date->format('Y-m-d 23:59:59'); diff --git a/inc/managers/class-site-manager.php b/inc/managers/class-site-manager.php index 11cf2e2..b18bcd9 100644 --- a/inc/managers/class-site-manager.php +++ b/inc/managers/class-site-manager.php @@ -321,9 +321,9 @@ class Site_Manager extends Base_Manager { // If membership is cancelled we do not add the grace period $grace_period = $status !== Membership_Status::CANCELLED ? (int) wu_get_setting('block_frontend_grace_period', 0) : 0; - $expiration_time = wu_date($membership->get_date_expiration())->timestamp + $grace_period * DAY_IN_SECONDS; + $expiration_time = wu_date($membership->get_date_expiration())->getTimestamp() + $grace_period * DAY_IN_SECONDS; - if ($expiration_time < wu_date()->timestamp) { + if ($expiration_time < wu_date()->getTimestamp()) { $checkout_pages = \WP_Ultimo\Checkout\Checkout_Pages::get_instance(); diff --git a/inc/models/class-discount-code.php b/inc/models/class-discount-code.php index 36c0625..1e5bbc1 100644 --- a/inc/models/class-discount-code.php +++ b/inc/models/class-discount-code.php @@ -519,6 +519,8 @@ class Discount_Code extends Base_Model { return new \WP_Error('discount_code', __('This coupon code is not valid.', 'wp-ultimo')); + return new \WP_Error( 'discount_code', __( 'The coupon code is not valid yet.', 'wp-ultimo' ) ); + } // end if; } // end if; @@ -527,7 +529,7 @@ class Discount_Code extends Base_Model { $expiration_date_instance = wu_date($expiration_date); - if ($now > $expiration_date) { + if ($now > $expiration_date_instance) { return new \WP_Error('discount_code', __('This coupon code is not valid.', 'wp-ultimo')); diff --git a/inc/models/class-membership.php b/inc/models/class-membership.php index 9e741f1..a10485e 100644 --- a/inc/models/class-membership.php +++ b/inc/models/class-membership.php @@ -2477,7 +2477,7 @@ class Membership extends Base_Model { } // end if; - return floor($today->diffInDays($expiration_date)); + return floor($today->diff($expiration_date)->days); } // end get_remaining_days_in_cycle; diff --git a/inc/tax/class-dashboard-taxes-tab.php b/inc/tax/class-dashboard-taxes-tab.php index 8d39e7d..48a1bef 100644 --- a/inc/tax/class-dashboard-taxes-tab.php +++ b/inc/tax/class-dashboard-taxes-tab.php @@ -198,15 +198,11 @@ class Dashboard_Taxes_Tab { $month_list = array(); - $start = date_i18n('Y-m-d 00:00:00', strtotime('first day of january this year')); + $current_year = date_i18n('Y'); - for ($i = 0; $i < 12; $i++) { - - $start_date = wu_date($start); - - $month_list[] = date_i18n('M y', $start_date->addMonths($i)->format('U')); - - } // end for; + for ($i = 1; $i <= 12; $i++) { + $month_list[] = date_i18n('M y', mktime(0, 0,0,$i,1, $current_year)); + } wp_register_script('wu-tax-stats', wu_get_asset('tax-statistics.js', 'js'), array('jquery', 'wu-functions', 'wu-ajax-list-table', 'moment', 'wu-block-ui', 'dashboard', 'wu-apex-charts', 'wu-vue-apex-charts'), wu_get_version(), true); diff --git a/tests/Admin_Pages/Dashboard_Admin_Page_Test.php b/tests/Admin_Pages/Dashboard_Admin_Page_Test.php new file mode 100644 index 0000000..8d3bfda --- /dev/null +++ b/tests/Admin_Pages/Dashboard_Admin_Page_Test.php @@ -0,0 +1,45 @@ +getMockBuilder( Dashboard_Admin_Page::class ) + ->disableOriginalConstructor() + ->setMethods( [ 'output' ] ) + ->getMock(); + + // Fake dates for testing + $dashboard_admin_page->start_date = '2023-01-01'; + $dashboard_admin_page->end_date = '2023-01-31'; + + // Execute register_scripts method + $dashboard_admin_page->register_scripts(); + + // Assert scripts are registered + $this->assertTrue( wp_script_is( 'wu-apex-charts', 'registered' ) ); + $this->assertTrue( wp_script_is( 'wu-vue-apex-charts', 'registered' ) ); + $this->assertTrue( wp_script_is( 'wu-dashboard-stats', 'registered' ) ); + + // Assert styles are registered + $this->assertTrue( wp_style_is( 'wu-apex-charts', 'registered' ) ); + + // Assert scripts are enqueued + $this->assertTrue( wp_script_is( 'wu-dashboard-stats', 'enqueued' ) ); + + // Verify localized script data is correct + $localized_vars = wp_scripts()->get_data( 'wu-dashboard-stats', 'data' ); + echo( $localized_vars ); + $this->assertStringContainsString( '"month_list":["Jan ', $localized_vars ); + $this->assertStringContainsString( '"today":"', $localized_vars ); // Check that today is included + $this->assertStringContainsString( '"new_mrr":"New MRR"', $localized_vars ); + } + +} \ No newline at end of file diff --git a/tests/Admin_Pages/Membership_Edit_Admin_Page_Test.php b/tests/Admin_Pages/Membership_Edit_Admin_Page_Test.php new file mode 100644 index 0000000..2fb1108 --- /dev/null +++ b/tests/Admin_Pages/Membership_Edit_Admin_Page_Test.php @@ -0,0 +1,63 @@ +generate_fake_memberships(); + $this->swap_time = strtotime( '+100 days' ); + + $this->membership = current( $faker->get_fake_data_generated( 'memberships' ) ); + $cart = new Cart( array() ); + $this->membership->schedule_swap( $cart, gmdate( 'Y-m-d H:i:s', $this->swap_time ) ); + // Mock Membership_Edit_Admin_Page with dependencies and methods. + $this->membership_edit_admin_page = new Membership_Edit_Admin_Page(); + } + + + /** + * Tests that page_loaded calls add_swap_notices. + */ + public function test_page_loaded_calls_add_swap_notices() { + $_REQUEST['id'] = $this->membership->get_id(); + $this->membership_edit_admin_page->page_loaded(); + + $membership = $this->membership_edit_admin_page->get_object(); + $this->assertInstanceOf( Membership::class, $membership ); + $this->assertEquals( $membership->get_id(), $this->membership->get_id() ); + $this->assertTrue( $this->membership_edit_admin_page->edit ); + + $notices = \WP_Ultimo()->notices->get_notices( 'network-admin' ); + $this->assertNotEmpty( $notices ); + $notice = array_shift( $notices ); + $this->assertEquals( 'warning', $notice['type'] ); + $this->assertFalse( $notice['dismissible_key'] ); + $this->assertNotEmpty( $notice['actions'] ); + $this->assertStringContainsString( gmdate( get_option( 'date_format' ), $this->swap_time ), $notice['message'] ); + } +} diff --git a/tests/WP_Ultimo/Date_Functions_Test.php b/tests/WP_Ultimo/Date_Functions_Test.php new file mode 100644 index 0000000..dcdf0cd --- /dev/null +++ b/tests/WP_Ultimo/Date_Functions_Test.php @@ -0,0 +1,11 @@ +assertEquals( -30, $days ); + } +} diff --git a/tests/WP_Ultimo/Models/Discount_Code_Test.php b/tests/WP_Ultimo/Models/Discount_Code_Test.php new file mode 100644 index 0000000..c8a3eba --- /dev/null +++ b/tests/WP_Ultimo/Models/Discount_Code_Test.php @@ -0,0 +1,128 @@ +set_active( true ); + + $result = $discount_code->is_valid(); + + $this->assertTrue( $result ); + } + + /** + * Tests that an inactive discount code returns an error. + */ + public function test_is_valid_inactive_discount_code() { + $discount_code = new Discount_Code(); + $discount_code->set_active( false ); + + $result = $discount_code->is_valid(); + + $this->assertInstanceOf( WP_Error::class, $result ); + $this->assertEquals( 'discount_code', $result->get_error_code() ); + $this->assertEquals( 'This coupon code is not valid.', $result->get_error_message() ); + } + + /** + * Tests that a discount code with max uses returns an error after being used maximum times. + */ + public function test_is_valid_max_uses_exceeded() { + $discount_code = new Discount_Code(); + $discount_code->set_active( true ); + $discount_code->set_max_uses( 5 ); + $discount_code->set_uses( 5 ); + + $result = $discount_code->is_valid(); + + $this->assertInstanceOf( WP_Error::class, $result ); + $this->assertEquals( 'discount_code', $result->get_error_code() ); + $this->assertEquals( 'This discount code was already redeemed the maximum amount of times allowed.', + $result->get_error_message() ); + } + + /** + * Tests that a discount code before the start date is invalid. + */ + public function test_is_valid_before_start_date() { + $discount_code = new Discount_Code(); + $discount_code->set_active( true ); + $discount_code->set_date_start( date( 'Y-m-d H:i:s', strtotime( '+1 day' ) ) ); + + $result = $discount_code->is_valid(); + + $this->assertInstanceOf( WP_Error::class, $result ); + $this->assertEquals( 'discount_code', $result->get_error_code() ); + $this->assertEquals( 'This coupon code is not valid.', $result->get_error_message() ); + } + + /** + * Tests that a discount code after the expiration date is invalid. + */ + public function test_is_valid_after_expiration_date() { + $discount_code = new Discount_Code(); + $discount_code->set_active( true ); + $discount_code->set_date_expiration( date( 'Y-m-d H:i:s', strtotime( '-1 day' ) ) ); + + $result = $discount_code->is_valid(); + + $this->assertInstanceOf( WP_Error::class, $result ); + $this->assertEquals( 'discount_code', $result->get_error_code() ); + $this->assertEquals( 'This coupon code is not valid.', $result->get_error_message() ); + } + + /** + * Tests that a discount code limited to specific products returns true for allowed products. + */ + public function test_is_valid_for_allowed_product() { + $product_id = 123; + $discount_code = new Discount_Code(); + $discount_code->set_active( true ); + $discount_code->set_limit_products( true ); + $discount_code->set_allowed_products( [ $product_id ] ); + + $result = $discount_code->is_valid( $product_id ); + + $this->assertTrue( $result ); + } + + /** + * Tests that a discount code limited to specific products returns an error for disallowed products. + */ + public function test_is_valid_for_disallowed_product() { + $allowed_product_id = 123; + $disallowed_product_id = 456; + $discount_code = new Discount_Code(); + $discount_code->set_active( true ); + $discount_code->set_limit_products( true ); + $discount_code->set_allowed_products( [ $allowed_product_id ] ); + + $result = $discount_code->is_valid( $disallowed_product_id ); + + $this->assertInstanceOf( WP_Error::class, $result ); + $this->assertEquals( 'discount_code', $result->get_error_code() ); + $this->assertEquals( 'This coupon code is not valid.', $result->get_error_message() ); + } + + /** + * Tests that a discount code with no product limits returns true. + */ + public function test_is_valid_no_product_limits() { + $discount_code = new Discount_Code(); + $discount_code->set_active( true ); + $discount_code->set_limit_products( false ); + + $result = $discount_code->is_valid(); + + $this->assertTrue( $result ); + } +} \ No newline at end of file diff --git a/tests/WP_Ultimo/Models/Domain_Test.php b/tests/WP_Ultimo/Models/Domain_Test.php new file mode 100644 index 0000000..17be507 --- /dev/null +++ b/tests/WP_Ultimo/Models/Domain_Test.php @@ -0,0 +1,44 @@ +set_domain( 'dogs.4thelols.uk' ); + + // Assert that it returns true for a valid SSL certificate. + $this->assertTrue( $domain->has_valid_ssl_certificate() ); + } + + /** + * Test that has_valid_ssl_certificate returns false when the SSL certificate is invalid. + */ + public function test_has_valid_ssl_certificate_with_invalid_certificate() { + // Mocking a domain with an invalid SSL certificate. + $domain = new Domain(); + $domain->set_domain( 'eeeeeeeeeeeeeeeeauauexample.com' ); + + // Assert that it returns false for an invalid SSL certificate. + $this->assertFalse( $domain->has_valid_ssl_certificate() ); + } + + /** + * Test that has_valid_ssl_certificate handles empty domain. + */ + public function test_has_valid_ssl_certificate_with_empty_domain() { + // Mocking a domain with an empty value. + $domain = new Domain(); + $domain->set_domain( '' ); + + // Assert that it returns false for an empty domain. + $this->assertFalse( $domain->has_valid_ssl_certificate() ); + } +} \ No newline at end of file diff --git a/tests/WP_Ultimo/Models/Membership_Test.php b/tests/WP_Ultimo/Models/Membership_Test.php new file mode 100644 index 0000000..03bb2a2 --- /dev/null +++ b/tests/WP_Ultimo/Models/Membership_Test.php @@ -0,0 +1,173 @@ +membership = new Membership(); + + // Set a default customer ID. + $this->membership->set_customer_id( 123 ); + } + + /** + * Test if the customer is allowed access to the membership. + */ + public function test_is_customer_allowed() { + // Admins with 'manage_network' capability should always return true. + $admin_user_id = $this->factory()->user->create( array( 'role' => 'administrator' ) ); + grant_super_admin( $admin_user_id ); + wp_set_current_user( $admin_user_id ); + $this->assertTrue( $this->membership->is_customer_allowed(), 'Failed asserting that admin is allowed.' ); + + // Regular customers are allowed if IDs match. + $customer_id = 123; + $this->assertTrue( + $this->membership->is_customer_allowed( $customer_id ), + 'Failed asserting that customer with matching ID is allowed.' + ); + + // Regular customers are denied if IDs do not match. + $wrong_customer_id = 456; + wp_set_current_user( $wrong_customer_id ); + $this->assertFalse( + $this->membership->is_customer_allowed( $wrong_customer_id ), + 'Failed asserting that customer with non-matching ID is denied.' + ); + } + + /** + * Test adding a product to the membership. + */ + public function test_add_product() { + // Add a product with a specific ID and quantity. + $quantity = 2; + $faker = new Faker(); + $faker->generate_fake_products(); + /** @var Product $product */ + $product = $faker->get_fake_data_generated( 'products' )[0]; + $product_id = $product->get_id(); + + $this->membership->add_product( $product_id, $quantity ); + + // Verify that the product is added with the correct quantity. + $addon_products = $this->membership->get_addon_ids(); + $this->assertContains( $product_id, $addon_products, 'Failed asserting that product ID was added.' ); + $this->assertEquals( + $quantity, + $this->membership->get_addon_products()[0]['quantity'], + 'Failed asserting that the product quantity is correct.' + ); + + // Add more of the same product and check the updated quantity. + $additional_quantity = 3; + $this->membership->add_product( $product_id, $additional_quantity ); + + $this->assertEquals( + $quantity + $additional_quantity, + $this->membership->get_addon_products()[0]['quantity'], + 'Failed asserting that the quantity was updated correctly for the same product.' + ); + } + + /** + * Test removing a product from the membership. + */ + public function test_remove_product() { + // Add a product with a specific quantity. + $quantity = 5; + $faker = new Faker(); + $faker->generate_fake_products(); + /** @var Product $product */ + $product = $faker->get_fake_data_generated( 'products' )[0]; + $product_id = $product->get_id(); + + $this->membership->add_product( $product_id, $quantity ); + + // Remove some of the product's quantity. + $remove_quantity = 3; + $this->membership->remove_product( $product_id, $remove_quantity ); + + // Verify the updated quantity. + $this->assertEquals( + $quantity - $remove_quantity, + $this->membership->get_addon_products()[0]['quantity'], + 'Failed asserting that the quantity was reduced correctly.' + ); + + // Remove the remaining quantity and verify it is removed. + $this->membership->remove_product( $product_id, $quantity ); + $addon_products = $this->membership->get_addon_ids(); + $this->assertNotContains( $product_id, $addon_products, 'Failed asserting that the product was removed.' ); + } + + /** + * Test get_remaining_days_in_cycle() method. + */ + public function test_get_remaining_days_in_cycle() { + $this->membership->set_amount( 12.99 ); + // Case 1: Non-recurring membership should return 10000. + $this->membership->set_recurring( false ); + $this->assertEquals( + 10000, + $this->membership->get_remaining_days_in_cycle(), + 'Failed asserting that non-recurring membership returns 10000.' + ); + + // Case 2: Invalid expiration date should return 0. + $this->membership->set_recurring( true ); + $this->membership->set_date_expiration( 'invalid-date' ); // Setting an invalid date. + $this->assertEquals( + 0, + $this->membership->get_remaining_days_in_cycle(), + 'Failed asserting that an invalid expiration date returns 0.' + ); + + // Case 3: No expiration date should return 0. + $this->membership->set_date_expiration( '' ); + $this->assertEquals( + 0, + $this->membership->get_remaining_days_in_cycle(), + 'Failed asserting that no expiration date returns 0.' + ); + + // Case 4: Expiration date is in the future and remaining days are calculated properly. + $today = new \DateTime( 'now', new \DateTimeZone( 'UTC' ) ); + $today->add( new \DateInterval( 'P10D' ) ); + $this->membership->set_date_expiration( $today->format( 'Y-m-d H:i:s' ) ); + $remaining_days = $this->membership->get_remaining_days_in_cycle(); + $this->assertEquals( + 10, + $remaining_days, + 'Failed asserting that 10 days remain when expiration date is 10 days in the future.' + ); + + // Case 5: Expiration date is in the past, should return 0. + $this->membership->set_date_expiration( date( 'Y-m-d H:i:s', strtotime( '-5 days' ) ) ); + $remaining_days = $this->membership->get_remaining_days_in_cycle(); + $this->assertEquals( + 0, + $remaining_days, + 'Failed asserting that remaining days return 0 when the expiration date is in the past.' + ); + } +} diff --git a/tests/WP_Ultimo/Tax/Dashboard_Taxes_Tab_Test.php b/tests/WP_Ultimo/Tax/Dashboard_Taxes_Tab_Test.php new file mode 100644 index 0000000..0f031ca --- /dev/null +++ b/tests/WP_Ultimo/Tax/Dashboard_Taxes_Tab_Test.php @@ -0,0 +1,34 @@ +getMockBuilder( Dashboard_Taxes_Tab::class ) + ->disableOriginalConstructor() + ->setMethods( array( 'output' ) ) + ->getMock(); + + // Execute register_scripts method. + $dashboard_admin_page->register_scripts(); + + // Assert scripts are registered. + $this->assertTrue( wp_script_is( 'wu-tax-stats', 'registered' ) ); + + // Assert scripts are enqueued. + $this->assertTrue( wp_script_is( 'wu-tax-stats', 'enqueued' ) ); + + // Verify localized script data is correct. + $localized_vars = wp_scripts()->get_data( 'wu-tax-stats', 'data' ); + $this->assertStringContainsString( '"month_list":["Jan ', $localized_vars ); + $this->assertStringContainsString( '"today":"', $localized_vars ); // Check that today is included. + $this->assertStringContainsString( '"net_profit_label":"Net Profit"', $localized_vars ); + } +}