using System; using System.IO; namespace GmRelay.Bot.Tests.Discord; public sealed class DiscordProjectStructureTests { 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"); } [Fact] public void Solution_ShouldIncludeDiscordWorkerProject() { var repoRoot = GetRepoRoot(); var solution = File.ReadAllText(Path.Combine(repoRoot, "GM-Relay.slnx")); Assert.Contains("src/GmRelay.DiscordBot/GmRelay.DiscordBot.csproj", solution); } [Fact] public void DiscordWorkerProject_ShouldExistWithoutTelegramDependency() { var repoRoot = GetRepoRoot(); var projectPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "GmRelay.DiscordBot.csproj"); Assert.True(File.Exists(projectPath), "Discord worker project should exist."); var project = File.ReadAllText(projectPath); Assert.Contains("Microsoft.NET.Sdk.Worker", project); Assert.Contains("NetCord.Hosting", project); Assert.Contains("GmRelay.ServiceDefaults.csproj", project); Assert.Contains("GmRelay.Shared.csproj", project); Assert.DoesNotContain("Telegram.Bot", project); Assert.DoesNotContain("GmRelay.Bot.csproj", project); } [Fact] public void TelegramWorkerProject_ShouldNotReferenceNetCord() { var repoRoot = GetRepoRoot(); var project = File.ReadAllText(Path.Combine(repoRoot, "src", "GmRelay.Bot", "GmRelay.Bot.csproj")); Assert.DoesNotContain("NetCord", project, StringComparison.OrdinalIgnoreCase); } [Fact] public void RuntimeWiring_ShouldIncludeDiscordServiceWithoutCouplingTelegram() { var repoRoot = GetRepoRoot(); var compose = File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")); var appHostProject = File.ReadAllText(Path.Combine(repoRoot, "src", "GmRelay.AppHost", "GmRelay.AppHost.csproj")); var appHostProgram = File.ReadAllText(Path.Combine(repoRoot, "src", "GmRelay.AppHost", "Program.cs")); var prChecks = File.ReadAllText(Path.Combine(repoRoot, ".gitea", "workflows", "pr-checks.yml")); var deploy = File.ReadAllText(Path.Combine(repoRoot, ".gitea", "workflows", "deploy.yml")); Assert.Contains("gmrelay-discord-bot:3.0.3", compose); Assert.Contains("Discord__Token=${DISCORD_BOT_TOKEN:?Set DISCORD_BOT_TOKEN in .env}", compose); Assert.Contains("src/GmRelay.DiscordBot/Dockerfile", deploy); Assert.Contains("DISCORD_BOT_TOKEN", deploy); Assert.Contains("dotnet build src/GmRelay.DiscordBot/GmRelay.DiscordBot.csproj --no-restore", prChecks); Assert.Contains("GmRelay.DiscordBot.csproj", appHostProject); Assert.Contains("Projects.GmRelay_DiscordBot", appHostProgram); } [Fact] public void Version_ShouldBeSynchronizedForDiscordFeatureRelease() { var repoRoot = GetRepoRoot(); Assert.Contains("3.0.3", File.ReadAllText(Path.Combine(repoRoot, "Directory.Build.props"))); Assert.Contains("VERSION: 3.0.3", File.ReadAllText(Path.Combine(repoRoot, ".gitea", "workflows", "deploy.yml"))); Assert.Contains("gmrelay-bot:3.0.3", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml"))); Assert.Contains("gmrelay-web:3.0.3", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml"))); Assert.Contains("gmrelay-discord-bot:3.0.3", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml"))); Assert.Contains( "v3.0.3", File.ReadAllText(Path.Combine(repoRoot, "src", "GmRelay.Web", "Components", "Layout", "NavMenu.razor"))); } [Fact] public void EnvExample_ShouldContainDiscordBotToken() { var repoRoot = GetRepoRoot(); var envExample = File.ReadAllText(Path.Combine(repoRoot, ".env.example")); Assert.Contains("DISCORD_BOT_TOKEN", envExample); } [Fact] public void Readme_ShouldNotAskForUnusedDiscordBotClientId() { var repoRoot = GetRepoRoot(); var readme = File.ReadAllText(Path.Combine(repoRoot, "README.md")); Assert.DoesNotContain("DISCORD_BOT_CLIENT_ID", readme); Assert.Contains("DISCORD_CLIENT_ID", readme); } [Fact] public void Compose_ShouldIncludeDiscordHealthcheck() { var repoRoot = GetRepoRoot(); var compose = File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")); var discordIndex = compose.IndexOf("discord:", StringComparison.Ordinal); Assert.True(discordIndex >= 0, "compose.yaml should contain discord service"); var nextServiceIndex = compose.IndexOf(" web:", StringComparison.Ordinal); var discordBlock = compose[discordIndex..nextServiceIndex]; Assert.Contains("healthcheck:", discordBlock); Assert.Contains("test:", discordBlock); Assert.Contains("localhost:8082/health", discordBlock); } }