47d106e288
PR Checks / test-and-build (pull_request) Successful in 11m55s
The Command_ShouldRenderEmbedOnSuccess test asserted the presence of WithEmbeds in DiscordNewSessionCommand.cs. After switching to deferred responses (InteractionCallback.DeferredMessage + ModifyResponseAsync), embeds are now set via message.Embeds = embeds instead. Bump version → 3.0.8 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
164 lines
6.0 KiB
C#
164 lines
6.0 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_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);
|
|
}
|
|
|
|
private static DateTimeOffset FutureDateAt1930()
|
|
{
|
|
var future = DateTimeOffset.UtcNow.AddDays(7);
|
|
return new DateTimeOffset(
|
|
future.Year,
|
|
future.Month,
|
|
future.Day,
|
|
19,
|
|
30,
|
|
0,
|
|
TimeSpan.Zero);
|
|
}
|
|
}
|