新增 CooperationOrder 模块(双 Tab 列表、获客员绑定、主 Tab 扫码)、订单来源/带客单绑定及配套 API 测试;同步引入 roleCode 权限匹配与相关单元测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
122 lines
4.7 KiB
Swift
122 lines
4.7 KiB
Swift
//
|
||
// ScenicSelectionViewModelTests.swift
|
||
// suixinkanTests
|
||
//
|
||
// Created by Codex on 2026/6/23.
|
||
//
|
||
|
||
import XCTest
|
||
@testable import suixinkan
|
||
|
||
@MainActor
|
||
/// 景区选择 ViewModel 测试,覆盖搜索、定位和切换持久化。
|
||
final class ScenicSelectionViewModelTests: XCTestCase {
|
||
/// 测试当前景区会优先展示并标记选中。
|
||
func testCurrentScenicMovesToFirstItem() {
|
||
let context = AccountContext()
|
||
context.replaceScopes(
|
||
scenic: [
|
||
BusinessScope(id: 1, name: "西湖景区", kind: .scenic),
|
||
BusinessScope(id: 2, name: "东湖景区", kind: .scenic)
|
||
],
|
||
stores: [],
|
||
currentScenicId: 2
|
||
)
|
||
let viewModel = ScenicSelectionViewModel()
|
||
|
||
viewModel.reload(from: context)
|
||
|
||
XCTAssertEqual(viewModel.items.map(\.id), [2, 1])
|
||
XCTAssertTrue(viewModel.items[0].isClosest)
|
||
}
|
||
|
||
/// 测试搜索会按景区名称和地址过滤。
|
||
func testSearchFiltersByNameAndAddress() {
|
||
let viewModel = ScenicSelectionViewModel()
|
||
let context = AccountContext()
|
||
context.replaceScopes(
|
||
scenic: [
|
||
BusinessScope(id: 1, name: "西湖景区", kind: .scenic, address: "杭州"),
|
||
BusinessScope(id: 2, name: "东湖景区", kind: .scenic, address: "武汉")
|
||
],
|
||
stores: []
|
||
)
|
||
viewModel.reload(from: context)
|
||
|
||
viewModel.searchQuery = "武汉"
|
||
|
||
XCTAssertEqual(viewModel.filteredItems.map(\.id), [2])
|
||
}
|
||
|
||
/// 测试定位成功后会计算距离并标记最近景区。
|
||
func testLocationSuccessMarksClosestScenic() {
|
||
let context = AccountContext()
|
||
context.replaceScopes(
|
||
scenic: [
|
||
BusinessScope(id: 1, name: "远景区", kind: .scenic, address: "远处", latitude: 31.0, longitude: 121.0),
|
||
BusinessScope(id: 2, name: "近景区", kind: .scenic, address: "附近", latitude: 30.0001, longitude: 120.0001)
|
||
],
|
||
stores: []
|
||
)
|
||
let viewModel = ScenicSelectionViewModel()
|
||
viewModel.reload(from: context)
|
||
|
||
viewModel.applyCurrentLocation(latitude: 30.0, longitude: 120.0)
|
||
|
||
XCTAssertEqual(viewModel.items.first(where: \.isClosest)?.id, 2)
|
||
XCTAssertEqual(viewModel.currentLocationText, "附近")
|
||
XCTAssertNotNil(viewModel.items.first(where: { $0.id == 2 })?.distanceMeters)
|
||
}
|
||
|
||
/// 测试定位失败时保留列表并展示提示。
|
||
func testLocationFailureKeepsItemsAndWarning() {
|
||
let context = AccountContext()
|
||
context.replaceScopes(scenic: [BusinessScope(id: 1, name: "西湖景区", kind: .scenic, address: "杭州")], stores: [])
|
||
let viewModel = ScenicSelectionViewModel()
|
||
viewModel.reload(from: context)
|
||
|
||
viewModel.applyLocationFailure("定位权限未开启")
|
||
|
||
XCTAssertEqual(viewModel.items.map(\.id), [1])
|
||
XCTAssertEqual(viewModel.locationWarning, "定位权限未开启")
|
||
}
|
||
|
||
/// 测试切换景区会联动门店并保存账号快照。
|
||
func testSelectScenicUpdatesStoreAndPersistsSnapshot() {
|
||
let suiteName = "ScenicSelectionViewModelTests.\(UUID().uuidString)"
|
||
let defaults = UserDefaults(suiteName: suiteName)!
|
||
defer { defaults.removePersistentDomain(forName: suiteName) }
|
||
let snapshotStore = AccountSnapshotStore(defaults: defaults)
|
||
let accountContext = AccountContext()
|
||
let permissionContext = PermissionContext()
|
||
accountContext.replaceScopes(
|
||
scenic: [
|
||
BusinessScope(id: 1, name: "西湖景区", kind: .scenic),
|
||
BusinessScope(id: 2, name: "东湖景区", kind: .scenic)
|
||
],
|
||
stores: [
|
||
BusinessScope(id: 10, name: "西湖门店", kind: .store, parentScenicId: 1),
|
||
BusinessScope(id: 20, name: "东湖门店", kind: .store, parentScenicId: 2)
|
||
],
|
||
currentScenicId: 1,
|
||
currentStoreId: 10
|
||
)
|
||
permissionContext.replaceRolePermissions([
|
||
RolePermissionResponse(role: RoleInfo(id: 7, name: "运营", roleCode: "scenic_operation"), scenic: [])
|
||
], currentRoleCode: "scenic_operation")
|
||
|
||
ScenicSelectionViewModel().select(
|
||
scenicId: 2,
|
||
accountContext: accountContext,
|
||
permissionContext: permissionContext,
|
||
snapshotStore: snapshotStore
|
||
)
|
||
|
||
XCTAssertEqual(accountContext.currentScenic?.id, 2)
|
||
XCTAssertEqual(accountContext.currentStore?.id, 20)
|
||
XCTAssertEqual(snapshotStore.load()?.currentScenicId, 2)
|
||
XCTAssertEqual(snapshotStore.load()?.currentStoreId, 20)
|
||
XCTAssertEqual(snapshotStore.load()?.currentRoleCode, "scenic_operation")
|
||
}
|
||
}
|