using System.Net; using System.Net.Http.Headers; using System.Reflection; using GameList.Infrastructure; using GameList.Endpoints; using GameList.Tests.Support; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using System.Linq; namespace GameList.Tests; public class HelperTests { [Fact] public void PasswordHasher_roundtrip_and_empty_guard() { var (hash, salt) = PasswordHasher.HashPassword("secret"); Assert.True(PasswordHasher.Verify("secret", hash, salt)); Assert.False(PasswordHasher.Verify("other", hash, salt)); Assert.Throws(() => PasswordHasher.HashPassword("")); } [Fact] public void UpdateIndexMetaBase_rewrites_content_value() { var webRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(webRoot); var index = Path.Combine(webRoot, "index.html"); File.WriteAllText(index, ""); var env = new FakeEnv { WebRootPath = webRoot }; var method = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public) .First(m => m.Name.Contains("UpdateIndexMetaBase")); method.Invoke(null, new object?[] { env, "/pick" }); var text = File.ReadAllText(index); Assert.Contains("content=\"/pick\"", text); } [Fact] public void UpdateIndexMetaBase_no_marker_no_change() { var webRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(webRoot); var index = Path.Combine(webRoot, "index.html"); File.WriteAllText(index, ""); var env = new FakeEnv { WebRootPath = webRoot }; var method = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public) .First(m => m.Name.Contains("UpdateIndexMetaBase")); method.Invoke(null, new object?[] { env, "/pick" }); Assert.Equal("", File.ReadAllText(index)); } [Fact] public async Task IsReachableImageAsync_rejects_redirect_and_accepts_image() { Assert.True(await EndpointHelpers.IsReachableImageAsync(null, new StubHttpClientFactory(new StubHttpMessageHandler()))); Assert.False(await EndpointHelpers.IsReachableImageAsync("http://127.0.0.1/img.png", new StubHttpClientFactory(new StubHttpMessageHandler()))); } [Fact] public void Link_root_helpers_handle_groups() { var roots = EndpointHelpers.BuildLinkRoots(new[] { (1, (int?)null), (2, 1), (3, (int?)null) }); Assert.Equal(1, roots[1]); Assert.Equal(1, roots[2]); Assert.Equal(3, roots[3]); var linked = EndpointHelpers.LinkedIdsFor(2, roots); Assert.Contains(1, linked); Assert.Contains(2, linked); } [Fact] public void Url_validation_rules() { Assert.True(EndpointHelpers.IsValidImageUrl("https://x.com/img.png")); Assert.False(EndpointHelpers.IsValidImageUrl("ftp://x/img.png")); Assert.True(EndpointHelpers.IsValidHttpUrl("http://x")); Assert.False(EndpointHelpers.IsValidHttpUrl("file://x")); } [Fact] public void Find_root_handles_cycles() { var parentMap = new Dictionary { {1, 2}, {2, 3}, {3, 1} }; var root = EndpointHelpers.FindRootId(1, parentMap); Assert.Equal(3, root); // cycle breaks on revisit } private class FakeEnv : IWebHostEnvironment { public string ApplicationName { get; set; } = ""; public IFileProvider WebRootFileProvider { get; set; } = null!; public string WebRootPath { get; set; } = ""; public string EnvironmentName { get; set; } = ""; public string ContentRootPath { get; set; } = ""; public IFileProvider ContentRootFileProvider { get; set; } = null!; } }