Files
suixinkan_ios_new/suixinkanTests/SessionBootstrapperTests.swift
汉秋 258c438f9a 优化冷启动流程:Launch Screen 对齐 Splash,仅阻塞 rolePermissions。
移除 SplashCoordinator,bootstrap 期间用 SplashView 覆盖避免空白闪屏,其余账号数据改为后台补充刷新。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 09:56:49 +08:00

145 lines
5.5 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// SessionBootstrapperTests.swift
// suixinkanTests
//
// Created by Codex on 2026/6/30.
//
import XCTest
@testable import suixinkan
@MainActor
///
final class SessionBootstrapperTests: XCTestCase {
/// token
func testRestoreWithoutTokenLogsOut() async {
let defaults = makeDefaults()
let tokenStore = SessionTokenStore(defaults: defaults, key: "test.token")
let snapshotStore = AccountSnapshotStore(defaults: defaults, key: "test.snapshot")
let bootstrapper = SessionBootstrapper(tokenStore: tokenStore, snapshotStore: snapshotStore)
let appSession = AppSession()
let accountContext = AccountContext()
let permissionContext = PermissionContext()
let toastCenter = ToastCenter()
await bootstrapper.restore(
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext,
profileAPI: MockUserProfileAPI(),
accountContextAPI: MockAccountContextAPI(),
toastCenter: toastCenter
)
XCTAssertEqual(appSession.phase, .loggedOut)
}
/// token rolePermissions loggedIn
func testRestoreMarksLoggedInAfterRolePermissions() async {
let defaults = makeDefaults()
let tokenStore = SessionTokenStore(defaults: defaults, key: "test.token")
try? tokenStore.save("token-1")
let snapshotStore = AccountSnapshotStore(defaults: defaults, key: "test.snapshot")
let bootstrapper = SessionBootstrapper(tokenStore: tokenStore, snapshotStore: snapshotStore)
let appSession = AppSession()
let accountContext = AccountContext()
let permissionContext = PermissionContext()
let toastCenter = ToastCenter()
let api = MockAccountContextAPI()
api.roles = [
RolePermissionResponse(
role: RoleInfo(id: 7, name: "运营", permission: [PermissionItem(id: 1, name: "首页", uri: "/home")]),
scenic: [ScenicInfo(id: 88, name: "东湖景区")]
)
]
await bootstrapper.restore(
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext,
profileAPI: MockUserProfileAPI(),
accountContextAPI: api,
toastCenter: toastCenter
)
XCTAssertEqual(appSession.phase, .loggedIn)
XCTAssertEqual(permissionContext.currentRole?.id, 7)
XCTAssertTrue(permissionContext.canAccess("/home"))
let profileExpectation = expectation(description: "profile loaded in background")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
if accountContext.profile?.displayName == "测试用户" {
profileExpectation.fulfill()
}
}
await fulfillment(of: [profileExpectation], timeout: 2)
}
/// rolePermissions
func testRestoreLogsOutWhenRolePermissionsExpired() async {
let defaults = makeDefaults()
let tokenStore = SessionTokenStore(defaults: defaults, key: "test.token")
try? tokenStore.save("token-1")
let snapshotStore = AccountSnapshotStore(defaults: defaults, key: "test.snapshot")
snapshotStore.save(AccountSnapshot(profile: AccountProfile(userId: "1", displayName: "本地用户")))
let bootstrapper = SessionBootstrapper(tokenStore: tokenStore, snapshotStore: snapshotStore)
let appSession = AppSession()
let accountContext = AccountContext()
let permissionContext = PermissionContext()
let toastCenter = ToastCenter()
let api = MockAccountContextAPI()
api.roleError = APIError.httpStatus(401, "登录状态已失效")
await bootstrapper.restore(
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext,
profileAPI: MockUserProfileAPI(),
accountContextAPI: api,
toastCenter: toastCenter
)
XCTAssertEqual(appSession.phase, .loggedOut)
XCTAssertNil(tokenStore.load())
XCTAssertNil(snapshotStore.load())
}
private func makeDefaults() -> UserDefaults {
let suiteName = "SessionBootstrapperTests.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
defaults.removePersistentDomain(forName: suiteName)
return defaults
}
}
@MainActor
private final class MockUserProfileAPI: UserProfileServing {
func userInfo() async throws -> UserInfoResponse {
try await Task.sleep(nanoseconds: 100_000_000)
return UserInfoResponse(nickname: "测试用户", roleName: "运营")
}
}
@MainActor
private final class MockAccountContextAPI: AccountContextServing {
var roles: [RolePermissionResponse] = []
var roleError: Error?
func rolePermissions() async throws -> [RolePermissionResponse] {
if let roleError { throw roleError }
return roles
}
func scenicListAll() async throws -> ScenicListAllResponse {
ScenicListAllResponse(total: 0, list: [])
}
func storeAll() async throws -> ListPayload<StoreItem> {
ListPayload(total: 0, list: [])
}
func scenicSpotListAll(scenicId: Int) async throws -> ListPayload<ScenicSpotItem> {
ListPayload(total: 0, list: [])
}
}