Files
digital-pilates/utils/fastingNotificationTest.ts
richarjiang cf069f3537 feat(fasting): 重构断食通知系统并增强可靠性
- 新增 useFastingNotifications hook 统一管理通知状态和同步逻辑
- 实现四阶段通知提醒:开始前30分钟、开始时、结束前30分钟、结束时
- 添加通知验证机制,确保通知正确设置和避免重复
- 新增 NotificationErrorAlert 组件显示通知错误并提供重试选项
- 实现断食计划持久化存储,应用重启后自动恢复
- 添加开发者测试面板用于验证通知系统可靠性
- 优化通知同步策略,支持选择性更新减少不必要的操作
- 修复个人页面编辑按钮样式问题
- 更新应用版本号至 1.0.18
2025-10-14 15:05:11 +08:00

341 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { FastingPlan } from '@/constants/Fasting';
import {
ensureFastingNotificationsReady,
resyncFastingNotifications,
verifyFastingNotifications
} from '@/services/fastingNotifications';
import { notificationService } from '@/services/notifications';
import { FastingSchedule } from '@/store/fastingSlice';
import { FastingNotificationIds } from '@/utils/fasting';
import dayjs from 'dayjs';
/**
* 断食通知系统测试工具
* 用于验证通知系统在各种场景下的可靠性
*/
export class FastingNotificationTester {
private testResults: Array<{
testName: string;
passed: boolean;
message: string;
timestamp: Date;
}> = [];
private logResult(testName: string, passed: boolean, message: string) {
const result = {
testName,
passed,
message,
timestamp: new Date(),
};
this.testResults.push(result);
console.log(`[${passed ? 'PASS' : 'FAIL'}] ${testName}: ${message}`);
}
/**
* 获取测试结果
*/
getTestResults() {
return this.testResults;
}
/**
* 清除测试结果
*/
clearResults() {
this.testResults = [];
}
/**
* 测试通知权限检查
*/
async testNotificationPermissions() {
try {
const ready = await ensureFastingNotificationsReady();
this.logResult(
'通知权限检查',
ready,
ready ? '通知权限已授予' : '通知权限未授予'
);
return ready;
} catch (error) {
this.logResult(
'通知权限检查',
false,
`权限检查失败: ${error instanceof Error ? error.message : '未知错误'}`
);
return false;
}
}
/**
* 测试通知安排
*/
async testNotificationScheduling(plan: FastingPlan) {
const now = dayjs();
const start = now.add(1, 'hour');
const end = start.add(plan.fastingHours, 'hour');
const schedule: FastingSchedule = {
planId: plan.id,
startISO: start.toISOString(),
endISO: end.toISOString(),
createdAtISO: now.toISOString(),
updatedAtISO: now.toISOString(),
origin: 'manual',
};
try {
const notificationIds = await resyncFastingNotifications({
schedule,
plan,
previousIds: {},
enabled: true,
});
const hasAllIds = !!(notificationIds.preStartId && notificationIds.startId &&
notificationIds.preEndId && notificationIds.endId);
this.logResult(
'通知安排测试',
hasAllIds,
hasAllIds
? '成功安排所有四个通知点'
: `缺少通知ID: ${JSON.stringify(notificationIds)}`
);
return { success: hasAllIds, notificationIds };
} catch (error) {
this.logResult(
'通知安排测试',
false,
`通知安排失败: ${error instanceof Error ? error.message : '未知错误'}`
);
return { success: false, notificationIds: {} };
}
}
/**
* 测试通知验证
*/
async testNotificationVerification(
schedule: FastingSchedule,
plan: FastingPlan,
notificationIds: FastingNotificationIds
) {
try {
const { isValid, updatedIds } = await verifyFastingNotifications({
schedule,
plan,
storedIds: notificationIds,
});
this.logResult(
'通知验证测试',
isValid,
isValid ? '通知验证通过' : '通知验证失败,已重新同步'
);
return { isValid, updatedIds };
} catch (error) {
this.logResult(
'通知验证测试',
false,
`通知验证失败: ${error instanceof Error ? error.message : '未知错误'}`
);
return { isValid: false, updatedIds: {} };
}
}
/**
* 测试通知取消
*/
async testNotificationCancellation(notificationIds: FastingNotificationIds) {
try {
// 取消所有通知
const cancelPromises = [];
if (notificationIds.preStartId) {
cancelPromises.push(notificationService.cancelNotification(notificationIds.preStartId));
}
if (notificationIds.startId) {
cancelPromises.push(notificationService.cancelNotification(notificationIds.startId));
}
if (notificationIds.preEndId) {
cancelPromises.push(notificationService.cancelNotification(notificationIds.preEndId));
}
if (notificationIds.endId) {
cancelPromises.push(notificationService.cancelNotification(notificationIds.endId));
}
await Promise.all(cancelPromises);
// 验证通知是否已取消
const scheduledNotifications = await notificationService.getAllScheduledNotifications();
const remainingIds = scheduledNotifications.map(n => n.identifier);
const cancelledIds = Object.values(notificationIds).filter(id =>
id && !remainingIds.includes(id)
);
const allCancelled = cancelledIds.length === Object.values(notificationIds).filter(id => id).length;
this.logResult(
'通知取消测试',
allCancelled,
allCancelled
? '所有通知已成功取消'
: `部分通知未取消: ${JSON.stringify(cancelledIds)}`
);
return allCancelled;
} catch (error) {
this.logResult(
'通知取消测试',
false,
`通知取消失败: ${error instanceof Error ? error.message : '未知错误'}`
);
return false;
}
}
/**
* 测试边界情况
*/
async testEdgeCases(plan: FastingPlan) {
const now = dayjs();
// 测试1: 已经过期的断食期
const pastStart = now.subtract(2, 'hour');
const pastEnd = pastStart.add(plan.fastingHours, 'hour');
const pastSchedule: FastingSchedule = {
planId: plan.id,
startISO: pastStart.toISOString(),
endISO: pastEnd.toISOString(),
createdAtISO: now.toISOString(),
updatedAtISO: now.toISOString(),
origin: 'manual',
};
try {
const pastNotificationIds = await resyncFastingNotifications({
schedule: pastSchedule,
plan,
previousIds: {},
enabled: true,
});
const hasNoIds = Object.values(pastNotificationIds).every(id => !id);
this.logResult(
'过期断食期测试',
hasNoIds,
hasNoIds ? '正确处理过期断食期,未安排通知' : '错误地为过期断食期安排了通知'
);
} catch (error) {
this.logResult(
'过期断食期测试',
false,
`过期断食期测试失败: ${error instanceof Error ? error.message : '未知错误'}`
);
}
// 测试2: 即将开始的断食期少于30分钟
const imminentStart = now.add(15, 'minute');
const imminentEnd = imminentStart.add(plan.fastingHours, 'hour');
const imminentSchedule: FastingSchedule = {
planId: plan.id,
startISO: imminentStart.toISOString(),
endISO: imminentEnd.toISOString(),
createdAtISO: now.toISOString(),
updatedAtISO: now.toISOString(),
origin: 'manual',
};
try {
const imminentNotificationIds = await resyncFastingNotifications({
schedule: imminentSchedule,
plan,
previousIds: {},
enabled: true,
});
// 应该只有开始时、结束前30分钟和结束时的通知
const hasCorrectIds = !imminentNotificationIds.preStartId &&
!!imminentNotificationIds.startId &&
!!imminentNotificationIds.preEndId &&
!!imminentNotificationIds.endId;
this.logResult(
'即将开始断食期测试',
hasCorrectIds,
hasCorrectIds
? '正确处理即将开始的断食期,只安排了必要的通知'
: '通知安排不正确'
);
} catch (error) {
this.logResult(
'即将开始断食期测试',
false,
`即将开始断食期测试失败: ${error instanceof Error ? error.message : '未知错误'}`
);
}
}
/**
* 运行完整的测试套件
*/
async runFullTestSuite(plan: FastingPlan) {
console.log('开始运行断食通知系统测试套件...');
this.clearResults();
// 1. 测试通知权限
const hasPermission = await this.testNotificationPermissions();
if (!hasPermission) {
console.log('通知权限未授予,跳过其他测试');
return this.getTestResults();
}
// 2. 测试通知安排
const { success: schedulingSuccess, notificationIds } = await this.testNotificationScheduling(plan);
if (!schedulingSuccess) {
console.log('通知安排失败,跳过后续测试');
return this.getTestResults();
}
// 3. 创建测试用的断食计划
const now = dayjs();
const start = now.add(1, 'hour');
const end = start.add(plan.fastingHours, 'hour');
const schedule: FastingSchedule = {
planId: plan.id,
startISO: start.toISOString(),
endISO: end.toISOString(),
createdAtISO: now.toISOString(),
updatedAtISO: now.toISOString(),
origin: 'manual',
};
// 4. 测试通知验证
await this.testNotificationVerification(schedule, plan, notificationIds);
// 5. 测试边界情况
await this.testEdgeCases(plan);
// 6. 测试通知取消
await this.testNotificationCancellation(notificationIds);
const results = this.getTestResults();
const passedCount = results.filter(r => r.passed).length;
const totalCount = results.length;
console.log(`测试完成: ${passedCount}/${totalCount} 通过`);
return results;
}
}
// 导出测试实例
export const fastingNotificationTester = new FastingNotificationTester();