Files
GmRelayBot/tests/GmRelay.Bot.Tests/Discord/DiscordNewSessionHandlerTests.cs
T
Toutsu c2cc7fd9a8
Deploy Telegram Bot / build-and-push (push) Successful in 5m46s
Deploy Telegram Bot / scan-images (push) Successful in 3m29s
Deploy Telegram Bot / deploy (push) Successful in 29s
fix(web): show discord sessions and integration labels
2026-05-26 14:43:33 +03:00

199 lines
7.6 KiB
C#

using GmRelay.DiscordBot.Features.Sessions;
namespace GmRelay.Bot.Tests.Discord;
public sealed class DiscordNewSessionHandlerTests
{
private static string GetRepoRoot()
{
var dir = AppContext.BaseDirectory;
while (!string.IsNullOrEmpty(dir) && !File.Exists(Path.Combine(dir, "Directory.Build.props")))
{
dir = Directory.GetParent(dir)?.FullName;
}
return dir ?? throw new InvalidOperationException("Could not find repo root");
}
// --- Runtime tests for ParseTimeInput (static, no DB) ---
[Fact]
public void ParseTimeInput_ShouldParseDiscordDateFormat()
{
var expected = FutureDateAt1930();
var result = DiscordNewSessionHandler.ParseTimeInput(
expected.ToString("yyyy-MM-dd HH:mm", System.Globalization.CultureInfo.InvariantCulture));
Assert.True(result.IsSuccess);
Assert.Equal(expected.Year, result.Value.Year);
Assert.Equal(expected.Month, result.Value.Month);
Assert.Equal(expected.Day, result.Value.Day);
Assert.Equal(19, result.Value.Hour);
Assert.Equal(30, result.Value.Minute);
}
[Fact]
public void ParseTimeInput_ShouldRejectPastDate()
{
var result = DiscordNewSessionHandler.ParseTimeInput("2020-01-01 00:00");
Assert.False(result.IsSuccess);
}
[Fact]
public void ParseTimeInput_ShouldParseRussianDateFormat()
{
var expected = FutureDateAt1930();
var result = DiscordNewSessionHandler.ParseTimeInput(
expected.ToString("dd.MM.yyyy HH:mm", System.Globalization.CultureInfo.InvariantCulture));
Assert.True(result.IsSuccess);
Assert.Equal(expected.Year, result.Value.Year);
Assert.Equal(expected.Month, result.Value.Month);
Assert.Equal(expected.Day, result.Value.Day);
}
[Fact]
public void ParseTimeInput_ShouldRejectInvalidFormat()
{
var result = DiscordNewSessionHandler.ParseTimeInput("not-a-date");
Assert.False(result.IsSuccess);
Assert.NotNull(result.Error);
}
// --- Source-level structural tests ---
[Fact]
public void Handler_ShouldExist()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
Assert.True(File.Exists(handlerPath), "DiscordNewSessionHandler should exist.");
}
[Fact]
public void Handler_ShouldUseDapperForDatabaseAccess()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.Contains("QueryAsync", source, StringComparison.Ordinal);
Assert.Contains("ExecuteAsync", source, StringComparison.Ordinal);
Assert.Contains("ExecuteScalarAsync", source, StringComparison.Ordinal);
}
[Fact]
public void Handler_ShouldUseNpgsqlDataSource()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.Contains("NpgsqlDataSource", source, StringComparison.Ordinal);
}
[Fact]
public void Handler_ShouldCheckPermissionsViaPermissionChecker()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.Contains("CanManageSchedule", source, StringComparison.Ordinal);
Assert.Contains("UnauthorizedAccessException", source, StringComparison.Ordinal);
}
[Fact]
public void Handler_ShouldBePlatformNeutral()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.DoesNotContain("telegram_chat_id", source, StringComparison.Ordinal);
Assert.DoesNotContain("telegram_id", source, StringComparison.Ordinal);
Assert.Contains("platform = 'Discord'", source, StringComparison.Ordinal);
}
[Fact]
public void Handler_ShouldUseTransactions()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.Contains("BeginTransactionAsync", source, StringComparison.Ordinal);
Assert.Contains("CommitAsync", source, StringComparison.Ordinal);
Assert.Contains("RollbackAsync", source, StringComparison.Ordinal);
}
[Fact]
public void Handler_ShouldNotRollbackCommittedTransactionAfterPostCommitFailure()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.Contains("transactionCommitted = false", source, StringComparison.Ordinal);
Assert.Contains("transactionCommitted = true", source, StringComparison.Ordinal);
Assert.Contains("if (!transactionCommitted)", source, StringComparison.Ordinal);
}
[Fact]
public void Handler_ShouldRespectCancellationToken()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.Contains("CancellationToken", source, StringComparison.Ordinal);
}
[Fact]
public void Command_ShouldRenderEmbedOnSuccess()
{
var repoRoot = GetRepoRoot();
var commandPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionCommand.cs");
var source = File.ReadAllText(commandPath);
Assert.Contains("DiscordSessionBatchRenderer.Render", source, StringComparison.Ordinal);
Assert.Contains("message.Embeds = embeds", source, StringComparison.Ordinal);
}
[Fact]
public void Handler_ShouldLeaveScheduleMessageCreationToInteractionResponse()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.DoesNotContain("SendScheduleAsync", source, StringComparison.Ordinal);
Assert.DoesNotContain("PlatformScheduleMessage", source, StringComparison.Ordinal);
}
[Fact]
public void Handler_ShouldStoreReadableDiscordGroupNameForWebCards()
{
var repoRoot = GetRepoRoot();
var handlerPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "Features", "Sessions", "DiscordNewSessionHandler.cs");
var source = File.ReadAllText(handlerPath);
Assert.Contains("groupName", source, StringComparison.Ordinal);
Assert.Contains("displayGroupName", source, StringComparison.Ordinal);
Assert.Contains("VALUES (@GroupName, 'Discord'", source, StringComparison.Ordinal);
}
private static DateTimeOffset FutureDateAt1930()
{
var future = DateTimeOffset.UtcNow.AddDays(7);
return new DateTimeOffset(
future.Year,
future.Month,
future.Day,
19,
30,
0,
TimeSpan.Zero);
}
}