Initial commit: suixinkan_ios UIKit rewrite project.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
154
suixinkan_iosTests/Invite/InviteTests.swift
Normal file
154
suixinkan_iosTests/Invite/InviteTests.swift
Normal file
@ -0,0 +1,154 @@
|
||||
//
|
||||
// InviteTests.swift
|
||||
// suixinkanTests
|
||||
//
|
||||
// Created by Codex on 2026/6/24.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import suixinkan_ios
|
||||
|
||||
@MainActor
|
||||
/// 邀请 API 和 ViewModel 测试。
|
||||
final class InviteTests: XCTestCase {
|
||||
/// 测试邀请 API path 和分页参数。
|
||||
func testInviteAPIUsesExpectedRequests() async throws {
|
||||
let session = InviteRecordingURLSession(responses: [Self.inviteInfoResponse, Self.inviteUsersResponse])
|
||||
let api = InviteAPI(client: APIClient(session: session))
|
||||
|
||||
let info = try await api.inviteInfo()
|
||||
let users = try await api.inviteUserList(page: 0, pageSize: 0)
|
||||
|
||||
XCTAssertEqual(info.inviteCode, "ABC123")
|
||||
XCTAssertEqual(users.first?.id, 1)
|
||||
XCTAssertEqual(session.requests.map { $0.url?.path }, [
|
||||
"/api/yf-handset-app/photog/invite-info",
|
||||
"/api/yf-handset-app/photog/invite/user-list"
|
||||
])
|
||||
XCTAssertEqual(inviteQueryItems(from: session.requests[1])["page"], "1")
|
||||
XCTAssertEqual(inviteQueryItems(from: session.requests[1])["page_size"], "1")
|
||||
}
|
||||
|
||||
/// 测试邀请页加载信息并生成二维码。
|
||||
func testInviteViewModelLoadsInfoAndGeneratesQRCode() async {
|
||||
let api = FakeInviteService()
|
||||
let viewModel = PhotographerInviteViewModel()
|
||||
|
||||
await viewModel.reload(api: api)
|
||||
|
||||
XCTAssertEqual(viewModel.inviteCode, "ABC123")
|
||||
XCTAssertEqual(viewModel.rules, ["规则1"])
|
||||
XCTAssertNotNil(viewModel.qrImage)
|
||||
}
|
||||
|
||||
/// 测试邀请记录加载邀请用户和钱包汇总。
|
||||
func testInviteRecordLoadsInviteRowsAndSummary() async {
|
||||
let inviteAPI = FakeInviteService()
|
||||
let walletAPI = FakeWalletService()
|
||||
let viewModel = InviteRecordViewModel()
|
||||
|
||||
await viewModel.reload(inviteAPI: inviteAPI, walletAPI: walletAPI, refresh: true)
|
||||
|
||||
XCTAssertEqual(viewModel.displayRows.first?.title, "摄影师")
|
||||
XCTAssertEqual(viewModel.totalRewardText, "¥ 100.00")
|
||||
XCTAssertEqual(walletAPI.summaryTypes, [2])
|
||||
}
|
||||
|
||||
/// 测试奖励分段调用钱包收益明细。
|
||||
func testInviteRecordRewardTabLoadsWalletDetails() async {
|
||||
let inviteAPI = FakeInviteService()
|
||||
let walletAPI = FakeWalletService()
|
||||
let viewModel = InviteRecordViewModel()
|
||||
|
||||
await viewModel.selectTab(.reward, inviteAPI: inviteAPI, walletAPI: walletAPI)
|
||||
|
||||
XCTAssertEqual(walletAPI.earningPages, [1])
|
||||
XCTAssertNil(viewModel.errorMessage, viewModel.errorMessage ?? "")
|
||||
XCTAssertEqual(viewModel.displayRows.first?.amount, "8.00")
|
||||
}
|
||||
|
||||
private static let inviteInfoResponse = Data(#"{"code":100000,"message":"ok","data":{"enable_invite":"1","invite_code":"ABC123","invite_url":"https://invite.example.com","description":["规则1"]}}"#.utf8)
|
||||
private static let inviteUsersResponse = Data(#"{"code":100000,"message":"ok","data":[{"id":"1","real_name":"摄影师","phone":"13800000000","avatar":"","created_at":"2026-06-24","invite_level":"1"}]}"#.utf8)
|
||||
}
|
||||
|
||||
/// 邀请 API 测试 URLSession。
|
||||
private final class InviteRecordingURLSession: URLSessionProtocol {
|
||||
private var responses: [Data]
|
||||
private(set) var requests: [URLRequest] = []
|
||||
|
||||
/// 初始化测试 Session。
|
||||
init(responses: [Data]) {
|
||||
self.responses = responses
|
||||
}
|
||||
|
||||
/// 记录请求并返回响应。
|
||||
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
|
||||
requests.append(request)
|
||||
let data = responses.removeFirst()
|
||||
return (data, HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!)
|
||||
}
|
||||
}
|
||||
|
||||
/// 邀请服务测试替身。
|
||||
@MainActor
|
||||
private final class FakeInviteService: InviteServing {
|
||||
func inviteInfo() async throws -> InviteInfoResponse {
|
||||
try JSONDecoder().decode(InviteInfoResponse.self, from: Data(#"{"enable_invite":"1","invite_code":"ABC123","invite_url":"https://invite.example.com","description":["规则1"]}"#.utf8))
|
||||
}
|
||||
|
||||
func inviteUserList(page: Int, pageSize: Int) async throws -> [InviteUserItem] {
|
||||
[try JSONDecoder().decode(InviteUserItem.self, from: Data(#"{"id":"1","real_name":"摄影师","phone":"13800000000","avatar":"","created_at":"2026-06-24","invite_level":"1"}"#.utf8))]
|
||||
}
|
||||
}
|
||||
|
||||
/// 钱包服务测试替身。
|
||||
@MainActor
|
||||
private final class FakeWalletService: WalletServing {
|
||||
var summaryTypes: [Int] = []
|
||||
var earningPages: [Int] = []
|
||||
|
||||
func walletSummary(type: Int) async throws -> WalletSummaryResponse {
|
||||
summaryTypes.append(type)
|
||||
return WalletSummaryResponse(amountTotal: "100.00", amountCurrentBalance: "20.00", amountWithdrawable: "50.00")
|
||||
}
|
||||
|
||||
func walletEarningDetail(startDate: String, endDate: String, page: Int, pageSize: Int) async throws -> WalletEarningDetailResponse {
|
||||
earningPages.append(page)
|
||||
let item = WalletEarningDetailItem(
|
||||
id: 1,
|
||||
amount: "8.00",
|
||||
type: 2,
|
||||
typeLabel: "邀请奖励",
|
||||
orderNumberSuffix: "0001",
|
||||
createdAt: "2026-06-24"
|
||||
)
|
||||
return WalletEarningDetailResponse(
|
||||
totalAmount: "8.00",
|
||||
total: 1,
|
||||
list: [WalletEarningDetailGroup(date: "2026-06-24", dayAmount: "8.00", items: [item])]
|
||||
)
|
||||
}
|
||||
|
||||
func walletWithdrawList(page: Int, pageSize: Int) async throws -> WalletWithdrawListResponse { WalletWithdrawListResponse() }
|
||||
func withdrawInfo() async throws -> WithdrawInfoResponse { try JSONDecoder().decode(WithdrawInfoResponse.self, from: Data("{}".utf8)) }
|
||||
func withdrawSendSms() async throws {}
|
||||
func withdrawApply(amount: String, smsCode: String) async throws {}
|
||||
func bankCardInfo() async throws -> BankCardInfoResponse { try JSONDecoder().decode(BankCardInfoResponse.self, from: Data("{}".utf8)) }
|
||||
func bankList() async throws -> BankListResponse { BankListResponse(banks: []) }
|
||||
func areas() async throws -> [AreaNode] { [] }
|
||||
func bankCardVerifyCode() async throws {}
|
||||
func updateBankInfo(_ request: UpdateBankInfoRequest) async throws {}
|
||||
func pointOverview(staffId: Int) async throws -> PointOverviewResponse { try JSONDecoder().decode(PointOverviewResponse.self, from: Data("{}".utf8)) }
|
||||
func pointWithdrawApply(points: Int, remark: String) async throws {}
|
||||
func pointWithdrawList(status: Int?, page: Int, pageSize: Int) async throws -> PointWithdrawListResponse { PointWithdrawListResponse(total: 0, list: []) }
|
||||
}
|
||||
|
||||
/// 从请求中提取 query 字典。
|
||||
private func inviteQueryItems(from request: URLRequest) -> [String: String] {
|
||||
guard let url = request.url, let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||
return [:]
|
||||
}
|
||||
return Dictionary(uniqueKeysWithValues: (components.queryItems ?? []).compactMap { item in
|
||||
item.value.map { (item.name, $0) }
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user