177 lines
6.3 KiB
Swift
177 lines
6.3 KiB
Swift
//
|
||
// MainTabBadgeViewModelTests.swift
|
||
// suixinkanTests
|
||
//
|
||
// Created by Codex on 2026/6/23.
|
||
//
|
||
|
||
import XCTest
|
||
@testable import suixinkan
|
||
|
||
@MainActor
|
||
/// 主 Tab 角标测试,覆盖待核销数量加载、封顶和失败清理。
|
||
final class MainTabBadgeViewModelTests: XCTestCase {
|
||
/// 测试无景区时清空角标且不请求接口。
|
||
func testMissingScenicClearsBadgeWithoutRequestingAPI() async {
|
||
let api = MockMainTabOrderService()
|
||
let viewModel = MainTabBadgeViewModel()
|
||
|
||
await viewModel.refreshPendingWriteOffCount(api: api, scenicId: nil, storeId: 66)
|
||
|
||
XCTAssertNil(viewModel.pendingWriteOffCount)
|
||
XCTAssertTrue(api.writeOffListCalls.isEmpty)
|
||
}
|
||
|
||
/// 测试接口成功时展示待核销总数。
|
||
func testSuccessfulRefreshStoresPendingWriteOffCount() async {
|
||
let api = MockMainTabOrderService()
|
||
api.writeOffResponse = ListPayload<WriteOffOrderItem>(total: 8, list: [])
|
||
let viewModel = MainTabBadgeViewModel()
|
||
|
||
await viewModel.refreshPendingWriteOffCount(api: api, scenicId: 88, storeId: 66)
|
||
|
||
XCTAssertEqual(viewModel.pendingWriteOffCount, 8)
|
||
XCTAssertEqual(api.writeOffListCalls, [.init(scenicId: 88, storeId: 66, page: 1, pageSize: 1)])
|
||
}
|
||
|
||
/// 测试待核销数量超过 99 时角标封顶。
|
||
func testSuccessfulRefreshCapsPendingWriteOffCountAtNinetyNine() async {
|
||
let api = MockMainTabOrderService()
|
||
api.writeOffResponse = ListPayload<WriteOffOrderItem>(total: 120, list: [])
|
||
let viewModel = MainTabBadgeViewModel()
|
||
|
||
await viewModel.refreshPendingWriteOffCount(api: api, scenicId: 88, storeId: nil)
|
||
|
||
XCTAssertEqual(viewModel.pendingWriteOffCount, 99)
|
||
}
|
||
|
||
/// 测试接口失败时清空角标。
|
||
func testRefreshFailureClearsBadge() async {
|
||
let api = MockMainTabOrderService()
|
||
api.writeOffError = APIError.httpStatus(500, "server error")
|
||
let viewModel = MainTabBadgeViewModel()
|
||
|
||
await viewModel.refreshPendingWriteOffCount(api: api, scenicId: 88, storeId: 66)
|
||
|
||
XCTAssertNil(viewModel.pendingWriteOffCount)
|
||
XCTAssertEqual(api.writeOffListCalls.count, 1)
|
||
}
|
||
}
|
||
|
||
@MainActor
|
||
/// 主 Tab 角标测试用订单服务,只实现角标依赖的核销列表记录。
|
||
private final class MockMainTabOrderService: OrderServing {
|
||
struct WriteOffListCall: Equatable {
|
||
let scenicId: Int
|
||
let storeId: Int?
|
||
let page: Int
|
||
let pageSize: Int
|
||
}
|
||
|
||
var writeOffResponse = ListPayload<WriteOffOrderItem>(total: 0, list: [])
|
||
var writeOffError: Error?
|
||
private(set) var writeOffListCalls: [WriteOffListCall] = []
|
||
|
||
/// 返回空订单管理列表,角标测试不会使用该方法。
|
||
func orderList(
|
||
scenicId: Int,
|
||
page: Int,
|
||
pageSize: Int,
|
||
orderStatus: Int?,
|
||
userPhone: String?,
|
||
startTime: String?,
|
||
endTime: String?,
|
||
isRefined: Int?,
|
||
isScenicAdmin: Bool
|
||
) async throws -> ListPayload<OrderEntity> {
|
||
ListPayload(total: 0, list: [])
|
||
}
|
||
|
||
/// 记录核销列表请求并返回预设响应。
|
||
func writeOffList(
|
||
scenicId: Int,
|
||
storeId: Int?,
|
||
page: Int,
|
||
pageSize: Int,
|
||
orderStatus: Int?,
|
||
orderVerifyStatus: Int?,
|
||
startTime: String?,
|
||
endTime: String?,
|
||
userPhone: String?
|
||
) async throws -> ListPayload<WriteOffOrderItem> {
|
||
writeOffListCalls.append(.init(scenicId: scenicId, storeId: storeId, page: page, pageSize: pageSize))
|
||
if let writeOffError {
|
||
throw writeOffError
|
||
}
|
||
return writeOffResponse
|
||
}
|
||
|
||
func updateRefinedChoice(orderNumber: String, isRefined: Int) async throws {}
|
||
func cancelOrder(orderNumber: String) async throws {}
|
||
func sendGift(orderNumber: String, photoNum: Int, videoNum: Int) async throws {}
|
||
func storeOrderList(
|
||
storeId: Int?,
|
||
page: Int,
|
||
pageSize: Int,
|
||
userPhone: String?,
|
||
orderStatus: Int?,
|
||
orderType: Int?,
|
||
startTime: String?,
|
||
endTime: String?
|
||
) async throws -> ListPayload<StoreOrderListItem> {
|
||
ListPayload(total: 0, list: [])
|
||
}
|
||
func storeOrderRefund(
|
||
storeId: Int,
|
||
orderNumber: String,
|
||
refundType: OrderRefundMode,
|
||
refundAmount: String,
|
||
refundReason: String
|
||
) async throws {}
|
||
|
||
/// 角标测试不会触发核销操作。
|
||
func writeOff(orderNumber: String) async throws {}
|
||
|
||
/// 角标测试不会请求订单详情。
|
||
func storeOrderDetail(storeId: Int, orderNumber: String) async throws -> StoreOrderDetailResponse {
|
||
try TestFixture.payload(StoreOrderDetailResponse.self, named: "store_order_detail_success")
|
||
}
|
||
|
||
/// 角标测试不会请求押金订单列表。
|
||
func depositOrderList(scenicId: Int, page: Int, pageSize: Int) async throws -> ListPayload<DepositOrderListItem> {
|
||
ListPayload(total: 0, list: [])
|
||
}
|
||
|
||
/// 角标测试不会触发押金核销。
|
||
func depositOrderWriteOff(orderNumber: String) async throws {}
|
||
|
||
/// 角标测试不会触发押金退款。
|
||
func depositOrderRefund(orderNumber: String, refundReason: String) async throws {}
|
||
|
||
/// 角标测试不会请求拍摄信息。
|
||
func storeOrderShootingDetail(
|
||
storeId: Int,
|
||
orderNumber: String,
|
||
scenicSpotId: Int,
|
||
photogUid: Int
|
||
) async throws -> StoreOrderShootingDetailResponse {
|
||
try TestFixture.payload(StoreOrderShootingDetailResponse.self, named: "store_order_shooting_detail_success")
|
||
}
|
||
|
||
/// 角标测试不会触发普通退款。
|
||
func orderRefund(orderNumber: String, refundType: OrderRefundMode, refundAmount: String, refundReason: String) async throws {}
|
||
|
||
/// 角标测试不会请求历史拍摄。
|
||
func multiTravelShootHistory(orderNumber: String) async throws -> MultiTravelShootHistoryResponse {
|
||
try TestFixture.payload(MultiTravelShootHistoryResponse.self, named: "multi_travel_shoot_history_success")
|
||
}
|
||
|
||
/// 角标测试不会请求已核销打卡点。
|
||
func multiTravelVerifiedScenicSpotList(orderNumber: String) async throws -> [MultiTravelVerifiedScenicSpotItem] {
|
||
[]
|
||
}
|
||
|
||
/// 角标测试不会提交多点旅拍素材。
|
||
func multiTravelUploadMaterial(_ request: MultiTravelUploadMaterialRequest) async throws {}
|
||
}
|