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();