Skip to content

Commit 89eecc4

Browse files
authored
fix: Enabled automatic retries for Auth and FCM (#121)
* Enabled automatic retries for Auth and FCM * More tests and API cleanup
1 parent dda6a4b commit 89eecc4

13 files changed

+326
-87
lines changed

FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseUserManagerTest.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.Net.Http.Headers;
2121
using System.Threading.Tasks;
2222
using FirebaseAdmin.Tests;
23+
using FirebaseAdmin.Util;
2324
using Google.Apis.Auth.OAuth2;
2425
using Google.Apis.Json;
2526
using Newtonsoft.Json.Linq;
@@ -1528,13 +1529,53 @@ public async Task DeleteUserNotFound()
15281529
Assert.Null(exception.InnerException);
15291530
}
15301531

1532+
[Fact]
1533+
public async Task ServiceUnvailable()
1534+
{
1535+
var handler = new MockMessageHandler()
1536+
{
1537+
StatusCode = HttpStatusCode.ServiceUnavailable,
1538+
Response = "{}",
1539+
};
1540+
var auth = this.CreateFirebaseAuth(handler);
1541+
1542+
var exception = await Assert.ThrowsAsync<FirebaseAuthException>(
1543+
async () => await auth.GetUserAsync("user1"));
1544+
1545+
Assert.Equal(ErrorCode.Unavailable, exception.ErrorCode);
1546+
Assert.Null(exception.AuthErrorCode);
1547+
Assert.NotNull(exception.HttpResponse);
1548+
Assert.Null(exception.InnerException);
1549+
Assert.Equal(5, handler.Calls);
1550+
}
1551+
1552+
[Fact]
1553+
public async Task TransportError()
1554+
{
1555+
var handler = new MockMessageHandler()
1556+
{
1557+
Exception = new HttpRequestException("Transport error"),
1558+
};
1559+
var auth = this.CreateFirebaseAuth(handler);
1560+
1561+
var exception = await Assert.ThrowsAsync<FirebaseAuthException>(
1562+
async () => await auth.GetUserAsync("user1"));
1563+
1564+
Assert.Equal(ErrorCode.Unknown, exception.ErrorCode);
1565+
Assert.Null(exception.AuthErrorCode);
1566+
Assert.Null(exception.HttpResponse);
1567+
Assert.NotNull(exception.InnerException);
1568+
Assert.Equal(5, handler.Calls);
1569+
}
1570+
15311571
private FirebaseAuth CreateFirebaseAuth(HttpMessageHandler handler)
15321572
{
1533-
var userManager = new FirebaseUserManager(new FirebaseUserManagerArgs
1573+
var userManager = new FirebaseUserManager(new FirebaseUserManager.Args
15341574
{
15351575
Credential = MockCredential,
15361576
ProjectId = MockProjectId,
15371577
ClientFactory = new MockHttpClientFactory(handler),
1578+
RetryOptions = RetryOptions.NoBackOff,
15381579
});
15391580
return new FirebaseAuth(new FirebaseAuth.FirebaseAuthArgs()
15401581
{

FirebaseAdmin/FirebaseAdmin.Tests/Auth/IAMSignerTest.cs

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
using System.Text;
1919
using System.Threading.Tasks;
2020
using FirebaseAdmin.Tests;
21+
using FirebaseAdmin.Util;
2122
using Google.Apis.Auth.OAuth2;
23+
using Google.Apis.Http;
2224
using Google.Apis.Json;
2325
using Xunit;
2426

@@ -100,8 +102,7 @@ public async Task Signer()
100102
},
101103
};
102104
var factory = new MockHttpClientFactory(handler);
103-
var signer = new FixedAccountIAMSigner(
104-
factory, GoogleCredential.FromAccessToken("token"), "test-service-account");
105+
var signer = this.CreateFixedAccountIAMSigner(factory);
105106
Assert.Equal("test-service-account", await signer.GetKeyIdAsync());
106107
byte[] data = Encoding.UTF8.GetBytes("Hello world");
107108
byte[] signature = await signer.SignDataAsync(data);
@@ -121,8 +122,7 @@ public async Task WelformedSignError()
121122
Response = @"{""error"": {""message"": ""test reason""}}",
122123
};
123124
var factory = new MockHttpClientFactory(handler);
124-
var signer = new FixedAccountIAMSigner(
125-
factory, GoogleCredential.FromAccessToken("token"), "test-service-account");
125+
var signer = this.CreateFixedAccountIAMSigner(factory);
126126
Assert.Equal("test-service-account", await signer.GetKeyIdAsync());
127127
byte[] data = Encoding.UTF8.GetBytes("Hello world");
128128
var ex = await Assert.ThrowsAsync<FirebaseAuthException>(
@@ -144,8 +144,7 @@ public async Task WelformedSignErrorWithCode()
144144
Response = @"{""error"": {""message"": ""test reason"", ""status"": ""UNAVAILABLE""}}",
145145
};
146146
var factory = new MockHttpClientFactory(handler);
147-
var signer = new FixedAccountIAMSigner(
148-
factory, GoogleCredential.FromAccessToken("token"), "test-service-account");
147+
var signer = this.CreateFixedAccountIAMSigner(factory);
149148
Assert.Equal("test-service-account", await signer.GetKeyIdAsync());
150149
byte[] data = Encoding.UTF8.GetBytes("Hello world");
151150
var ex = await Assert.ThrowsAsync<FirebaseAuthException>(
@@ -167,8 +166,7 @@ public async Task UnexpectedSignError()
167166
Response = "not json",
168167
};
169168
var factory = new MockHttpClientFactory(handler);
170-
var signer = new FixedAccountIAMSigner(
171-
factory, GoogleCredential.FromAccessToken("token"), "test-service-account");
169+
var signer = this.CreateFixedAccountIAMSigner(factory);
172170
Assert.Equal("test-service-account", await signer.GetKeyIdAsync());
173171
byte[] data = Encoding.UTF8.GetBytes("Hello world");
174172
var ex = await Assert.ThrowsAsync<FirebaseAuthException>(
@@ -182,5 +180,58 @@ public async Task UnexpectedSignError()
182180
Assert.NotNull(ex.HttpResponse);
183181
Assert.Null(ex.InnerException);
184182
}
183+
184+
[Fact]
185+
public async Task Unavailable()
186+
{
187+
var handler = new MockMessageHandler()
188+
{
189+
StatusCode = HttpStatusCode.ServiceUnavailable,
190+
Response = @"{""error"": {""message"": ""test reason""}}",
191+
};
192+
var factory = new MockHttpClientFactory(handler);
193+
var signer = this.CreateFixedAccountIAMSigner(factory);
194+
byte[] data = Encoding.UTF8.GetBytes("Hello world");
195+
var ex = await Assert.ThrowsAsync<FirebaseAuthException>(
196+
async () => await signer.SignDataAsync(data));
197+
198+
Assert.Equal(ErrorCode.Unavailable, ex.ErrorCode);
199+
Assert.Equal("test reason", ex.Message);
200+
Assert.Null(ex.AuthErrorCode);
201+
Assert.NotNull(ex.HttpResponse);
202+
Assert.Null(ex.InnerException);
203+
Assert.Equal(5, handler.Calls);
204+
}
205+
206+
[Fact]
207+
public async Task TransportError()
208+
{
209+
var handler = new MockMessageHandler()
210+
{
211+
Exception = new HttpRequestException("Transport error"),
212+
};
213+
var factory = new MockHttpClientFactory(handler);
214+
var signer = this.CreateFixedAccountIAMSigner(factory);
215+
byte[] data = Encoding.UTF8.GetBytes("Hello world");
216+
var ex = await Assert.ThrowsAsync<FirebaseAuthException>(
217+
async () => await signer.SignDataAsync(data));
218+
219+
Assert.Equal(ErrorCode.Unknown, ex.ErrorCode);
220+
Assert.Null(ex.AuthErrorCode);
221+
Assert.Null(ex.HttpResponse);
222+
Assert.NotNull(ex.InnerException);
223+
Assert.Equal(5, handler.Calls);
224+
}
225+
226+
private FixedAccountIAMSigner CreateFixedAccountIAMSigner(HttpClientFactory factory)
227+
{
228+
return new FixedAccountIAMSigner(new FixedAccountIAMSigner.Args()
229+
{
230+
ClientFactory = factory,
231+
Credential = GoogleCredential.FromAccessToken("token"),
232+
KeyId = "test-service-account",
233+
RetryOptions = RetryOptions.NoBackOff,
234+
});
235+
}
185236
}
186237
}

