@@ -104,6 +104,102 @@ final class APNSURLSessionClientTests: XCTestCase {
104104 }
105105 }
106106
107+ func testSendAlert_propagatesAllHeaders( ) async throws {
108+ let apnsID = UUID ( )
109+ var alert = APNSAlertNotification (
110+ alert: . init( title: . raw( " title " ) ) ,
111+ expiration: . immediately,
112+ priority: . immediately,
113+ topic: " com.example.app " ,
114+ payload: EmptyPayload ( ) ,
115+ apnsID: apnsID
116+ )
117+ alert. collapseID = " collapse-123 "
118+
119+ _ = try await client. sendAlertNotification ( alert, deviceToken: Self . validDeviceToken)
120+
121+ let sent = try XCTUnwrap ( server. getSentNotifications ( ) . first)
122+ XCTAssertEqual ( sent. deviceToken, Self . validDeviceToken)
123+ XCTAssertEqual ( sent. pushType, " alert " )
124+ XCTAssertEqual ( sent. topic, " com.example.app " )
125+ XCTAssertEqual ( sent. priority, " 10 " )
126+ XCTAssertEqual ( sent. expiration, " 0 " )
127+ XCTAssertEqual ( sent. collapseID, " collapse-123 " )
128+ XCTAssertEqual ( sent. apnsID, apnsID)
129+ }
130+
131+ func testSendAlert_unregisteredCarriesTimestamp( ) async throws {
132+ do {
133+ _ = try await client. sendAlertNotification (
134+ Self . makeAlert ( ) ,
135+ deviceToken: APNSTestServer . unregisteredDeviceToken
136+ )
137+ XCTFail ( " Expected an APNSError to be thrown " )
138+ } catch let error as APNSError {
139+ XCTAssertEqual ( error. responseStatus, 410 )
140+ XCTAssertEqual ( error. reason, . unregistered)
141+ let timestamp = try XCTUnwrap ( error. timestamp)
142+ XCTAssertEqual (
143+ timestamp. timeIntervalSince1970,
144+ Double ( APNSTestServer . unregisteredTimestampMilliseconds) / 1000 ,
145+ accuracy: 0.001
146+ )
147+ }
148+ }
149+
150+ func testInjectedClockDrivesTokenRefresh( ) async throws {
151+ let clock = TestClock < Duration > ( )
152+ let testServer = server!
153+ let clockedClient = APNSURLSessionClient (
154+ configuration: . init(
155+ environment: . custom( url: " http://127.0.0.1 " , port: testServer. port) ,
156+ privateKey: try P256 . Signing. PrivateKey ( pemRepresentation: Self . jwtPrivateKey) ,
157+ keyIdentifier: " MY_KEY_ID " ,
158+ teamIdentifier: " MY_TEAM_ID " ,
159+ clock: clock
160+ )
161+ )
162+
163+ _ = try await clockedClient. sendAlertNotification (
164+ Self . makeAlert ( ) ,
165+ deviceToken: Self . validDeviceToken
166+ )
167+
168+ // Advance past the manager's 55 minute refresh window so a new token must be minted.
169+ clock. now = clock. now. advanced ( by: . init( secondsComponent: 3360 , attosecondsComponent: 0 ) )
170+
171+ _ = try await clockedClient. sendAlertNotification (
172+ Self . makeAlert ( ) ,
173+ deviceToken: Self . validDeviceToken
174+ )
175+
176+ let sent = testServer. getSentNotifications ( )
177+ XCTAssertEqual ( sent. count, 2 )
178+ let firstAuthorization = try XCTUnwrap ( sent [ 0 ] . authorization)
179+ let secondAuthorization = try XCTUnwrap ( sent [ 1 ] . authorization)
180+ XCTAssertNotEqual ( firstAuthorization, secondAuthorization)
181+ }
182+
183+ func testSendAlert_customSessionIsUsed( ) async throws {
184+ let customSession = URLSession ( configuration: . ephemeral)
185+ let customClient = APNSURLSessionClient (
186+ configuration: . init(
187+ environment: . custom( url: " http://127.0.0.1 " , port: server. port) ,
188+ privateKey: try P256 . Signing. PrivateKey ( pemRepresentation: Self . jwtPrivateKey) ,
189+ keyIdentifier: " MY_KEY_ID " ,
190+ teamIdentifier: " MY_TEAM_ID "
191+ ) ,
192+ session: customSession
193+ )
194+
195+ let response = try await customClient. sendAlertNotification (
196+ Self . makeAlert ( ) ,
197+ deviceToken: Self . validDeviceToken
198+ )
199+
200+ XCTAssertNotNil ( response. apnsID)
201+ }
202+
107203 // MARK: - Helpers
108204
109205 private static let validDeviceToken = String ( repeating: " a " , count: 64 )
0 commit comments