Files
suixinkan_ios_new/suixinkanTests/PushNotificationTests.swift
汉秋 c32a610ee0 Add APNs push notification registration and payload routing.
Introduce AppDelegate, explicit Info.plist and entitlements, and wire PushNotificationManager into login lifecycle for device token upload and notification handling.

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)
})
}