From 4f548ad761ac5e8b69a07e2b59c9542bd9b4e3d6 Mon Sep 17 00:00:00 2001 From: Andreas Ylivainio Date: Mon, 1 Dec 2025 17:00:18 +0100 Subject: [PATCH 1/6] Work in progress --- .../CommerceCatalogIntegrationTests.cs | 10 + .../CommerceCatalogNegativeTests.cs | 256 ++++++++++++++ ...izely.TestContainers.Commerce.Tests.csproj | 9 +- .../Properties/AssemblyInfo.cs | 4 + .../Startup.cs | 15 +- .../StartupTests.cs | 180 ++++++++++ .../Optimizely.TestContainers.Shared.csproj | 1 + .../OptimizelyIntegrationTestBase.cs | 35 +- .../MediaIntegrationTests.cs | 328 ++++++++++++++++++ .../Models/Pages/NewsPage.cs | 2 +- .../NewsPageIntegrationTests.cs | 102 +++--- .../Properties/AssemblyInfo.cs | 4 + .../StartPageIntegrationTests.cs | 83 +++++ 13 files changed, 949 insertions(+), 80 deletions(-) create mode 100644 src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs create mode 100644 src/Optimizely.TestContainers.Commerce.Tests/Properties/AssemblyInfo.cs create mode 100644 src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs create mode 100644 src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs create mode 100644 src/OptimizelyTestContainers.Tests/Properties/AssemblyInfo.cs create mode 100644 src/OptimizelyTestContainers.Tests/StartPageIntegrationTests.cs diff --git a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs index 89765a6..f0bc1e8 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs +++ b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs @@ -13,10 +13,20 @@ namespace Optimizely.TestContainers.Commerce.Tests; +/// +/// Integration tests for Commerce catalog functionality (catalogs, nodes, products). +/// Tests Commerce-specific content types and operations. +/// +[Collection("CommerceCatalogIntegrationTests")] public class CommerceCatalogIntegrationTests() : OptimizelyIntegrationTestBase(includeCommerce: true) { + /// + /// Configure web host with Commerce-specific Startup and services. + /// The base class provides CMS, Commerce, and Find configuration automatically. + /// protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) { + // Register the Startup class that configures Commerce services and content types webHostBuilder.UseStartup(); } diff --git a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs new file mode 100644 index 0000000..668fd56 --- /dev/null +++ b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs @@ -0,0 +1,256 @@ +using System.ComponentModel.DataAnnotations; +using System.Globalization; +using EPiServer; +using EPiServer.Commerce.Catalog.ContentTypes; +using EPiServer.Core; +using EPiServer.DataAccess; +using EPiServer.Security; +using Mediachase.Commerce; +using Mediachase.Commerce.Catalog; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Optimizely.TestContainers.Commerce.Tests.Models.Commerce; +using Optimizely.TestContainers.Shared; + +namespace Optimizely.TestContainers.Commerce.Tests; + +/// +/// Negative/edge case integration tests for Commerce catalog functionality. +/// Tests error handling, validation, and edge cases for Commerce operations. +/// +[Collection("CommerceCatalogNegativeTests")] +public class CommerceCatalogNegativeTests() : OptimizelyIntegrationTestBase(includeCommerce: true) +{ + /// + /// Configure web host with Commerce-specific Startup and services. + /// The base class provides CMS, Commerce, and Find configuration automatically. + /// + protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) + { + // Register the Startup class that configures Commerce services and content types + webHostBuilder.UseStartup(); + } + + [Fact] + public void Cannot_Load_NonExistent_Product() + { + // Arrange + var contentRepository = Services.GetRequiredService(); + var nonExistentReference = new ContentReference(99999); + + // Act & Assert + Assert.Throws(() => contentRepository.Get(nonExistentReference)); + } + + [Fact] + public void TryGet_Returns_False_For_NonExistent_Product() + { + // Arrange + var contentRepository = Services.GetRequiredService(); + var nonExistentReference = new ContentReference(99999); + + // Act + var result = contentRepository.TryGet(nonExistentReference, out var product); + + // Assert + Assert.False(result); + Assert.Null(product); + } + + [Fact] + public void Cannot_Save_Catalog_Without_Name() + { + // Arrange + var referenceConverter = Services.GetRequiredService(); + var contentRepository = Services.GetRequiredService(); + + var rootLink = referenceConverter.GetRootLink(); + + var catalog = contentRepository.GetDefault(rootLink); + catalog.Name = ""; // Empty name + catalog.DefaultCurrency = Currency.USD; + catalog.DefaultLanguage = "en"; + catalog.WeightBase = "kgs"; + catalog.LengthBase = "cm"; + + // Act & Assert + Assert.Throws(() => contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess)); + } + + [Fact] + public void Can_Delete_Product() + { + // Arrange + var referenceConverter = Services.GetRequiredService(); + var contentRepository = Services.GetRequiredService(); + + var rootLink = referenceConverter.GetRootLink(); + + var catalog = contentRepository.GetDefault(rootLink); + catalog.Name = "Delete Test Catalog"; + catalog.DefaultCurrency = Currency.USD; + catalog.DefaultLanguage = "en"; + catalog.WeightBase = "kgs"; + catalog.LengthBase = "cm"; + + var catalogReference = contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess); + + var node = contentRepository.GetDefault(catalogReference, CultureInfo.GetCultureInfo("en")); + node.Name = "Delete Test Node"; + var nodeReference = contentRepository.Save(node, SaveAction.Publish, AccessLevel.NoAccess); + + var product = contentRepository.GetDefault(nodeReference, CultureInfo.GetCultureInfo("en")); + product.Name = "To Be Deleted"; + product.Description = new XhtmlString("

Test

"); + var productReference = contentRepository.Save(product, SaveAction.Publish, AccessLevel.NoAccess); + + // Act (Delete) + contentRepository.Delete(productReference, true, AccessLevel.NoAccess); + + // Assert + var result = contentRepository.TryGet(productReference, out var deleted); + Assert.False(result); + } + + [Fact] + public void Can_Update_Existing_Product() + { + // Arrange + var referenceConverter = Services.GetRequiredService(); + var contentRepository = Services.GetRequiredService(); + + var rootLink = referenceConverter.GetRootLink(); + + var catalog = contentRepository.GetDefault(rootLink); + catalog.Name = "Update Test Catalog"; + catalog.DefaultCurrency = Currency.USD; + catalog.DefaultLanguage = "en"; + catalog.WeightBase = "kgs"; + catalog.LengthBase = "cm"; + + var catalogReference = contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess); + + var node = contentRepository.GetDefault(catalogReference, CultureInfo.GetCultureInfo("en")); + node.Name = "Update Test Node"; + var nodeReference = contentRepository.Save(node, SaveAction.Publish, AccessLevel.NoAccess); + + var product = contentRepository.GetDefault(nodeReference, CultureInfo.GetCultureInfo("en")); + product.Name = "Original Product Name"; + product.Description = new XhtmlString("

Original Description

"); + var productReference = contentRepository.Save(product, SaveAction.Publish, AccessLevel.NoAccess); + + // Act (Update) + var writable = contentRepository.Get(productReference).CreateWritableClone() as TestProduct; + writable!.Description = new XhtmlString("

Updated Description

"); + contentRepository.Save(writable, SaveAction.Publish, AccessLevel.NoAccess); + + // Assert + var loaded = contentRepository.Get(productReference); + Assert.Equal("

Updated Description

", loaded.Description?.ToHtmlString()); + } + + [Fact] + public void Can_Create_Product_As_Draft() + { + // Arrange + var referenceConverter = Services.GetRequiredService(); + var contentRepository = Services.GetRequiredService(); + + var rootLink = referenceConverter.GetRootLink(); + + var catalog = contentRepository.GetDefault(rootLink); + catalog.Name = "Draft Test Catalog"; + catalog.DefaultCurrency = Currency.USD; + catalog.DefaultLanguage = "en"; + catalog.WeightBase = "kgs"; + catalog.LengthBase = "cm"; + + var catalogReference = contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess); + + var node = contentRepository.GetDefault(catalogReference, CultureInfo.GetCultureInfo("en")); + node.Name = "Draft Test Node"; + var nodeReference = contentRepository.Save(node, SaveAction.Publish, AccessLevel.NoAccess); + + var product = contentRepository.GetDefault(nodeReference, CultureInfo.GetCultureInfo("en")); + product.Name = "Draft Product"; + product.Description = new XhtmlString("

Draft Description

"); + + // Act (Save as draft) + var productReference = contentRepository.Save(product, SaveAction.CheckOut, AccessLevel.NoAccess); + var loaded = contentRepository.Get(productReference); + + // Assert + Assert.NotNull(loaded); + Assert.Equal("Draft Product", loaded.Name); + Assert.False(loaded.Status == VersionStatus.Published); + } + + [Fact] + public void Cannot_Get_Wrong_Content_Type_From_Catalog() + { + // Arrange + var referenceConverter = Services.GetRequiredService(); + var contentRepository = Services.GetRequiredService(); + + var rootLink = referenceConverter.GetRootLink(); + + var catalog = contentRepository.GetDefault(rootLink); + catalog.Name = "Type Test Catalog"; + catalog.DefaultCurrency = Currency.USD; + catalog.DefaultLanguage = "en"; + catalog.WeightBase = "kgs"; + catalog.LengthBase = "cm"; + + var catalogReference = contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess); + + // Act & Assert - Try to get Catalog as Product + Assert.Throws(() => contentRepository.Get(catalogReference)); + } + + [Fact] + public void Can_Create_Multiple_Products_In_Same_Node() + { + // Arrange + var referenceConverter = Services.GetRequiredService(); + var contentRepository = Services.GetRequiredService(); + + var rootLink = referenceConverter.GetRootLink(); + + var catalog = contentRepository.GetDefault(rootLink); + catalog.Name = "Multiple Products Catalog"; + catalog.DefaultCurrency = Currency.USD; + catalog.DefaultLanguage = "en"; + catalog.WeightBase = "kgs"; + catalog.LengthBase = "cm"; + + var catalogReference = contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess); + + var node = contentRepository.GetDefault(catalogReference, CultureInfo.GetCultureInfo("en")); + node.Name = "Multiple Products Node"; + var nodeReference = contentRepository.Save(node, SaveAction.Publish, AccessLevel.NoAccess); + + // Create first product + var product1 = contentRepository.GetDefault(nodeReference, CultureInfo.GetCultureInfo("en")); + product1.Name = "Product 1"; + product1.Description = new XhtmlString("

Description 1

"); + var productRef1 = contentRepository.Save(product1, SaveAction.Publish, AccessLevel.NoAccess); + + // Create second product + var product2 = contentRepository.GetDefault(nodeReference, CultureInfo.GetCultureInfo("en")); + product2.Name = "Product 2"; + product2.Description = new XhtmlString("

Description 2

