760 lines
25 KiB
PHP
760 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* Yoone订阅插件测试套件
|
|
*
|
|
* 用于测试插件的核心功能和集成
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* 测试套件类
|
|
*/
|
|
class Yoone_Test_Suite {
|
|
|
|
/**
|
|
* 测试结果
|
|
*/
|
|
private $test_results = array();
|
|
|
|
/**
|
|
* 构造函数
|
|
*/
|
|
public function __construct() {
|
|
add_action('admin_menu', array($this, 'add_test_menu'));
|
|
add_action('wp_ajax_yoone_run_tests', array($this, 'ajax_run_tests'));
|
|
}
|
|
|
|
/**
|
|
* 添加测试菜单
|
|
*/
|
|
public function add_test_menu() {
|
|
add_submenu_page(
|
|
'yoone-subscriptions',
|
|
__('功能测试', 'yoone-subscriptions'),
|
|
__('测试', 'yoone-subscriptions'),
|
|
'manage_options',
|
|
'yoone-tests',
|
|
array($this, 'display_test_page')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 显示测试页面
|
|
*/
|
|
public function display_test_page() {
|
|
?>
|
|
<div class="wrap">
|
|
<h1><?php _e('Yoone订阅插件功能测试', 'yoone-subscriptions'); ?></h1>
|
|
|
|
<div class="test-controls">
|
|
<button type="button" class="button button-primary" id="run-all-tests">
|
|
<?php _e('运行所有测试', 'yoone-subscriptions'); ?>
|
|
</button>
|
|
<button type="button" class="button" id="run-subscription-tests">
|
|
<?php _e('订阅测试', 'yoone-subscriptions'); ?>
|
|
</button>
|
|
<button type="button" class="button" id="run-payment-tests">
|
|
<?php _e('支付测试', 'yoone-subscriptions'); ?>
|
|
</button>
|
|
<button type="button" class="button" id="run-bundle-tests">
|
|
<?php _e('捆绑测试', 'yoone-subscriptions'); ?>
|
|
</button>
|
|
<button type="button" class="button" id="run-cron-tests">
|
|
<?php _e('定时任务测试', 'yoone-subscriptions'); ?>
|
|
</button>
|
|
</div>
|
|
|
|
<div id="test-progress" style="display: none;">
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" style="width: 0%"></div>
|
|
</div>
|
|
<p class="progress-text">准备测试...</p>
|
|
</div>
|
|
|
|
<div id="test-results">
|
|
<h2><?php _e('测试结果', 'yoone-subscriptions'); ?></h2>
|
|
<div class="test-summary">
|
|
<span class="test-count">总计: <strong>0</strong></span>
|
|
<span class="test-passed">通过: <strong>0</strong></span>
|
|
<span class="test-failed">失败: <strong>0</strong></span>
|
|
<span class="test-skipped">跳过: <strong>0</strong></span>
|
|
</div>
|
|
<div class="test-details"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.test-controls {
|
|
margin: 20px 0;
|
|
padding: 15px;
|
|
background: #f9f9f9;
|
|
border: 1px solid #ddd;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.test-controls .button {
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.progress-bar {
|
|
width: 100%;
|
|
height: 20px;
|
|
background: #f0f0f0;
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
margin: 10px 0;
|
|
}
|
|
|
|
.progress-fill {
|
|
height: 100%;
|
|
background: #007cba;
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
.progress-text {
|
|
text-align: center;
|
|
margin: 10px 0;
|
|
}
|
|
|
|
.test-summary {
|
|
display: flex;
|
|
gap: 20px;
|
|
margin: 20px 0;
|
|
padding: 15px;
|
|
background: #fff;
|
|
border: 1px solid #ddd;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.test-summary span {
|
|
padding: 5px 10px;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.test-count { background: #e9ecef; }
|
|
.test-passed { background: #d4edda; color: #155724; }
|
|
.test-failed { background: #f8d7da; color: #721c24; }
|
|
.test-skipped { background: #fff3cd; color: #856404; }
|
|
|
|
.test-item {
|
|
margin: 10px 0;
|
|
padding: 15px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 3px;
|
|
background: #fff;
|
|
}
|
|
|
|
.test-item.passed {
|
|
border-left: 4px solid #28a745;
|
|
}
|
|
|
|
.test-item.failed {
|
|
border-left: 4px solid #dc3545;
|
|
}
|
|
|
|
.test-item.skipped {
|
|
border-left: 4px solid #ffc107;
|
|
}
|
|
|
|
.test-name {
|
|
font-weight: bold;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.test-description {
|
|
color: #666;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.test-result {
|
|
font-family: monospace;
|
|
background: #f8f9fa;
|
|
padding: 10px;
|
|
border-radius: 3px;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.test-error {
|
|
color: #dc3545;
|
|
background: #f8d7da;
|
|
padding: 10px;
|
|
border-radius: 3px;
|
|
margin-top: 10px;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
jQuery(document).ready(function($) {
|
|
var testSuite = {
|
|
init: function() {
|
|
this.bindEvents();
|
|
},
|
|
|
|
bindEvents: function() {
|
|
$('#run-all-tests').on('click', function() {
|
|
testSuite.runTests('all');
|
|
});
|
|
|
|
$('#run-subscription-tests').on('click', function() {
|
|
testSuite.runTests('subscription');
|
|
});
|
|
|
|
$('#run-payment-tests').on('click', function() {
|
|
testSuite.runTests('payment');
|
|
});
|
|
|
|
$('#run-bundle-tests').on('click', function() {
|
|
testSuite.runTests('bundle');
|
|
});
|
|
|
|
$('#run-cron-tests').on('click', function() {
|
|
testSuite.runTests('cron');
|
|
});
|
|
},
|
|
|
|
runTests: function(suite) {
|
|
$('#test-progress').show();
|
|
$('.test-controls .button').prop('disabled', true);
|
|
|
|
$.ajax({
|
|
url: ajaxurl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'yoone_run_tests',
|
|
suite: suite,
|
|
nonce: '<?php echo wp_create_nonce('yoone_test_nonce'); ?>'
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
testSuite.displayResults(response.data);
|
|
} else {
|
|
alert('测试运行失败: ' + response.data.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
alert('测试请求失败');
|
|
},
|
|
complete: function() {
|
|
$('#test-progress').hide();
|
|
$('.test-controls .button').prop('disabled', false);
|
|
}
|
|
});
|
|
},
|
|
|
|
displayResults: function(results) {
|
|
var summary = results.summary;
|
|
var tests = results.tests;
|
|
|
|
// 更新摘要
|
|
$('.test-count strong').text(summary.total);
|
|
$('.test-passed strong').text(summary.passed);
|
|
$('.test-failed strong').text(summary.failed);
|
|
$('.test-skipped strong').text(summary.skipped);
|
|
|
|
// 显示测试详情
|
|
var detailsHtml = '';
|
|
tests.forEach(function(test) {
|
|
detailsHtml += '<div class="test-item ' + test.status + '">';
|
|
detailsHtml += '<div class="test-name">' + test.name + '</div>';
|
|
detailsHtml += '<div class="test-description">' + test.description + '</div>';
|
|
|
|
if (test.result) {
|
|
detailsHtml += '<div class="test-result">' + test.result + '</div>';
|
|
}
|
|
|
|
if (test.error) {
|
|
detailsHtml += '<div class="test-error">' + test.error + '</div>';
|
|
}
|
|
|
|
detailsHtml += '</div>';
|
|
});
|
|
|
|
$('.test-details').html(detailsHtml);
|
|
}
|
|
};
|
|
|
|
testSuite.init();
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* AJAX运行测试
|
|
*/
|
|
public function ajax_run_tests() {
|
|
check_ajax_referer('yoone_test_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(array('message' => __('权限不足', 'yoone-subscriptions')));
|
|
}
|
|
|
|
$suite = sanitize_text_field($_POST['suite']);
|
|
|
|
$results = $this->run_test_suite($suite);
|
|
|
|
wp_send_json_success($results);
|
|
}
|
|
|
|
/**
|
|
* 运行测试套件
|
|
*
|
|
* @param string $suite 测试套件名称
|
|
* @return array 测试结果
|
|
*/
|
|
public function run_test_suite($suite = 'all') {
|
|
$this->test_results = array();
|
|
|
|
switch ($suite) {
|
|
case 'subscription':
|
|
$this->run_subscription_tests();
|
|
break;
|
|
case 'payment':
|
|
$this->run_payment_tests();
|
|
break;
|
|
case 'bundle':
|
|
$this->run_bundle_tests();
|
|
break;
|
|
case 'cron':
|
|
$this->run_cron_tests();
|
|
break;
|
|
case 'all':
|
|
default:
|
|
$this->run_subscription_tests();
|
|
$this->run_payment_tests();
|
|
$this->run_bundle_tests();
|
|
$this->run_cron_tests();
|
|
break;
|
|
}
|
|
|
|
return $this->compile_results();
|
|
}
|
|
|
|
/**
|
|
* 运行订阅测试
|
|
*/
|
|
private function run_subscription_tests() {
|
|
// 运行完整的订阅流程测试
|
|
$this->add_test_result(
|
|
'subscription_flow_test',
|
|
'订阅完整流程测试',
|
|
'测试订阅的完整生命周期流程',
|
|
function() {
|
|
if (!class_exists('Yoone_Subscription_Flow_Test')) {
|
|
throw new Exception('Yoone_Subscription_Flow_Test类不存在');
|
|
}
|
|
|
|
$flow_test = new Yoone_Subscription_Flow_Test();
|
|
$flow_results = $flow_test->run_full_test();
|
|
|
|
// 分析流程测试结果
|
|
$error_count = 0;
|
|
$warning_count = 0;
|
|
|
|
foreach ($flow_results as $result) {
|
|
if ($result['type'] === 'error') {
|
|
$error_count++;
|
|
} elseif ($result['type'] === 'warning') {
|
|
$warning_count++;
|
|
}
|
|
}
|
|
|
|
if ($error_count > 0) {
|
|
throw new Exception("流程测试发现 {$error_count} 个错误");
|
|
}
|
|
|
|
return $warning_count === 0 ? true : "通过但有 {$warning_count} 个警告";
|
|
}
|
|
);
|
|
|
|
// 测试订阅类是否存在
|
|
$this->add_test_result(
|
|
'subscription_class_exists',
|
|
'订阅类存在性测试',
|
|
'检查Yoone_Subscription类是否正确加载',
|
|
function() {
|
|
return class_exists('Yoone_Subscription');
|
|
}
|
|
);
|
|
|
|
// 测试订阅创建
|
|
$this->add_test_result(
|
|
'subscription_creation',
|
|
'订阅创建测试',
|
|
'测试创建新订阅的功能',
|
|
function() {
|
|
if (!class_exists('Yoone_Subscription')) {
|
|
throw new Exception('Yoone_Subscription类不存在');
|
|
}
|
|
|
|
$subscription = new Yoone_Subscription();
|
|
$subscription->set_customer_id(1);
|
|
$subscription->set_status('active');
|
|
$subscription->set_billing_period('month');
|
|
$subscription->set_billing_interval(1);
|
|
$subscription->set_start_date(current_time('mysql'));
|
|
|
|
// 添加订阅商品
|
|
$subscription->add_item(array(
|
|
'product_id' => 100,
|
|
'quantity' => 1,
|
|
'line_total' => 29.99
|
|
));
|
|
|
|
// 验证属性设置
|
|
return $subscription->get_customer_id() === 1 &&
|
|
$subscription->get_status() === 'active' &&
|
|
count($subscription->get_items()) === 1;
|
|
}
|
|
);
|
|
|
|
// 测试订阅状态管理
|
|
$this->add_test_result(
|
|
'subscription_status_management',
|
|
'订阅状态管理测试',
|
|
'测试订阅状态的变更功能',
|
|
function() {
|
|
if (!class_exists('Yoone_Subscription')) {
|
|
throw new Exception('Yoone_Subscription类不存在');
|
|
}
|
|
|
|
$subscription = new Yoone_Subscription();
|
|
$subscription->set_status('active');
|
|
|
|
// 测试暂停
|
|
$subscription->pause();
|
|
if ($subscription->get_status() !== 'paused') {
|
|
throw new Exception('暂停功能失败');
|
|
}
|
|
|
|
// 测试恢复
|
|
$subscription->resume();
|
|
if ($subscription->get_status() !== 'active') {
|
|
throw new Exception('恢复功能失败');
|
|
}
|
|
|
|
// 测试取消
|
|
$subscription->cancel();
|
|
if ($subscription->get_status() !== 'cancelled') {
|
|
throw new Exception('取消功能失败');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
);
|
|
|
|
// 测试下次付款日期计算
|
|
$this->add_test_result(
|
|
'next_payment_calculation',
|
|
'下次付款日期计算测试',
|
|
'测试下次付款日期的计算逻辑',
|
|
function() {
|
|
if (!class_exists('Yoone_Subscription')) {
|
|
throw new Exception('Yoone_Subscription类不存在');
|
|
}
|
|
|
|
$subscription = new Yoone_Subscription();
|
|
$subscription->set_billing_period('month');
|
|
$subscription->set_billing_interval(1);
|
|
$subscription->set_start_date('2024-01-01 00:00:00');
|
|
|
|
$next_payment = $subscription->calculate_next_payment_date();
|
|
$expected = '2024-02-01 00:00:00';
|
|
|
|
return $next_payment === $expected;
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 运行支付测试
|
|
*/
|
|
private function run_payment_tests() {
|
|
// 测试支付网关接口
|
|
$this->add_test_result(
|
|
'payment_gateway_interface',
|
|
'支付网关接口测试',
|
|
'检查支付网关接口是否正确定义',
|
|
function() {
|
|
return interface_exists('Yoone_Payment_Gateway_Interface');
|
|
}
|
|
);
|
|
|
|
// 测试Moneris网关类
|
|
$this->add_test_result(
|
|
'moneris_gateway_class',
|
|
'Moneris网关类测试',
|
|
'检查Moneris支付网关类是否存在',
|
|
function() {
|
|
return class_exists('Yoone_Moneris_Gateway');
|
|
}
|
|
);
|
|
|
|
// 测试支付令牌管理
|
|
$this->add_test_result(
|
|
'payment_token_management',
|
|
'支付令牌管理测试',
|
|
'测试支付令牌的创建和管理',
|
|
function() {
|
|
if (!class_exists('Yoone_Payment_Token')) {
|
|
throw new Exception('Yoone_Payment_Token类不存在');
|
|
}
|
|
|
|
$token = new Yoone_Payment_Token();
|
|
$token->set_customer_id(1);
|
|
$token->set_gateway_id('moneris');
|
|
$token->set_token('test_token_123');
|
|
$token->set_card_type('visa');
|
|
$token->set_last_four('1234');
|
|
|
|
return $token->get_customer_id() === 1 &&
|
|
$token->get_token() === 'test_token_123';
|
|
}
|
|
);
|
|
|
|
// 测试支付处理流程
|
|
$this->add_test_result(
|
|
'payment_processing_flow',
|
|
'支付处理流程测试',
|
|
'测试支付处理的基本流程',
|
|
function() {
|
|
// 这里只测试类和方法的存在性,不进行实际支付
|
|
if (!class_exists('Yoone_Moneris_Gateway')) {
|
|
throw new Exception('Moneris网关类不存在');
|
|
}
|
|
|
|
$gateway = new Yoone_Moneris_Gateway();
|
|
|
|
// 检查必要方法是否存在
|
|
$required_methods = array(
|
|
'process_payment',
|
|
'process_subscription_payment',
|
|
'create_payment_token',
|
|
'delete_payment_token'
|
|
);
|
|
|
|
foreach ($required_methods as $method) {
|
|
if (!method_exists($gateway, $method)) {
|
|
throw new Exception("方法 {$method} 不存在");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 运行捆绑测试
|
|
*/
|
|
private function run_bundle_tests() {
|
|
// 测试捆绑类
|
|
$this->add_test_result(
|
|
'bundle_class_exists',
|
|
'捆绑类存在性测试',
|
|
'检查Yoone_Bundle类是否正确加载',
|
|
function() {
|
|
return class_exists('Yoone_Bundle');
|
|
}
|
|
);
|
|
|
|
// 测试价格计算
|
|
$this->add_test_result(
|
|
'bundle_price_calculation',
|
|
'捆绑价格计算测试',
|
|
'测试捆绑产品的价格计算功能',
|
|
function() {
|
|
if (!class_exists('Yoone_Bundle')) {
|
|
throw new Exception('Yoone_Bundle类不存在');
|
|
}
|
|
|
|
$bundle = new Yoone_Bundle();
|
|
$bundle->set_discount_type('percentage');
|
|
$bundle->set_discount_value(10); // 10%折扣
|
|
|
|
// 添加测试商品
|
|
$bundle->add_item(array(
|
|
'product_id' => 1,
|
|
'quantity' => 2,
|
|
'price' => 100
|
|
));
|
|
|
|
$bundle->add_item(array(
|
|
'product_id' => 2,
|
|
'quantity' => 1,
|
|
'price' => 50
|
|
));
|
|
|
|
// 原价应该是 (100 * 2) + (50 * 1) = 250
|
|
// 折扣后应该是 250 * 0.9 = 225
|
|
$calculated_price = $bundle->calculate_price();
|
|
|
|
return abs($calculated_price - 225) < 0.01; // 允许小数点误差
|
|
}
|
|
);
|
|
|
|
// 测试数量验证
|
|
$this->add_test_result(
|
|
'bundle_quantity_validation',
|
|
'捆绑数量验证测试',
|
|
'测试捆绑产品的数量验证功能',
|
|
function() {
|
|
if (!class_exists('Yoone_Bundle')) {
|
|
throw new Exception('Yoone_Bundle类不存在');
|
|
}
|
|
|
|
$bundle = new Yoone_Bundle();
|
|
$bundle->set_min_quantity(2);
|
|
$bundle->set_max_quantity(10);
|
|
|
|
// 测试有效数量
|
|
if (!$bundle->validate_quantity(5)) {
|
|
throw new Exception('有效数量验证失败');
|
|
}
|
|
|
|
// 测试无效数量(太少)
|
|
if ($bundle->validate_quantity(1)) {
|
|
throw new Exception('最小数量验证失败');
|
|
}
|
|
|
|
// 测试无效数量(太多)
|
|
if ($bundle->validate_quantity(15)) {
|
|
throw new Exception('最大数量验证失败');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 运行定时任务测试
|
|
*/
|
|
private function run_cron_tests() {
|
|
// 测试定时任务类
|
|
$this->add_test_result(
|
|
'cron_class_exists',
|
|
'定时任务类存在性测试',
|
|
'检查Yoone_Cron类是否正确加载',
|
|
function() {
|
|
return class_exists('Yoone_Cron');
|
|
}
|
|
);
|
|
|
|
// 测试定时任务调度
|
|
$this->add_test_result(
|
|
'cron_scheduling',
|
|
'定时任务调度测试',
|
|
'测试定时任务的调度功能',
|
|
function() {
|
|
if (!class_exists('Yoone_Cron')) {
|
|
throw new Exception('Yoone_Cron类不存在');
|
|
}
|
|
|
|
// 检查WordPress定时任务是否已注册
|
|
$scheduled_events = wp_get_scheduled_event('yoone_process_subscription_renewals');
|
|
|
|
return $scheduled_events !== false;
|
|
}
|
|
);
|
|
|
|
// 测试日志清理功能
|
|
$this->add_test_result(
|
|
'log_cleanup_function',
|
|
'日志清理功能测试',
|
|
'测试日志清理功能是否正常',
|
|
function() {
|
|
if (!class_exists('Yoone_Logger')) {
|
|
throw new Exception('Yoone_Logger类不存在');
|
|
}
|
|
|
|
// 检查清理方法是否存在
|
|
return method_exists('Yoone_Logger', 'cleanup_old_logs');
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 添加测试结果
|
|
*
|
|
* @param string $id 测试ID
|
|
* @param string $name 测试名称
|
|
* @param string $description 测试描述
|
|
* @param callable $test_function 测试函数
|
|
*/
|
|
private function add_test_result($id, $name, $description, $test_function) {
|
|
$result = array(
|
|
'id' => $id,
|
|
'name' => $name,
|
|
'description' => $description,
|
|
'status' => 'passed',
|
|
'result' => '',
|
|
'error' => '',
|
|
'execution_time' => 0
|
|
);
|
|
|
|
$start_time = microtime(true);
|
|
|
|
try {
|
|
$test_result = call_user_func($test_function);
|
|
|
|
if ($test_result === true) {
|
|
$result['result'] = '测试通过';
|
|
} elseif ($test_result === false) {
|
|
$result['status'] = 'failed';
|
|
$result['result'] = '测试失败';
|
|
} else {
|
|
$result['result'] = '测试通过: ' . $test_result;
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$result['status'] = 'failed';
|
|
$result['error'] = $e->getMessage();
|
|
} catch (Error $e) {
|
|
$result['status'] = 'failed';
|
|
$result['error'] = $e->getMessage();
|
|
}
|
|
|
|
$result['execution_time'] = round((microtime(true) - $start_time) * 1000, 2);
|
|
|
|
$this->test_results[] = $result;
|
|
|
|
// 记录测试日志
|
|
Yoone_Logger::info("测试执行: {$name}", array(
|
|
'test_id' => $id,
|
|
'status' => $result['status'],
|
|
'execution_time' => $result['execution_time']
|
|
));
|
|
}
|
|
|
|
/**
|
|
* 编译测试结果
|
|
*
|
|
* @return array 编译后的结果
|
|
*/
|
|
private function compile_results() {
|
|
$summary = array(
|
|
'total' => count($this->test_results),
|
|
'passed' => 0,
|
|
'failed' => 0,
|
|
'skipped' => 0
|
|
);
|
|
|
|
foreach ($this->test_results as $result) {
|
|
$summary[$result['status']]++;
|
|
}
|
|
|
|
return array(
|
|
'summary' => $summary,
|
|
'tests' => $this->test_results
|
|
);
|
|
}
|
|
}
|
|
|
|
// 初始化测试套件
|
|
if (is_admin()) {
|
|
new Yoone_Test_Suite();
|
|
}
|