FirebaseAdmin/FirebaseAdmin.Tests/Messaging/FirebaseMessagingClientTest.cs

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using System.Net.Http.Headers;
2020
using System.Threading.Tasks;
2121
using FirebaseAdmin.Tests;
22+
using FirebaseAdmin.Util;
2223
using Google.Apis.Auth.OAuth2;
2324
using Google.Apis.Http;
2425
using Newtonsoft.Json;
@@ -40,27 +41,42 @@ public class FirebaseMessagingClientTest
4041
[Fact]
4142
public void NoProjectId()
4243
{
43-
var clientFactory = new HttpClientFactory();
44-
Assert.Throws<ArgumentException>(
45-
() => new FirebaseMessagingClient(clientFactory, MockCredential, null));
46-
Assert.Throws<ArgumentException>(
47-
() => new FirebaseMessagingClient(clientFactory, MockCredential, string.Empty));
44+
var args = new FirebaseMessagingClient.Args()
45+
{
46+
ClientFactory = new HttpClientFactory(),
47+
Credential = null,
48+
};
49+
50+
args.ProjectId = null;
51+
Assert.Throws<ArgumentException>(() => new FirebaseMessagingClient(args));
52+
53+
args.ProjectId = string.Empty;
54+
Assert.Throws<ArgumentException>(() => new FirebaseMessagingClient(args));
4855
}
4956

