Files
suixinkan_ios_uikit/suixinkan_iosTests/QueueManagement/QueueManagementViewModelTests.swift
2026-06-26 14:33:31 +08:00

228 lines
9.3 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.

//
// QueueManagementViewModelTests.swift
// suixinkanTests
//
// Created by Codex on 2026/6/25.
//
import XCTest
@testable import suixinkan_ios
@MainActor
///
final class QueueManagementViewModelTests: XCTestCase {
override func setUp() {
super.setUp()
clearQueueDefaults()
}
func testReloadWithoutScenicClearsState() async {
let api = ScenicQueueMock()
let viewModel = QueueManagementViewModel()
await viewModel.reload(api: api, scenicId: nil, userId: "77", spots: sampleSpots)
XCTAssertTrue(viewModel.items.isEmpty)
XCTAssertNil(viewModel.selectedSpotId)
XCTAssertFalse(viewModel.queueGatePassed)
XCTAssertEqual(viewModel.queueCount, 0)
}
func testReloadWithoutSavedSpotShowsGateAndDoesNotRequestQueue() async {
let api = ScenicQueueMock()
let viewModel = QueueManagementViewModel()
await viewModel.reload(api: api, scenicId: 88, userId: "77", spots: sampleSpots)
XCTAssertEqual(viewModel.scenicSpots.map(\.id), [901, 902])
XCTAssertNil(viewModel.selectedSpotId)
XCTAssertFalse(viewModel.queueGatePassed)
XCTAssertTrue(api.homeRequests.isEmpty)
XCTAssertTrue(api.statsRequests.isEmpty)
}
func testReloadWithSavedSpotLoadsHomeAndStats() async {
saveSelectedSpot()
let api = ScenicQueueMock()
api.homeResponses = [ScenicQueueHomeData(list: ScenicQueueHomeListBlock(list: [ticket(id: 1001), ticket(id: 1002)], total: 2), time: "2026-06-25 10:01")]
api.statsResponse = ScenicQueueStatsData(queueCount: 5, avgWaitMin: 12.5, time: "2026-06-25 10:02")
let viewModel = QueueManagementViewModel()
await viewModel.reload(api: api, scenicId: 88, userId: "77", spots: sampleSpots)
XCTAssertEqual(viewModel.selectedSpotId, 901)
XCTAssertTrue(viewModel.queueGatePassed)
XCTAssertEqual(viewModel.items.map(\.id), [1001, 1002])
XCTAssertEqual(viewModel.queueCount, 5)
XCTAssertEqual(viewModel.avgWaitMin, 12.5)
XCTAssertEqual(viewModel.lastSyncTimeText, "2026-06-25 10:02")
XCTAssertEqual(api.homeRequests.first?.type, QueueListType.queueing.rawValue)
}
func testStatsFailureKeepsQueueList() async {
saveSelectedSpot()
let api = ScenicQueueMock()
api.homeResponses = [ScenicQueueHomeData(list: ScenicQueueHomeListBlock(list: [ticket(id: 1001)], total: 1), time: "home-time")]
api.statsError = QueueTestError.sample
let viewModel = QueueManagementViewModel()
await viewModel.reload(api: api, scenicId: 88, userId: "77", spots: sampleSpots)
XCTAssertEqual(viewModel.items.map(\.id), [1001])
XCTAssertEqual(viewModel.queueCount, 0)
XCTAssertEqual(viewModel.lastSyncTimeText, "home-time")
}
func testCallPassFinishRequeueAndUserMark() async throws {
saveSelectedSpot()
let api = ScenicQueueMock()
api.homeResponses = Array(repeating: ScenicQueueHomeData(list: ScenicQueueHomeListBlock(list: [ticket(id: 1001)], total: 1)), count: 8)
let viewModel = QueueManagementViewModel()
await viewModel.reload(api: api, scenicId: 88, userId: "77", spots: sampleSpots)
try await viewModel.callQueue(api: api, id: 1001)
XCTAssertEqual(api.callIds, [1001])
XCTAssertEqual(viewModel.items.first?.isCalled, 1)
try await viewModel.passQueue(api: api, scenicId: 88, userId: "77", id: 1001)
XCTAssertEqual(api.passIds, [1001])
try await viewModel.finishQueue(api: api, scenicId: 88, userId: "77", id: 1001)
XCTAssertEqual(api.finishIds, [1001])
try await viewModel.requeue(api: api, scenicId: 88, userId: "77", id: 1001, operatorId: 77)
XCTAssertEqual(api.requeueRequests.map(\.recordId), [1001])
XCTAssertEqual(api.requeueRequests.map(\.operatorId), [77])
XCTAssertEqual(viewModel.selectedListType, .queueing)
try await viewModel.userMark(
api: api,
scenicId: 88,
userId: "77",
request: ScenicQueueUserMarkRequest(uid: 7001, scenicId: 88, markAsFreelancePhotog: 1, queueBanDays: 14, operatorId: 77)
)
XCTAssertEqual(api.userMarkRequests.first?.uid, 7001)
XCTAssertEqual(api.userMarkRequests.first?.queueBanDays, 14)
}
func testSocketSubscriptionAndParse() {
XCTAssertEqual(ScenicQueueSocketClient.subscriptionPayloads(scenicSpotId: 901), [
#"{"type":306,"params":{"scenic_spot_id":901}}"#,
#"{"type":307,"params":{"scenic_spot_id":901}}"#
])
let message = ScenicQueueSocketClient.parseMessage("""
{"code":100000,"data":{"action":"scenic_queue_ticket_called","params":{"scenic_spot_id":"901","record_id":"1002","operator_uid":"77","event_id":"evt-1"}}}
""")
XCTAssertEqual(message?.code, 100000)
XCTAssertTrue(message?.isScenicQueueEvent == true)
XCTAssertEqual(message?.data?.params?.scenicSpotId, 901)
XCTAssertEqual(message?.data?.params?.recordId, 1002)
XCTAssertEqual(message?.data?.params?.operatorUid, 77)
XCTAssertEqual(message?.data?.params?.eventId, "evt-1")
}
}
private let sampleSpots = [
ScenicSpotItem(id: 901, name: "东门打卡点"),
ScenicSpotItem(id: 902, name: "西门打卡点")
]
private func saveSelectedSpot() {
ScenicQueueSettingsStore.saveSelectedSpot(id: 901, name: "东门打卡点", userId: "77", scenicId: 88)
}
private func clearQueueDefaults() {
let defaults = UserDefaults.standard
[
ScenicQueueLocalSettings.selectedSpotIdKey,
ScenicQueueLocalSettings.selectedSpotNameKey,
ScenicQueueSettingsStore.scopedKey(base: ScenicQueueLocalSettings.selectedSpotIdKey, userId: "77", scenicId: 88),
ScenicQueueSettingsStore.scopedKey(base: ScenicQueueLocalSettings.selectedSpotNameKey, userId: "77", scenicId: 88),
ScenicQueueSettingsStore.scopedSpotKey(base: ScenicQueueLocalSettings.settingsSnapshotKey, userId: "77", scenicId: 88, spotId: 901)
].forEach(defaults.removeObject)
}
private func ticket(id: Int64, isCalled: Int = 0) -> ScenicQueueTicket {
ScenicQueueTicket(
id: id,
queueCode: "A\(id)",
mobile: "13800000000",
status: 0,
statusText: "",
waitMin: 5,
aheadCount: 1,
isCalled: isCalled,
createdAt: "2026-06-25 10:00:00",
calledAt: "",
expiredAt: "",
finishedAt: "",
queueCountToday: 2,
uid: 7001
)
}
@MainActor
final class ScenicQueueMock: ScenicQueueServing {
var homeResponses: [ScenicQueueHomeData] = []
var statsResponse = ScenicQueueStatsData()
var statsError: Error?
var settingResponse = ScenicQueueSettingData()
var callIds: [Int64] = []
var passIds: [Int64] = []
var finishIds: [Int64] = []
var requeueRequests: [(recordId: Int64, operatorId: Int)] = []
var userMarkRequests: [ScenicQueueUserMarkRequest] = []
var saveSettingRequests: [ScenicQueueSaveSettingRequest] = []
var homeRequests: [(scenicId: Int, scenicSpotId: Int, type: Int, page: Int, pageSize: Int)] = []
var statsRequests: [(scenicId: Int, scenicSpotId: Int)] = []
var qrcodeURL = "https://example.com/qrcode.png"
var logs = ScenicQueueSettingChangeLogData()
func scenicQueueStats(scenicId: Int, scenicSpotId: Int) async throws -> ScenicQueueStatsData {
statsRequests.append((scenicId, scenicSpotId))
if let statsError { throw statsError }
return statsResponse
}
func scenicQueueHome(scenicId: Int, scenicSpotId: Int, type: Int, page: Int, pageSize: Int) async throws -> ScenicQueueHomeData {
homeRequests.append((scenicId, scenicSpotId, type, page, pageSize))
return homeResponses.isEmpty ? ScenicQueueHomeData() : homeResponses.removeFirst()
}
func socketToken() async throws -> SocketTokenResponse { SocketTokenResponse(socketToken: "token") }
func scenicQueueCall(id: Int64) async throws -> ScenicQueueCallData {
callIds.append(id)
return ScenicQueueCallData(id: id, status: 1, statusText: "已叫号")
}
func scenicQueuePass(id: Int64) async throws -> ScenicQueuePassData {
passIds.append(id)
return ScenicQueuePassData(id: id)
}
func scenicQueueFinish(id: Int64) async throws -> ScenicQueueFinishData {
finishIds.append(id)
return ScenicQueueFinishData(id: id)
}
func scenicQueueRequeueInsertBefore(recordId: Int64, operatorId: Int) async throws {
requeueRequests.append((recordId, operatorId))
}
func scenicQueueUserMark(_ request: ScenicQueueUserMarkRequest) async throws {
userMarkRequests.append(request)
}
func scenicQueueSetting(scenicId: Int, scenicSpotId: Int?) async throws -> ScenicQueueSettingData { settingResponse }
func scenicQueueSaveSetting(_ request: ScenicQueueSaveSettingRequest) async throws {
saveSettingRequests.append(request)
}
func scenicQueueShootQueueQRCode(scenicId: Int, scenicSpotId: Int) async throws -> ScenicQueueShootQueueQRCodeData {
ScenicQueueShootQueueQRCodeData(qrcodeUrl: qrcodeURL)
}
func scenicQueueSettingChangeLog(scenicId: Int, scenicSpotId: Int?, page: Int, pageSize: Int) async throws -> ScenicQueueSettingChangeLogData {
logs
}
}
private enum QueueTestError: Error {
case sample
}