"); + + // Act + var productRef2 = contentRepository.Save(product2, SaveAction.Publish, AccessLevel.NoAccess); + + // Assert + var loaded1 = contentRepository.Get(productRef1); + var loaded2 = contentRepository.Get(productRef2); + + Assert.NotNull(loaded1); + Assert.NotNull(loaded2); + Assert.Equal("Product 1", loaded1.Name); + Assert.Equal("Product 2", loaded2.Name); + Assert.NotEqual(loaded1.ContentLink, loaded2.ContentLink); + } +} \ No newline at end of file diff --git a/src/Optimizely.TestContainers.Commerce.Tests/Optimizely.TestContainers.Commerce.Tests.csproj b/src/Optimizely.TestContainers.Commerce.Tests/Optimizely.TestContainers.Commerce.Tests.csproj index fc4b8b3..ce75822 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/Optimizely.TestContainers.Commerce.Tests.csproj +++ b/src/Optimizely.TestContainers.Commerce.Tests/Optimizely.TestContainers.Commerce.Tests.csproj @@ -23,6 +23,7 @@ + @@ -44,9 +45,5 @@ PreserveNewest - - - - - - + + \ No newline at end of file diff --git a/src/Optimizely.TestContainers.Commerce.Tests/Properties/AssemblyInfo.cs b/src/Optimizely.TestContainers.Commerce.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b7bf16a --- /dev/null +++ b/src/Optimizely.TestContainers.Commerce.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using Xunit; + +// We do not want integration tests to run in parallel at all to avoid deadlocks and database disposing itself multiple times +[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/src/Optimizely.TestContainers.Commerce.Tests/Startup.cs b/src/Optimizely.TestContainers.Commerce.Tests/Startup.cs index d82c17d..7d94077 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/Startup.cs +++ b/src/Optimizely.TestContainers.Commerce.Tests/Startup.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; namespace Optimizely.TestContainers.Commerce.Tests; @@ -13,12 +12,11 @@ public class Startup(IWebHostEnvironment webHostingEnvironment) { public void ConfigureServices(IServiceCollection services) { - if (webHostingEnvironment.IsDevelopment()) - { - AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(webHostingEnvironment.ContentRootPath, "App_Data")); + AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(webHostingEnvironment.ContentRootPath, "App_Data")); + + services.Configure(options => options.Enabled = false); - services.Configure(options => options.Enabled = false); - } + services.AddHttpContextAccessor(); services .AddCmsAspNetIdentity() @@ -30,11 +28,6 @@ public void ConfigureServices(IServiceCollection services) public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); diff --git a/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs new file mode 100644 index 0000000..16ffc7c --- /dev/null +++ b/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs @@ -0,0 +1,180 @@ +using EPiServer; +using EPiServer.Scheduler; +using Mediachase.Commerce.Catalog; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Moq; + +namespace Optimizely.TestContainers.Commerce.Tests; + +public class StartupTests +{ + [Fact] + public void ConfigureServices_Should_Add_CMS_And_Commerce_Services() + { + // Arrange + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + services.AddRouting(); + services.AddHttpContextAccessor(); + // Register IHttpContextFactory - required by EPiServer framework + var mockHttpContextFactory = new Mock(); + services.AddSingleton(mockHttpContextFactory.Object); + services.AddCms(); // Add CMS services + services.AddCommerce(); // Add Commerce services + + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Production); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + var startup = new Startup(mockEnvironment.Object); + + // Act + startup.ConfigureServices(services); + + // Assert - Check that core CMS services are registered + Assert.Contains(services, s => s.ServiceType == typeof(IContentRepository)); + Assert.Contains(services, s => s.ServiceType == typeof(IContentLoader)); + + // Check that Commerce services are registered + Assert.Contains(services, s => s.ServiceType == typeof(ReferenceConverter)); + } + + [Fact] + public void ConfigureServices_In_Development_Should_Configure_Scheduler_Options() + { + // Arrange + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + services.AddRouting(); + services.AddHttpContextAccessor(); + // Register IHttpContextFactory - required by EPiServer framework + var mockHttpContextFactory = new Mock(); + services.AddSingleton(mockHttpContextFactory.Object); + services.AddCms(); // Add CMS services + services.AddCommerce(); // Add Commerce services + + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Development); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + var startup = new Startup(mockEnvironment.Object); + + // Act + startup.ConfigureServices(services); + + // Assert - Check that scheduler options are configured + var serviceProvider = services.BuildServiceProvider(); + var schedulerOptions = serviceProvider.GetService>(); + Assert.NotNull(schedulerOptions); + Assert.False(schedulerOptions.Value.Enabled); + } + + [Fact] + public void ConfigureServices_In_Production_Should_Not_Configure_Scheduler_Options() + { + // Arrange + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + services.AddRouting(); + services.AddHttpContextAccessor(); + // Register IHttpContextFactory - required by EPiServer framework + var mockHttpContextFactory = new Mock(); + services.AddSingleton(mockHttpContextFactory.Object); + services.AddCms(); // Add CMS services + services.AddCommerce(); // Add Commerce services + + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Production); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + var startup = new Startup(mockEnvironment.Object); + + // Act + startup.ConfigureServices(services); + + // Assert - Scheduler should use default configuration + var serviceProvider = services.BuildServiceProvider(); + var schedulerOptions = serviceProvider.GetService>(); + + Assert.NotNull(schedulerOptions); + } + + [Fact(Skip = "Mock setup incomplete - IApplicationBuilder requires additional configuration for middleware pipeline testing")] + public void Configure_Should_Setup_Middleware_Pipeline() + { + // Arrange + var mockAppBuilder = new Mock(); + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Production); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + var startup = new Startup(mockEnvironment.Object); + + // Setup application services with routing + var services = new ServiceCollection(); + services.AddRouting(); + var serviceProvider = services.BuildServiceProvider(); + + mockAppBuilder.Setup(x => x.Use(It.IsAny>())) + .Returns(mockAppBuilder.Object); + mockAppBuilder.Setup(x => x.New()).Returns(mockAppBuilder.Object); + mockAppBuilder.Setup(x => x.ApplicationServices).Returns(serviceProvider); + + // Act + startup.Configure(mockAppBuilder.Object, mockEnvironment.Object); + + // Assert + mockAppBuilder.Verify(x => x.Use(It.IsAny>()), Times.AtLeastOnce); + } + + [Fact(Skip = "Mock setup incomplete - IApplicationBuilder requires additional configuration for developer exception page testing")] + public void Configure_In_Development_Should_Use_DeveloperExceptionPage() + { + // Arrange + var mockAppBuilder = new Mock(); + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Development); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + var startup = new Startup(mockEnvironment.Object); + + // Setup application services with routing + var services = new ServiceCollection(); + services.AddRouting(); + var serviceProvider = services.BuildServiceProvider(); + + mockAppBuilder.Setup(x => x.Use(It.IsAny>())) + .Returns(mockAppBuilder.Object); + mockAppBuilder.Setup(x => x.New()).Returns(mockAppBuilder.Object); + mockAppBuilder.Setup(x => x.ApplicationServices).Returns(serviceProvider); + + // Act + startup.Configure(mockAppBuilder.Object, mockEnvironment.Object); + + // Assert + mockAppBuilder.Verify(x => x.Use(It.IsAny>()), Times.AtLeastOnce); + } + + [Fact] + public void Startup_Constructor_Should_Accept_WebHostEnvironment() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Production); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + // Act + var startup = new Startup(mockEnvironment.Object); + + // Assert + Assert.NotNull(startup); + } +} \ No newline at end of file diff --git a/src/Optimizely.TestContainers.Shared/Optimizely.TestContainers.Shared.csproj b/src/Optimizely.TestContainers.Shared/Optimizely.TestContainers.Shared.csproj index ef25340..13d83f1 100644 --- a/src/Optimizely.TestContainers.Shared/Optimizely.TestContainers.Shared.csproj +++ b/src/Optimizely.TestContainers.Shared/Optimizely.TestContainers.Shared.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Optimizely.TestContainers.Shared/OptimizelyIntegrationTestBase.cs b/src/Optimizely.TestContainers.Shared/OptimizelyIntegrationTestBase.cs index 41b9d35..6026b3e 100644 --- a/src/Optimizely.TestContainers.Shared/OptimizelyIntegrationTestBase.cs +++ b/src/Optimizely.TestContainers.Shared/OptimizelyIntegrationTestBase.cs @@ -45,37 +45,54 @@ public virtual async Task InitializeAsync() services.Configure(o => { o.SetConnectionString(cmsDatabaseConnectionString); + }); + + if (!string.IsNullOrWhiteSpace(commerceDatabaseConnectionString)) + { + services.Configure(o => + { + o.ConnectionStrings.Add(new ConnectionStringOptions + { + ConnectionString = commerceDatabaseConnectionString, + Name = "EcfSqlConnection" + }); + }); + } }) .ConfigureAppConfiguration((context, configBuilder) => { var testSettings = new Dictionary { - ["ConnectionStrings:EPiServerDB"] = cmsDatabaseConnectionString, - ["ConnectionStrings:EcfSqlConnection"] = commerceDatabaseConnectionString, + ["ConnectionStrings:EPiServerDB"] = cmsDatabaseConnectionString }; - + + if(!string.IsNullOrWhiteSpace(commerceDatabaseConnectionString)) + { + testSettings["ConnectionStrings:EcfSqlConnection"] = commerceDatabaseConnectionString; + } + configBuilder.AddInMemoryCollection(testSettings); }); - // To configure apps separately with Cms and Commerce Startup files in separate projects + // This enables the integration tests to configure apps separately with Cms and Commerce Startup files in separate projects ConfigureWebHostBuilder(webHostBuilder); }) .ConfigureCmsDefaults() .Build(); - // Run initialization engine (simulate application startup) + Services = _host.Services; + + // Run initialization engine (simulate application startup) var initializer = _host.Services.GetRequiredService(); if (initializer.InitializationState != InitializationState.Initialized) - initializer.Initialize(); - - Services = _host.Services; + initializer.Initialize(); await _host.StartAsync(); } protected abstract void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder); - + public async Task DisposeAsync() { await _host.StopAsync(); diff --git a/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs b/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs new file mode 100644 index 0000000..8118299 --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs @@ -0,0 +1,328 @@ +using System.Reflection; +using EPiServer; +using EPiServer.Core; +using EPiServer.DataAccess; +using EPiServer.Framework.Blobs; +using EPiServer.Security; +using EPiServer.Web; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Optimizely.TestContainers.Models.Media; +using Optimizely.TestContainers.Shared; +using OptimizelyTestContainers.Tests.Models.Media; +using OptimizelyTestContainers.Tests.Models.Pages; + +namespace OptimizelyTestContainers.Tests; + +/// +/// Integration tests for media content types (ImageFile, VideoFile, GenericMedia). +/// Tests blob storage, media properties, and asset management using the unified fixture pattern. +/// +[Collection("MediaIntegrationTests")] +public class MediaIntegrationTests() : OptimizelyIntegrationTestBase(includeCommerce: false) +{ + /// + /// Configure web host with CMS-specific Startup and services. + /// The base class provides Commerce and Find configuration automatically. + /// + protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) + { + // Register the Startup class that configures CMS services and content types + webHostBuilder.UseStartup(); + + // Register additional test-specific services + webHostBuilder.ConfigureServices(services => + { + services.AddTransient(); + }); + } + + [Fact] + public void Can_Create_And_Read_ImageFile() + { + // Arrange + var repo = Services.GetRequiredService(); + var blobFactory = Services.GetRequiredService(); + + // Import test data to get StartPage + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); + + // Get Assets folder + var assetsFolder = repo.GetChildren(ContentReference.GlobalBlockFolder).FirstOrDefault() + ?? repo.GetDefault(ContentReference.GlobalBlockFolder); + + if (assetsFolder.ContentLink.ID == 0) + { + assetsFolder.Name = "Assets"; + repo.Save(assetsFolder, SaveAction.Publish, AccessLevel.NoAccess); + } + + // Create ImageFile + var imageFile = repo.GetDefault(assetsFolder.ContentLink); + imageFile.Name = "test-image.jpg"; + imageFile.Copyright = "© 2024 Test Company"; + + // Create a simple blob with test data + var blob = blobFactory.CreateBlob(imageFile.BinaryDataContainer, ".jpg"); + using (var stream = blob.OpenWrite()) + { + var testData = new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }; // JPEG header + stream.Write(testData, 0, testData.Length); + } + imageFile.BinaryData = blob; + + // Act (Save and Load ImageFile) + var savedRef = repo.Save(imageFile, SaveAction.Publish, AccessLevel.NoAccess); + var loaded = repo.Get(savedRef); + + // Assert + Assert.NotNull(loaded); + Assert.Equal("test-image.jpg", loaded.Name); + Assert.Equal("© 2024 Test Company", loaded.Copyright); + Assert.NotNull(loaded.BinaryData); + } + + [Fact] + public void Can_Create_And_Read_VideoFile() + { + // Arrange + var repo = Services.GetRequiredService(); + var blobFactory = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); + + // Get Assets folder + var assetsFolder = repo.GetChildren(ContentReference.GlobalBlockFolder).FirstOrDefault() + ?? repo.GetDefault(ContentReference.GlobalBlockFolder); + + if (assetsFolder.ContentLink.ID == 0) + { + assetsFolder.Name = "Assets"; + repo.Save(assetsFolder, SaveAction.Publish, AccessLevel.NoAccess); + } + + // Create VideoFile + var videoFile = repo.GetDefault(assetsFolder.ContentLink); + videoFile.Name = "test-video.mp4"; + videoFile.Copyright = "© 2024 Video Productions"; + videoFile.PreviewImage = ContentReference.EmptyReference; + + // Create a simple blob with test data + var blob = blobFactory.CreateBlob(videoFile.BinaryDataContainer, ".mp4"); + using (var stream = blob.OpenWrite()) + { + var testData = new byte[] { 0x00, 0x00, 0x00, 0x18 }; // MP4 signature + stream.Write(testData, 0, testData.Length); + } + videoFile.BinaryData = blob; + + // Act (Save and Load VideoFile) + var savedRef = repo.Save(videoFile, SaveAction.Publish, AccessLevel.NoAccess); + var loaded = repo.Get(savedRef); + + // Assert + Assert.NotNull(loaded); + Assert.Equal("test-video.mp4", loaded.Name); + Assert.Equal("© 2024 Video Productions", loaded.Copyright); + Assert.NotNull(loaded.BinaryData); + } + + [Fact] + public void Can_Create_And_Read_GenericMedia() + { + // Arrange + var repo = Services.GetRequiredService(); + var blobFactory = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); + + // Get Assets folder + var assetsFolder = repo.GetChildren(ContentReference.GlobalBlockFolder).FirstOrDefault() + ?? repo.GetDefault(ContentReference.GlobalBlockFolder); + + if (assetsFolder.ContentLink.ID == 0) + { + assetsFolder.Name = "Assets"; + repo.Save(assetsFolder, SaveAction.Publish, AccessLevel.NoAccess); + } + + // Create GenericMedia + var genericMedia = repo.GetDefault(assetsFolder.ContentLink); + genericMedia.Name = "test-document.pdf"; + genericMedia.Description = "Test media file description"; + + // Create a simple blob with test data + var blob = blobFactory.CreateBlob(genericMedia.BinaryDataContainer, ".pdf"); + using (var stream = blob.OpenWrite()) + { + var testData = new byte[] { 0x25, 0x50, 0x44, 0x46 }; // PDF signature + stream.Write(testData, 0, testData.Length); + } + genericMedia.BinaryData = blob; + + // Act (Save and Load GenericMedia) + var savedRef = repo.Save(genericMedia, SaveAction.Publish, AccessLevel.NoAccess); + var loaded = repo.Get(savedRef); + + // Assert + Assert.NotNull(loaded); + Assert.Equal("test-document.pdf", loaded.Name); + Assert.Equal("Test media file description", loaded.Description); + Assert.NotNull(loaded.BinaryData); + } + + [Fact] + public void ImageFile_Properties_Should_Persist_After_Save() + { + // Arrange + var repo = Services.GetRequiredService(); + var blobFactory = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); + + // Get Assets folder + var assetsFolder = repo.GetChildren(ContentReference.GlobalBlockFolder).FirstOrDefault() + ?? repo.GetDefault(ContentReference.GlobalBlockFolder); + + if (assetsFolder.ContentLink.ID == 0) + { + assetsFolder.Name = "Assets"; + repo.Save(assetsFolder, SaveAction.Publish, AccessLevel.NoAccess); + } + + var imageFile = repo.GetDefault(assetsFolder.ContentLink); + var expectedCopyright = "© Test Copyright 2024"; + imageFile.Name = "copyright-test.jpg"; + imageFile.Copyright = expectedCopyright; + + var blob = blobFactory.CreateBlob(imageFile.BinaryDataContainer, ".jpg"); + using (var stream = blob.OpenWrite()) + { + var testData = new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }; + stream.Write(testData, 0, testData.Length); + } + imageFile.BinaryData = blob; + + // Act + var savedRef = repo.Save(imageFile, SaveAction.Publish, AccessLevel.NoAccess); + var loaded = repo.Get(savedRef); + + // Assert + Assert.Equal(expectedCopyright, loaded.Copyright); + } + + [Fact(Skip = "Fails due to known issue with VideoFile PreviewImage not persisting correctly.")] + public void VideoFile_PreviewImage_Should_Persist_After_Save() + { + // Arrange + var repo = Services.GetRequiredService(); + var blobFactory = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); + + // Get Assets folder + var assetsFolder = repo.GetChildren(ContentReference.GlobalBlockFolder).FirstOrDefault() + ?? repo.GetDefault(ContentReference.GlobalBlockFolder); + + if (assetsFolder.ContentLink.ID == 0) + { + assetsFolder.Name = "Assets"; + assetsFolder.ContentLink = repo.Save(assetsFolder, SaveAction.Publish, AccessLevel.NoAccess); + } + + var videoFile = repo.GetDefault(assetsFolder.ContentLink); + var expectedPreviewImage = new ContentReference(999); + videoFile.Name = "preview-test.mp4"; + videoFile.Copyright = "Test"; + videoFile.PreviewImage = expectedPreviewImage; + + var blob = blobFactory.CreateBlob(videoFile.BinaryDataContainer, ".mp4"); + using (var stream = blob.OpenWrite()) + { + var testData = new byte[] { 0x00, 0x00, 0x00, 0x18 }; + stream.Write(testData, 0, testData.Length); + } + videoFile.BinaryData = blob; + + // Act + var savedRef = repo.Save(videoFile, SaveAction.Publish, AccessLevel.NoAccess); + var loaded = repo.Get(savedRef); + + // Assert + Assert.Equal(expectedPreviewImage, loaded.PreviewImage); + } +} \ No newline at end of file diff --git a/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPage.cs b/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPage.cs index 412db1e..afc00d0 100644 --- a/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPage.cs +++ b/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPage.cs @@ -8,5 +8,5 @@ namespace OptimizelyTestContainers.Tests.Models.Pages; DisplayName = "News Page")] public class NewsPage : PageData { - public virtual string Title { get; set; } = null!; + public virtual string? Title { get; set; } } \ No newline at end of file diff --git a/src/OptimizelyTestContainers.Tests/NewsPageIntegrationTests.cs b/src/OptimizelyTestContainers.Tests/NewsPageIntegrationTests.cs index aaff11b..2f6566f 100644 --- a/src/OptimizelyTestContainers.Tests/NewsPageIntegrationTests.cs +++ b/src/OptimizelyTestContainers.Tests/NewsPageIntegrationTests.cs @@ -1,70 +1,66 @@ -using System.Reflection; -using EPiServer; -using EPiServer.Core; -using EPiServer.DataAccess; -using EPiServer.Security; -using EPiServer.Web; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Optimizely.TestContainers.Shared; +using EPiServer.Core; +using EPiServer.DataAnnotations; using OptimizelyTestContainers.Tests.Models.Pages; namespace OptimizelyTestContainers.Tests; -public class NewsPageIntegrationTest() : OptimizelyIntegrationTestBase(includeCommerce: false) +public class NewsPageIntegrationTests { - protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) + [Fact] + public void NewsPage_Should_Have_ContentType_Attribute() + { + // Arrange & Act + var attribute = typeof(NewsPage).GetCustomAttributes(typeof(ContentTypeAttribute), false) + .FirstOrDefault() as ContentTypeAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal(Guid.Parse("7B873919-11AC-4DF4-B9E8-09F414F76164"), Guid.Parse(attribute.GUID)); + Assert.Equal("News Page", attribute.DisplayName); + } + + [Fact] + public void NewsPage_Should_Inherit_From_PageData() { - webHostBuilder.UseStartup(); + // Arrange & Act + var isPageData = typeof(PageData).IsAssignableFrom(typeof(NewsPage)); - webHostBuilder.ConfigureServices(services => - { - // Add data importer service to setup default content for the tests - services.AddTransient(); - }); + // Assert + Assert.True(isPageData); } - + [Fact] - public void Can_Create_And_Read_NewsPage() + public void Title_Property_Should_Be_Virtual() { // Arrange - var repo = Services.GetRequiredService(); - - // Import test data - var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; - var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); - var dataImporter = Services.GetRequiredService(); - - // Run data importer service to set up default content for the tests - dataImporter.Import(episerverDataFile); - - // Find StartPage from root - var startPage = repo.GetChildren(ContentReference.RootPage).First(); - - // Setup site definition - var siteDefinitionRepo = Services.GetRequiredService(); - siteDefinitionRepo.Save(new SiteDefinition() - { - Name = "TestSite", - StartPage = startPage.ContentLink, - SiteUrl = new Uri("http://localhost"), - }); + var property = typeof(NewsPage).GetProperty(nameof(NewsPage.Title)); + + // Act & Assert + Assert.NotNull(property); + Assert.True(property.GetMethod?.IsVirtual); + } + + [Fact] + public void NewsPage_Can_Be_Instantiated() + { + // Act + var newsPage = new NewsPage(); + + // Assert + Assert.NotNull(newsPage); + } - // Find first site - var allSites = siteDefinitionRepo.List(); - var site = allSites.First(); - - // Create NewsPage - var news = repo.GetDefault(site.StartPage); - news.Name = "Alien Invasion"; - news.Title = "Martians Landed in Stockholm"; + [Fact] + public void Title_Property_Can_Be_Set_And_Retrieved() + { + // Arrange + var newsPage = new NewsPage(); + var expectedTitle = "Test News Title"; - // Act (Save and Load NewsPage) - var savedRef = repo.Save(news, SaveAction.Publish, AccessLevel.NoAccess); - var loaded = repo.Get(savedRef); + // Act + newsPage.Title = expectedTitle; // Assert - Assert.NotNull(loaded); - Assert.Equal("Martians Landed in Stockholm", loaded.Title); + Assert.Equal(expectedTitle, newsPage.Title); } } \ No newline at end of file diff --git a/src/OptimizelyTestContainers.Tests/Properties/AssemblyInfo.cs b/src/OptimizelyTestContainers.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b7bf16a --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using Xunit; + +// We do not want integration tests to run in parallel at all to avoid deadlocks and database disposing itself multiple times +[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/src/OptimizelyTestContainers.Tests/StartPageIntegrationTests.cs b/src/OptimizelyTestContainers.Tests/StartPageIntegrationTests.cs new file mode 100644 index 0000000..f367a72 --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/StartPageIntegrationTests.cs @@ -0,0 +1,83 @@ +using System.ComponentModel.DataAnnotations; +using EPiServer.Core; +using EPiServer.DataAbstraction; +using EPiServer.DataAnnotations; +using OptimizelyTestContainers.Tests.Models.Pages; + +namespace OptimizelyTestContainers.Tests; + +public class StartPageTests +{ + [Fact] + public void StartPage_Should_Have_ContentType_Attribute() + { + // Arrange & Act + var attribute = typeof(StartPage).GetCustomAttributes(typeof(ContentTypeAttribute), false) + .FirstOrDefault() as ContentTypeAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal(Guid.Parse("19671657-B684-4D95-A61F-8DD4FE60D559"), Guid.Parse(attribute.GUID)); + } + + [Fact] + public void StartPage_Should_Inherit_From_PageData() + { + // Arrange & Act + var isPageData = typeof(PageData).IsAssignableFrom(typeof(StartPage)); + + // Assert + Assert.True(isPageData); + } + + [Fact] + public void MainContentArea_Property_Should_Have_Display_Attribute() + { + // Arrange + var property = typeof(StartPage).GetProperty(nameof(StartPage.MainContentArea)); + + // Act + var displayAttribute = property?.GetCustomAttributes(typeof(DisplayAttribute), false) + .FirstOrDefault() as DisplayAttribute; + + // Assert + Assert.NotNull(displayAttribute); + Assert.Equal(SystemTabNames.Content, displayAttribute.GroupName); + Assert.Equal(320, displayAttribute.Order); + } + + [Fact] + public void MainContentArea_Property_Should_Have_CultureSpecific_Attribute() + { + // Arrange + var property = typeof(StartPage).GetProperty(nameof(StartPage.MainContentArea)); + + // Act + var attribute = property?.GetCustomAttributes(typeof(CultureSpecificAttribute), false) + .FirstOrDefault(); + + // Assert + Assert.NotNull(attribute); + } + + [Fact] + public void MainContentArea_Property_Should_Be_Virtual() + { + // Arrange + var property = typeof(StartPage).GetProperty(nameof(StartPage.MainContentArea)); + + // Act & Assert + Assert.NotNull(property); + Assert.True(property.GetMethod?.IsVirtual); + } + + [Fact] + public void StartPage_Can_Be_Instantiated() + { + // Act + var startPage = new StartPage(); + + // Assert + Assert.NotNull(startPage); + } +} \ No newline at end of file From 75b9ccc59763d493ead0231c7a4f7f386f9bc71f Mon Sep 17 00:00:00 2001 From: Andreas Ylivainio Date: Mon, 1 Dec 2025 17:07:13 +0100 Subject: [PATCH 2/6] More work in progress --- .../CommerceCatalogIntegrationTests.cs | 10 - .../CommerceCatalogNegativeTests.cs | 15 +- .../Models/Commerce/TestProductTests.cs | 144 +++++++++ .../Startup.cs | 15 +- .../StartupTests.cs | 54 +--- .../MediaIntegrationTests.cs | 19 +- .../Models/Media/GenericMediaTests.cs | 65 ++++ .../Models/Media/ImageFileTests.cs | 78 +++++ .../Models/Media/VideoFileTests.cs | 120 ++++++++ .../Models/Pages/NewsPage.cs | 2 +- .../Models/Pages/NewsPageTests.cs | 66 +++++ .../Models/Pages/StartPageTests.cs | 83 ++++++ .../NewsPageIntegrationTests.cs | 102 ++++--- .../NewsPageNegativeTests.cs | 278 ++++++++++++++++++ .../OptimizelyDataImporterTests.cs | 198 +++++++++++++ .../OptimizelyTestContainers.Tests.csproj | 1 + .../StartupTests.cs | 141 +++++++++ 17 files changed, 1252 insertions(+), 139 deletions(-) create mode 100644 src/Optimizely.TestContainers.Commerce.Tests/Models/Commerce/TestProductTests.cs create mode 100644 src/OptimizelyTestContainers.Tests/Models/Media/GenericMediaTests.cs create mode 100644 src/OptimizelyTestContainers.Tests/Models/Media/ImageFileTests.cs create mode 100644 src/OptimizelyTestContainers.Tests/Models/Media/VideoFileTests.cs create mode 100644 src/OptimizelyTestContainers.Tests/Models/Pages/NewsPageTests.cs create mode 100644 src/OptimizelyTestContainers.Tests/Models/Pages/StartPageTests.cs create mode 100644 src/OptimizelyTestContainers.Tests/NewsPageNegativeTests.cs create mode 100644 src/OptimizelyTestContainers.Tests/OptimizelyDataImporterTests.cs create mode 100644 src/OptimizelyTestContainers.Tests/StartupTests.cs diff --git a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs index f0bc1e8..89765a6 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs +++ b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs @@ -13,20 +13,10 @@ namespace Optimizely.TestContainers.Commerce.Tests; -/// -/// Integration tests for Commerce catalog functionality (catalogs, nodes, products). -/// Tests Commerce-specific content types and operations. -/// -[Collection("CommerceCatalogIntegrationTests")] public class CommerceCatalogIntegrationTests() : OptimizelyIntegrationTestBase(includeCommerce: true) { - /// - /// Configure web host with Commerce-specific Startup and services. - /// The base class provides CMS, Commerce, and Find configuration automatically. - /// protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) { - // Register the Startup class that configures Commerce services and content types webHostBuilder.UseStartup(); } diff --git a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs index 668fd56..a96ffbb 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs +++ b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs @@ -1,4 +1,3 @@ -using System.ComponentModel.DataAnnotations; using System.Globalization; using EPiServer; using EPiServer.Commerce.Catalog.ContentTypes; @@ -14,20 +13,10 @@ namespace Optimizely.TestContainers.Commerce.Tests; -/// -/// Negative/edge case integration tests for Commerce catalog functionality. -/// Tests error handling, validation, and edge cases for Commerce operations. -/// -[Collection("CommerceCatalogNegativeTests")] public class CommerceCatalogNegativeTests() : OptimizelyIntegrationTestBase(includeCommerce: true) { - /// - /// Configure web host with Commerce-specific Startup and services. - /// The base class provides CMS, Commerce, and Find configuration automatically. - /// protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) { - // Register the Startup class that configures Commerce services and content types webHostBuilder.UseStartup(); } @@ -74,7 +63,7 @@ public void Cannot_Save_Catalog_Without_Name() catalog.LengthBase = "cm"; // Act & Assert - Assert.Throws(() => contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess)); + Assert.Throws(() => contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess)); } [Fact] @@ -253,4 +242,4 @@ public void Can_Create_Multiple_Products_In_Same_Node() Assert.Equal("Product 2", loaded2.Name); Assert.NotEqual(loaded1.ContentLink, loaded2.ContentLink); } -} \ No newline at end of file +} diff --git a/src/Optimizely.TestContainers.Commerce.Tests/Models/Commerce/TestProductTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/Models/Commerce/TestProductTests.cs new file mode 100644 index 0000000..8812364 --- /dev/null +++ b/src/Optimizely.TestContainers.Commerce.Tests/Models/Commerce/TestProductTests.cs @@ -0,0 +1,144 @@ +using System.ComponentModel.DataAnnotations; +using EPiServer.Commerce.Catalog.ContentTypes; +using EPiServer.Commerce.Catalog.DataAnnotations; +using EPiServer.Core; +using EPiServer.DataAbstraction; +using EPiServer.DataAnnotations; +using Optimizely.TestContainers.Commerce.Tests.Models.Commerce; + +namespace Optimizely.TestContainers.Commerce.Tests.Models.Commerce; + +public class TestProductTests +{ + [Fact] + public void TestProduct_Should_Have_CatalogContentType_Attribute() + { + // Arrange & Act + var attribute = typeof(TestProduct).GetCustomAttributes(typeof(CatalogContentTypeAttribute), false) + .FirstOrDefault() as CatalogContentTypeAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal("0B06DE9B-6AE3-40FB-909E-E718CCC260AE", attribute.GUID); + Assert.Equal("Test Product", attribute.DisplayName); + Assert.Equal("Test product for integration tests.", attribute.Description); + } + + [Fact] + public void TestProduct_Should_Inherit_From_ProductContent() + { + // Arrange & Act + var isProductContent = typeof(ProductContent).IsAssignableFrom(typeof(TestProduct)); + + // Assert + Assert.True(isProductContent); + } + + [Fact] + public void Description_Property_Should_Be_Virtual() + { + // Arrange + var property = typeof(TestProduct).GetProperty(nameof(TestProduct.Description)); + + // Act & Assert + Assert.NotNull(property); + Assert.True(property.GetMethod?.IsVirtual); + } + + [Fact] + public void Description_Property_Should_Have_Display_Attribute() + { + // Arrange + var property = typeof(TestProduct).GetProperty(nameof(TestProduct.Description)); + + // Act + var displayAttribute = property?.GetCustomAttributes(typeof(DisplayAttribute), false) + .FirstOrDefault() as DisplayAttribute; + + // Assert + Assert.NotNull(displayAttribute); + Assert.Equal("Description", displayAttribute.Name); + Assert.Equal(SystemTabNames.Content, displayAttribute.GroupName); + Assert.Equal(1, displayAttribute.Order); + } + + [Fact] + public void Description_Property_Should_Have_Searchable_Attribute() + { + // Arrange + var property = typeof(TestProduct).GetProperty(nameof(TestProduct.Description)); + + // Act + var attribute = property?.GetCustomAttributes(typeof(SearchableAttribute), false) + .FirstOrDefault(); + + // Assert + Assert.NotNull(attribute); + } + + [Fact] + public void Description_Property_Should_Have_CultureSpecific_Attribute() + { + // Arrange + var property = typeof(TestProduct).GetProperty(nameof(TestProduct.Description)); + + // Act + var attribute = property?.GetCustomAttributes(typeof(CultureSpecificAttribute), false) + .FirstOrDefault(); + + // Assert + Assert.NotNull(attribute); + } + + [Fact] + public void Description_Property_Should_Have_Tokenize_Attribute() + { + // Arrange + var property = typeof(TestProduct).GetProperty(nameof(TestProduct.Description)); + + // Act + var attribute = property?.GetCustomAttributes(typeof(TokenizeAttribute), false) + .FirstOrDefault(); + + // Assert + Assert.NotNull(attribute); + } + + [Fact] + public void Description_Property_Should_Have_IncludeInDefaultSearch_Attribute() + { + // Arrange + var property = typeof(TestProduct).GetProperty(nameof(TestProduct.Description)); + + // Act + var attribute = property?.GetCustomAttributes(typeof(IncludeInDefaultSearchAttribute), false) + .FirstOrDefault(); + + // Assert + Assert.NotNull(attribute); + } + + [Fact] + public void TestProduct_Can_Be_Instantiated() + { + // Act + var testProduct = new TestProduct(); + + // Assert + Assert.NotNull(testProduct); + } + + [Fact] + public void Description_Property_Can_Be_Set_And_Retrieved() + { + // Arrange + var testProduct = new TestProduct(); + var expectedDescription = new XhtmlString("

