// // TravelAlbumViewModelTests.swift // suixinkanTests // // Created by Codex on 2026/6/29. // import XCTest @testable import suixinkan @MainActor /// 旅拍相册 ViewModel 测试。 final class TravelAlbumViewModelTests: XCTestCase { /// 测试先拍再买模式缺少单价时创建失败。 func testCreatePreShootRequiresSinglePrice() async { let viewModel = TravelAlbumEntryViewModel() let api = MockTravelAlbumAPI() let success = await viewModel.confirmCreateTask( api: api, mode: TravelAlbumEntryViewModel.modePreShoot, freeCount: "3", singlePrice: "", packagePrice: "99", order: nil, scenicSelected: true ) XCTAssertFalse(success) XCTAssertEqual(viewModel.errorMessage, "请输入单张照片价格") XCTAssertEqual(api.createCallCount, 0) } /// 测试买了再拍模式缺少订单时创建失败。 func testCreatePreOrderRequiresOrder() async { let viewModel = TravelAlbumEntryViewModel() let api = MockTravelAlbumAPI() let success = await viewModel.confirmCreateTask( api: api, mode: TravelAlbumEntryViewModel.modePreOrder, freeCount: "", singlePrice: "", packagePrice: "", order: nil, scenicSelected: true ) XCTAssertFalse(success) XCTAssertEqual(viewModel.errorMessage, "请选择绑定订单") } /// 测试创建成功后刷新列表。 func testCreateSuccessReloadsList() async { let viewModel = TravelAlbumEntryViewModel() let api = MockTravelAlbumAPI() let success = await viewModel.confirmCreateTask( api: api, mode: TravelAlbumEntryViewModel.modePreShoot, freeCount: "3", singlePrice: "9.9", packagePrice: "99", order: nil, scenicSelected: true ) XCTAssertTrue(success) XCTAssertEqual(api.createCallCount, 1) XCTAssertEqual(api.listCallCount, 1) } /// 测试相册名称按日期递增。 func testBuildTodayAlbumNameIncrementsSuffix() { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" let today = formatter.string(from: Date()) let name = TravelAlbumEntryViewModel.buildTodayAlbumName( existingNames: ["\(today)-001", "\(today)-002", "2026-06-28-001"] ) XCTAssertTrue(name.hasSuffix("003")) } } @MainActor private final class MockTravelAlbumAPI: TravelAlbumServing { var createCallCount = 0 var listCallCount = 0 func availableOrders() async throws -> [TravelAlbumAvailableOrder] { [] } func createAlbum(_ request: TravelAlbumCreateRequest) async throws -> TravelAlbumCreateResponse { createCallCount += 1 return TravelAlbumCreateResponse(id: 9) } func albumList(page: Int, pageSize: Int) async throws -> ListPayload { listCallCount += 1 return ListPayload(total: 0, list: []) } func albumInfo(id: Int) async throws -> TravelAlbumItem { throw APIError.emptyData } func editAlbum(_ request: TravelAlbumEditRequest) async throws {} func deleteAlbum(_ request: TravelAlbumDeleteRequest) async throws {} func materialList(userEquityTravelId: Int, page: Int, pageSize: Int) async throws -> ListPayload { ListPayload(total: 0, list: []) } func uploadMaterial(_ request: TravelAlbumUploadMaterialRequest) async throws -> TravelAlbumMaterial { throw APIError.emptyData } func deleteMaterial(_ request: TravelAlbumDeleteMaterialRequest) async throws {} func mpCode(id: Int) async throws -> TravelAlbumMpCodeResponse { TravelAlbumMpCodeResponse(mpCodeOssURL: "") } }