@@ -182,6 +182,94 @@ class DeepLinkTests: XCTestCase {
182
182
private func createCookieValue( nameValuePairs values: Any ... ) -> String {
183
183
values. take ( 2 ) . map { " \( $0 [ 0 ] ) = \( $0 [ 1 ] ) " } . joined ( separator: " ;, " )
184
184
}
185
+
186
+ // MARK: - GreenFi SMS Deep Link Tests
187
+
188
+ func testGreenFiSMSDeepLinkRedirect( ) {
189
+ let expectation1 = expectation ( description: " Deep link resolves successfully " )
190
+ let expectation2 = expectation ( description: " Attribution info extracted " )
191
+
192
+ let greenfiSmsUrl = " https://links.greenfi.com/a/JsvVI "
193
+ let destinationUrl = " https://app.greenfi.com/dashboard "
194
+ let campaignId = 123456
195
+ let templateId = 789012
196
+ let messageId = " sms-campaign-123 "
197
+
198
+ let mockUrlDelegate = MockUrlDelegate ( returnValue: true )
199
+ mockUrlDelegate. callback = { url, context in
200
+ XCTAssertEqual ( url. absoluteString, destinationUrl)
201
+ XCTAssertEqual ( context. action. type, IterableAction . actionTypeOpenUrl)
202
+ XCTAssertTrue ( Thread . isMainThread)
203
+ expectation1. fulfill ( )
204
+ }
205
+
206
+ // Create network session that simulates GreenFi's 303 redirect response
207
+ let networkSession = MockNetworkSession ( )
208
+ networkSession. responseCallback = { _ in
209
+ MockNetworkSession . MockResponse (
210
+ statusCode: 303 , // GreenFi returns 303 redirect
211
+ data: Dictionary < AnyHashable , Any > ( ) . toJsonData ( ) ,
212
+ delay: 0.0 ,
213
+ error: nil ,
214
+ headerFields: [
215
+ " Location " : destinationUrl,
216
+ " Set-Cookie " : self . createCookieValue ( nameValuePairs: " iterableEmailCampaignId " , campaignId, " iterableTemplateId " , templateId, " iterableMessageId " , messageId) ,
217
+ ]
218
+ )
219
+ }
220
+
221
+ let networkSessionProvider = MockRedirectNetworkSessionProvider ( networkSession: networkSession)
222
+ let deepLinkManager = DeepLinkManager ( redirectNetworkSessionProvider: networkSessionProvider)
223
+
224
+ let ( isIterableLink, attributionInfoFuture) = deepLinkManager. handleUniversalLink (
225
+ URL ( string: greenfiSmsUrl) !,
226
+ urlDelegate: mockUrlDelegate,
227
+ urlOpener: MockUrlOpener ( )
228
+ )
229
+
230
+ XCTAssertTrue ( isIterableLink, " GreenFi URL should be recognized as Iterable deep link " )
231
+
232
+ attributionInfoFuture. onSuccess { attributionInfo in
233
+ XCTAssertNotNil ( attributionInfo, " Should extract attribution info from 303 response " )
234
+ XCTAssertEqual ( attributionInfo? . campaignId, NSNumber ( value: campaignId) )
235
+ XCTAssertEqual ( attributionInfo? . templateId, NSNumber ( value: templateId) )
236
+ XCTAssertEqual ( attributionInfo? . messageId, messageId)
237
+ expectation2. fulfill ( )
238
+ }
239
+
240
+ wait ( for: [ expectation1, expectation2] , timeout: testExpectationTimeout)
241
+ }
242
+
243
+ func testGreenFiDeepLinkWithoutRedirect( ) {
244
+ let expectation1 = expectation ( description: " Deep link handled without redirect " )
245
+
246
+ let greenfiSmsUrl = " https://links.greenfi.com/a/JsvVI "
247
+
248
+ let mockUrlDelegate = MockUrlDelegate ( returnValue: true )
249
+ mockUrlDelegate. callback = { url, context in
250
+ // When no redirect occurs, should get original URL
251
+ XCTAssertEqual ( url. absoluteString, greenfiSmsUrl)
252
+ XCTAssertEqual ( context. action. type, IterableAction . actionTypeOpenUrl)
253
+ expectation1. fulfill ( )
254
+ }
255
+
256
+ // Use no-redirect provider to simulate timeout/failure scenario
257
+ let deepLinkManager = DeepLinkManager ( redirectNetworkSessionProvider: createNoRedirectNetworkSessionProvider ( ) )
258
+
259
+ let ( isIterableLink, attributionInfoFuture) = deepLinkManager. handleUniversalLink (
260
+ URL ( string: greenfiSmsUrl) !,
261
+ urlDelegate: mockUrlDelegate,
262
+ urlOpener: MockUrlOpener ( )
263
+ )
264
+
265
+ XCTAssertTrue ( isIterableLink, " GreenFi URL should be recognized as Iterable deep link " )
266
+
267
+ attributionInfoFuture. onSuccess { attributionInfo in
268
+ XCTAssertNil ( attributionInfo, " Should not have attribution info when redirect fails " )
269
+ }
270
+
271
+ wait ( for: [ expectation1] , timeout: testExpectationTimeout)
272
+ }
185
273
}
186
274
187
275
private struct MockNoRedirectNetworkSessionProvider : RedirectNetworkSessionProvider {
0 commit comments