Files
GmRelayBot/tests/GmRelay.Bot.Tests/Web/TelegramAuthServiceTests.cs
T
Toutsu 5319592964 feat(e2e): shared initData / Login Widget payload builder for E2E tests
- Add TelegramAuthPayloadBuilder in GmRelay.Shared for C# tests.
- Refactor TelegramAuthServiceTests to use the shared builder.
- Add Python equivalent (telegram_init_data.py) for E2E runner.
- Add self-contained Python tests and E2E README.

Closes #144

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 11:53:22 +03:00

246 lines
8.0 KiB
C#

using System.Text.Json;
using GmRelay.Shared.Telegram;
using GmRelay.Web.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
namespace GmRelay.Bot.Tests.Web;
public sealed class TelegramAuthServiceTests
{
[Fact]
public void Verify_ShouldAcceptValidTelegramPayload()
{
const string botToken = "test-bot-token";
var result = TelegramAuthPayloadBuilder.BuildLoginWidget(
botToken,
424242L,
"Ada",
"Lovelace",
"ada");
var query = ParseQueryString(result.QueryString);
var service = new TelegramAuthService(CreateConfiguration(botToken));
var verified = service.Verify(query, out var telegramId, out var name);
Assert.True(verified);
Assert.Equal(424242L, telegramId);
Assert.Equal("Ada Lovelace", name);
}
[Fact]
public void Verify_ShouldRejectTamperedHash()
{
const string botToken = "test-bot-token";
var result = TelegramAuthPayloadBuilder.BuildLoginWidget(botToken, 424242L, "Ada");
var tamperedQuery = ParseQueryString(result.QueryString.Replace("hash=", "hash=00", StringComparison.Ordinal));
var service = new TelegramAuthService(CreateConfiguration(botToken));
var verified = service.Verify(tamperedQuery, out _, out _);
Assert.False(verified);
}
[Fact]
public void Verify_ShouldRejectExpiredPayload()
{
const string botToken = "test-bot-token";
var expiredAuthDate = DateTimeOffset.UtcNow.AddDays(-2).ToUnixTimeSeconds();
var result = TelegramAuthPayloadBuilder.BuildLoginWidget(
botToken,
424242L,
"Ada",
authDate: expiredAuthDate);
var query = ParseQueryString(result.QueryString);
var service = new TelegramAuthService(CreateConfiguration(botToken));
var verified = service.Verify(query, out _, out _);
Assert.False(verified);
}
[Fact]
public void VerifyWebAppInitData_ShouldAcceptValidTelegramWebAppPayload()
{
const string botToken = "test-bot-token";
var result = TelegramAuthPayloadBuilder.BuildMiniAppInitData(
botToken,
424242L,
"Ada",
"Lovelace",
"ada",
queryId: "AAHdF6IQAAAAAN0XohDhrOrc");
var service = new TelegramAuthService(CreateConfiguration(botToken));
var verified = service.VerifyWebAppInitData(result.InitDataRaw, out var telegramId, out var name);
Assert.True(verified);
Assert.Equal(424242L, telegramId);
Assert.Equal("Ada Lovelace", name);
}
[Fact]
public void VerifyWebAppInitData_ShouldRejectTamperedHash()
{
const string botToken = "test-bot-token";
var result = TelegramAuthPayloadBuilder.BuildMiniAppInitData(botToken, 424242L, "Ada");
var tamperedInitData = result.InitDataRaw.Replace("hash=", "hash=00", StringComparison.Ordinal);
var service = new TelegramAuthService(CreateConfiguration(botToken));
var verified = service.VerifyWebAppInitData(tamperedInitData, out _, out _);
Assert.False(verified);
}
[Fact]
public void VerifyWebAppInitData_ShouldRejectExpiredPayload()
{
const string botToken = "test-bot-token";
var expiredAuthDate = DateTimeOffset.UtcNow.AddDays(-2).ToUnixTimeSeconds();
var result = TelegramAuthPayloadBuilder.BuildMiniAppInitData(
botToken,
424242L,
"Ada",
authDate: expiredAuthDate);
var service = new TelegramAuthService(CreateConfiguration(botToken));
var verified = service.VerifyWebAppInitData(result.InitDataRaw, out _, out _);
Assert.False(verified);
}
[Fact]
public void VerifyLoginPayload_ShouldAcceptValidTelegramWidgetCallbackPayload()
{
const string botToken = "test-bot-token";
var authDate = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var result = TelegramAuthPayloadBuilder.BuildLoginWidget(
botToken,
424242L,
"Ada",
"Lovelace",
"ada",
"https://t.me/i/userpic/320/ada.jpg",
authDate);
var payload = new TelegramLoginPayload(
result.TelegramId,
result.FirstName,
result.LastName,
result.Username,
result.PhotoUrl,
result.AuthDate,
result.Hash);
var service = new TelegramAuthService(CreateConfiguration(botToken));
var verified = service.VerifyLoginPayload(payload, out var telegramId, out var name);
Assert.True(verified);
Assert.Equal(424242L, telegramId);
Assert.Equal("Ada Lovelace", name);
}
[Fact]
public void VerifyLoginPayload_ShouldRejectTamperedCallbackHash()
{
var payload = new TelegramLoginPayload(
424242,
"Ada",
null,
null,
null,
DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
"00");
var service = new TelegramAuthService(CreateConfiguration("test-bot-token"));
var verified = service.VerifyLoginPayload(payload, out _, out _);
Assert.False(verified);
}
[Fact]
public void VerifyLoginPayload_ShouldRejectExpiredCallbackPayload()
{
const string botToken = "test-bot-token";
var authDate = DateTimeOffset.UtcNow.AddDays(-2).ToUnixTimeSeconds();
var result = TelegramAuthPayloadBuilder.BuildLoginWidget(
botToken,
424242L,
"Ada",
authDate: authDate);
var payload = new TelegramLoginPayload(
result.TelegramId,
result.FirstName,
result.LastName,
result.Username,
result.PhotoUrl,
result.AuthDate,
result.Hash);
var service = new TelegramAuthService(CreateConfiguration(botToken));
var verified = service.VerifyLoginPayload(payload, out _, out _);
Assert.False(verified);
}
[Fact]
public void VerifyLoginPayload_ShouldRejectMissingRequiredCallbackFields()
{
var payload = new TelegramLoginPayload(
0,
"",
null,
null,
null,
DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
"");
var service = new TelegramAuthService(CreateConfiguration("test-bot-token"));
var verified = service.VerifyLoginPayload(payload, out _, out _);
Assert.False(verified);
}
[Fact]
public void TelegramLoginPayload_ShouldDeserializeTelegramWidgetSnakeCaseJson()
{
var payload = JsonSerializer.Deserialize<TelegramLoginPayload>(
"""
{
"id": 424242,
"first_name": "Ada",
"last_name": "Lovelace",
"username": "ada",
"photo_url": "https://t.me/i/userpic/320/ada.jpg",
"auth_date": 1714300000,
"hash": "abcdef"
}
""");
Assert.NotNull(payload);
Assert.Equal(424242L, payload.Id);
Assert.Equal("Ada", payload.FirstName);
Assert.Equal("Lovelace", payload.LastName);
Assert.Equal("ada", payload.Username);
Assert.Equal("https://t.me/i/userpic/320/ada.jpg", payload.PhotoUrl);
Assert.Equal(1714300000L, payload.AuthDate);
Assert.Equal("abcdef", payload.Hash);
}
private static IConfiguration CreateConfiguration(string botToken) =>
new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["Telegram:BotToken"] = botToken
})
.Build();
private static QueryCollection ParseQueryString(string queryString)
{
var parsed = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(queryString);
return new QueryCollection(parsed.ToDictionary(
pair => pair.Key,
pair => pair.Value));
}
}