202 lines
8.1 KiB
Swift
202 lines
8.1 KiB
Swift
//
|
||
// ProfileSecondaryPagesTests.swift
|
||
// suixinkanTests
|
||
//
|
||
// Created by Codex on 2026/6/22.
|
||
//
|
||
|
||
import XCTest
|
||
@testable import suixinkan
|
||
|
||
@MainActor
|
||
/// 个人中心二级页面测试,覆盖设置、账号切换和实名认证基础逻辑。
|
||
final class ProfileSecondaryPagesTests: XCTestCase {
|
||
/// 测试设置页版本号展示规则。
|
||
func testSettingsVersionTextUsesBuildNumberWhenNeeded() {
|
||
XCTAssertEqual(
|
||
SettingsDisplayPolicy.versionText(infoDictionary: [
|
||
"CFBundleShortVersionString": "1.2",
|
||
"CFBundleVersion": "45"
|
||
]),
|
||
"1.2.45"
|
||
)
|
||
XCTAssertEqual(
|
||
SettingsDisplayPolicy.versionText(infoDictionary: [
|
||
"CFBundleShortVersionString": "1.2.3",
|
||
"CFBundleVersion": "45"
|
||
]),
|
||
"1.2.3"
|
||
)
|
||
XCTAssertEqual(SettingsDisplayPolicy.versionText(infoDictionary: nil), "1.0.0")
|
||
}
|
||
|
||
/// 测试协议页面映射到预期 H5 路径。
|
||
func testAgreementPagesUseExpectedPaths() {
|
||
let pages: [(AgreementPage, String, String)] = [
|
||
(.about, "关于我们", "/h5/app/about-us"),
|
||
(.userAgreement, "用户协议", "/h5/app/user-agreement"),
|
||
(.privacyPolicy, "隐私政策", "/h5/app/privacy-policy"),
|
||
(.walletUserNotice, "钱包用户须知", "/h5/app/wallet-user-notice"),
|
||
(.walletPrivacy, "钱包隐私政策", "/h5/app/wallet-privacy")
|
||
]
|
||
|
||
for (page, title, path) in pages {
|
||
XCTAssertEqual(page.title, title)
|
||
XCTAssertEqual(page.id, title)
|
||
XCTAssertEqual(page.url.path, path)
|
||
}
|
||
}
|
||
|
||
/// 测试账号切换列表接口使用旧工程一致的 v9 accounts 路径。
|
||
func testSwitchableAccountsUsesExpectedEndpoint() async throws {
|
||
let session = ProfileSecondaryURLSession(responses: [
|
||
try TestFixture.data(named: "v9_login_multi_success")
|
||
])
|
||
let api = ProfileAPI(client: APIClient(session: session))
|
||
|
||
let response = try await api.switchableAccounts()
|
||
|
||
XCTAssertEqual(response.accounts.count, 2)
|
||
let request = try XCTUnwrap(session.requests.first)
|
||
XCTAssertEqual(request.httpMethod, "GET")
|
||
XCTAssertEqual(request.url?.path, "/api/app/v9/accounts")
|
||
}
|
||
|
||
/// 测试账号切换 ViewModel 能加载账号并默认选中第一项。
|
||
func testAccountSwitchViewModelLoadsAccounts() async throws {
|
||
let session = ProfileSecondaryURLSession(responses: [
|
||
try TestFixture.data(named: "v9_login_multi_success")
|
||
])
|
||
let api = ProfileAPI(client: APIClient(session: session))
|
||
let viewModel = AccountSwitchViewModel()
|
||
|
||
try await viewModel.load(api: api)
|
||
|
||
XCTAssertEqual(viewModel.accounts.map(\.businessUserId), [101, 201])
|
||
XCTAssertEqual(viewModel.selectedAccount?.businessUserId, 101)
|
||
XCTAssertFalse(viewModel.loading)
|
||
}
|
||
|
||
/// 测试账号切换提交使用 set-user 接口和账号对应请求体。
|
||
func testAccountSwitchViewModelSubmitsSetUser() async throws {
|
||
let session = ProfileSecondaryURLSession(responses: [
|
||
try TestFixture.data(named: "v9_set_store_user_success")
|
||
])
|
||
let api = AuthAPI(client: APIClient(session: session))
|
||
let account = AccountSwitchAccount(
|
||
accountType: V9StoreUser.accountTypeValue,
|
||
businessUserId: 201,
|
||
title: "示例门店",
|
||
subtitle: "示例景区",
|
||
phone: "13800138000",
|
||
avatar: "",
|
||
scenicName: "示例景区",
|
||
storeId: 20,
|
||
storeName: "示例门店",
|
||
scenicId: 10,
|
||
isCurrent: false
|
||
)
|
||
let viewModel = AccountSwitchViewModel()
|
||
|
||
let response = try await viewModel.switchAccount(account, api: api)
|
||
|
||
XCTAssertFalse(response.token.isEmpty)
|
||
let request = try XCTUnwrap(session.requests.first)
|
||
XCTAssertEqual(request.httpMethod, "POST")
|
||
XCTAssertEqual(request.url?.path, "/api/app/v9/set-user")
|
||
let json = try XCTUnwrap(JSONSerialization.jsonObject(with: XCTUnwrap(request.httpBody)) as? [String: Any])
|
||
XCTAssertEqual(json["store_user_id"] as? Int, 201)
|
||
XCTAssertNil(json["ss_user_id"] as? Int)
|
||
}
|
||
|
||
/// 测试实名认证信息解码兼容字符串数字字段。
|
||
func testRealNameInfoDecodesLossyFields() async throws {
|
||
let session = ProfileSecondaryURLSession(responses: [
|
||
try TestFixture.data(named: "real_name_info_success")
|
||
])
|
||
let api = ProfileAPI(client: APIClient(session: session))
|
||
|
||
let response = try await api.realNameInfo()
|
||
let info = try XCTUnwrap(response.realNameInfo)
|
||
|
||
XCTAssertEqual(info.realName, "测试摄影师")
|
||
XCTAssertEqual(info.idCardNo, "320100********0012")
|
||
XCTAssertTrue(info.verified)
|
||
XCTAssertFalse(info.isLongValid)
|
||
XCTAssertEqual(info.auditStatus, 2)
|
||
XCTAssertEqual(info.auditorId, 7)
|
||
XCTAssertEqual(info.auditor?.name, "审核员")
|
||
XCTAssertEqual(session.requests.first?.url?.path, "/api/yf-handset-app/photog/real-name/info")
|
||
}
|
||
|
||
/// 测试实名认证短信和提交接口路径及请求体。
|
||
func testRealNameSmsAndSubmitUseExpectedEndpoints() async throws {
|
||
let session = ProfileSecondaryURLSession(responses: [
|
||
try TestFixture.data(named: "empty_success"),
|
||
try TestFixture.data(named: "empty_success")
|
||
])
|
||
let api = ProfileAPI(client: APIClient(session: session))
|
||
|
||
try await api.realNameSmsVerifyCode()
|
||
try await api.realNameSubmit(
|
||
RealNameAuthRequest(
|
||
realName: "测试摄影师",
|
||
idCardNo: "11010519491231002X",
|
||
smsVerifyCode: "123456",
|
||
startDate: "2026-01-01",
|
||
endDate: "2046-01-01",
|
||
isLongValid: 0,
|
||
frontUrl: "https://cdn.example.com/front.jpg",
|
||
backUrl: "https://cdn.example.com/back.jpg"
|
||
)
|
||
)
|
||
|
||
XCTAssertEqual(session.requests.map { $0.url?.path }, [
|
||
"/api/yf-handset-app/photog/real-name/sms-verify-code",
|
||
"/api/yf-handset-app/photog/real-name/submit"
|
||
])
|
||
let json = try XCTUnwrap(JSONSerialization.jsonObject(with: XCTUnwrap(session.requests.last?.httpBody)) as? [String: Any])
|
||
XCTAssertEqual(json["real_name"] as? String, "测试摄影师")
|
||
XCTAssertEqual(json["id_card_no"] as? String, "11010519491231002X")
|
||
XCTAssertEqual(json["sms_verify_code"] as? String, "123456")
|
||
XCTAssertEqual(json["is_long_valid"] as? Int, 0)
|
||
}
|
||
|
||
/// 测试身份证号校验和格式归一化。
|
||
func testRealNameIDCardValidation() {
|
||
XCTAssertTrue(RealNameAuthViewModel.isValidMainlandIDCardNumber("11010519491231002x"))
|
||
XCTAssertEqual(RealNameAuthViewModel.normalizedIDCardNumber(" 11010519491231002x "), "11010519491231002X")
|
||
XCTAssertFalse(RealNameAuthViewModel.isValidMainlandIDCardNumber("110105194912310021"))
|
||
XCTAssertFalse(RealNameAuthViewModel.isValidMainlandIDCardNumber("123"))
|
||
}
|
||
|
||
/// 测试首页系统设置 URI 已接入真实设置页。
|
||
func testHomeSystemSettingsRouteResolvesToSettingsPage() {
|
||
XCTAssertEqual(
|
||
HomeMenuRouter.resolve(uri: "system_settings", title: "设置中心"),
|
||
.destination(.settings)
|
||
)
|
||
}
|
||
}
|
||
|
||
/// 个人中心测试用 URLSession 替身,按顺序返回预设响应。
|
||
private final class ProfileSecondaryURLSession: URLSessionProtocol {
|
||
private var responses: [Data]
|
||
private(set) var requests: [URLRequest] = []
|
||
|
||
/// 初始化顺序响应。
|
||
init(responses: [Data]) {
|
||
self.responses = responses
|
||
}
|
||
|
||
/// 记录请求并返回下一份响应。
|
||
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
|
||
requests.append(request)
|
||
let data = responses.isEmpty ? Data() : responses.removeFirst()
|
||
return (
|
||
data,
|
||
HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
|
||
)
|
||
}
|
||
}
|