Files
suixinkan_ios_new/suixinkanTests/PushNotificationTests.swift
汉秋 0314033a7f 新增 APNs 推送注册与 Payload 路由
引入 AppDelegate、显式 Info.plist 与 entitlements,并将 PushNotificationManager 接入登录生命周期,用于 device token 上报与通知处理。

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

134 lines
5.0 KiB
Swift
Raw 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.

//
// PushNotificationTests.swift
// suixinkanTests
//
// Created by Codex on 2026/6/26.
//
import Foundation
import XCTest
@testable import suixinkan
/// APNs tokenpayload token
final class PushNotificationTests: XCTestCase {
func testAPNsDeviceTokenUsesLowercaseHexString() {
let token = APNsDeviceToken.hexString(from: Data([0x00, 0x0f, 0x10, 0xab, 0xff]))
XCTAssertEqual(token, "000f10abff")
}
func testPushPayloadRoutesTopLevelFields() {
XCTAssertEqual(PushPayload(userInfo: ["type": "1"]).route, .payment)
XCTAssertEqual(PushPayload(userInfo: ["route": "verification_order"]).route, .verificationOrder)
XCTAssertEqual(PushPayload(userInfo: ["uri": "/scenic-queue"]).route, .queue)
XCTAssertEqual(PushPayload(userInfo: ["action": "task_management"]).route, .task)
XCTAssertEqual(PushPayload(userInfo: ["route": "photographer_orders"]).route, .order)
}
func testPushPayloadRoutesNestedJSONString() {
let payload = PushPayload(userInfo: [
"extras": #"{"data":{"uri":"message_center","title":""}}"#
])
XCTAssertEqual(payload.route, .messageCenter)
}
func testPushPayloadRoutesNestedDictionaryAndChineseKeywords() {
let payload = PushPayload(userInfo: [
"data": [
"extra": [
"action": "叫号提醒"
]
]
])
XCTAssertEqual(payload.route, .queue)
}
func testPushPayloadFallsBackToMessageCenter() {
let payload = PushPayload(userInfo: ["title": "系统消息"])
XCTAssertEqual(payload.route, .messageCenter)
}
func testRegisterPushTokenUsesCurrentBackendEndpointAndLegacyQueryName() async throws {
let session = PushRecordingURLSession()
let api = PushAPI(client: APIClient(session: session))
try await api.registerJPushId("000f10abff")
let request = try XCTUnwrap(session.requests.first)
XCTAssertEqual(request.httpMethod, "POST")
XCTAssertEqual(request.url?.path, "/api/app/user/register-jpush-id")
XCTAssertEqual(queryItems(from: request)["jpush_reg_id"], "000f10abff")
}
func testPushManagerRoutesHomeDestinationsThroughHomeNavigationStack() {
let router = AppRouter()
let manager = pushManager(router: router)
manager.handleRemoteNotification(PushPayload(userInfo: ["route": "payment_qr"]))
XCTAssertEqual(router.selectedTab, .home)
XCTAssertEqual(router.router(for: .home).path, [.home(.paymentCollection)])
router.router(for: .home).reset()
manager.handleRemoteNotification(PushPayload(userInfo: ["route": "task_management"]))
XCTAssertEqual(router.router(for: .home).path, [.home(.taskManagement)])
router.router(for: .home).reset()
manager.handleRemoteNotification(PushPayload(userInfo: ["uri": "/scenic-queue"]))
XCTAssertEqual(router.router(for: .home).path, [.home(.queueManagement)])
router.router(for: .home).reset()
manager.handleRemoteNotification(PushPayload(userInfo: ["title": "未知通知"]))
XCTAssertEqual(router.router(for: .home).path, [.home(.messageCenter)])
}
func testPushManagerRoutesOrdersEntry() {
let router = AppRouter()
let manager = pushManager(router: router)
manager.handleRemoteNotification(PushPayload(userInfo: ["route": "photographer_orders"]))
XCTAssertEqual(router.selectedTab, .orders)
XCTAssertEqual(router.selectedOrdersEntry, .storeOrders)
manager.handleRemoteNotification(PushPayload(userInfo: ["route": "verification_order"]))
XCTAssertEqual(router.selectedTab, .orders)
XCTAssertEqual(router.selectedOrdersEntry, .verificationOrders)
}
private func pushManager(router: AppRouter) -> PushNotificationManager {
let session = AppSession()
let manager = PushNotificationManager()
manager.configure(
api: PushAPI(client: APIClient(session: PushRecordingURLSession())),
session: session,
router: router
)
return manager
}
}
private final class PushRecordingURLSession: URLSessionProtocol {
private(set) var requests: [URLRequest] = []
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
requests.append(request)
let data = #"{"code":100000,"msg":"success","data":{}}"#.data(using: .utf8)!
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
return (data, response)
}
}
private func queryItems(from request: URLRequest) -> [String: String] {
guard let url = request.url,
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
else {
return [:]
}
return Dictionary(uniqueKeysWithValues: (components.queryItems ?? []).compactMap { item in
guard let value = item.value else { return nil }
return (item.name, value)
})
}