5fa7e26f72
- DiscordAuthServiceTests: authorize URL, token exchange, profile fetch - PlatformIdentityTests: Telegram fallback, Discord identity, avatar URL Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
140 lines
4.7 KiB
C#
140 lines
4.7 KiB
C#
using System.Net;
|
|
using System.Text.Json;
|
|
using GmRelay.Web;
|
|
using GmRelay.Web.Services;
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
namespace GmRelay.Bot.Tests.Web;
|
|
|
|
public class DiscordAuthServiceTests
|
|
{
|
|
[Fact]
|
|
public void BuildAuthorizeUrl_GeneratesCorrectUrl()
|
|
{
|
|
var config = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Discord:ClientId"] = "12345",
|
|
["Discord:ClientSecret"] = "secret",
|
|
["Discord:RedirectUri"] = "https://example.com/callback"
|
|
})
|
|
.Build();
|
|
|
|
var service = new DiscordAuthService(new TestHttpClientFactory(), config);
|
|
var url = service.BuildAuthorizeUrl("state123");
|
|
|
|
Assert.Contains("client_id=12345", url);
|
|
Assert.Contains("response_type=code", url);
|
|
Assert.Contains("state=state123", url);
|
|
Assert.Contains("https%3A%2F%2Fexample.com%2Fcallback", url);
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildAuthorizeUrl_WithMissingConfig_ThrowsInvalidOperationException()
|
|
{
|
|
var config = new ConfigurationBuilder().Build();
|
|
var service = new DiscordAuthService(new TestHttpClientFactory(), config);
|
|
|
|
Assert.Throws<InvalidOperationException>(() => service.BuildAuthorizeUrl("state"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ExchangeCodeAsync_WithValidCode_ReturnsUser()
|
|
{
|
|
var handler = new TestHttpMessageHandler((request) =>
|
|
{
|
|
if (request.RequestUri?.AbsolutePath == "/api/oauth2/token")
|
|
{
|
|
var response = new DiscordTokenResponse("access_123", "Bearer", 604800, "identify");
|
|
return new HttpResponseMessage(HttpStatusCode.OK)
|
|
{
|
|
Content = new StringContent(JsonSerializer.Serialize(response))
|
|
};
|
|
}
|
|
|
|
if (request.RequestUri?.AbsolutePath == "/api/users/@me")
|
|
{
|
|
var user = new DiscordUser("98765", "TestUser", "0", "avatar123", null);
|
|
return new HttpResponseMessage(HttpStatusCode.OK)
|
|
{
|
|
Content = new StringContent(JsonSerializer.Serialize(user))
|
|
};
|
|
}
|
|
|
|
return new HttpResponseMessage(HttpStatusCode.NotFound);
|
|
});
|
|
|
|
var config = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Discord:ClientId"] = "client",
|
|
["Discord:ClientSecret"] = "secret",
|
|
["Discord:RedirectUri"] = "https://example.com/callback"
|
|
})
|
|
.Build();
|
|
|
|
var factory = new TestHttpClientFactory(handler);
|
|
var service = new DiscordAuthService(factory, config);
|
|
|
|
var result = await service.ExchangeCodeAsync("valid_code");
|
|
|
|
Assert.NotNull(result);
|
|
Assert.Equal("98765", result.Id);
|
|
Assert.Equal("TestUser", result.Username);
|
|
Assert.Equal("https://cdn.discordapp.com/avatars/98765/avatar123.png", result.AvatarUrl);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ExchangeCodeAsync_WithInvalidCode_ReturnsNull()
|
|
{
|
|
var handler = new TestHttpMessageHandler((request) =>
|
|
{
|
|
return new HttpResponseMessage(HttpStatusCode.BadRequest);
|
|
});
|
|
|
|
var config = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Discord:ClientId"] = "client",
|
|
["Discord:ClientSecret"] = "secret",
|
|
["Discord:RedirectUri"] = "https://example.com/callback"
|
|
})
|
|
.Build();
|
|
|
|
var factory = new TestHttpClientFactory(handler);
|
|
var service = new DiscordAuthService(factory, config);
|
|
|
|
var result = await service.ExchangeCodeAsync("invalid_code");
|
|
|
|
Assert.Null(result);
|
|
}
|
|
|
|
private class TestHttpClientFactory : IHttpClientFactory
|
|
{
|
|
private readonly HttpMessageHandler? _handler;
|
|
|
|
public TestHttpClientFactory(HttpMessageHandler? handler = null)
|
|
{
|
|
_handler = handler;
|
|
}
|
|
|
|
public HttpClient CreateClient(string name) =>
|
|
_handler is not null ? new HttpClient(_handler) : new HttpClient();
|
|
}
|
|
|
|
private class TestHttpMessageHandler : HttpMessageHandler
|
|
{
|
|
private readonly Func<HttpRequestMessage, HttpResponseMessage> _handler;
|
|
|
|
public TestHttpMessageHandler(Func<HttpRequestMessage, HttpResponseMessage> handler)
|
|
{
|
|
_handler = handler;
|
|
}
|
|
|
|
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
{
|
|
return Task.FromResult(_handler(request));
|
|
}
|
|
}
|
|
}
|