162 lines
6.2 KiB
Swift
162 lines
6.2 KiB
Swift
//
|
|
// InviteTests.swift
|
|
// suixinkanTests
|
|
//
|
|
|
|
import XCTest
|
|
@testable import suixinkan
|
|
|
|
/// 邀请 API 和 ViewModel 测试。
|
|
@MainActor
|
|
final class InviteTests: XCTestCase {
|
|
/// 测试邀请 API path 和分页参数。
|
|
func testInviteAPIUsesExpectedRequests() async throws {
|
|
let session = MockURLSession(responses: [Self.inviteInfoEnvelope, Self.inviteUsersEnvelope])
|
|
let api = InviteAPI(client: APIClient(environment: .testing, 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?.realName, "摄影师")
|
|
XCTAssertEqual(session.requests.map { $0.url?.path }, [
|
|
"/api/yf-handset-app/photog/invite-info",
|
|
"/api/yf-handset-app/photog/invite/user-list",
|
|
])
|
|
let query = queryItems(from: session.requests[1])
|
|
XCTAssertEqual(query["page"], "1")
|
|
XCTAssertEqual(query["page_size"], "1")
|
|
}
|
|
|
|
/// 测试邀请信息模型宽松解码。
|
|
func testInviteInfoResponseDecodesLossyFields() throws {
|
|
let json = Data(#"{"enable_invite":"1","invite_code":123,"invite_url":"https://invite.example.com","description":["规则1"]}"#.utf8)
|
|
let response = try JSONDecoder().decode(InviteInfoResponse.self, from: json)
|
|
|
|
XCTAssertTrue(response.enableInvite)
|
|
XCTAssertEqual(response.inviteCode, "123")
|
|
XCTAssertEqual(response.description, ["规则1"])
|
|
}
|
|
|
|
/// 测试邀请页加载信息并生成二维码。
|
|
func testPhotographerInviteViewModelLoadsInfoAndGeneratesQRCode() async {
|
|
let viewModel = PhotographerInviteViewModel()
|
|
|
|
await viewModel.reload(api: FakeInviteService())
|
|
|
|
XCTAssertEqual(viewModel.inviteCode, "ABC123")
|
|
XCTAssertEqual(viewModel.inviteUrl, "https://invite.example.com")
|
|
XCTAssertEqual(viewModel.rules, ["规则1"])
|
|
XCTAssertNotNil(viewModel.qrImage)
|
|
XCTAssertFalse(viewModel.isLoading)
|
|
}
|
|
|
|
/// 测试邀请记录首次加载邀请用户和钱包汇总。
|
|
func testInviteRecordLoadsInviteRowsAndSummary() async {
|
|
let inviteAPI = FakeInviteService()
|
|
let walletAPI = FakeWalletService()
|
|
let viewModel = InviteRecordViewModel()
|
|
|
|
await viewModel.loadInitial(inviteAPI: inviteAPI, walletAPI: walletAPI)
|
|
|
|
XCTAssertEqual(viewModel.inviteRows.first?.title, "摄影师")
|
|
XCTAssertEqual(viewModel.totalRewardText, "100.00")
|
|
XCTAssertEqual(viewModel.withdrawableText, "50.00")
|
|
XCTAssertEqual(walletAPI.summaryTypes, [2])
|
|
XCTAssertFalse(viewModel.canLoadMoreInvite)
|
|
}
|
|
|
|
/// 测试奖励 Tab 加载钱包流水并使用 type=2。
|
|
func testInviteRecordRewardTabLoadsWalletTransactions() async {
|
|
let inviteAPI = FakeInviteService()
|
|
let walletAPI = FakeWalletService()
|
|
let viewModel = InviteRecordViewModel()
|
|
|
|
await viewModel.selectTab(.reward, inviteAPI: inviteAPI, walletAPI: walletAPI)
|
|
|
|
XCTAssertEqual(viewModel.selectedTab, .reward)
|
|
XCTAssertEqual(viewModel.rewardRows.first?.amount, "8.00")
|
|
XCTAssertEqual(walletAPI.transactionQueries.first?.type, 2)
|
|
XCTAssertEqual(walletAPI.transactionQueries.first?.page, 1)
|
|
}
|
|
|
|
/// 测试首页菜单 catalog 包含邀请相关 URI。
|
|
func testHomeMenuCatalogContainsInviteRoutes() {
|
|
XCTAssertEqual(HomeMenuCatalog.item(for: "registration_invitation")?.title, "注册邀请")
|
|
XCTAssertEqual(HomeMenuCatalog.item(for: "photographer_invite")?.title, "邀请摄影师")
|
|
XCTAssertEqual(HomeMenuCatalog.item(for: "invite_record")?.title, "邀请记录")
|
|
}
|
|
|
|
private static let inviteInfoEnvelope = Data(#"{"code":100000,"msg":"success","data":{"enable_invite":"1","invite_code":"ABC123","invite_url":"https://invite.example.com","description":["规则1"]}}"#.utf8)
|
|
private static let inviteUsersEnvelope = Data(#"{"code":100000,"msg":"success","data":[{"id":"1","real_name":"摄影师","phone":"13800000000","avatar":"","created_at":"2026-06-24","invite_level":"1"}]}"#.utf8)
|
|
}
|
|
|
|
@MainActor
|
|
/// 邀请服务测试替身。
|
|
private final class FakeInviteService: InviteServing {
|
|
func inviteInfo() async throws -> InviteInfoResponse {
|
|
InviteInfoResponse(
|
|
enableInvite: true,
|
|
inviteCode: "ABC123",
|
|
inviteUrl: "https://invite.example.com",
|
|
description: ["规则1"]
|
|
)
|
|
}
|
|
|
|
func inviteUserList(page: Int, pageSize: Int) async throws -> [InviteUserItem] {
|
|
[
|
|
InviteUserItem(
|
|
id: 1,
|
|
realName: "摄影师",
|
|
phone: "13800000000",
|
|
avatar: "",
|
|
createdAt: "2026-06-24",
|
|
inviteLevel: 1
|
|
),
|
|
]
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
/// 钱包服务测试替身。
|
|
private final class FakeWalletService: WalletServing {
|
|
private(set) var summaryTypes: [Int] = []
|
|
private(set) var transactionQueries: [(page: Int, pageSize: Int, type: Int)] = []
|
|
|
|
func walletSummary(type: Int) async throws -> WalletSummaryResponse {
|
|
summaryTypes.append(type)
|
|
return WalletSummaryResponse(amountTotal: "100.00", amountCurrentBalance: "20.00", amountWithdrawable: "50.00")
|
|
}
|
|
|
|
func walletTransactionList(
|
|
startDate: String,
|
|
endDate: String,
|
|
page: Int,
|
|
pageSize: Int,
|
|
type: Int
|
|
) async throws -> WalletTransactionListResponse {
|
|
transactionQueries.append((page, pageSize, type))
|
|
return WalletTransactionListResponse(
|
|
total: 1,
|
|
list: [
|
|
WalletTransactionItem(
|
|
id: 1,
|
|
amount: "8.00",
|
|
transactionType: 2,
|
|
transactionTypeLabel: "邀请奖励",
|
|
createdAt: "2026-06-24",
|
|
withdrawLabel: "可提现"
|
|
),
|
|
]
|
|
)
|
|
}
|
|
}
|
|
|
|
private func queryItems(from request: URLRequest) -> [String: String] {
|
|
URLComponents(url: request.url!, resolvingAgainstBaseURL: false)?
|
|
.queryItems?
|
|
.reduce(into: [String: String]()) { result, item in
|
|
result[item.name] = item.value
|
|
} ?? [:]
|
|
}
|