Files
suixinkan_uikit/suixinkanTests/StoreAccountDeregistrationFixtures.swift

140 lines
7.2 KiB
Swift

import Foundation
@testable import suixinkan
/// 从已实测响应整理的脱敏测试夹具;覆盖变化场景时显式覆盖字段,不宣称为真实服务端返回。
enum StoreAccountDeregistrationFixtures {
/// 2026-08-28 15:29 实测 userinfo 的150015响应;无time字段,data.status为字符串。
static let observedCoolingRestrictionEnvelope = Data(#"""
{
"code": 150015,
"msg": "账号处于注销冷静期,请先撤销注销后再继续使用",
"data": {
"status": "cooling",
"cooling_until": "2026-09-04 15:27:24",
"remaining_seconds": 604681
}
}
"""#.utf8)
/// 同次150015请求后的真实status响应,仅将注销记录ID替换为测试值301。
static let observedCoolingStatusEnvelope = Data(#"""
{
"code": 100000,
"msg": "success",
"data": {
"deregister": {
"id": 301,
"status": 1,
"status_label": "冷静期中",
"reason": "Test account deregistration",
"apply_time": "2026-08-28 15:27:24",
"cooling_until": "2026-09-04 15:27:24",
"remaining_seconds": 604681,
"cancel_time": null,
"blocked_code": "",
"blocked_reason": "",
"completed_at": null
}
},
"time": "2026-08-28 15:29:22"
}
"""#.utf8)
/// 真实样例的结构,身份 ID 替换为测试用的 101/201。
static func eligibilityData(overrides: [String: Any] = [:]) throws -> Data {
var data: [String: Any] = [
"can_apply": false, "store_user_id": 101, "finance_identity_id": 201,
"wallet_balance_fen": 0, "wallet_balance": "0.00", "points_balance": 0,
"wallet_waived": false, "points_waived": false,
"unfulfilled_count": 37, "fulfillment_in_progress_count": 0,
"risk_end_at": "2026-08-27 15:47:32", "eligible_at": "2026-09-03 15:47:32", "risk_window_hours": 168,
"blockers": [
["code": "ORDER_UNFULFILLED", "message": "账户仍有未履约订单或带单", "action": "complete_orders"],
["code": "RISK_WINDOW_NOT_EXPIRED", "message": "最后一笔业务的风险结束时间尚未超过7天", "action": "wait_risk_window"],
["code": "WALLET_WAIVER_MISSING", "message": "请先确认放弃现金余额", "action": "confirm_wallet_waiver"],
["code": "POINTS_WAIVER_MISSING", "message": "请先确认放弃积分", "action": "confirm_points_waiver"],
],
"deregister": NSNull(),
]
data.merge(overrides) { _, new in new }
return try JSONSerialization.data(withJSONObject: data)
}
/// 解码已实测结构,供协议替身返回值使用。
static func eligibility(overrides: [String: Any] = [:]) throws -> StoreAccountDeregistrationEligibility {
try JSONDecoder().decode(StoreAccountDeregistrationEligibility.self, from: eligibilityData(overrides: overrides))
}
/// 构造所有条件满足的规则测试输入;不是通过真实资产放弃或申请获得的响应。
static func ready(overrides: [String: Any] = [:]) throws -> StoreAccountDeregistrationEligibility {
var fields: [String: Any] = ["can_apply": true, "wallet_waived": true, "points_waived": true,
"unfulfilled_count": 0, "blockers": []]
fields.merge(overrides) { _, new in new }
return try eligibility(overrides: fields)
}
/// 2026-08-28 零资产确认后的真实草稿结构,仅将记录 ID 替换为测试值 301。
static func draftRecord(overrides: [String: Any] = [:]) -> [String: Any] {
var record: [String: Any] = [
"id": 301, "status": 0, "status_label": "待确认", "reason": "",
"apply_time": NSNull(), "cooling_until": NSNull(), "remaining_seconds": 0,
"cancel_time": NSNull(), "blocked_code": "", "blocked_reason": "", "completed_at": NSNull(),
]
record.merge(overrides) { _, new in new }
return record
}
/// 解码实测草稿状态;覆盖字段时用于验证未知或矛盾记录不会放行。
static func draftStatus(overrides: [String: Any] = [:]) throws -> StoreAccountDeregistrationStatus {
try JSONDecoder().decode(StoreAccountDeregistrationStatus.self,
from: JSONSerialization.data(withJSONObject: ["deregister": draftRecord(overrides: overrides)]))
}
/// 两次真实确认之间及之后的条件结构,没有历史业务时风险时间为 null。
static func draftEligibility(pointsWaived: Bool) throws -> StoreAccountDeregistrationEligibility {
try eligibility(overrides: [
"can_apply": pointsWaived, "wallet_waived": true, "points_waived": pointsWaived,
"unfulfilled_count": 0, "risk_end_at": NSNull(), "eligible_at": NSNull(),
"blockers": pointsWaived ? [] : [
["code": "POINTS_WAIVER_MISSING", "message": "请先确认放弃积分", "action": "confirm_points_waiver"],
],
"deregister": draftRecord(),
])
}
/// 实测的冷静期和主动撤销记录,ID 脱敏;覆盖字段只用于防御性测试。
static func lifecycleRecord(cancelled: Bool = false, overrides: [String: Any] = [:]) -> [String: Any] {
var record = draftRecord(overrides: [
"status": cancelled ? 9 : 1, "status_label": cancelled ? "已撤销" : "冷静期中",
"reason": "API test; cancel immediately", "apply_time": "2026-08-28 14:23:40",
"cooling_until": "2026-09-04 14:23:40", "remaining_seconds": cancelled ? 0 : 604776,
"cancel_time": cancelled ? "2026-08-28 14:24:04" : NSNull(),
"blocked_code": cancelled ? "CANCELLED_BY_USER" : "",
"blocked_reason": cancelled ? "用户主动撤销注销" : "",
])
record.merge(overrides) { _, new in new }
return record
}
/// 解码冷静期/已撤销实测样例。
static func lifecycleStatus(cancelled: Bool = false, overrides: [String: Any] = [:]) throws -> StoreAccountDeregistrationStatus {
try JSONDecoder().decode(StoreAccountDeregistrationStatus.self,
from: JSONSerialization.data(withJSONObject: ["deregister": lifecycleRecord(cancelled: cancelled, overrides: overrides)]))
}
/// 冷静期及撤销后真实查询均返回两项确认失效,不能在冷静期重新确认资产。
static func lifecycleEligibility(cancelled: Bool = false) throws -> StoreAccountDeregistrationEligibility {
try eligibility(overrides: ["unfulfilled_count": 0, "risk_end_at": NSNull(), "eligible_at": NSNull(),
"blockers": [
["code": "WALLET_WAIVER_MISSING", "message": "请先确认放弃现金余额", "action": "confirm_wallet_waiver"],
["code": "POINTS_WAIVER_MISSING", "message": "请先确认放弃积分", "action": "confirm_points_waiver"],
], "deregister": lifecycleRecord(cancelled: cancelled)])
}
/// 统一成功 Envelope,仅用于 MockURLSession。
static func envelope(_ data: Data) throws -> Data {
try JSONSerialization.data(withJSONObject: ["code": 100000, "msg": "success",
"data": JSONSerialization.jsonObject(with: data)])
}
}