subscription/tests/class-yoone-test-suite.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();
}