Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using FirebaseAdmin.Messaging;
Expand Down Expand Up @@ -120,5 +121,29 @@ public async Task SendMulticast()
Assert.NotNull(response.Responses[0].Exception);
Assert.NotNull(response.Responses[1].Exception);
}

[Fact]
public async Task SubscribeToTopic()
{
var response = await FirebaseMessaging.DefaultInstance.SubscribeToTopicAsync("test-topic", new List<string> { "token1", "token2" });
Assert.NotNull(response);
Assert.Equal(2, response.FailureCount);
Assert.Equal("invalid-argument", response.Errors[0].Reason);
Assert.Equal(0, response.Errors[0].Index);
Assert.Equal("invalid-argument", response.Errors[1].Reason);
Assert.Equal(1, response.Errors[1].Index);
}

[Fact]
public async Task UnsubscribeFromTopic()
{
var response = await FirebaseMessaging.DefaultInstance.UnsubscribeFromTopicAsync("test-topic", new List<string> { "token1", "token2" });
Assert.NotNull(response);
Assert.Equal(2, response.FailureCount);
Assert.Equal("invalid-argument", response.Errors[0].Reason);
Assert.Equal(0, response.Errors[0].Index);
Assert.Equal("invalid-argument", response.Errors[1].Reason);
Assert.Equal(1, response.Errors[1].Index);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using FirebaseAdmin.Tests;
Expand Down Expand Up @@ -118,6 +119,56 @@ await Assert.ThrowsAsync<OperationCanceledException>(
new Message() { Topic = "test-topic" }, canceller.Token));
}

[Fact]
public async Task SubscribeWithClientFactory()
{
var handler = new MockMessageHandler()
{
Response = @"{""results"":[{}]}",
};
var factory = new MockHttpClientFactory(handler);

var app = FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromAccessToken("test-token"),
HttpClientFactory = factory,
ProjectId = "test-project",
});
FirebaseMessaging messaging = FirebaseMessaging.GetMessaging(app);
Assert.NotNull(messaging);
Assert.Same(messaging, FirebaseMessaging.GetMessaging(app));

var response = await messaging.SubscribeToTopicAsync("test-topic", new List<string> { "test-token" });
Assert.Equal(0, response.FailureCount);
Assert.Equal(1, response.SuccessCount);
app.Delete();
}

[Fact]
public async Task UnsubscribeWithClientFactory()
{
var handler = new MockMessageHandler()
{
Response = @"{""results"":[{}]}",
};
var factory = new MockHttpClientFactory(handler);

var app = FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromAccessToken("test-token"),
HttpClientFactory = factory,
ProjectId = "test-project",
});
FirebaseMessaging messaging = FirebaseMessaging.GetMessaging(app);
Assert.NotNull(messaging);
Assert.Same(messaging, FirebaseMessaging.GetMessaging(app));

var response = await messaging.UnsubscribeFromTopicAsync("test-topic", new List<string> { "test-token" });
Assert.Equal(0, response.FailureCount);
Assert.Equal(1, response.SuccessCount);
app.Delete();
}

