test(web): add Discord auth and platform identity tests
- DiscordAuthServiceTests: authorize URL, token exchange, profile fetch - PlatformIdentityTests: Telegram fallback, Discord identity, avatar URL Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using System.Security.Claims;
|
||||
using GmRelay.Web.Services;
|
||||
|
||||
namespace GmRelay.Bot.Tests.Web;
|
||||
|
||||
public class PlatformIdentityTests
|
||||
{
|
||||
[Fact]
|
||||
public void TryGetPlatformIdentity_TelegramUser_ReturnsTelegramPlatform()
|
||||
{
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, "12345"),
|
||||
new Claim("TelegramId", "12345")
|
||||
}));
|
||||
|
||||
var result = user.TryGetPlatformIdentity(out var platform, out var externalUserId);
|
||||
|
||||
Assert.True(result);
|
||||
Assert.Equal("Telegram", platform);
|
||||
Assert.Equal("12345", externalUserId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetPlatformIdentity_TelegramUserWithPlatformClaim_ReturnsTelegramPlatform()
|
||||
{
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, "12345"),
|
||||
new Claim("Platform", "Telegram")
|
||||
}));
|
||||
|
||||
var result = user.TryGetPlatformIdentity(out var platform, out var externalUserId);
|
||||
|
||||
Assert.True(result);
|
||||
Assert.Equal("Telegram", platform);
|
||||
Assert.Equal("12345", externalUserId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetPlatformIdentity_DiscordUser_ReturnsDiscordPlatform()
|
||||
{
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, "98765"),
|
||||
new Claim("DiscordId", "98765"),
|
||||
new Claim("Platform", "Discord")
|
||||
}));
|
||||
|
||||
var result = user.TryGetPlatformIdentity(out var platform, out var externalUserId);
|
||||
|
||||
Assert.True(result);
|
||||
Assert.Equal("Discord", platform);
|
||||
Assert.Equal("98765", externalUserId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetPlatformIdentity_UnknownUser_ReturnsFalse()
|
||||
{
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.Name, "Anonymous")
|
||||
}));
|
||||
|
||||
var result = user.TryGetPlatformIdentity(out _, out _);
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetDiscordId_DiscordUser_ReturnsTrue()
|
||||
{
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim("DiscordId", "123")
|
||||
}));
|
||||
|
||||
var result = user.TryGetDiscordId(out var discordId);
|
||||
Assert.True(result);
|
||||
Assert.Equal("123", discordId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetDiscordId_TelegramUser_ReturnsFalse()
|
||||
{
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim("TelegramId", "123")
|
||||
}));
|
||||
|
||||
var result = user.TryGetDiscordId(out _);
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAvatarUrl_DiscordUserWithAvatar_ReturnsUrl()
|
||||
{
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim("AvatarUrl", "https://cdn.discordapp.com/avatars/1/abc.png")
|
||||
}));
|
||||
|
||||
var result = user.GetAvatarUrl();
|
||||
Assert.Equal("https://cdn.discordapp.com/avatars/1/abc.png", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAvatarUrl_UserWithoutAvatar_ReturnsNull()
|
||||
{
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(Array.Empty<Claim>()));
|
||||
|
||||
var result = user.GetAvatarUrl();
|
||||
Assert.Null(result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user