Files
suixinkan_ios_new/suixinkanTests/OrderDetailViewModelTests.swift

121 lines
4.9 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.

//
// OrderDetailViewModelTests.swift
// suixinkanTests
//
// Created by Codex on 2026/6/22.
//
import XCTest
@testable import suixinkan
@MainActor
/// ViewModel
final class OrderDetailViewModelTests: XCTestCase {
/// ID
func testLoadRequestsDetailWhenStoreIdExists() async throws {
let item = try firstOrder()
let api = DetailMockOrderService()
api.detailResponses = [try TestFixture.payload(StoreOrderDetailResponse.self, named: "store_order_detail_success")]
let viewModel = OrderDetailViewModel(item: item)
await viewModel.load(api: api, fallbackStoreId: 66)
XCTAssertEqual(api.detailCalls.count, 1)
XCTAssertEqual(api.detailCalls[0].storeId, 66)
XCTAssertEqual(api.detailCalls[0].orderNumber, "ORDV2001")
XCTAssertEqual(viewModel.display.projectName, "亲子跟拍")
XCTAssertEqual(viewModel.projectInfo?.settleSpotNum, 3)
XCTAssertEqual(viewModel.shootingList.first?.scenicSpotName, "东门")
XCTAssertNil(viewModel.errorMessage)
}
/// ID
func testLoadWithoutStoreIdUsesSummaryOnly() async throws {
let item = try firstOrder()
let api = DetailMockOrderService()
let viewModel = OrderDetailViewModel(item: item)
await viewModel.load(api: api, fallbackStoreId: nil)
XCTAssertTrue(api.detailCalls.isEmpty)
XCTAssertEqual(viewModel.display.orderNumber, "ORDV2001")
XCTAssertEqual(viewModel.contextMessage, "缺少门店上下文,当前展示订单摘要。")
}
///
func testLoadFailureKeepsSummaryAndExposesError() async throws {
let item = try firstOrder()
let api = DetailMockOrderService()
api.detailError = APIError.httpStatus(500, "server error")
let viewModel = OrderDetailViewModel(item: item)
await viewModel.load(api: api, fallbackStoreId: 66)
XCTAssertEqual(api.detailCalls.count, 1)
XCTAssertEqual(viewModel.display.orderNumber, "ORDV2001")
XCTAssertEqual(viewModel.contextMessage, "详情加载失败,当前展示订单摘要。")
XCTAssertNotNil(viewModel.errorMessage)
}
///
func testDetailResponseDecodesMixedNumericAndStringFields() throws {
let detail = try TestFixture.payload(StoreOrderDetailResponse.self, named: "store_order_detail_success")
XCTAssertEqual(detail.orderType, 19)
XCTAssertEqual(detail.actualRefundAmount, "0")
XCTAssertEqual(detail.userId, 7001)
XCTAssertEqual(detail.multiTravel?.projectInfo?.singleSpotVideoNum, 1)
XCTAssertEqual(detail.multiTravel?.shootingList.first?.startAvg, 4.8)
}
/// fixture
private func firstOrder() throws -> OrderEntity {
let payload = try TestFixture.payload(ListPayload<OrderEntity>.self, named: "order_v2_success")
return try XCTUnwrap(payload.list.first)
}
}
@MainActor
/// ViewModel
private final class DetailMockOrderService: OrderServing {
var detailResponses: [StoreOrderDetailResponse] = []
var detailError: Error?
private(set) var detailCalls: [(storeId: Int, orderNumber: 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> {
XCTFail("orderList should not be called")
return ListPayload(total: 0, list: [])
}
///
func writeOffList(scenicId: Int, storeId: Int?, page: Int, pageSize: Int) async throws -> ListPayload<WriteOffOrderItem> {
XCTFail("writeOffList should not be called")
return ListPayload(total: 0, list: [])
}
///
func writeOff(orderNumber: String) async throws {
XCTFail("writeOff should not be called")
}
///
func storeOrderDetail(storeId: Int, orderNumber: String) async throws -> StoreOrderDetailResponse {
detailCalls.append((storeId: storeId, orderNumber: orderNumber))
if let detailError {
throw detailError
}
return detailResponses.isEmpty ? try TestFixture.payload(StoreOrderDetailResponse.self, named: "store_order_detail_success") : detailResponses.removeFirst()
}
}