Files
suixinkan_ios_new/suixinkanTests/WriteOffListViewModelTests.swift

223 lines
8.3 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// WriteOffListViewModelTests.swift
// suixinkanTests
//
// Created by Codex on 2026/6/29.
//
import XCTest
@testable import suixinkan
@MainActor
/// ViewModel
final class WriteOffListViewModelTests: XCTestCase {
///
func testReloadClearsDataWhenScenicIsMissing() async throws {
let api = WriteOffMockOrderService()
api.responses = [try TestFixture.payload(ListPayload<WriteOffOrderItem>.self, named: "writeoff_list_success")]
let viewModel = WriteOffListViewModel()
try await viewModel.reload(api: api, scenicId: 88, storeId: 66)
XCTAssertFalse(viewModel.orders.isEmpty)
try await viewModel.reload(api: api, scenicId: nil)
XCTAssertTrue(viewModel.orders.isEmpty)
XCTAssertEqual(viewModel.total, 0)
XCTAssertEqual(api.listCalls.count, 1)
}
/// Tab
func testReloadUsesStatusAndPhoneFilters() async throws {
let api = WriteOffMockOrderService()
api.responses = [
try TestFixture.payload(ListPayload<WriteOffOrderItem>.self, named: "writeoff_list_success"),
try TestFixture.payload(ListPayload<WriteOffOrderItem>.self, named: "writeoff_list_success")
]
let viewModel = WriteOffListViewModel()
viewModel.searchPhone = "13800000000"
viewModel.selectedStatus = 18
viewModel.filterStartDate = Self.date("2026-05-01")
viewModel.filterEndDate = Self.date("2026-05-31")
try await viewModel.reload(api: api, scenicId: 88, storeId: 66)
XCTAssertEqual(api.listCalls[0].orderStatus, 18)
XCTAssertNil(api.listCalls[0].orderVerifyStatus)
XCTAssertEqual(api.listCalls[0].userPhone, "13800000000")
XCTAssertEqual(api.listCalls[0].startTime, "2026-05-01")
XCTAssertEqual(api.listCalls[0].endTime, "2026-05-31")
viewModel.selectedStatus = 1
try await viewModel.reload(api: api, scenicId: 88)
XCTAssertNil(api.listCalls[1].orderStatus)
XCTAssertEqual(api.listCalls[1].orderVerifyStatus, 1)
}
///
func testVerifyRefreshesListAfterSuccess() async throws {
let api = WriteOffMockOrderService()
api.responses = [try TestFixture.payload(ListPayload<WriteOffOrderItem>.self, named: "writeoff_list_success")]
let viewModel = WriteOffListViewModel()
try await viewModel.verify(api: api, scenicId: 88, storeId: 66, orderNumber: "VERIFY001")
XCTAssertEqual(api.verifiedOrderNumbers, ["VERIFY001"])
XCTAssertEqual(api.listCalls.count, 1)
XCTAssertEqual(viewModel.orders.count, 1)
XCTAssertFalse(viewModel.isVerifying)
}
///
func testMatchedWriteOffOrderFindsCurrentListItem() async throws {
let api = WriteOffMockOrderService()
api.responses = [try TestFixture.payload(ListPayload<WriteOffOrderItem>.self, named: "writeoff_list_success")]
let viewModel = WriteOffListViewModel()
try await viewModel.reload(api: api, scenicId: 88)
let result = try XCTUnwrap(viewModel.matchedWriteOffOrder(for: "order_number=VERIFY001"))
XCTAssertEqual(result.orderNumber, "VERIFY001")
XCTAssertEqual(result.matched?.orderNumber, "VERIFY001")
}
/// canVerify
func testCanVerifyExcludesVerifiedStatus() throws {
let viewModel = WriteOffListViewModel()
let verified = try JSONDecoder().decode(
WriteOffOrderItem.self,
from: Data(
"""
{
"order_number": "V2",
"order_verification_status": "",
"pay_time": "",
"project_name": "",
"user_phone": "",
"order_amount": "",
"order_status_name": "",
"order_verification_time": ""
}
""".utf8
)
)
XCTAssertFalse(viewModel.canVerify(verified))
}
private static func date(_ text: String) -> Date {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd"
return formatter.date(from: text)!
}
}
@MainActor
/// writeOffList
private final class WriteOffMockOrderService: OrderServing {
struct ListCall: Equatable {
let scenicId: Int
let storeId: Int?
let page: Int
let orderStatus: Int?
let orderVerifyStatus: Int?
let startTime: String?
let endTime: String?
let userPhone: String?
}
var responses: [ListPayload<WriteOffOrderItem>] = []
private(set) var listCalls: [ListCall] = []
private(set) var verifiedOrderNumbers: [String] = []
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> {
listCalls.append(
ListCall(
scenicId: scenicId,
storeId: storeId,
page: page,
orderStatus: orderStatus,
orderVerifyStatus: orderVerifyStatus,
startTime: startTime,
endTime: endTime,
userPhone: userPhone
)
)
return responses.isEmpty ? ListPayload(total: 0, list: []) : responses.removeFirst()
}
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 {
verifiedOrderNumbers.append(orderNumber)
}
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 {}
}