using System; using System.IO; using System.IO.Compression; using System.Linq; using Cthangover.Core.Mods.Providers; using Cthangover.Core.Mods.Testing; namespace Mods.ModManager.Tests { /// Storage and fallback contracts, including malformed files and compressed mods. public class ModDocumentationTests { [ModTest("documentation/language_fallback")] static void LanguageFallback(ModTestContext ctx) { var docs = new[] { new ModDocument("de", "docs/de.md", "Deutsch"), new ModDocument("en", "docs/en.md", "English"), new ModDocument("ru", "docs/ru.md", "Русский"), new ModDocument("default", "docs/default.md", "Default"), }; ModAssert.Equal("ru", ModDocumentation.Select(docs, "RU_ru").Language, "regional language"); ModAssert.Equal("default", ModDocumentation.Select(docs, "ja").Language, "author fallback"); ModAssert.Equal("en", ModDocumentation.Select(docs.Take(3).ToArray(), "ja").Language, "English fallback"); ModAssert.Equal("de", ModDocumentation.Select(docs.Take(1).ToArray(), "ja").Language, "first available"); ModAssert.Null(ModDocumentation.Select(Array.Empty(), "ru"), "legacy mod"); } [ModTest("documentation/folder_and_zip")] static void FolderAndZip(ModTestContext ctx) { var root = Path.Combine(ctx.SandboxDir, "documentation"); var zip = Path.Combine(ctx.SandboxDir, "documentation.zip"); Directory.CreateDirectory(Path.Combine(root, "docs", "assets")); File.WriteAllText(Path.Combine(root, "docs", "ru.md"), "# Русский\n![Example](assets/picture.png)"); File.WriteAllText(Path.Combine(root, "docs", "en.md"), "# English"); File.WriteAllText(Path.Combine(root, "docs", "README.md"), "Not a language"); File.WriteAllText(Path.Combine(root, "docs", "ja.md"), " \n "); File.WriteAllBytes(Path.Combine(root, "docs", "ko.md"), new byte[] { 0xFF, 0xFF }); File.WriteAllBytes(Path.Combine(root, "docs", "assets", "picture.png"), new byte[] { 1, 2, 3 }); try { using var folder = new FolderModFileProvider(root, "documentation_fixture"); var local = ModDocumentation.Discover(folder); ModAssert.Equal(2, local.Count, "only readable language files"); ModAssert.True(ModDocumentation.Select(local, "ru").Text.Contains("Русский"), "UTF-8 preserved"); ModAssert.Null(ModDocumentation.ReadResource(folder, "docs/en.md", 2), "size limit"); ModAssert.Null(ModDocumentation.ReadResource(folder, "docs/missing.md", 100), "missing file"); ZipFile.CreateFromDirectory(root, zip); using var archive = new ZipModFileProvider(zip, "documentation_fixture"); var packed = ModDocumentation.Discover(archive); ModAssert.Equal(local.Count, packed.Count, "ZIP discovery"); ModAssert.Equal(ModDocumentation.Select(local, "ru").Text, ModDocumentation.Select(packed, "ru").Text, "ZIP text"); ModAssert.Equal(3, ModDocumentation.ReadResource(archive, "docs/assets/picture.png", 100).Length, "ZIP image bytes"); } finally { if (File.Exists(zip)) File.Delete(zip); Directory.Delete(root, true); } } [ModTest("documentation/resource_paths")] static void ResourcePaths(ModTestContext ctx) { ModAssert.Equal("docs/assets/a.png", ModDocumentation.ResolveResource("docs/ru.md", "assets/a.png")); ModAssert.Equal("docs/assets/a.png", ModDocumentation.ResolveResource("docs/ru/page.md", "../assets/a.png")); foreach (var path in new[] { "../../secret.png", "/tmp/a.png", "C:\\a.png", "https://example.com/a.png", "file:///etc/passwd", "\\server\\a.png" }) ModAssert.Null(ModDocumentation.ResolveResource("docs/ru.md", path), path); } [ModTest("documentation/markdown_literals")] static void MarkdownLiterals(ModTestContext ctx) { var view = new ModMarkdownView(); ctx.SandboxRoot.AddChild(view); view.Display(null, new ModDocument("en", "docs/en.md", "# Heading\n\n**Bold** and `code`\n\n```json\n[url=javascript:bad]literal[/url]\n```\n\n\n\n[Visible](javascript:bad)")); var text = view.GetParsedText(); ModAssert.True(text.Contains("Heading") && text.Contains("Bold and code"), "Markdown text preserved"); ModAssert.True(text.Contains("[url=javascript:bad]literal[/url]"), "code never becomes BBCode"); ModAssert.True(text.Contains(""), "HTML never executes"); ModAssert.True(text.Contains("Visible"), "unsupported link retains label"); view.Display(null, null); ModAssert.Equal("", view.GetParsedText(), "clearing previous document"); } } }