using System.IO; using System.Text; using System.Text.Json; using SharepointToolbox.Core.Models; using SharepointToolbox.Infrastructure.Persistence; namespace SharepointToolbox.Tests.Services; [Trait("Category", "Unit")] public class BrandingRepositoryTests : IDisposable { private readonly string _tempFile; public BrandingRepositoryTests() { _tempFile = Path.GetTempFileName(); File.Delete(_tempFile); } public void Dispose() { if (File.Exists(_tempFile)) File.Delete(_tempFile); if (File.Exists(_tempFile + ".tmp")) File.Delete(_tempFile + ".tmp"); } private BrandingRepository CreateRepository() => new(_tempFile); [Fact] public async Task LoadAsync_MissingFile_ReturnsDefaultBrandingSettings() { var repo = CreateRepository(); var settings = await repo.LoadAsync(); Assert.Null(settings.MspLogo); } [Fact] public async Task SaveAndLoad_RoundTrips_MspLogo() { var repo = CreateRepository(); var logo = new LogoData { Base64 = "abc123==", MimeType = "image/png" }; var original = new BrandingSettings { MspLogo = logo }; await repo.SaveAsync(original); var loaded = await repo.LoadAsync(); Assert.NotNull(loaded.MspLogo); Assert.Equal("abc123==", loaded.MspLogo.Base64); Assert.Equal("image/png", loaded.MspLogo.MimeType); } [Fact] public async Task SaveAsync_CreatesDirectoryIfNotExists() { var tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName(), "subdir"); var filePath = Path.Combine(tempDir, "branding.json"); var repo = new BrandingRepository(filePath); try { await repo.SaveAsync(new BrandingSettings()); Assert.True(File.Exists(filePath), "File must be created even when directory did not exist"); } finally { if (File.Exists(filePath)) File.Delete(filePath); if (Directory.Exists(tempDir)) Directory.Delete(tempDir, recursive: true); } } [Fact] public async Task TenantProfile_WithClientLogo_SerializesAndDeserializesCorrectly() { var logo = new LogoData { Base64 = "xyz==", MimeType = "image/jpeg" }; var profile = new TenantProfile { Name = "Contoso", TenantUrl = "https://contoso.sharepoint.com", ClientId = "client-id-123", ClientLogo = logo }; var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true }; var json = JsonSerializer.Serialize(profile, options); // Verify camelCase key exists using var doc = JsonDocument.Parse(json); Assert.True(doc.RootElement.TryGetProperty("clientLogo", out var clientLogoElem), "JSON must contain 'clientLogo' key (camelCase)"); Assert.Equal(JsonValueKind.Object, clientLogoElem.ValueKind); // Deserialize back var readOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; var loaded = JsonSerializer.Deserialize(json, readOptions); Assert.NotNull(loaded?.ClientLogo); Assert.Equal("xyz==", loaded.ClientLogo.Base64); Assert.Equal("image/jpeg", loaded.ClientLogo.MimeType); } [Fact] public async Task TenantProfile_WithoutClientLogo_SerializesWithNullAndDeserializesWithNull() { var profile = new TenantProfile { Name = "Fabrikam", TenantUrl = "https://fabrikam.sharepoint.com", ClientId = "client-id-456" }; var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true }; var json = JsonSerializer.Serialize(profile, options); // Deserialize back — ClientLogo should be null (forward compatible) var readOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; var loaded = JsonSerializer.Deserialize(json, readOptions); Assert.NotNull(loaded); Assert.Null(loaded.ClientLogo); } }