5057
[Fact]
5158
public void NoCredential()
5259
{
53-
var clientFactory = new HttpClientFactory();
54-
Assert.Throws<ArgumentNullException>(
55-
() => new FirebaseMessagingClient(clientFactory, null, "test-project"));
60+
var args = new FirebaseMessagingClient.Args()
61+
{
62+
ClientFactory = new HttpClientFactory(),
63+
Credential = null,
64+
ProjectId = "test-project",
65+
};
66+
Assert.Throws<ArgumentNullException>(() => new FirebaseMessagingClient(args));
5667
}
5768

5869
[Fact]
5970
public void NoClientFactory()
6071
{
6172
var clientFactory = new HttpClientFactory();
62-
Assert.Throws<ArgumentNullException>(
63-
() => new FirebaseMessagingClient(null, MockCredential, "test-project"));
73+
var args = new FirebaseMessagingClient.Args()
74+
{
75+
ClientFactory = null,
76+
Credential = MockCredential,
77+
ProjectId = "test-project",
78+
};
79+
Assert.Throws<ArgumentNullException>(() => new FirebaseMessagingClient(args));
6480
}
6581

6682
[Fact]
@@ -74,7 +90,7 @@ public async Task SendAsync()
7490
},
7591
};
7692
var factory = new MockHttpClientFactory(handler);
77-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
93+
var client = this.CreateMessagingClient(factory);
7894
var message = new Message()
7995
{
8096
Topic = "test-topic",
@@ -101,7 +117,7 @@ public async Task SendDryRunAsync()
101117
},
102118
};
103119
var factory = new MockHttpClientFactory(handler);
104-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
120+
var client = this.CreateMessagingClient(factory);
105121
var message = new Message()
106122
{
107123
Topic = "test-topic",
@@ -161,7 +177,7 @@ public async Task SendAllAsync()
161177
},
162178
};
163179
var factory = new MockHttpClientFactory(handler);
164-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
180+
var client = this.CreateMessagingClient(factory);
165181
var message1 = new Message()
166182
{
167183
Token = "test-token1",
@@ -236,7 +252,7 @@ public async Task SendAllAsyncWithError()
236252
},
237253
};
238254
var factory = new MockHttpClientFactory(handler);
239-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
255+
var client = this.CreateMessagingClient(factory);
240256
var message1 = new Message()
241257
{
242258
Token = "test-token1",
@@ -309,7 +325,12 @@ public async Task SendAllAsyncWithErrorNoDetail()
309325
},
310326
};
311327
var factory = new MockHttpClientFactory(handler);
312-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
328+
var client = new FirebaseMessagingClient(new FirebaseMessagingClient.Args()
329+
{
330+
ClientFactory = factory,
331+
Credential = MockCredential,
332+
ProjectId = "test-project",
333+
});
313334
var message1 = new Message()
314335
{
315336
Token = "test-token1",
@@ -341,7 +362,7 @@ public async Task SendAllAsyncWithErrorNoDetail()
341362
public async Task SendAllAsyncNullList()
342363
{
343364
var factory = new MockHttpClientFactory(new MockMessageHandler());
344-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
365+
var client = this.CreateMessagingClient(factory);
345366

346367
await Assert.ThrowsAsync<ArgumentNullException>(() => client.SendAllAsync(null));
347368
}
@@ -350,15 +371,15 @@ public async Task SendAllAsyncNullList()
350371
public async Task SendAllAsyncWithNoMessages()
351372
{
352373
var factory = new MockHttpClientFactory(new MockMessageHandler());
353-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
374+
var client = this.CreateMessagingClient(factory);
354375
await Assert.ThrowsAsync<ArgumentException>(() => client.SendAllAsync(Enumerable.Empty<Message>()));
355376
}
356377

357378
[Fact]
358379
public async Task SendAllAsyncWithTooManyMessages()
359380
{
360381
var factory = new MockHttpClientFactory(new MockMessageHandler());
361-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
382+
var client = this.CreateMessagingClient(factory);
362383
var messages = Enumerable.Range(0, 101).Select(_ => new Message { Topic = "test-topic" });
363384
await Assert.ThrowsAsync<ArgumentException>(() => client.SendAllAsync(messages));
364385
}
@@ -383,7 +404,7 @@ public async Task HttpErrorAsync()
383404
}",
384405
};
385406
var factory = new MockHttpClientFactory(handler);
386-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
407+
var client = this.CreateMessagingClient(factory);
387408
var message = new Message()
388409
{
389410
Topic = "test-topic",
@@ -418,7 +439,7 @@ public async Task HttpErrorNoDetailAsync()
418439
}",
419440
};
420441
var factory = new MockHttpClientFactory(handler);
421-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
442+
var client = this.CreateMessagingClient(factory);
422443
var message = new Message()
423444
{
424445
Topic = "test-topic",
@@ -448,7 +469,7 @@ public async Task HttpErrorNonJsonAsync()
448469
Response = "not json",
449470
};
450471
var factory = new MockHttpClientFactory(handler);
451-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
472+
var client = this.CreateMessagingClient(factory);
452473
var message = new Message()
453474
{
454475
Topic = "test-topic",
@@ -471,6 +492,32 @@ public async Task HttpErrorNonJsonAsync()
471492
Assert.Equal(1, handler.Calls);
472493
}
473494

