138 lines
4.9 KiB
Swift
138 lines
4.9 KiB
Swift
//
|
||
// AccountContextLoaderTests.swift
|
||
// suixinkanTests
|
||
//
|
||
// Created by Codex on 2026/6/22.
|
||
//
|
||
|
||
import XCTest
|
||
@testable import suixinkan
|
||
|
||
@MainActor
|
||
/// 账号上下文加载器测试,覆盖权限、景区兜底和门店容错。
|
||
final class AccountContextLoaderTests: XCTestCase {
|
||
/// 测试权限、用户资料、景区和门店成功时会写入上下文。
|
||
func testRefreshLoadsRolePermissionScenicAndStoreContext() async throws {
|
||
let accountContext = AccountContext()
|
||
let permissionContext = PermissionContext()
|
||
let api = MockAccountContextAPI()
|
||
api.roles = [
|
||
RolePermissionResponse(
|
||
role: RoleInfo(id: 7, name: "运营", permission: [PermissionItem(id: 1, name: "首页", uri: "/home")]),
|
||
scenic: [ScenicInfo(id: 88, name: "东湖景区")]
|
||
)
|
||
]
|
||
api.scenicResponse = ScenicListAllResponse(total: 1, list: [ScenicListItem(id: 88, name: "东湖景区")])
|
||
api.storeResponse = ListPayload(total: 1, list: [StoreItem(id: 66, scenicId: 88, name: "东湖门店")])
|
||
|
||
try await AccountContextLoader().refresh(
|
||
accountContext: accountContext,
|
||
permissionContext: permissionContext,
|
||
profileAPI: MockUserProfileAPI(),
|
||
accountContextAPI: api
|
||
)
|
||
|
||
XCTAssertEqual(accountContext.profile?.displayName, "测试用户")
|
||
XCTAssertEqual(permissionContext.currentRole?.id, 7)
|
||
XCTAssertTrue(permissionContext.canAccess("/home"))
|
||
XCTAssertEqual(accountContext.currentScenic?.id, 88)
|
||
XCTAssertEqual(accountContext.currentStore?.id, 66)
|
||
}
|
||
|
||
/// 测试景区接口失败时会从角色权限景区兜底。
|
||
func testRefreshFallsBackToRoleScenicsWhenScenicAPIThrows() async throws {
|
||
let accountContext = AccountContext()
|
||
let permissionContext = PermissionContext()
|
||
let api = MockAccountContextAPI()
|
||
api.roles = [
|
||
RolePermissionResponse(
|
||
role: RoleInfo(id: 7, name: "运营"),
|
||
scenic: [
|
||
ScenicInfo(id: 88, name: "东湖景区"),
|
||
ScenicInfo(id: 88, name: "东湖景区")
|
||
]
|
||
)
|
||
]
|
||
api.scenicError = APIError.networkFailed("景区接口失败")
|
||
|
||
try await AccountContextLoader().refresh(
|
||
accountContext: accountContext,
|
||
permissionContext: permissionContext,
|
||
profileAPI: MockUserProfileAPI(),
|
||
accountContextAPI: api
|
||
)
|
||
|
||
XCTAssertEqual(accountContext.scenicScopes.map(\.id), [88])
|
||
}
|
||
|
||
/// 测试门店接口失败时不阻断上下文加载。
|
||
func testRefreshAllowsStoreFailure() async throws {
|
||
let accountContext = AccountContext()
|
||
let permissionContext = PermissionContext()
|
||
let api = MockAccountContextAPI()
|
||
api.roles = [
|
||
RolePermissionResponse(
|
||
role: RoleInfo(id: 7, name: "运营"),
|
||
scenic: [ScenicInfo(id: 88, name: "东湖景区")]
|
||
)
|
||
]
|
||
api.storeError = APIError.networkFailed("门店接口失败")
|
||
|
||
try await AccountContextLoader().refresh(
|
||
accountContext: accountContext,
|
||
permissionContext: permissionContext,
|
||
profileAPI: MockUserProfileAPI(),
|
||
accountContextAPI: api
|
||
)
|
||
|
||
XCTAssertEqual(accountContext.currentScenic?.id, 88)
|
||
XCTAssertTrue(accountContext.storeScopes.isEmpty)
|
||
XCTAssertNil(accountContext.currentStore)
|
||
}
|
||
}
|
||
|
||
@MainActor
|
||
/// 用户资料服务测试替身,返回固定用户资料。
|
||
private final class MockUserProfileAPI: UserProfileServing {
|
||
/// 获取固定用户资料。
|
||
func userInfo() async throws -> UserInfoResponse {
|
||
UserInfoResponse(nickname: "测试用户", roleName: "运营")
|
||
}
|
||
}
|
||
|
||
@MainActor
|
||
/// 账号上下文服务测试替身,可分别控制各接口成功或失败。
|
||
private final class MockAccountContextAPI: AccountContextServing {
|
||
var roles: [RolePermissionResponse] = []
|
||
var scenicResponse = ScenicListAllResponse(total: 0, list: [])
|
||
var storeResponse = ListPayload(total: 0, list: [StoreItem]())
|
||
var scenicError: Error?
|
||
var storeError: Error?
|
||
|
||
/// 获取测试角色权限。
|
||
func rolePermissions() async throws -> [RolePermissionResponse] {
|
||
roles
|
||
}
|
||
|
||
/// 获取测试景区列表,或按配置抛出错误。
|
||
func scenicListAll() async throws -> ScenicListAllResponse {
|
||
if let scenicError {
|
||
throw scenicError
|
||
}
|
||
return scenicResponse
|
||
}
|
||
|
||
/// 获取测试门店列表,或按配置抛出错误。
|
||
func storeAll() async throws -> ListPayload<StoreItem> {
|
||
if let storeError {
|
||
throw storeError
|
||
}
|
||
return storeResponse
|
||
}
|
||
|
||
/// 获取测试景点列表。
|
||
func scenicSpotListAll(scenicId: Int) async throws -> ListPayload<ScenicSpotItem> {
|
||
ListPayload(total: 0, list: [])
|
||
}
|
||
}
|