Files
suixinkan_ios_new/suixinkanTests/PaymentViewModelTests.swift
汉秋 5d4613f317 新增支付与钱包模块,并接入首页路由
引入真实收款与钱包页面替换首页占位入口,通过 RootView 接入 API,并支持银行卡 OSS 上传及二维码保存到相册。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 17:46:01 +08:00

109 lines
4.7 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.

//
// PaymentViewModelTests.swift
// suixinkanTests
//
// Created by Codex on 2026/6/22.
//
import XCTest
@testable import suixinkan
@MainActor
///
final class PaymentViewModelTests: XCTestCase {
///
func testLoadPayCodeWithoutScenicClearsState() async {
let api = PaymentMockAPI()
let viewModel = PaymentCollectionViewModel()
viewModel.staticPayUrl = "old"
await viewModel.loadPayCode(api: api, scenicId: nil)
XCTAssertTrue(viewModel.staticPayUrl.isEmpty)
XCTAssertEqual(api.payCodeScenicIds, [])
XCTAssertEqual(viewModel.errorMessage, "请先选择景区")
}
/// URL
func testLoadPayCodeUsesScenicIdAndAppliesStaticURL() async {
let api = PaymentMockAPI()
api.payCodeResponse = PayCodeResponse(staticPayUrl: "https://pay.example.com/static", dynamicPayUrl: "https://pay.example.com/dynamic")
let viewModel = PaymentCollectionViewModel()
await viewModel.loadPayCode(api: api, scenicId: 9)
XCTAssertEqual(api.payCodeScenicIds, [9])
XCTAssertEqual(viewModel.currentPayUrl, "https://pay.example.com/static")
XCTAssertTrue(viewModel.hasStaticPayCode)
}
/// URL
func testApplyDynamicAmountBuildsDynamicURL() async {
let viewModel = PaymentCollectionViewModel()
viewModel.dynamicPayUrl = "https://pay.example.com/dynamic?scenic_id=9"
viewModel.amountText = "12.345"
viewModel.remarkText = "茶水"
XCTAssertTrue(viewModel.applyDynamicAmount())
XCTAssertTrue(viewModel.currentPayUrl.contains("amount=12.35"))
XCTAssertTrue(viewModel.currentPayUrl.contains("remark="))
XCTAssertEqual(viewModel.status, .waiting)
}
///
func testPollingFindsNewPaymentRecord() async {
let api = PaymentMockAPI()
api.recordResponses = [
PaymentCollectionRecordResponse(list: [
PaymentCollectionRecordItem(orderNumber: "A", userPhone: "138", orderAmount: "8.00", createDate: "2026-06-22", createTime: "10:00")
]),
PaymentCollectionRecordResponse(list: [
PaymentCollectionRecordItem(orderNumber: "A", userPhone: "138", orderAmount: "8.00", createDate: "2026-06-22", createTime: "10:00"),
PaymentCollectionRecordItem(orderNumber: "B", userPhone: "139", orderAmount: "12.35", createDate: "2026-06-22", createTime: "10:01")
])
]
let viewModel = PaymentCollectionViewModel()
viewModel.amountText = "12.35"
await viewModel.primePaymentRecords(api: api, scenicId: 9)
await viewModel.pollUntilPaymentDetected(api: api, scenicId: 9, maxAttempts: 1, intervalNanoseconds: 0)
XCTAssertEqual(viewModel.status, .success(PaymentCollectionRecordItem(orderNumber: "B", userPhone: "139", orderAmount: "12.35", createDate: "2026-06-22", createTime: "10:01")))
}
///
func testRecordGroupsFallbackFromList() {
let response = PaymentCollectionRecordResponse(list: [
PaymentCollectionRecordItem(orderNumber: "A", userPhone: "138", orderAmount: "8.00", createDate: "2026-06-21", createTime: "10:00"),
PaymentCollectionRecordItem(orderNumber: "B", userPhone: "139", orderAmount: "12.00", createDate: "2026-06-21", createTime: "10:01")
])
let groups = PaymentCollectionRecordViewModel.makeGroups(from: response)
XCTAssertEqual(groups.count, 1)
XCTAssertEqual(groups[0].analyse.orderCount, 2)
XCTAssertEqual(groups[0].analyse.orderAmountSum, "20")
}
}
@MainActor
/// API
private final class PaymentMockAPI: PaymentServing {
var payCodeResponse = PayCodeResponse()
var recordResponses: [PaymentCollectionRecordResponse] = []
private(set) var payCodeScenicIds: [Int] = []
private(set) var recordScenicIds: [Int] = []
/// ID
func payCode(scenicId: Int) async throws -> PayCodeResponse {
payCodeScenicIds.append(scenicId)
return payCodeResponse
}
/// ID
func paymentCollectionRecords(scenicId: Int) async throws -> PaymentCollectionRecordResponse {
recordScenicIds.append(scenicId)
return recordResponses.isEmpty ? PaymentCollectionRecordResponse() : recordResponses.removeFirst()
}
}