495+
[Fact]
496+
public async Task Unavailable()
497+
{
498+
var handler = new MockMessageHandler()
499+
{
500+
StatusCode = HttpStatusCode.ServiceUnavailable,
501+
Response = "ServiceUnavailable",
502+
};
503+
var factory = new MockHttpClientFactory(handler);
504+
var client = this.CreateMessagingClient(factory);
505+
var message = new Message()
506+
{
507+
Topic = "test-topic",
508+
};
509+
510+
var ex = await Assert.ThrowsAsync<FirebaseMessagingException>(
511+
async () => await client.SendAsync(message));
512+
513+
Assert.Equal(ErrorCode.Unavailable, ex.ErrorCode);
514+
Assert.Equal("Unexpected HTTP response with status: 503 (ServiceUnavailable)\nServiceUnavailable", ex.Message);
515+
Assert.Null(ex.MessagingErrorCode);
516+
Assert.NotNull(ex.HttpResponse);
517+
Assert.Null(ex.InnerException);
518+
Assert.Equal(5, handler.Calls);
519+
}
520+
474521
[Fact]
475522
public async Task TransportError()
476523
{
@@ -479,7 +526,7 @@ public async Task TransportError()
479526
Exception = new HttpRequestException("Transport error"),
480527
};
481528
var factory = new MockHttpClientFactory(handler);
482-
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
529+
var client = this.CreateMessagingClient(factory);
483530
var message = new Message()
484531
{
485532
Topic = "test-topic",
@@ -500,7 +547,18 @@ public async Task TransportError()
500547
handler.LastRequestBody);
501548
Assert.Equal("test-topic", req.Message.Topic);
502549
Assert.False(req.ValidateOnly);
503-
Assert.Equal(1, handler.Calls);
550+
Assert.Equal(5, handler.Calls);
551+
}
552+
553+
private FirebaseMessagingClient CreateMessagingClient(HttpClientFactory factory)
554+
{
555+
return new FirebaseMessagingClient(new FirebaseMessagingClient.Args()
556+
{
557+
ClientFactory = factory,
558+
Credential = MockCredential,
559+
ProjectId = "test-project",
560+
RetryOptions = RetryOptions.NoBackOff,
561+
});
504562
}
505563

506564
private void CheckHeaders(HttpRequestHeaders header)

0 commit comments

Comments
 (0)