Test product description

"); + + // Act + testProduct.Description = expectedDescription; + + // Assert + Assert.Equal(expectedDescription, testProduct.Description); + } +} diff --git a/src/Optimizely.TestContainers.Commerce.Tests/Startup.cs b/src/Optimizely.TestContainers.Commerce.Tests/Startup.cs index 7d94077..d82c17d 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/Startup.cs +++ b/src/Optimizely.TestContainers.Commerce.Tests/Startup.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace Optimizely.TestContainers.Commerce.Tests; @@ -12,11 +13,12 @@ public class Startup(IWebHostEnvironment webHostingEnvironment) { public void ConfigureServices(IServiceCollection services) { - AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(webHostingEnvironment.ContentRootPath, "App_Data")); - - services.Configure(options => options.Enabled = false); + if (webHostingEnvironment.IsDevelopment()) + { + AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(webHostingEnvironment.ContentRootPath, "App_Data")); - services.AddHttpContextAccessor(); + services.Configure(options => options.Enabled = false); + } services .AddCmsAspNetIdentity() @@ -28,6 +30,11 @@ public void ConfigureServices(IServiceCollection services) public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); diff --git a/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs index 16ffc7c..e155ce3 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs +++ b/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs @@ -1,6 +1,6 @@ -using EPiServer; +using EPiServer.Commerce.Catalog; +using EPiServer.Core; using EPiServer.Scheduler; -using Mediachase.Commerce.Catalog; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -18,16 +18,6 @@ public void ConfigureServices_Should_Add_CMS_And_Commerce_Services() { // Arrange var services = new ServiceCollection(); - services.AddLogging(); - services.AddOptions(); - services.AddRouting(); - services.AddHttpContextAccessor(); - // Register IHttpContextFactory - required by EPiServer framework - var mockHttpContextFactory = new Mock(); - services.AddSingleton(mockHttpContextFactory.Object); - services.AddCms(); // Add CMS services - services.AddCommerce(); // Add Commerce services - var mockEnvironment = new Mock(); mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Production); mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); @@ -50,16 +40,6 @@ public void ConfigureServices_In_Development_Should_Configure_Scheduler_Options( { // Arrange var services = new ServiceCollection(); - services.AddLogging(); - services.AddOptions(); - services.AddRouting(); - services.AddHttpContextAccessor(); - // Register IHttpContextFactory - required by EPiServer framework - var mockHttpContextFactory = new Mock(); - services.AddSingleton(mockHttpContextFactory.Object); - services.AddCms(); // Add CMS services - services.AddCommerce(); // Add Commerce services - var mockEnvironment = new Mock(); mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Development); mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); @@ -81,16 +61,6 @@ public void ConfigureServices_In_Production_Should_Not_Configure_Scheduler_Optio { // Arrange var services = new ServiceCollection(); - services.AddLogging(); - services.AddOptions(); - services.AddRouting(); - services.AddHttpContextAccessor(); - // Register IHttpContextFactory - required by EPiServer framework - var mockHttpContextFactory = new Mock(); - services.AddSingleton(mockHttpContextFactory.Object); - services.AddCms(); // Add CMS services - services.AddCommerce(); // Add Commerce services - var mockEnvironment = new Mock(); mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Production); mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); @@ -107,7 +77,7 @@ public void ConfigureServices_In_Production_Should_Not_Configure_Scheduler_Optio Assert.NotNull(schedulerOptions); } - [Fact(Skip = "Mock setup incomplete - IApplicationBuilder requires additional configuration for middleware pipeline testing")] + [Fact] public void Configure_Should_Setup_Middleware_Pipeline() { // Arrange @@ -118,15 +88,10 @@ public void Configure_Should_Setup_Middleware_Pipeline() var startup = new Startup(mockEnvironment.Object); - // Setup application services with routing - var services = new ServiceCollection(); - services.AddRouting(); - var serviceProvider = services.BuildServiceProvider(); - mockAppBuilder.Setup(x => x.Use(It.IsAny>())) .Returns(mockAppBuilder.Object); mockAppBuilder.Setup(x => x.New()).Returns(mockAppBuilder.Object); - mockAppBuilder.Setup(x => x.ApplicationServices).Returns(serviceProvider); + mockAppBuilder.Setup(x => x.ApplicationServices).Returns(new ServiceCollection().BuildServiceProvider()); // Act startup.Configure(mockAppBuilder.Object, mockEnvironment.Object); @@ -135,7 +100,7 @@ public void Configure_Should_Setup_Middleware_Pipeline() mockAppBuilder.Verify(x => x.Use(It.IsAny>()), Times.AtLeastOnce); } - [Fact(Skip = "Mock setup incomplete - IApplicationBuilder requires additional configuration for developer exception page testing")] + [Fact] public void Configure_In_Development_Should_Use_DeveloperExceptionPage() { // Arrange @@ -146,15 +111,10 @@ public void Configure_In_Development_Should_Use_DeveloperExceptionPage() var startup = new Startup(mockEnvironment.Object); - // Setup application services with routing - var services = new ServiceCollection(); - services.AddRouting(); - var serviceProvider = services.BuildServiceProvider(); - mockAppBuilder.Setup(x => x.Use(It.IsAny>())) .Returns(mockAppBuilder.Object); mockAppBuilder.Setup(x => x.New()).Returns(mockAppBuilder.Object); - mockAppBuilder.Setup(x => x.ApplicationServices).Returns(serviceProvider); + mockAppBuilder.Setup(x => x.ApplicationServices).Returns(new ServiceCollection().BuildServiceProvider()); // Act startup.Configure(mockAppBuilder.Object, mockEnvironment.Object); @@ -177,4 +137,4 @@ public void Startup_Constructor_Should_Accept_WebHostEnvironment() // Assert Assert.NotNull(startup); } -} \ No newline at end of file +} diff --git a/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs b/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs index 8118299..c054748 100644 --- a/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs +++ b/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using EPiServer; using EPiServer.Core; using EPiServer.DataAccess; @@ -14,23 +14,12 @@ namespace OptimizelyTestContainers.Tests; -/// -/// Integration tests for media content types (ImageFile, VideoFile, GenericMedia). -/// Tests blob storage, media properties, and asset management using the unified fixture pattern. -/// -[Collection("MediaIntegrationTests")] public class MediaIntegrationTests() : OptimizelyIntegrationTestBase(includeCommerce: false) { - /// - /// Configure web host with CMS-specific Startup and services. - /// The base class provides Commerce and Find configuration automatically. - /// protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) { - // Register the Startup class that configures CMS services and content types webHostBuilder.UseStartup(); - // Register additional test-specific services webHostBuilder.ConfigureServices(services => { services.AddTransient(); @@ -270,7 +259,7 @@ public void ImageFile_Properties_Should_Persist_After_Save() Assert.Equal(expectedCopyright, loaded.Copyright); } - [Fact(Skip = "Fails due to known issue with VideoFile PreviewImage not persisting correctly.")] + [Fact] public void VideoFile_PreviewImage_Should_Persist_After_Save() { // Arrange @@ -301,7 +290,7 @@ public void VideoFile_PreviewImage_Should_Persist_After_Save() if (assetsFolder.ContentLink.ID == 0) { assetsFolder.Name = "Assets"; - assetsFolder.ContentLink = repo.Save(assetsFolder, SaveAction.Publish, AccessLevel.NoAccess); + repo.Save(assetsFolder, SaveAction.Publish, AccessLevel.NoAccess); } var videoFile = repo.GetDefault(assetsFolder.ContentLink); @@ -325,4 +314,4 @@ public void VideoFile_PreviewImage_Should_Persist_After_Save() // Assert Assert.Equal(expectedPreviewImage, loaded.PreviewImage); } -} \ No newline at end of file +} diff --git a/src/OptimizelyTestContainers.Tests/Models/Media/GenericMediaTests.cs b/src/OptimizelyTestContainers.Tests/Models/Media/GenericMediaTests.cs new file mode 100644 index 0000000..8b12928 --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/Models/Media/GenericMediaTests.cs @@ -0,0 +1,65 @@ +using EPiServer.Core; +using EPiServer.DataAnnotations; +using OptimizelyTestContainers.Tests.Models.Media; + +namespace OptimizelyTestContainers.Tests.Models.Media; + +public class GenericMediaTests +{ + [Fact] + public void GenericMedia_Should_Have_ContentType_Attribute() + { + // Arrange & Act + var attribute = typeof(GenericMedia).GetCustomAttributes(typeof(ContentTypeAttribute), false) + .FirstOrDefault() as ContentTypeAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal("EE3BD195-7CB0-4756-AB5F-E5E223CD9820", attribute.GUID); + } + + [Fact] + public void GenericMedia_Should_Inherit_From_MediaData() + { + // Arrange & Act + var isMediaData = typeof(MediaData).IsAssignableFrom(typeof(GenericMedia)); + + // Assert + Assert.True(isMediaData); + } + + [Fact] + public void Description_Property_Should_Be_Virtual() + { + // Arrange + var property = typeof(GenericMedia).GetProperty(nameof(GenericMedia.Description)); + + // Act & Assert + Assert.NotNull(property); + Assert.True(property.GetMethod?.IsVirtual); + } + + [Fact] + public void GenericMedia_Can_Be_Instantiated() + { + // Act + var genericMedia = new GenericMedia(); + + // Assert + Assert.NotNull(genericMedia); + } + + [Fact] + public void Description_Property_Can_Be_Set_And_Retrieved() + { + // Arrange + var genericMedia = new GenericMedia(); + var expectedDescription = "This is a test media description"; + + // Act + genericMedia.Description = expectedDescription; + + // Assert + Assert.Equal(expectedDescription, genericMedia.Description); + } +} diff --git a/src/OptimizelyTestContainers.Tests/Models/Media/ImageFileTests.cs b/src/OptimizelyTestContainers.Tests/Models/Media/ImageFileTests.cs new file mode 100644 index 0000000..6c624b6 --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/Models/Media/ImageFileTests.cs @@ -0,0 +1,78 @@ +using EPiServer.Core; +using EPiServer.DataAnnotations; +using EPiServer.Framework.DataAnnotations; +using OptimizelyTestContainers.Tests.Models.Media; + +namespace OptimizelyTestContainers.Tests.Models.Media; + +public class ImageFileTests +{ + [Fact] + public void ImageFile_Should_Have_ContentType_Attribute() + { + // Arrange & Act + var attribute = typeof(ImageFile).GetCustomAttributes(typeof(ContentTypeAttribute), false) + .FirstOrDefault() as ContentTypeAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal("0A89E464-56D4-449F-AEA8-2BF774AB8730", attribute.GUID); + } + + [Fact] + public void ImageFile_Should_Have_MediaDescriptor_Attribute() + { + // Arrange & Act + var attribute = typeof(ImageFile).GetCustomAttributes(typeof(MediaDescriptorAttribute), false) + .FirstOrDefault() as MediaDescriptorAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal("jpg,jpeg,jpe,ico,gif,bmp,png", attribute.ExtensionString); + } + + [Fact] + public void ImageFile_Should_Inherit_From_ImageData() + { + // Arrange & Act + var isImageData = typeof(ImageData).IsAssignableFrom(typeof(ImageFile)); + + // Assert + Assert.True(isImageData); + } + + [Fact] + public void Copyright_Property_Should_Be_Virtual() + { + // Arrange + var property = typeof(ImageFile).GetProperty(nameof(ImageFile.Copyright)); + + // Act & Assert + Assert.NotNull(property); + Assert.True(property.GetMethod?.IsVirtual); + } + + [Fact] + public void ImageFile_Can_Be_Instantiated() + { + // Act + var imageFile = new ImageFile(); + + // Assert + Assert.NotNull(imageFile); + } + + [Fact] + public void Copyright_Property_Can_Be_Set_And_Retrieved() + { + // Arrange + var imageFile = new ImageFile(); + var expectedCopyright = "© 2024 Test Company"; + + // Act + imageFile.Copyright = expectedCopyright; + + // Assert + Assert.Equal(expectedCopyright, imageFile.Copyright); + } +} diff --git a/src/OptimizelyTestContainers.Tests/Models/Media/VideoFileTests.cs b/src/OptimizelyTestContainers.Tests/Models/Media/VideoFileTests.cs new file mode 100644 index 0000000..862a19c --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/Models/Media/VideoFileTests.cs @@ -0,0 +1,120 @@ +using System.ComponentModel.DataAnnotations; +using EPiServer.Core; +using EPiServer.DataAnnotations; +using EPiServer.Framework.DataAnnotations; +using EPiServer.Web; +using Optimizely.TestContainers.Models.Media; + +namespace OptimizelyTestContainers.Tests.Models.Media; + +public class VideoFileTests +{ + [Fact] + public void VideoFile_Should_Have_ContentType_Attribute() + { + // Arrange & Act + var attribute = typeof(VideoFile).GetCustomAttributes(typeof(ContentTypeAttribute), false) + .FirstOrDefault() as ContentTypeAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal("85468104-E06F-47E5-A317-FC9B83D3CBA6", attribute.GUID); + } + + [Fact] + public void VideoFile_Should_Have_MediaDescriptor_Attribute() + { + // Arrange & Act + var attribute = typeof(VideoFile).GetCustomAttributes(typeof(MediaDescriptorAttribute), false) + .FirstOrDefault() as MediaDescriptorAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal("flv,mp4,webm", attribute.ExtensionString); + } + + [Fact] + public void VideoFile_Should_Inherit_From_VideoData() + { + // Arrange & Act + var isVideoData = typeof(VideoData).IsAssignableFrom(typeof(VideoFile)); + + // Assert + Assert.True(isVideoData); + } + + [Fact] + public void Copyright_Property_Should_Be_Virtual() + { + // Arrange + var property = typeof(VideoFile).GetProperty(nameof(VideoFile.Copyright)); + + // Act & Assert + Assert.NotNull(property); + Assert.True(property.GetMethod?.IsVirtual); + } + + [Fact] + public void PreviewImage_Property_Should_Be_Virtual() + { + // Arrange + var property = typeof(VideoFile).GetProperty(nameof(VideoFile.PreviewImage)); + + // Act & Assert + Assert.NotNull(property); + Assert.True(property.GetMethod?.IsVirtual); + } + + [Fact] + public void PreviewImage_Property_Should_Have_UIHint_Attribute() + { + // Arrange + var property = typeof(VideoFile).GetProperty(nameof(VideoFile.PreviewImage)); + + // Act + var attribute = property?.GetCustomAttributes(typeof(UIHintAttribute), false) + .FirstOrDefault() as UIHintAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal(UIHint.Image, attribute.UIHint); + } + + [Fact] + public void VideoFile_Can_Be_Instantiated() + { + // Act + var videoFile = new VideoFile(); + + // Assert + Assert.NotNull(videoFile); + } + + [Fact] + public void Copyright_Property_Can_Be_Set_And_Retrieved() + { + // Arrange + var videoFile = new VideoFile(); + var expectedCopyright = "© 2024 Video Productions"; + + // Act + videoFile.Copyright = expectedCopyright; + + // Assert + Assert.Equal(expectedCopyright, videoFile.Copyright); + } + + [Fact] + public void PreviewImage_Property_Can_Be_Set_And_Retrieved() + { + // Arrange + var videoFile = new VideoFile(); + var expectedReference = new ContentReference(123); + + // Act + videoFile.PreviewImage = expectedReference; + + // Assert + Assert.Equal(expectedReference, videoFile.PreviewImage); + } +} diff --git a/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPage.cs b/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPage.cs index afc00d0..412db1e 100644 --- a/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPage.cs +++ b/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPage.cs @@ -8,5 +8,5 @@ namespace OptimizelyTestContainers.Tests.Models.Pages; DisplayName = "News Page")] public class NewsPage : PageData { - public virtual string? Title { get; set; } + public virtual string Title { get; set; } = null!; } \ No newline at end of file diff --git a/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPageTests.cs b/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPageTests.cs new file mode 100644 index 0000000..b4b267d --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/Models/Pages/NewsPageTests.cs @@ -0,0 +1,66 @@ +using EPiServer.Core; +using EPiServer.DataAnnotations; +using OptimizelyTestContainers.Tests.Models.Pages; + +namespace OptimizelyTestContainers.Tests.Models.Pages; + +public class NewsPageTests +{ + [Fact] + public void NewsPage_Should_Have_ContentType_Attribute() + { + // Arrange & Act + var attribute = typeof(NewsPage).GetCustomAttributes(typeof(ContentTypeAttribute), false) + .FirstOrDefault() as ContentTypeAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal("7B873919-11AC-4DF4-B9E8-09F414F76164", attribute.GUID); + Assert.Equal("News Page", attribute.DisplayName); + } + + [Fact] + public void NewsPage_Should_Inherit_From_PageData() + { + // Arrange & Act + var isPageData = typeof(PageData).IsAssignableFrom(typeof(NewsPage)); + + // Assert + Assert.True(isPageData); + } + + [Fact] + public void Title_Property_Should_Be_Virtual() + { + // Arrange + var property = typeof(NewsPage).GetProperty(nameof(NewsPage.Title)); + + // Act & Assert + Assert.NotNull(property); + Assert.True(property.GetMethod?.IsVirtual); + } + + [Fact] + public void NewsPage_Can_Be_Instantiated() + { + // Act + var newsPage = new NewsPage(); + + // Assert + Assert.NotNull(newsPage); + } + + [Fact] + public void Title_Property_Can_Be_Set_And_Retrieved() + { + // Arrange + var newsPage = new NewsPage(); + var expectedTitle = "Test News Title"; + + // Act + newsPage.Title = expectedTitle; + + // Assert + Assert.Equal(expectedTitle, newsPage.Title); + } +} diff --git a/src/OptimizelyTestContainers.Tests/Models/Pages/StartPageTests.cs b/src/OptimizelyTestContainers.Tests/Models/Pages/StartPageTests.cs new file mode 100644 index 0000000..e1c7ac1 --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/Models/Pages/StartPageTests.cs @@ -0,0 +1,83 @@ +using System.ComponentModel.DataAnnotations; +using EPiServer.Core; +using EPiServer.DataAbstraction; +using EPiServer.DataAnnotations; +using OptimizelyTestContainers.Tests.Models.Pages; + +namespace OptimizelyTestContainers.Tests.Models.Pages; + +public class StartPageTests +{ + [Fact] + public void StartPage_Should_Have_ContentType_Attribute() + { + // Arrange & Act + var attribute = typeof(StartPage).GetCustomAttributes(typeof(ContentTypeAttribute), false) + .FirstOrDefault() as ContentTypeAttribute; + + // Assert + Assert.NotNull(attribute); + Assert.Equal("19671657-B684-4D95-A61F-8DD4FE60D559", attribute.GUID); + } + + [Fact] + public void StartPage_Should_Inherit_From_PageData() + { + // Arrange & Act + var isPageData = typeof(PageData).IsAssignableFrom(typeof(StartPage)); + + // Assert + Assert.True(isPageData); + } + + [Fact] + public void MainContentArea_Property_Should_Have_Display_Attribute() + { + // Arrange + var property = typeof(StartPage).GetProperty(nameof(StartPage.MainContentArea)); + + // Act + var displayAttribute = property?.GetCustomAttributes(typeof(DisplayAttribute), false) + .FirstOrDefault() as DisplayAttribute; + + // Assert + Assert.NotNull(displayAttribute); + Assert.Equal(SystemTabNames.Content, displayAttribute.GroupName); + Assert.Equal(320, displayAttribute.Order); + } + + [Fact] + public void MainContentArea_Property_Should_Have_CultureSpecific_Attribute() + { + // Arrange + var property = typeof(StartPage).GetProperty(nameof(StartPage.MainContentArea)); + + // Act + var attribute = property?.GetCustomAttributes(typeof(CultureSpecificAttribute), false) + .FirstOrDefault(); + + // Assert + Assert.NotNull(attribute); + } + + [Fact] + public void MainContentArea_Property_Should_Be_Virtual() + { + // Arrange + var property = typeof(StartPage).GetProperty(nameof(StartPage.MainContentArea)); + + // Act & Assert + Assert.NotNull(property); + Assert.True(property.GetMethod?.IsVirtual); + } + + [Fact] + public void StartPage_Can_Be_Instantiated() + { + // Act + var startPage = new StartPage(); + + // Assert + Assert.NotNull(startPage); + } +} diff --git a/src/OptimizelyTestContainers.Tests/NewsPageIntegrationTests.cs b/src/OptimizelyTestContainers.Tests/NewsPageIntegrationTests.cs index 2f6566f..aaff11b 100644 --- a/src/OptimizelyTestContainers.Tests/NewsPageIntegrationTests.cs +++ b/src/OptimizelyTestContainers.Tests/NewsPageIntegrationTests.cs @@ -1,66 +1,70 @@ -using EPiServer.Core; -using EPiServer.DataAnnotations; +using System.Reflection; +using EPiServer; +using EPiServer.Core; +using EPiServer.DataAccess; +using EPiServer.Security; +using EPiServer.Web; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Optimizely.TestContainers.Shared; using OptimizelyTestContainers.Tests.Models.Pages; namespace OptimizelyTestContainers.Tests; -public class NewsPageIntegrationTests +public class NewsPageIntegrationTest() : OptimizelyIntegrationTestBase(includeCommerce: false) { - [Fact] - public void NewsPage_Should_Have_ContentType_Attribute() - { - // Arrange & Act - var attribute = typeof(NewsPage).GetCustomAttributes(typeof(ContentTypeAttribute), false) - .FirstOrDefault() as ContentTypeAttribute; - - // Assert - Assert.NotNull(attribute); - Assert.Equal(Guid.Parse("7B873919-11AC-4DF4-B9E8-09F414F76164"), Guid.Parse(attribute.GUID)); - Assert.Equal("News Page", attribute.DisplayName); - } - - [Fact] - public void NewsPage_Should_Inherit_From_PageData() + protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) { - // Arrange & Act - var isPageData = typeof(PageData).IsAssignableFrom(typeof(NewsPage)); + webHostBuilder.UseStartup(); - // Assert - Assert.True(isPageData); + webHostBuilder.ConfigureServices(services => + { + // Add data importer service to setup default content for the tests + services.AddTransient(); + }); } - + [Fact] - public void Title_Property_Should_Be_Virtual() + public void Can_Create_And_Read_NewsPage() { // Arrange - var property = typeof(NewsPage).GetProperty(nameof(NewsPage.Title)); - - // Act & Assert - Assert.NotNull(property); - Assert.True(property.GetMethod?.IsVirtual); - } - - [Fact] - public void NewsPage_Can_Be_Instantiated() - { - // Act - var newsPage = new NewsPage(); - - // Assert - Assert.NotNull(newsPage); - } + var repo = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + + // Run data importer service to set up default content for the tests + dataImporter.Import(episerverDataFile); + + // Find StartPage from root + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); - [Fact] - public void Title_Property_Can_Be_Set_And_Retrieved() - { - // Arrange - var newsPage = new NewsPage(); - var expectedTitle = "Test News Title"; + // Find first site + var allSites = siteDefinitionRepo.List(); + var site = allSites.First(); + + // Create NewsPage + var news = repo.GetDefault(site.StartPage); + news.Name = "Alien Invasion"; + news.Title = "Martians Landed in Stockholm"; - // Act - newsPage.Title = expectedTitle; + // Act (Save and Load NewsPage) + var savedRef = repo.Save(news, SaveAction.Publish, AccessLevel.NoAccess); + var loaded = repo.Get(savedRef); // Assert - Assert.Equal(expectedTitle, newsPage.Title); + Assert.NotNull(loaded); + Assert.Equal("Martians Landed in Stockholm", loaded.Title); } } \ No newline at end of file diff --git a/src/OptimizelyTestContainers.Tests/NewsPageNegativeTests.cs b/src/OptimizelyTestContainers.Tests/NewsPageNegativeTests.cs new file mode 100644 index 0000000..e02c850 --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/NewsPageNegativeTests.cs @@ -0,0 +1,278 @@ +using System.Reflection; +using EPiServer; +using EPiServer.Core; +using EPiServer.DataAccess; +using EPiServer.Security; +using EPiServer.Web; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Optimizely.TestContainers.Shared; +using OptimizelyTestContainers.Tests.Models.Pages; + +namespace OptimizelyTestContainers.Tests; + +public class NewsPageNegativeTests() : OptimizelyIntegrationTestBase(includeCommerce: false) +{ + protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) + { + webHostBuilder.UseStartup(); + + webHostBuilder.ConfigureServices(services => + { + services.AddTransient(); + }); + } + + [Fact] + public void Cannot_Load_NonExistent_NewsPage() + { + // Arrange + var repo = Services.GetRequiredService(); + var nonExistentReference = new ContentReference(99999); + + // Act & Assert + Assert.Throws(() => repo.Get(nonExistentReference)); + } + + [Fact] + public void TryGet_Returns_False_For_NonExistent_Content() + { + // Arrange + var repo = Services.GetRequiredService(); + var nonExistentReference = new ContentReference(99999); + + // Act + var result = repo.TryGet(nonExistentReference, out var content); + + // Assert + Assert.False(result); + Assert.Null(content); + } + + [Fact] + public void Cannot_Save_NewsPage_Without_Name() + { + // Arrange + var repo = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); + + var allSites = siteDefinitionRepo.List(); + var site = allSites.First(); + + // Create NewsPage without name + var news = repo.GetDefault(site.StartPage); + news.Name = ""; // Empty name + news.Title = "Test Title"; + + // Act & Assert + Assert.Throws(() => repo.Save(news, SaveAction.Publish, AccessLevel.NoAccess)); + } + + [Fact] + public void Can_Save_NewsPage_As_Draft() + { + // Arrange + var repo = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); + + var allSites = siteDefinitionRepo.List(); + var site = allSites.First(); + + // Create NewsPage + var news = repo.GetDefault(site.StartPage); + news.Name = "Draft News"; + news.Title = "Draft Title"; + + // Act (Save as draft) + var savedRef = repo.Save(news, SaveAction.CheckOut, AccessLevel.NoAccess); + var loaded = repo.Get(savedRef); + + // Assert + Assert.NotNull(loaded); + Assert.Equal("Draft Title", loaded.Title); + Assert.False(loaded.Status == VersionStatus.Published); + } + + [Fact] + public void Can_Delete_NewsPage() + { + // Arrange + var repo = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); + + var allSites = siteDefinitionRepo.List(); + var site = allSites.First(); + + // Create and save NewsPage + var news = repo.GetDefault(site.StartPage); + news.Name = "To Be Deleted"; + news.Title = "Delete Test"; + var savedRef = repo.Save(news, SaveAction.Publish, AccessLevel.NoAccess); + + // Act (Delete) + repo.Delete(savedRef, true, AccessLevel.NoAccess); + + // Assert + var result = repo.TryGet(savedRef, out var deleted); + Assert.False(result); + } + + [Fact] + public void Can_Create_Multiple_NewsPages_With_Same_Title() + { + // Arrange + var repo = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); + + var allSites = siteDefinitionRepo.List(); + var site = allSites.First(); + + // Create first NewsPage + var news1 = repo.GetDefault(site.StartPage); + news1.Name = "Duplicate Test 1"; + news1.Title = "Same Title"; + var savedRef1 = repo.Save(news1, SaveAction.Publish, AccessLevel.NoAccess); + + // Create second NewsPage with same title + var news2 = repo.GetDefault(site.StartPage); + news2.Name = "Duplicate Test 2"; + news2.Title = "Same Title"; + + // Act + var savedRef2 = repo.Save(news2, SaveAction.Publish, AccessLevel.NoAccess); + + // Assert - Both should be saved successfully + var loaded1 = repo.Get(savedRef1); + var loaded2 = repo.Get(savedRef2); + + Assert.NotNull(loaded1); + Assert.NotNull(loaded2); + Assert.Equal("Same Title", loaded1.Title); + Assert.Equal("Same Title", loaded2.Title); + Assert.NotEqual(loaded1.ContentLink, loaded2.ContentLink); + } + + [Fact] + public void Can_Update_Existing_NewsPage() + { + // Arrange + var repo = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Setup site definition + var siteDefinitionRepo = Services.GetRequiredService(); + siteDefinitionRepo.Save(new SiteDefinition() + { + Name = "TestSite", + StartPage = startPage.ContentLink, + SiteUrl = new Uri("http://localhost"), + }); + + var allSites = siteDefinitionRepo.List(); + var site = allSites.First(); + + // Create NewsPage + var news = repo.GetDefault(site.StartPage); + news.Name = "Original Name"; + news.Title = "Original Title"; + var savedRef = repo.Save(news, SaveAction.Publish, AccessLevel.NoAccess); + + // Act (Update) + var writable = repo.Get(savedRef).CreateWritableClone() as NewsPage; + writable!.Title = "Updated Title"; + repo.Save(writable, SaveAction.Publish, AccessLevel.NoAccess); + + // Assert + var loaded = repo.Get(savedRef); + Assert.Equal("Updated Title", loaded.Title); + } + + [Fact] + public void Cannot_Get_Wrong_Content_Type() + { + // Arrange + var repo = Services.GetRequiredService(); + + // Import test data + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); + var dataImporter = Services.GetRequiredService(); + dataImporter.Import(episerverDataFile); + + var startPage = repo.GetChildren(ContentReference.RootPage).First(); + + // Act & Assert - Try to get StartPage as NewsPage + Assert.Throws(() => repo.Get(startPage.ContentLink)); + } +} diff --git a/src/OptimizelyTestContainers.Tests/OptimizelyDataImporterTests.cs b/src/OptimizelyTestContainers.Tests/OptimizelyDataImporterTests.cs new file mode 100644 index 0000000..562eb34 --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/OptimizelyDataImporterTests.cs @@ -0,0 +1,198 @@ +using EPiServer.Core; +using EPiServer.Core.Transfer; +using EPiServer.Enterprise; +using Microsoft.Extensions.Logging; +using Moq; + +namespace OptimizelyTestContainers.Tests; + +public class OptimizelyDataImporterTests +{ + private readonly Mock> _mockLogger; + private readonly Mock _mockDataImporter; + private readonly Mock _mockContentEvents; + + public OptimizelyDataImporterTests() + { + _mockLogger = new Mock>(); + _mockDataImporter = new Mock(); + _mockContentEvents = new Mock(); + } + + [Fact] + public void Import_Should_Subscribe_To_PublishedContent_Event() + { + // Arrange + var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); + var importLog = new TransferLog(); + var tempFile = CreateTempImportFile(); + + _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(importLog); + + // Act + importer.Import(tempFile); + + // Assert + _mockContentEvents.VerifyAdd(x => x.PublishedContent += It.IsAny>(), Times.Once); + + // Cleanup + File.Delete(tempFile); + } + + [Fact] + public void Import_Should_Call_DataImporter_With_Correct_Options() + { + // Arrange + var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); + var importLog = new TransferLog(); + var tempFile = CreateTempImportFile(); + + _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(importLog); + + // Act + importer.Import(tempFile); + + // Assert + _mockDataImporter.Verify(x => x.Import( + It.IsAny(), + ContentReference.RootPage, + It.Is(o => + o.KeepIdentity == true && + o.EnsureContentNameUniqueness == false && + o.ValidateDestination == true && + o.TransferType == TypeOfTransfer.Importing && + o.AutoCloseStream == true + )), Times.Once); + + // Cleanup + File.Delete(tempFile); + } + + [Fact] + public void Import_Should_Throw_Exception_When_Errors_Present() + { + // Arrange + var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); + var importLog = new TransferLog(); + importLog.AddError("Test error message"); + var tempFile = CreateTempImportFile(); + + _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(importLog); + + // Act & Assert + var exception = Assert.Throws(() => importer.Import(tempFile)); + Assert.Equal("Test error message", exception.Message); + + // Cleanup + File.Delete(tempFile); + } + + [Fact] + public void Import_Should_Log_Warnings_When_Present() + { + // Arrange + var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); + var importLog = new TransferLog(); + importLog.AddWarning("Test warning 1"); + importLog.AddWarning("Test warning 2"); + var tempFile = CreateTempImportFile(); + + _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(importLog); + + // Act + importer.Import(tempFile); + + // Assert + _mockLogger.Verify( + x => x.Log( + LogLevel.Warning, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains("Test warning 1")), + It.IsAny(), + It.IsAny>()), + Times.Once); + + _mockLogger.Verify( + x => x.Log( + LogLevel.Warning, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains("Test warning 2")), + It.IsAny(), + It.IsAny>()), + Times.Once); + + // Cleanup + File.Delete(tempFile); + } + + [Fact] + public void Import_Should_Not_Log_Warnings_When_None_Present() + { + // Arrange + var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); + var importLog = new TransferLog(); + var tempFile = CreateTempImportFile(); + + _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(importLog); + + // Act + importer.Import(tempFile); + + // Assert + _mockLogger.Verify( + x => x.Log( + LogLevel.Warning, + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>()), + Times.Never); + + // Cleanup + File.Delete(tempFile); + } + + [Fact] + public void Import_Should_Throw_FileNotFoundException_When_File_Does_Not_Exist() + { + // Arrange + var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); + var nonExistentFile = Path.Combine(Path.GetTempPath(), $"nonexistent_{Guid.NewGuid()}.episerverdata"); + + // Act & Assert + Assert.Throws(() => importer.Import(nonExistentFile)); + } + + [Fact] + public void Import_Should_Complete_Successfully_With_No_Errors_Or_Warnings() + { + // Arrange + var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); + var importLog = new TransferLog(); + var tempFile = CreateTempImportFile(); + + _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(importLog); + + // Act + var exception = Record.Exception(() => importer.Import(tempFile)); + + // Assert + Assert.Null(exception); + + // Cleanup + File.Delete(tempFile); + } + + private string CreateTempImportFile() + { + var tempFile = Path.Combine(Path.GetTempPath(), $"test_{Guid.NewGuid()}.episerverdata"); + File.WriteAllText(tempFile, "test content"); + return tempFile; + } +} diff --git a/src/OptimizelyTestContainers.Tests/OptimizelyTestContainers.Tests.csproj b/src/OptimizelyTestContainers.Tests/OptimizelyTestContainers.Tests.csproj index 4702198..b653720 100644 --- a/src/OptimizelyTestContainers.Tests/OptimizelyTestContainers.Tests.csproj +++ b/src/OptimizelyTestContainers.Tests/OptimizelyTestContainers.Tests.csproj @@ -22,6 +22,7 @@
+ diff --git a/src/OptimizelyTestContainers.Tests/StartupTests.cs b/src/OptimizelyTestContainers.Tests/StartupTests.cs new file mode 100644 index 0000000..4a5d8d1 --- /dev/null +++ b/src/OptimizelyTestContainers.Tests/StartupTests.cs @@ -0,0 +1,141 @@ +using EPiServer.Cms.UI.AspNetIdentity; +using EPiServer.Core; +using EPiServer.DataAccess; +using EPiServer.Scheduler; +using EPiServer.Web.Routing; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Moq; + +namespace OptimizelyTestContainers.Tests; + +public class StartupTests +{ + [Fact] + public void ConfigureServices_Should_Add_CMS_Services() + { + // Arrange + var services = new ServiceCollection(); + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Production); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + var startup = new Startup(mockEnvironment.Object); + + // Act + startup.ConfigureServices(services); + + // Assert - Check that core CMS services are registered + Assert.Contains(services, s => s.ServiceType == typeof(IContentRepository)); + Assert.Contains(services, s => s.ServiceType == typeof(IContentLoader)); + } + + [Fact] + public void ConfigureServices_In_Development_Should_Configure_Scheduler_Options() + { + // Arrange + var services = new ServiceCollection(); + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Development); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + var startup = new Startup(mockEnvironment.Object); + + // Act + startup.ConfigureServices(services); + + // Assert - Check that scheduler options are configured + var serviceProvider = services.BuildServiceProvider(); + var schedulerOptions = serviceProvider.GetService>(); + Assert.NotNull(schedulerOptions); + Assert.False(schedulerOptions.Value.Enabled); + } + + [Fact] + public void ConfigureServices_In_Production_Should_Not_Configure_Scheduler_Options() + { + // Arrange + var services = new ServiceCollection(); + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Production); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + var startup = new Startup(mockEnvironment.Object); + + // Act + startup.ConfigureServices(services); + + // Assert - Scheduler should use default configuration (enabled) + var serviceProvider = services.BuildServiceProvider(); + var schedulerOptions = serviceProvider.GetService>(); + + // In production, scheduler is not explicitly disabled, so it should be enabled by default + Assert.NotNull(schedulerOptions); + } + + [Fact] + public void Configure_Should_Setup_Middleware_Pipeline() + { + // Arrange + var mockAppBuilder = new Mock(); + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Production); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + var startup = new Startup(mockEnvironment.Object); + + // Setup for middleware chain + mockAppBuilder.Setup(x => x.Use(It.IsAny>())) + .Returns(mockAppBuilder.Object); + mockAppBuilder.Setup(x => x.New()).Returns(mockAppBuilder.Object); + mockAppBuilder.Setup(x => x.ApplicationServices).Returns(new ServiceCollection().BuildServiceProvider()); + + // Act + startup.Configure(mockAppBuilder.Object, mockEnvironment.Object); + + // Assert + mockAppBuilder.Verify(x => x.Use(It.IsAny>()), Times.AtLeastOnce); + } + + [Fact] + public void Configure_In_Development_Should_Use_DeveloperExceptionPage() + { + // Arrange + var mockAppBuilder = new Mock(); + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Development); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + var startup = new Startup(mockEnvironment.Object); + + mockAppBuilder.Setup(x => x.Use(It.IsAny>())) + .Returns(mockAppBuilder.Object); + mockAppBuilder.Setup(x => x.New()).Returns(mockAppBuilder.Object); + mockAppBuilder.Setup(x => x.ApplicationServices).Returns(new ServiceCollection().BuildServiceProvider()); + + // Act + startup.Configure(mockAppBuilder.Object, mockEnvironment.Object); + + // Assert - Middleware should be added + mockAppBuilder.Verify(x => x.Use(It.IsAny>()), Times.AtLeastOnce); + } + + [Fact] + public void Startup_Constructor_Should_Accept_WebHostEnvironment() + { + // Arrange + var mockEnvironment = new Mock(); + mockEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Production); + mockEnvironment.Setup(x => x.ContentRootPath).Returns(Path.GetTempPath()); + + // Act + var startup = new Startup(mockEnvironment.Object); + + // Assert + Assert.NotNull(startup); + } +} From aa55942a397ec04abddc448e59004b90dad02072 Mon Sep 17 00:00:00 2001 From: Andreas Ylivainio Date: Mon, 1 Dec 2025 17:13:04 +0100 Subject: [PATCH 3/6] It builds! --- .../StartupTests.cs | 4 +- .../OptimizelyDataImporterTests.cs | 52 +++++++++---------- .../StartupTests.cs | 5 +- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs index e155ce3..29193dc 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs +++ b/src/Optimizely.TestContainers.Commerce.Tests/StartupTests.cs @@ -1,6 +1,6 @@ -using EPiServer.Commerce.Catalog; -using EPiServer.Core; +using EPiServer; using EPiServer.Scheduler; +using Mediachase.Commerce.Catalog; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; diff --git a/src/OptimizelyTestContainers.Tests/OptimizelyDataImporterTests.cs b/src/OptimizelyTestContainers.Tests/OptimizelyDataImporterTests.cs index 562eb34..d00b221 100644 --- a/src/OptimizelyTestContainers.Tests/OptimizelyDataImporterTests.cs +++ b/src/OptimizelyTestContainers.Tests/OptimizelyDataImporterTests.cs @@ -8,33 +8,26 @@ namespace OptimizelyTestContainers.Tests; public class OptimizelyDataImporterTests { - private readonly Mock> _mockLogger; - private readonly Mock _mockDataImporter; - private readonly Mock _mockContentEvents; - - public OptimizelyDataImporterTests() - { - _mockLogger = new Mock>(); - _mockDataImporter = new Mock(); - _mockContentEvents = new Mock(); - } + private readonly Mock> _mockLogger = new(); + private readonly Mock _mockDataImporter = new(); + private readonly Mock _mockContentEvents = new(); [Fact] public void Import_Should_Subscribe_To_PublishedContent_Event() { // Arrange var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); - var importLog = new TransferLog(); + var mockImportLog = CreateMockImportLog(); var tempFile = CreateTempImportFile(); _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(importLog); + .Returns(mockImportLog); // Act importer.Import(tempFile); // Assert - _mockContentEvents.VerifyAdd(x => x.PublishedContent += It.IsAny>(), Times.Once); + _mockContentEvents.VerifyAdd(x => x.PublishedContent += It.IsAny>(), Times.Once); // Cleanup File.Delete(tempFile); @@ -45,11 +38,11 @@ public void Import_Should_Call_DataImporter_With_Correct_Options() { // Arrange var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); - var importLog = new TransferLog(); + var mockImportLog = CreateMockImportLog(); var tempFile = CreateTempImportFile(); _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(importLog); + .Returns(mockImportLog); // Act importer.Import(tempFile); @@ -75,12 +68,11 @@ public void Import_Should_Throw_Exception_When_Errors_Present() { // Arrange var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); - var importLog = new TransferLog(); - importLog.AddError("Test error message"); + var mockImportLog = CreateMockImportLog(errors: new List { "Test error message" }); var tempFile = CreateTempImportFile(); _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(importLog); + .Returns(mockImportLog); // Act & Assert var exception = Assert.Throws(() => importer.Import(tempFile)); @@ -95,13 +87,11 @@ public void Import_Should_Log_Warnings_When_Present() { // Arrange var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); - var importLog = new TransferLog(); - importLog.AddWarning("Test warning 1"); - importLog.AddWarning("Test warning 2"); + var mockImportLog = CreateMockImportLog(warnings: new List { "Test warning 1", "Test warning 2" }); var tempFile = CreateTempImportFile(); _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(importLog); + .Returns(mockImportLog); // Act importer.Import(tempFile); @@ -134,11 +124,11 @@ public void Import_Should_Not_Log_Warnings_When_None_Present() { // Arrange var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); - var importLog = new TransferLog(); + var mockImportLog = CreateMockImportLog(); var tempFile = CreateTempImportFile(); _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(importLog); + .Returns(mockImportLog); // Act importer.Import(tempFile); @@ -173,11 +163,11 @@ public void Import_Should_Complete_Successfully_With_No_Errors_Or_Warnings() { // Arrange var importer = new OptimizelyDataImporter(_mockLogger.Object, _mockDataImporter.Object, _mockContentEvents.Object); - var importLog = new TransferLog(); + var mockImportLog = CreateMockImportLog(); var tempFile = CreateTempImportFile(); _mockDataImporter.Setup(x => x.Import(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(importLog); + .Returns(mockImportLog); // Act var exception = Record.Exception(() => importer.Import(tempFile)); @@ -195,4 +185,12 @@ private string CreateTempImportFile() File.WriteAllText(tempFile, "test content"); return tempFile; } -} + + private ITransferLog CreateMockImportLog(List? errors = null, List? warnings = null) + { + var mockLog = new Mock(); + mockLog.SetupGet(x => x.Errors).Returns(errors ?? []); + mockLog.SetupGet(x => x.Warnings).Returns(warnings ?? []); + return mockLog.Object; + } +} \ No newline at end of file diff --git a/src/OptimizelyTestContainers.Tests/StartupTests.cs b/src/OptimizelyTestContainers.Tests/StartupTests.cs index 4a5d8d1..bf07e4b 100644 --- a/src/OptimizelyTestContainers.Tests/StartupTests.cs +++ b/src/OptimizelyTestContainers.Tests/StartupTests.cs @@ -1,8 +1,5 @@ -using EPiServer.Cms.UI.AspNetIdentity; -using EPiServer.Core; -using EPiServer.DataAccess; +using EPiServer; using EPiServer.Scheduler; -using EPiServer.Web.Routing; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; From 33f49fadabbb0c9d4b2e093c00e4719ade9bd2ef Mon Sep 17 00:00:00 2001 From: Andreas Ylivainio Date: Wed, 3 Dec 2025 10:09:33 +0100 Subject: [PATCH 4/6] Update tests from other repo --- .../CommerceCatalogIntegrationTests.cs | 10 ++++++++++ .../CommerceCatalogNegativeTests.cs | 15 +++++++++++++-- .../Models/Commerce/TestProductTests.cs | 5 ++--- .../MediaIntegrationTests.cs | 19 +++++++++++++++---- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs index 89765a6..f0bc1e8 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs +++ b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs @@ -13,10 +13,20 @@ namespace Optimizely.TestContainers.Commerce.Tests; +/// +/// Integration tests for Commerce catalog functionality (catalogs, nodes, products). +/// Tests Commerce-specific content types and operations. +/// +[Collection("CommerceCatalogIntegrationTests")] public class CommerceCatalogIntegrationTests() : OptimizelyIntegrationTestBase(includeCommerce: true) { + /// + /// Configure web host with Commerce-specific Startup and services. + /// The base class provides CMS, Commerce, and Find configuration automatically. + /// protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) { + // Register the Startup class that configures Commerce services and content types webHostBuilder.UseStartup(); } diff --git a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs index a96ffbb..8ff1143 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs +++ b/src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogNegativeTests.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using System.Globalization; using EPiServer; using EPiServer.Commerce.Catalog.ContentTypes; @@ -13,10 +14,20 @@ namespace Optimizely.TestContainers.Commerce.Tests; +/// +/// Negative/edge case integration tests for Commerce catalog functionality. +/// Tests error handling, validation, and edge cases for Commerce operations. +/// +[Collection("CommerceCatalogNegativeTests")] public class CommerceCatalogNegativeTests() : OptimizelyIntegrationTestBase(includeCommerce: true) { + /// + /// Configure web host with Commerce-specific Startup and services. + /// The base class provides CMS, Commerce, and Find configuration automatically. + /// protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) { + // Register the Startup class that configures Commerce services and content types webHostBuilder.UseStartup(); } @@ -63,7 +74,7 @@ public void Cannot_Save_Catalog_Without_Name() catalog.LengthBase = "cm"; // Act & Assert - Assert.Throws(() => contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess)); + Assert.Throws(() => contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess)); } [Fact] @@ -242,4 +253,4 @@ public void Can_Create_Multiple_Products_In_Same_Node() Assert.Equal("Product 2", loaded2.Name); Assert.NotEqual(loaded1.ContentLink, loaded2.ContentLink); } -} +} \ No newline at end of file diff --git a/src/Optimizely.TestContainers.Commerce.Tests/Models/Commerce/TestProductTests.cs b/src/Optimizely.TestContainers.Commerce.Tests/Models/Commerce/TestProductTests.cs index 8812364..ee6b5e1 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/Models/Commerce/TestProductTests.cs +++ b/src/Optimizely.TestContainers.Commerce.Tests/Models/Commerce/TestProductTests.cs @@ -4,7 +4,6 @@ using EPiServer.Core; using EPiServer.DataAbstraction; using EPiServer.DataAnnotations; -using Optimizely.TestContainers.Commerce.Tests.Models.Commerce; namespace Optimizely.TestContainers.Commerce.Tests.Models.Commerce; @@ -19,7 +18,7 @@ public void TestProduct_Should_Have_CatalogContentType_Attribute() // Assert Assert.NotNull(attribute); - Assert.Equal("0B06DE9B-6AE3-40FB-909E-E718CCC260AE", attribute.GUID); + Assert.Equal(Guid.Parse("0B06DE9B-6AE3-40FB-909E-E718CCC260AE"), Guid.Parse(attribute.GUID)); Assert.Equal("Test Product", attribute.DisplayName); Assert.Equal("Test product for integration tests.", attribute.Description); } @@ -141,4 +140,4 @@ public void Description_Property_Can_Be_Set_And_Retrieved() // Assert Assert.Equal(expectedDescription, testProduct.Description); } -} +} \ No newline at end of file diff --git a/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs b/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs index c054748..f81bda3 100644 --- a/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs +++ b/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs @@ -14,12 +14,23 @@ namespace OptimizelyTestContainers.Tests; -public class MediaIntegrationTests() : OptimizelyIntegrationTestBase(includeCommerce: false) +/// +/// Integration tests for media content types (ImageFile, VideoFile, GenericMedia). +/// Tests blob storage, media properties, and asset management using the unified fixture pattern. +/// +[Collection("MediaIntegrationTests")] +public class MediaIntegrationTests() : OptimizelyIntegrationTestBase(includeCommerce: true) { + /// + /// Configure web host with CMS-specific Startup and services. + /// The base class provides Commerce and Find configuration automatically. + /// protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder) { + // Register the Startup class that configures CMS services and content types webHostBuilder.UseStartup(); + // Register additional test-specific services webHostBuilder.ConfigureServices(services => { services.AddTransient(); @@ -259,7 +270,7 @@ public void ImageFile_Properties_Should_Persist_After_Save() Assert.Equal(expectedCopyright, loaded.Copyright); } - [Fact] + [Fact(Skip = "Fails due to known issue with VideoFile PreviewImage not persisting correctly.")] public void VideoFile_PreviewImage_Should_Persist_After_Save() { // Arrange @@ -290,7 +301,7 @@ public void VideoFile_PreviewImage_Should_Persist_After_Save() if (assetsFolder.ContentLink.ID == 0) { assetsFolder.Name = "Assets"; - repo.Save(assetsFolder, SaveAction.Publish, AccessLevel.NoAccess); + assetsFolder.ContentLink = repo.Save(assetsFolder, SaveAction.Publish, AccessLevel.NoAccess); } var videoFile = repo.GetDefault(assetsFolder.ContentLink); @@ -314,4 +325,4 @@ public void VideoFile_PreviewImage_Should_Persist_After_Save() // Assert Assert.Equal(expectedPreviewImage, loaded.PreviewImage); } -} +} \ No newline at end of file From fd9f21958f2f087b91f728259838e255c77edea0 Mon Sep 17 00:00:00 2001 From: Andreas Ylivainio Date: Wed, 3 Dec 2025 10:14:53 +0100 Subject: [PATCH 5/6] Use slnx for GitHub workflow --- .github/workflows/dotnet.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 5e1bb65..dc41bd3 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -24,8 +24,8 @@ jobs: with: dotnet-version: 8.0.x - name: Restore dependencies - run: dotnet restore + run: dotnet restore OptimizelyTestContainers.slnx - name: Build - run: dotnet build --no-restore + run: dotnet build OptimizelyTestContainers.slnx --no-restore - name: Test - run: dotnet test --no-build --verbosity normal + run: dotnet test OptimizelyTestContainers.slnx --no-build --verbosity normal From c5f55aba2767b6bab8994a6a0864832a9a679982 Mon Sep 17 00:00:00 2001 From: Andreas Ylivainio Date: Wed, 3 Dec 2025 11:20:08 +0100 Subject: [PATCH 6/6] Upgrade to .NET 10 - Update to use .NET 10 - Update all packages - Remove excessive logging --- ...timizely.TestContainers.Commerce.Tests.csproj | 12 ++++++------ .../Optimizely.TestContainers.Shared.csproj | 10 ++++++---- .../MediaIntegrationTests.cs | 2 +- .../NewsPageNegativeTests.cs | 9 +++++++++ .../OptimizelyDataImporter.cs | 16 ++++++++++------ .../OptimizelyTestContainers.Tests.csproj | 10 +++++----- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/Optimizely.TestContainers.Commerce.Tests/Optimizely.TestContainers.Commerce.Tests.csproj b/src/Optimizely.TestContainers.Commerce.Tests/Optimizely.TestContainers.Commerce.Tests.csproj index ce75822..fb0add9 100644 --- a/src/Optimizely.TestContainers.Commerce.Tests/Optimizely.TestContainers.Commerce.Tests.csproj +++ b/src/Optimizely.TestContainers.Commerce.Tests/Optimizely.TestContainers.Commerce.Tests.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable enable false @@ -20,13 +20,13 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Optimizely.TestContainers.Shared/Optimizely.TestContainers.Shared.csproj b/src/Optimizely.TestContainers.Shared/Optimizely.TestContainers.Shared.csproj index 13d83f1..40de16f 100644 --- a/src/Optimizely.TestContainers.Shared/Optimizely.TestContainers.Shared.csproj +++ b/src/Optimizely.TestContainers.Shared/Optimizely.TestContainers.Shared.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable enable false @@ -16,10 +16,12 @@ - - + + + - + + diff --git a/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs b/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs index f81bda3..ce7d45a 100644 --- a/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs +++ b/src/OptimizelyTestContainers.Tests/MediaIntegrationTests.cs @@ -270,7 +270,7 @@ public void ImageFile_Properties_Should_Persist_After_Save() Assert.Equal(expectedCopyright, loaded.Copyright); } - [Fact(Skip = "Fails due to known issue with VideoFile PreviewImage not persisting correctly.")] + [Fact] public void VideoFile_PreviewImage_Should_Persist_After_Save() { // Arrange diff --git a/src/OptimizelyTestContainers.Tests/NewsPageNegativeTests.cs b/src/OptimizelyTestContainers.Tests/NewsPageNegativeTests.cs index e02c850..34ad44c 100644 --- a/src/OptimizelyTestContainers.Tests/NewsPageNegativeTests.cs +++ b/src/OptimizelyTestContainers.Tests/NewsPageNegativeTests.cs @@ -268,6 +268,15 @@ public void Cannot_Get_Wrong_Content_Type() var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; var episerverDataFile = Path.Combine(basePath, "DefaultSiteContent.episerverdata"); var dataImporter = Services.GetRequiredService(); + /* TODO: + OptimizelyTestContainers.Tests.NewsPageNegativeTests.Cannot_Save_NewsPage_Without_Name (10s 443ms): Error Message: + System.Exception : Failed to Deserialize object to Dynamic Data Store. BinaryFormatter serialization and deserial + ization have been removed. See https://aka.ms/binaryformatter for more information. + Stack Trace: + at OptimizelyTestContainers.Tests.OptimizelyDataImporter.Import(String importFilePath) in D:\Git\Valtech\Optimi + zelyTestContainers\src\OptimizelyTestContainers.Tests\OptimizelyDataImporter.cs:line 35 + + */ dataImporter.Import(episerverDataFile); var startPage = repo.GetChildren(ContentReference.RootPage).First(); diff --git a/src/OptimizelyTestContainers.Tests/OptimizelyDataImporter.cs b/src/OptimizelyTestContainers.Tests/OptimizelyDataImporter.cs index 79dd2bf..9e33f4a 100644 --- a/src/OptimizelyTestContainers.Tests/OptimizelyDataImporter.cs +++ b/src/OptimizelyTestContainers.Tests/OptimizelyDataImporter.cs @@ -9,10 +9,11 @@ public class OptimizelyDataImporter(ILogger logger, IDat { public void Import(string importFilePath) { + /* contentEvents.PublishedContent += (s, e) => { logger.LogInformation("Published: {ContentName}", e.Content.Name); - }; + };*/ using var stream = File.OpenRead(importFilePath); @@ -28,22 +29,25 @@ public void Import(string importFilePath) var importLog = dataImporter.Import(stream, ContentReference.RootPage, options); var errors = importLog.Errors.ToList(); - var warnings = importLog.Warnings.ToList(); + - if (errors.Count != 0) + if (errors.Count > 0) { - throw new Exception(errors.First()); + throw new AggregateException(errors.Select(err => new Exception(err))); } + /* + + var warnings = importLog.Warnings.ToList(); if (warnings.Count == 0) { return; } - + foreach (var warning in warnings) { logger.LogWarning(warning); Console.WriteLine(warning); - } + }*/ } } \ No newline at end of file diff --git a/src/OptimizelyTestContainers.Tests/OptimizelyTestContainers.Tests.csproj b/src/OptimizelyTestContainers.Tests/OptimizelyTestContainers.Tests.csproj index b653720..9a33a34 100644 --- a/src/OptimizelyTestContainers.Tests/OptimizelyTestContainers.Tests.csproj +++ b/src/OptimizelyTestContainers.Tests/OptimizelyTestContainers.Tests.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable enable false @@ -20,12 +20,12 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all