// // JXIntegrationTests.m // AICityTests // // Feature: 003-ios-api-https // 剧星平台集成测试 // #import #import "JXPlaybackProgressStorage.h" #import "JXAPIService.h" #import "JXCountFormatter.h" @interface JXIntegrationTests : XCTestCase @property (nonatomic, strong) JXPlaybackProgressStorage *storage; @property (nonatomic, strong) JXAPIService *apiService; @end @implementation JXIntegrationTests - (void)setUp { [super setUp]; self.storage = [JXPlaybackProgressStorage sharedStorage]; self.apiService = [JXAPIService sharedService]; // 清空所有数据 [self.storage clearAll]; } - (void)tearDown { [self.storage clearAll]; [super tearDown]; } #pragma mark - 存储与API集成测试 - (void)testStorageAPIIntegration { // Given NSString *episodeId = @"integration_ep_001"; NSString *dramaId = @"integration_drama_001"; XCTestExpectation *expectation = [self expectationWithDescription:@"Storage API integration"]; // When - 保存进度到本地 [self.storage saveProgressWithEpisodeId:episodeId dramaId:dramaId position:45000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) { XCTAssertTrue(success, @"本地保存应该成功"); // Then - 验证本地数据 JXPlaybackProgressModel *progress = [self.storage getProgressWithEpisodeId:episodeId]; XCTAssertNotNil(progress, @"应该能获取到本地数据"); XCTAssertEqual(progress.position, 45000); XCTAssertEqualWithAccuracy(progress.progress, 0.375f, 0.01f); // 45000/120000 [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; } - (void)testMultipleEpisodesIntegration { // Given NSString *dramaId = @"multi_integration_drama"; NSArray *episodes = @[@"ep001", @"ep002", @"ep003"]; XCTestExpectation *expectation = [self expectationWithDescription:@"Multiple episodes"]; // When - 保存多集进度 __block NSInteger completedCount = 0; for (NSInteger i = 0; i < episodes.count; i++) { NSString *episodeId = episodes[i]; [self.storage saveProgressWithEpisodeId:episodeId dramaId:dramaId position:(i + 1) * 30000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) { completedCount++; if (completedCount == episodes.count) { // Then - 验证所有进度 NSArray *progresses = [self.storage getProgressByDramaId:dramaId]; XCTAssertEqual(progresses.count, 3, @"应该有3集的进度"); // 验证进度按更新时间排序 for (NSInteger j = 0; j < progresses.count - 1; j++) { XCTAssertTrue([progresses[j].updatedAt compare:progresses[j+1].updatedAt] != NSOrderedAscending, @"进度应该按更新时间排序"); } [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:10.0 handler:nil]; } - (void)testProgressUpdateFlow { // Given NSString *episodeId = @"update_flow_ep"; NSString *dramaId = @"update_flow_drama"; XCTestExpectation *expectation1 = [self expectationWithDescription:@"Initial save"]; XCTestExpectation *expectation2 = [self expectationWithDescription:@"Update progress"]; XCTestExpectation *expectation3 = [self expectationWithDescription:@"Complete progress"]; // Step 1: 初始进度 [self.storage saveProgressWithEpisodeId:episodeId dramaId:dramaId position:30000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) { XCTAssertTrue(success); JXPlaybackProgressModel *progress = [self.storage getProgressWithEpisodeId:episodeId]; XCTAssertEqual(progress.position, 30000); [expectation1 fulfill]; // Step 2: 更新进度 [self.storage saveProgressWithEpisodeId:episodeId dramaId:dramaId position:90000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) { XCTAssertTrue(success); JXPlaybackProgressModel *updatedProgress = [self.storage getProgressWithEpisodeId:episodeId]; XCTAssertEqual(updatedProgress.position, 90000); XCTAssertEqualWithAccuracy(updatedProgress.progress, 0.75f, 0.01f); [expectation2 fulfill]; // Step 3: 完成观看 [self.storage saveProgressWithEpisodeId:episodeId dramaId:dramaId position:114000 duration:120000 isCompleted:YES synced:NO completion:^(BOOL success, NSError *error) { XCTAssertTrue(success); JXPlaybackProgressModel *completedProgress = [self.storage getProgressWithEpisodeId:episodeId]; XCTAssertTrue(completedProgress.isCompleted); XCTAssertTrue(completedProgress.progress >= 0.95f); [expectation3 fulfill]; }]; }]; }]; [self waitForExpectationsWithTimeout:15.0 handler:nil]; } - (void)testSyncStatusManagement { // Given NSArray *episodeIds = @[@"sync_ep_001", @"sync_ep_002", @"sync_ep_003"]; NSString *dramaId = @"sync_drama"; XCTestExpectation *expectation = [self expectationWithDescription:@"Sync management"]; // When - 保存未同步的进度 __block NSInteger savedCount = 0; for (NSString *episodeId in episodeIds) { [self.storage saveProgressWithEpisodeId:episodeId dramaId:dramaId position:30000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) { savedCount++; if (savedCount == episodeIds.count) { // 验证未同步状态 NSArray *unsyncedProgresses = [self.storage getUnsyncedProgresses]; XCTAssertTrue(unsyncedProgresses.count >= episodeIds.count, @"应该有未同步的进度"); // 标记第一个为已同步 [self.storage markAsSyncedWithEpisodeId:episodeIds[0]]; // 验证同步状态 JXPlaybackProgressModel *syncedProgress = [self.storage getProgressWithEpisodeId:episodeIds[0]]; XCTAssertTrue(syncedProgress.synced, @"应该被标记为已同步"); NSArray *remainingUnsynced = [self.storage getUnsyncedProgresses]; BOOL foundSyncedInUnsynced = NO; for (JXPlaybackProgressModel *progress in remainingUnsynced) { if ([progress.episodeId isEqualToString:episodeIds[0]]) { foundSyncedInUnsynced = YES; break; } } XCTAssertFalse(foundSyncedInUnsynced, @"已同步的进度不应该在未同步列表中"); [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:10.0 handler:nil]; } #pragma mark - 数据一致性测试 - (void)testDataConsistencyAcrossOperations { // Given NSString *dramaId = @"consistency_drama"; NSArray *episodeIds = @[@"ep001", @"ep002", @"ep003", @"ep004", @"ep005"]; XCTestExpectation *expectation = [self expectationWithDescription:@"Data consistency"]; // When - 并发保存多个进度 __block NSInteger completedOperations = 0; for (NSString *episodeId in episodeIds) { int64_t position = [episodeId hash] % 100000; [self.storage saveProgressWithEpisodeId:episodeId dramaId:dramaId position:position duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) { completedOperations++; if (completedOperations == episodeIds.count) { // Then - 验证数据一致性 NSArray *allProgresses = [self.storage getProgressByDramaId:dramaId]; XCTAssertEqual(allProgresses.count, episodeIds.count, @"应该有所有集的进度"); // 验证每个进度的数据完整性 for (NSString *episodeId in episodeIds) { JXPlaybackProgressModel *progress = [self.storage getProgressWithEpisodeId:episodeId]; XCTAssertNotNil(progress, @"进度应该存在"); XCTAssertEqualObjects(progress.dramaId, dramaId); XCTAssertTrue(progress.position >= 0); XCTAssertEqual(progress.duration, 120000); XCTAssertTrue(progress.progress >= 0.0f && progress.progress <= 1.0f); } [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:10.0 handler:nil]; } #pragma mark - 格式化工具集成测试 - (void)testCountFormatterIntegration { // 测试格式化工具在实际场景中的使用 NSArray *testCounts = @[@0, @999, @1500, @12345, @123456, @1234567, @123456789]; for (NSNumber *count in testCounts) { long long countValue = [count longLongValue]; // 测试所有格式化方法都能正常工作 NSString *basicFormat = [JXCountFormatter formatCount:countValue]; NSString *likeFormat = [JXCountFormatter formatLikeCount:countValue]; NSString *commentFormat = [JXCountFormatter formatCommentCount:countValue]; NSString *shareFormat = [JXCountFormatter formatShareCount:countValue]; NSString *favoriteFormat = [JXCountFormatter formatFavoriteCount:countValue]; NSString *playFormat = [JXCountFormatter formatPlayCount:countValue]; // 验证格式化结果不为空 XCTAssertNotNil(basicFormat); XCTAssertNotNil(likeFormat); XCTAssertNotNil(commentFormat); XCTAssertNotNil(shareFormat); XCTAssertNotNil(favoriteFormat); XCTAssertNotNil(playFormat); // 验证非零值的格式化一致性 if (countValue > 0) { XCTAssertEqualObjects(basicFormat, likeFormat); XCTAssertEqualObjects(basicFormat, commentFormat); XCTAssertEqualObjects(basicFormat, shareFormat); XCTAssertEqualObjects(basicFormat, favoriteFormat); } } } #pragma mark - 错误处理测试 - (void)testErrorHandlingIntegration { // 测试各种错误情况下的系统行为 // 测试空字符串输入 XCTestExpectation *expectation1 = [self expectationWithDescription:@"Empty string handling"]; [self.storage saveProgressWithEpisodeId:@"" dramaId:@"test_drama" position:30000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) { // 应该处理错误而不崩溃 [expectation1 fulfill]; }]; // 测试极端数值 XCTestExpectation *expectation2 = [self expectationWithDescription:@"Extreme values"]; [self.storage saveProgressWithEpisodeId:@"extreme_test" dramaId:@"test_drama" position:LLONG_MAX duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) { // 应该处理极端值而不崩溃 [expectation2 fulfill]; }]; [self waitForExpectationsWithTimeout:10.0 handler:nil]; // 验证系统仍然正常工作 XCTestExpectation *expectation3 = [self expectationWithDescription:@"Normal operation after errors"]; [self.storage saveProgressWithEpisodeId:@"normal_after_error" dramaId:@"test_drama" position:30000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) { XCTAssertTrue(success, @"正常操作应该仍然有效"); [expectation3 fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; } #pragma mark - 单例模式测试 - (void)testSingletonBehavior { // 验证存储管理器的单例行为 JXPlaybackProgressStorage *storage1 = [JXPlaybackProgressStorage sharedStorage]; JXPlaybackProgressStorage *storage2 = [JXPlaybackProgressStorage sharedStorage]; XCTAssertTrue(storage1 == storage2, @"应该返回同一个实例"); // 验证API服务的单例行为 JXAPIService *service1 = [JXAPIService sharedService]; JXAPIService *service2 = [JXAPIService sharedService]; XCTAssertTrue(service1 == service2, @"应该返回同一个实例"); } #pragma mark - 清理操作测试 - (void)testCleanupOperations { // Given - 创建测试数据 NSArray *episodeIds = @[@"cleanup_ep_001", @"cleanup_ep_002", @"cleanup_ep_003"]; NSString *dramaId = @"cleanup_drama"; XCTestExpectation *setupExpectation = [self expectationWithDescription:@"Setup data"]; __block NSInteger setupCount = 0; for (NSString *episodeId in episodeIds) { [self.storage saveProgressWithEpisodeId:episodeId dramaId:dramaId position:30000 duration:120000 isCompleted:NO synced:YES completion:^(BOOL success, NSError *error) { setupCount++; if (setupCount == episodeIds.count) { [setupExpectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:5.0 handler:nil]; // When - 执行清理操作 [self.storage cleanupOldRecords]; // Then - 验证新数据不会被清理(因为刚创建) for (NSString *episodeId in episodeIds) { JXPlaybackProgressModel *progress = [self.storage getProgressWithEpisodeId:episodeId]; XCTAssertNotNil(progress, @"新数据不应该被清理"); } // 测试按剧删除 [self.storage deleteProgressByDramaId:dramaId]; NSArray *remainingProgresses = [self.storage getProgressByDramaId:dramaId]; XCTAssertEqual(remainingProgresses.count, 0, @"删除后应该没有进度"); } @end