为 album_list 与 album_trailer 接入首页路由,在 Profile 中新增 DEBUG 首页菜单预览,并扩展 Assets 测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
190 lines
10 KiB
Swift
190 lines
10 KiB
Swift
//
|
||
// HomeMenuRouterTests.swift
|
||
// suixinkanTests
|
||
//
|
||
// Created by Codex on 2026/6/22.
|
||
//
|
||
|
||
import XCTest
|
||
@testable import suixinkan
|
||
|
||
/// 首页菜单路由测试,覆盖 URI 映射、别名和未知路由诊断。
|
||
final class HomeMenuRouterTests: XCTestCase {
|
||
override func setUp() {
|
||
super.setUp()
|
||
HomeRouteDiagnostics.resetUnknownRoutes()
|
||
}
|
||
|
||
override func tearDown() {
|
||
HomeRouteDiagnostics.resetUnknownRoutes()
|
||
super.tearDown()
|
||
}
|
||
|
||
/// 测试已迁移的首页路由会解析到真实目标。
|
||
func testMigratedRoutesResolveToDestinations() {
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "space_settings", title: ""), .destination(.profileSpace))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "scenicselection", title: ""), .destination(.scenicSelection))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "store", title: ""), .destination(.moreFunctions))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "wallet", title: ""), .destination(.wallet))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "payment_collection", title: ""), .destination(.paymentCollection))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "payment_qr", title: ""), .destination(.paymentCollection))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "payment_code", title: ""), .destination(.paymentCollection))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "permission_apply", title: ""), .destination(.permissionApply))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "permission_apply_status", title: ""), .destination(.permissionApplyStatus))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "scenicapplication", title: ""), .destination(.scenicApplication))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "task_management", title: ""), .destination(.taskManagement))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "task_management_editor", title: ""), .destination(.taskManagement))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "task_create", title: ""), .destination(.taskCreate))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "cloud_management", title: ""), .destination(.cloudStorage))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "cloud_storage_transit", title: ""), .destination(.cloudStorageTransit))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "asset_management", title: ""), .destination(.materialLibrary))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "material_upload", title: ""), .destination(.materialUpload))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "album_list", title: ""), .destination(.albumList))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "album_trailer", title: ""), .destination(.albumTrailer))
|
||
}
|
||
|
||
/// 测试已知但未迁移的首页路由会进入安全占位。
|
||
func testKnownUnmigratedRoutesResolveToPlaceholders() {
|
||
XCTAssertEqual(
|
||
HomeMenuRouter.resolve(uri: "scenic_settlement", title: ""),
|
||
.destination(.modulePlaceholder(uri: "scenic_settlement", title: "景区结算"))
|
||
)
|
||
XCTAssertEqual(
|
||
HomeMenuRouter.resolve(uri: "scenic_settlement_review", title: ""),
|
||
.destination(.modulePlaceholder(uri: "scenic_settlement_review", title: "结算审核"))
|
||
)
|
||
XCTAssertEqual(
|
||
HomeMenuRouter.resolve(uri: "sample_upload", title: ""),
|
||
.destination(.modulePlaceholder(uri: "sample_upload", title: "上传样片"))
|
||
)
|
||
}
|
||
|
||
/// 测试 Tab 路由仍集中在 HomeMenuRouter。
|
||
func testTabRoutesStayCentralized() {
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "photographer_orders", title: ""), .orders(.storeOrders))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "/scenic-order-manage", title: ""), .orders(.storeOrders))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "verification_order", title: ""), .orders(.verificationOrders))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "photographer_stats", title: ""), .tab(.statistics))
|
||
}
|
||
|
||
/// 测试飞控相关入口属于已知不支持范围。
|
||
func testFlightControlRoutesAreKnownUnsupportedScope() {
|
||
XCTAssertEqual(
|
||
HomeMenuRouter.resolve(uri: "fly", title: ""),
|
||
.unsupported(uri: "fly", title: "飞行管理", reason: "DJI/飞控模块已明确不纳入 iOS 迁移和后续开发范围。")
|
||
)
|
||
}
|
||
|
||
/// 测试未知 URI 会显式进入 placeholder。
|
||
func testUnknownRouteIsExplicitPlaceholder() {
|
||
XCTAssertEqual(
|
||
HomeMenuRouter.resolve(uri: "android_only_feature", title: "Android 专项"),
|
||
.placeholder(uri: "android_only_feature", title: "Android 专项")
|
||
)
|
||
}
|
||
|
||
/// 测试 canonicalURI 会使用当前权限可用的同义 URI。
|
||
func testCanonicalURIUsesAvailableAlias() {
|
||
let available: Set<String> = ["task_management_editor", "photographer_invite", "pm_manager", "payment_qr"]
|
||
XCTAssertEqual(HomeMenuRouter.canonicalURI(for: "task_management", availableURIs: available), "task_management_editor")
|
||
XCTAssertEqual(HomeMenuRouter.canonicalURI(for: "registration_invitation", availableURIs: available), "photographer_invite")
|
||
XCTAssertEqual(HomeMenuRouter.canonicalURI(for: "pm", availableURIs: available), "pm_manager")
|
||
XCTAssertEqual(HomeMenuRouter.canonicalURI(for: "payment_code", availableURIs: available), "payment_qr")
|
||
}
|
||
|
||
/// 测试 menuAliasKey 会把同义入口分到同一组。
|
||
func testMenuAliasKeyGroupsDuplicateAndroidEntries() {
|
||
XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "task_management_editor"), "task_management")
|
||
XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "photographer_invite"), "registration_invitation")
|
||
XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "pm_manager"), "pm")
|
||
XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "payment_code"), "payment_collection")
|
||
XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "deposit_order_shooting_info"), "deposit_order")
|
||
XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "/scenic-order-manage"), "photographer_orders")
|
||
XCTAssertEqual(HomeMenuRouter.menuAliasKey(for: "android_only_module"), "android_only_module")
|
||
}
|
||
|
||
/// 测试未知路由诊断会去重并更新标题。
|
||
func testUnknownRouteDiagnosticsDeduplicatesAndUpdatesTitle() {
|
||
HomeRouteDiagnostics.recordUnknown(uri: "android_feature", title: "旧标题")
|
||
HomeRouteDiagnostics.recordUnknown(uri: "android_feature", title: "新标题")
|
||
HomeRouteDiagnostics.recordUnknown(uri: "another_feature", title: "")
|
||
|
||
let records = HomeRouteDiagnostics.unknownRoutes()
|
||
|
||
XCTAssertEqual(records.count, 2)
|
||
XCTAssertEqual(records.first { $0.uri == "android_feature" }?.title, "新标题")
|
||
XCTAssertEqual(records.first { $0.uri == "android_feature" }?.count, 2)
|
||
XCTAssertEqual(records.first { $0.uri == "another_feature" }?.count, 1)
|
||
}
|
||
|
||
/// 测试未知路由诊断报告是 Markdown 且按出现次数排序。
|
||
func testUnknownRouteDiagnosticsReportIsMarkdownAndSortedByCount() {
|
||
HomeRouteDiagnostics.recordUnknown(uri: "rare_feature", title: "低频入口")
|
||
HomeRouteDiagnostics.recordUnknown(uri: "hot_feature", title: "高频入口")
|
||
HomeRouteDiagnostics.recordUnknown(uri: "hot_feature", title: "高频入口")
|
||
|
||
let report = HomeRouteDiagnostics.unknownRouteReport(generatedAt: Date(timeIntervalSince1970: 0))
|
||
|
||
XCTAssertTrue(report.contains("# Home Route Diagnostics"))
|
||
XCTAssertTrue(report.contains("| hot_feature | 高频入口 | 2 |"))
|
||
XCTAssertLessThan(
|
||
report.range(of: "hot_feature")!.lowerBound,
|
||
report.range(of: "rare_feature")!.lowerBound
|
||
)
|
||
}
|
||
|
||
/// 测试权限路由审计会区分可路由、不支持和未知入口。
|
||
func testPermissionRouteAuditSeparatesRoutableUnsupportedAndUnknownEntries() {
|
||
let permissions = [
|
||
rolePermission(
|
||
roleId: 1,
|
||
permissions: [
|
||
permission(id: 1, name: "任务管理", uri: "task_management", children: [
|
||
permission(id: 2, name: "发布任务", uri: "task_create")
|
||
]),
|
||
permission(id: 3, name: "飞控", uri: "pilot_controller"),
|
||
permission(id: 4, name: "Android 专项入口", uri: "android_only_module"),
|
||
permission(id: 5, name: "重复任务", uri: "task_management")
|
||
]
|
||
)
|
||
]
|
||
|
||
let audit = HomePermissionRouteAuditor.audit(permissions: permissions, currentRoleId: 1)
|
||
|
||
XCTAssertEqual(audit.routable.map(\.uri), ["task_management", "task_create"])
|
||
XCTAssertEqual(audit.unsupported.map(\.uri), ["pilot_controller"])
|
||
XCTAssertEqual(audit.unknown.map(\.uri), ["android_only_module"])
|
||
XCTAssertTrue(audit.hasUnknownRoutes)
|
||
}
|
||
|
||
#if DEBUG
|
||
/// 测试 DEBUG 菜单列表不依赖权限,包含已迁移和未迁移入口。
|
||
func testDebugAllMenuItemsExposeKnownMenusWithoutPermissions() {
|
||
let items = HomeMenuRouter.debugAllMenuItems()
|
||
let uris = items.map(\.uri)
|
||
|
||
XCTAssertTrue(uris.contains("album_list"))
|
||
XCTAssertTrue(uris.contains("wallet"))
|
||
XCTAssertTrue(uris.contains("sample_management"))
|
||
XCTAssertEqual(HomeMenuRouter.resolve(uri: "album_list", title: ""), .destination(.albumList))
|
||
XCTAssertEqual(
|
||
HomeMenuRouter.resolve(uri: "sample_management", title: ""),
|
||
.destination(.modulePlaceholder(uri: "sample_management", title: "样片管理"))
|
||
)
|
||
}
|
||
#endif
|
||
|
||
/// 创建角色权限测试实体。
|
||
private func rolePermission(roleId: Int, permissions: [PermissionItem]) -> RolePermissionResponse {
|
||
RolePermissionResponse(
|
||
role: RoleInfo(id: roleId, name: "角色\(roleId)", permission: permissions),
|
||
scenic: []
|
||
)
|
||
}
|
||
|
||
/// 创建权限节点测试实体。
|
||
private func permission(id: Int, name: String, uri: String, children: [PermissionItem] = []) -> PermissionItem {
|
||
PermissionItem(id: id, name: name, uri: uri, children: children)
|
||
}
|
||
}
|