// // ScenicSpotContextTests.swift // suixinkanTests // // Created by Codex on 2026/6/22. // import XCTest @testable import suixinkan_ios @MainActor /// 景点上下文测试,覆盖按景区懒加载和失败隔离。 final class ScenicSpotContextTests: XCTestCase { /// 测试景点列表会按当前景区 ID 请求并写入上下文。 func testReloadRequestsSpotsForScenicId() async { let context = ScenicSpotContext() let api = MockScenicSpotAPI() api.spotResponse = ListPayload(total: 1, list: [ScenicSpotItem(id: 9, name: "观景台")]) await context.reload(scenicId: 88, api: api) XCTAssertEqual(api.requestedScenicIds, [88]) XCTAssertEqual(context.scenicId, 88) XCTAssertEqual(context.spots.map(\.id), [9]) XCTAssertEqual(context.loadState, .loaded) } /// 测试景点接口失败时只标记景点模块失败。 func testReloadFailureOnlyAffectsScenicSpotContext() async { let context = ScenicSpotContext() let api = MockScenicSpotAPI() api.error = APIError.networkFailed("景点接口失败") await context.reload(scenicId: 88, api: api) XCTAssertEqual(api.requestedScenicIds, [88]) XCTAssertTrue(context.spots.isEmpty) if case .failed = context.loadState { XCTAssertTrue(true) } else { XCTFail("景点接口失败时应进入 failed 状态") } } } @MainActor /// 景点服务测试替身,记录请求过的景区 ID。 private final class MockScenicSpotAPI: AccountContextServing { var requestedScenicIds: [Int] = [] var spotResponse = ListPayload(total: 0, list: [ScenicSpotItem]()) var error: Error? /// 获取空角色权限,当前测试不使用。 func rolePermissions() async throws -> [RolePermissionResponse] { [] } /// 获取空景区列表,当前测试不使用。 func scenicListAll() async throws -> ScenicListAllResponse { ScenicListAllResponse(total: 0, list: []) } /// 获取空门店列表,当前测试不使用。 func storeAll() async throws -> ListPayload { ListPayload(total: 0, list: []) } /// 获取测试景点列表,并记录景区 ID。 func scenicSpotListAll(scenicId: Int) async throws -> ListPayload { requestedScenicIds.append(scenicId) if let error { throw error } return spotResponse } }