public void Dispose()
{
FirebaseApp.DeleteAll();
Expand Down
145 changes: 145 additions & 0 deletions FirebaseAdmin/FirebaseAdmin.Tests/Messaging/InstanceIdClientTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using FirebaseAdmin.Messaging;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Http;
using Xunit;

namespace FirebaseAdmin.Tests.Messaging
{
public class InstanceIdClientTest
{
private static readonly GoogleCredential MockCredential =
GoogleCredential.FromAccessToken("test-token");

[Fact]
public void NoCredential()
{
var clientFactory = new HttpClientFactory();
Assert.Throws<ArgumentNullException>(
() => new InstanceIdClient(clientFactory, null));
}

[Fact]
public void NoClientFactory()
{
var clientFactory = new HttpClientFactory();
Assert.Throws<ArgumentNullException>(
() => new InstanceIdClient(null, MockCredential));
}

[Fact]
public async Task SubscribeToTopicAsync()
{
var handler = new MockMessageHandler()
{
Response = @"{""results"":[{}]}",
};
var factory = new MockHttpClientFactory(handler);

var client = new InstanceIdClient(factory, MockCredential);

var result = await client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" });

Assert.Equal(1, result.SuccessCount);
}

[Fact]
public async Task UnsubscribeFromTopicAsync()
{
var handler = new MockMessageHandler()
{
Response = @"{""results"":[{}]}",
};
var factory = new MockHttpClientFactory(handler);

var client = new InstanceIdClient(factory, MockCredential);

var result = await client.UnsubscribeFromTopicAsync("test-topic", new List<string> { "abc123" });

Assert.Equal(1, result.SuccessCount);
}

[Fact]
public async Task BadRequest()
{
var handler = new MockMessageHandler()
{
StatusCode = HttpStatusCode.BadRequest,
Response = "BadRequest",
};
var factory = new MockHttpClientFactory(handler);

var client = new InstanceIdClient(factory, MockCredential);

await Assert.ThrowsAsync<FirebaseMessagingException>(
() => client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" }));
}

[Fact]
public async Task Unauthorized()
{
var handler = new MockMessageHandler()
{
StatusCode = HttpStatusCode.Unauthorized,
Response = "Unauthorized",
};
var factory = new MockHttpClientFactory(handler);

var client = new InstanceIdClient(factory, MockCredential);

await Assert.ThrowsAsync<FirebaseMessagingException>(
() => client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" }));
}

[Fact]
public async Task Forbidden()
{
var handler = new MockMessageHandler()
{
StatusCode = HttpStatusCode.Forbidden,
Response = "Forbidden",
};
var factory = new MockHttpClientFactory(handler);

var client = new InstanceIdClient(factory, MockCredential);

await Assert.ThrowsAsync<FirebaseMessagingException>(
() => client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" }));
}

[Fact]
public async Task NotFound()
{
var handler = new MockMessageHandler()
{
StatusCode = HttpStatusCode.NotFound,
Response = "NotFound",
};
var factory = new MockHttpClientFactory(handler);

var client = new InstanceIdClient(factory, MockCredential);

await Assert.ThrowsAsync<FirebaseMessagingException>(
() => client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" }));
}

[Fact]
public async Task ServiceUnavailable()
{
var handler = new MockMessageHandler()
{
StatusCode = HttpStatusCode.ServiceUnavailable,
Response = "ServiceUnavailable",
};
var factory = new MockHttpClientFactory(handler);

var client = new InstanceIdClient(factory, MockCredential);

await Assert.ThrowsAsync<FirebaseMessagingException>(
() => client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" }));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using FirebaseAdmin.Messaging;
using Newtonsoft.Json;
using Xunit;

namespace FirebaseAdmin.Tests.Messaging
{
public class TopicManagementResponseTest
{
[Fact]
public void SuccessfulReponse()
{
var topicManagementResults = new List<string> { null };
var response = new TopicManagementResponse(topicManagementResults);

Assert.Empty(response.Errors);
Assert.Equal(1, response.SuccessCount);
}

[Fact]
public void UnsuccessfulResponse()
{
var topicManagementResults = new List<string> { null, "NOT_FOUND" };
var response = new TopicManagementResponse(topicManagementResults);

Assert.Single(response.Errors);
Assert.Equal(1, response.SuccessCount);
Assert.NotEmpty(response.Errors);
Assert.Equal("registration-token-not-registered", response.Errors[0].Reason);
Assert.Equal(1, response.Errors[0].Index);
}

[Fact]
public void NullResponse()
{
Assert.Throws<ArgumentNullException>(() =>
{
new TopicManagementResponse(null);
});
}

[Fact]
public void EmptyResponse()
{
Assert.Throws<ArgumentException>(() =>
{
new TopicManagementResponse(new List<string>());
});
}

[Fact]
public void UnknownError()
{
var topicManagementResults = new List<string> { "NOT_A_REAL_ERROR_CODE" };
var response = new TopicManagementResponse(topicManagementResults);

Assert.Single(response.Errors);
Assert.Equal("unknown-error", response.Errors[0].Reason);
Assert.Equal(0, response.Errors[0].Index);
}

[Fact]
public void UnexpectedResponse()
{
var topicManagementResults = new List<string> { "NOT_A_REAL_CODE" };
var response = new TopicManagementResponse(topicManagementResults);

Assert.Single(response.Errors);
Assert.Equal("unknown-error", response.Errors[0].Reason);
Assert.Equal(0, response.SuccessCount);
}

[Fact]
public void CountsSuccessAndErrors()
{
var topicManagementResults = new List<string> { "NOT_FOUND", null, "INVALID_ARGUMENT", null, null };
var response = new TopicManagementResponse(topicManagementResults);

Assert.Equal(2, response.FailureCount);
Assert.Equal(3, response.SuccessCount);
Assert.Equal("registration-token-not-registered", response.Errors[0].Reason);
Assert.NotEmpty(response.Errors);
Assert.Equal(0, response.Errors[0].Index);
Assert.Equal("invalid-argument", response.Errors[1].Reason);
Assert.Equal(2, response.Errors[1].Index);
}
}
}
47 changes: 47 additions & 0 deletions FirebaseAdmin/FirebaseAdmin/Messaging/ErrorInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Collections.Generic;

namespace FirebaseAdmin.Messaging
{
/// <summary>
/// A topic management error.
/// </summary>
public sealed class ErrorInfo
{
// Server error codes as defined in https://developers.google.com/instance-id/reference/server
// TODO: Should we handle other error codes here (e.g. PERMISSION_DENIED)?
private static IReadOnlyDictionary<string, string> errorCodes;
private readonly string unknownError = "unknown-error";

/// <summary>
/// Initializes a new instance of the <see cref="ErrorInfo"/> class.
/// </summary>
/// <param name="index">Index of the error in the error codes.</param>
/// <param name="reason">Reason for the error.</param>
public ErrorInfo(int index, string reason)
{
errorCodes = new Dictionary<string, string>
{
{ "INVALID_ARGUMENT", "invalid-argument" },
{ "NOT_FOUND", "registration-token-not-registered" },
{ "INTERNAL", "internal-error" },
{ "TOO_MANY_TOPICS", "too-many-topics" },
};

this.Index = index;
this.Reason = errorCodes.ContainsKey(reason)
? errorCodes[reason] : this.unknownError;
}

/// <summary>
/// Gets the registration token to which this error is related to.
/// </summary>
/// <returns>An index into the original registration token list.</returns>
public int Index { get; private set; }

/// <summary>
/// Gets the nature of the error.
/// </summary>
/// <returns>A non-null, non-empty error message.</returns>
public string Reason { get; private set; }
}
}
Loading