Skip to content

Commit 4f548ad

Browse files
Work in progress
1 parent 065cdd7 commit 4f548ad

File tree

13 files changed

+949
-80
lines changed

13 files changed

+949
-80
lines changed

src/Optimizely.TestContainers.Commerce.Tests/CommerceCatalogIntegrationTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,20 @@
1313

1414
namespace Optimizely.TestContainers.Commerce.Tests;
1515

16+
/// <summary>
17+
/// Integration tests for Commerce catalog functionality (catalogs, nodes, products).
18+
/// Tests Commerce-specific content types and operations.
19+
/// </summary>
20+
[Collection("CommerceCatalogIntegrationTests")]
1621
public class CommerceCatalogIntegrationTests() : OptimizelyIntegrationTestBase(includeCommerce: true)
1722
{
23+
/// <summary>
24+
/// Configure web host with Commerce-specific Startup and services.
25+
/// The base class provides CMS, Commerce, and Find configuration automatically.
26+
/// </summary>
1827
protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder)
1928
{
29+
// Register the Startup class that configures Commerce services and content types
2030
webHostBuilder.UseStartup<Startup>();
2131
}
2232

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using System.Globalization;
3+
using EPiServer;
4+
using EPiServer.Commerce.Catalog.ContentTypes;
5+
using EPiServer.Core;
6+
using EPiServer.DataAccess;
7+
using EPiServer.Security;
8+
using Mediachase.Commerce;
9+
using Mediachase.Commerce.Catalog;
10+
using Microsoft.AspNetCore.Hosting;
11+
using Microsoft.Extensions.DependencyInjection;
12+
using Optimizely.TestContainers.Commerce.Tests.Models.Commerce;
13+
using Optimizely.TestContainers.Shared;
14+
15+
namespace Optimizely.TestContainers.Commerce.Tests;
16+
17+
/// <summary>
18+
/// Negative/edge case integration tests for Commerce catalog functionality.
19+
/// Tests error handling, validation, and edge cases for Commerce operations.
20+
/// </summary>
21+
[Collection("CommerceCatalogNegativeTests")]
22+
public class CommerceCatalogNegativeTests() : OptimizelyIntegrationTestBase(includeCommerce: true)
23+
{
24+
/// <summary>
25+
/// Configure web host with Commerce-specific Startup and services.
26+
/// The base class provides CMS, Commerce, and Find configuration automatically.
27+
/// </summary>
28+
protected override void ConfigureWebHostBuilder(IWebHostBuilder webHostBuilder)
29+
{
30+
// Register the Startup class that configures Commerce services and content types
31+
webHostBuilder.UseStartup<Startup>();
32+
}
33+
34+
[Fact]
35+
public void Cannot_Load_NonExistent_Product()
36+
{
37+
// Arrange
38+
var contentRepository = Services.GetRequiredService<IContentRepository>();
39+
var nonExistentReference = new ContentReference(99999);
40+
41+
// Act & Assert
42+
Assert.Throws<ContentNotFoundException>(() => contentRepository.Get<TestProduct>(nonExistentReference));
43+
}
44+
45+
[Fact]
46+
public void TryGet_Returns_False_For_NonExistent_Product()
47+
{
48+
// Arrange
49+
var contentRepository = Services.GetRequiredService<IContentRepository>();
50+
var nonExistentReference = new ContentReference(99999);
51+
52+
// Act
53+
var result = contentRepository.TryGet<TestProduct>(nonExistentReference, out var product);
54+
55+
// Assert
56+
Assert.False(result);
57+
Assert.Null(product);
58+
}
59+
60+
[Fact]
61+
public void Cannot_Save_Catalog_Without_Name()
62+
{
63+
// Arrange
64+
var referenceConverter = Services.GetRequiredService<ReferenceConverter>();
65+
var contentRepository = Services.GetRequiredService<IContentRepository>();
66+
67+
var rootLink = referenceConverter.GetRootLink();
68+
69+
var catalog = contentRepository.GetDefault<CatalogContent>(rootLink);
70+
catalog.Name = ""; // Empty name
71+
catalog.DefaultCurrency = Currency.USD;
72+
catalog.DefaultLanguage = "en";
73+
catalog.WeightBase = "kgs";
74+
catalog.LengthBase = "cm";
75+
76+
// Act & Assert
77+
Assert.Throws<ValidationException>(() => contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess));
78+
}
79+
80+
[Fact]
81+
public void Can_Delete_Product()
82+
{
83+
// Arrange
84+
var referenceConverter = Services.GetRequiredService<ReferenceConverter>();
85+
var contentRepository = Services.GetRequiredService<IContentRepository>();
86+
87+
var rootLink = referenceConverter.GetRootLink();
88+
89+
var catalog = contentRepository.GetDefault<CatalogContent>(rootLink);
90+
catalog.Name = "Delete Test Catalog";
91+
catalog.DefaultCurrency = Currency.USD;
92+
catalog.DefaultLanguage = "en";
93+
catalog.WeightBase = "kgs";
94+
catalog.LengthBase = "cm";
95+
96+
var catalogReference = contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess);
97+
98+
var node = contentRepository.GetDefault<NodeContent>(catalogReference, CultureInfo.GetCultureInfo("en"));
99+
node.Name = "Delete Test Node";
100+
var nodeReference = contentRepository.Save(node, SaveAction.Publish, AccessLevel.NoAccess);
101+
102+
var product = contentRepository.GetDefault<TestProduct>(nodeReference, CultureInfo.GetCultureInfo("en"));
103+
product.Name = "To Be Deleted";
104+
product.Description = new XhtmlString("<p>Test</p>");
105+
var productReference = contentRepository.Save(product, SaveAction.Publish, AccessLevel.NoAccess);
106+
107+
// Act (Delete)
108+
contentRepository.Delete(productReference, true, AccessLevel.NoAccess);
109+
110+
// Assert
111+
var result = contentRepository.TryGet<TestProduct>(productReference, out var deleted);
112+
Assert.False(result);
113+
}
114+
115+
[Fact]
116+
public void Can_Update_Existing_Product()
117+
{
118+
// Arrange
119+
var referenceConverter = Services.GetRequiredService<ReferenceConverter>();
120+
var contentRepository = Services.GetRequiredService<IContentRepository>();
121+
122+
var rootLink = referenceConverter.GetRootLink();
123+
124+
var catalog = contentRepository.GetDefault<CatalogContent>(rootLink);
125+
catalog.Name = "Update Test Catalog";
126+
catalog.DefaultCurrency = Currency.USD;
127+
catalog.DefaultLanguage = "en";
128+
catalog.WeightBase = "kgs";
129+
catalog.LengthBase = "cm";
130+
131+
var catalogReference = contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess);
132+
133+
var node = contentRepository.GetDefault<NodeContent>(catalogReference, CultureInfo.GetCultureInfo("en"));
134+
node.Name = "Update Test Node";
135+
var nodeReference = contentRepository.Save(node, SaveAction.Publish, AccessLevel.NoAccess);
136+
137+
var product = contentRepository.GetDefault<TestProduct>(nodeReference, CultureInfo.GetCultureInfo("en"));
138+
product.Name = "Original Product Name";
139+
product.Description = new XhtmlString("<p>Original Description</p>");
140+
var productReference = contentRepository.Save(product, SaveAction.Publish, AccessLevel.NoAccess);
141+
142+
// Act (Update)
143+
var writable = contentRepository.Get<TestProduct>(productReference).CreateWritableClone() as TestProduct;
144+
writable!.Description = new XhtmlString("<p>Updated Description</p>");
145+
contentRepository.Save(writable, SaveAction.Publish, AccessLevel.NoAccess);
146+
147+
// Assert
148+
var loaded = contentRepository.Get<TestProduct>(productReference);
149+
Assert.Equal("<p>Updated Description</p>", loaded.Description?.ToHtmlString());
150+
}
151+
152+
[Fact]
153+
public void Can_Create_Product_As_Draft()
154+
{
155+
// Arrange
156+
var referenceConverter = Services.GetRequiredService<ReferenceConverter>();
157+
var contentRepository = Services.GetRequiredService<IContentRepository>();
158+
159+
var rootLink = referenceConverter.GetRootLink();
160+
161+
var catalog = contentRepository.GetDefault<CatalogContent>(rootLink);
162+
catalog.Name = "Draft Test Catalog";
163+
catalog.DefaultCurrency = Currency.USD;
164+
catalog.DefaultLanguage = "en";
165+
catalog.WeightBase = "kgs";
166+
catalog.LengthBase = "cm";
167+
168+
var catalogReference = contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess);
169+
170+
var node = contentRepository.GetDefault<NodeContent>(catalogReference, CultureInfo.GetCultureInfo("en"));
171+
node.Name = "Draft Test Node";
172+
var nodeReference = contentRepository.Save(node, SaveAction.Publish, AccessLevel.NoAccess);
173+
174+
var product = contentRepository.GetDefault<TestProduct>(nodeReference, CultureInfo.GetCultureInfo("en"));
175+
product.Name = "Draft Product";
176+
product.Description = new XhtmlString("<p>Draft Description</p>");
177+
178+
// Act (Save as draft)
179+
var productReference = contentRepository.Save(product, SaveAction.CheckOut, AccessLevel.NoAccess);
180+
var loaded = contentRepository.Get<TestProduct>(productReference);
181+
182+
// Assert
183+
Assert.NotNull(loaded);
184+
Assert.Equal("Draft Product", loaded.Name);
185+
Assert.False(loaded.Status == VersionStatus.Published);
186+
}
187+
188+
[Fact]
189+
public void Cannot_Get_Wrong_Content_Type_From_Catalog()
190+
{
191+
// Arrange
192+
var referenceConverter = Services.GetRequiredService<ReferenceConverter>();
193+
var contentRepository = Services.GetRequiredService<IContentRepository>();
194+
195+
var rootLink = referenceConverter.GetRootLink();
196+
197+
var catalog = contentRepository.GetDefault<CatalogContent>(rootLink);
198+
catalog.Name = "Type Test Catalog";
199+
catalog.DefaultCurrency = Currency.USD;
200+
catalog.DefaultLanguage = "en";
201+
catalog.WeightBase = "kgs";
202+
catalog.LengthBase = "cm";
203+
204+
var catalogReference = contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess);
205+
206+
// Act & Assert - Try to get Catalog as Product
207+
Assert.Throws<TypeMismatchException>(() => contentRepository.Get<TestProduct>(catalogReference));
208+
}
209+
210+
[Fact]
211+
public void Can_Create_Multiple_Products_In_Same_Node()
212+
{
213+
// Arrange
214+
var referenceConverter = Services.GetRequiredService<ReferenceConverter>();
215+
var contentRepository = Services.GetRequiredService<IContentRepository>();
216+
217+
var rootLink = referenceConverter.GetRootLink();
218+
219+
var catalog = contentRepository.GetDefault<CatalogContent>(rootLink);
220+
catalog.Name = "Multiple Products Catalog";
221+
catalog.DefaultCurrency = Currency.USD;
222+
catalog.DefaultLanguage = "en";
223+
catalog.WeightBase = "kgs";
224+
catalog.LengthBase = "cm";
225+
226+
var catalogReference = contentRepository.Save(catalog, SaveAction.Publish, AccessLevel.NoAccess);
227+
228+
var node = contentRepository.GetDefault<NodeContent>(catalogReference, CultureInfo.GetCultureInfo("en"));
229+
node.Name = "Multiple Products Node";
230+
var nodeReference = contentRepository.Save(node, SaveAction.Publish, AccessLevel.NoAccess);
231+
232+
// Create first product
233+
var product1 = contentRepository.GetDefault<TestProduct>(nodeReference, CultureInfo.GetCultureInfo("en"));
234+
product1.Name = "Product 1";
235+
product1.Description = new XhtmlString("<p>Description 1</p>");
236+
var productRef1 = contentRepository.Save(product1, SaveAction.Publish, AccessLevel.NoAccess);
237+
238+
// Create second product
239+
var product2 = contentRepository.GetDefault<TestProduct>(nodeReference, CultureInfo.GetCultureInfo("en"));
240+
product2.Name = "Product 2";
241+
product2.Description = new XhtmlString("<p>Description 2</p>");
242+
243+
// Act
244+
var productRef2 = contentRepository.Save(product2, SaveAction.Publish, AccessLevel.NoAccess);
245+
246+
// Assert
247+
var loaded1 = contentRepository.Get<TestProduct>(productRef1);
248+
var loaded2 = contentRepository.Get<TestProduct>(productRef2);
249+
250+
Assert.NotNull(loaded1);
251+
Assert.NotNull(loaded2);
252+
Assert.Equal("Product 1", loaded1.Name);
253+
Assert.Equal("Product 2", loaded2.Name);
254+
Assert.NotEqual(loaded1.ContentLink, loaded2.ContentLink);
255+
}
256+
}

src/Optimizely.TestContainers.Commerce.Tests/Optimizely.TestContainers.Commerce.Tests.csproj

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<PackageReference Include="EPiServer.CMS" Version="12.33.1" />
2424
<PackageReference Include="EPiServer.Commerce" Version="14.40.0" />
2525
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
26+
<PackageReference Include="Moq" Version="4.17.2" />
2627
<PackageReference Include="Testcontainers.MsSql" Version="4.6.0" />
2728
<PackageReference Include="xunit" Version="2.9.3" />
2829
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.1">
@@ -44,9 +45,5 @@
4445
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
4546
</None>
4647
</ItemGroup>
47-
48-
<ItemGroup>
49-
<Folder Include="Models\" />
50-
</ItemGroup>
51-
52-
</Project>
48+
49+
</Project>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
using Xunit;
2+
3+
// We do not want integration tests to run in parallel at all to avoid deadlocks and database disposing itself multiple times
4+
[assembly: CollectionBehavior(DisableTestParallelization = true)]

src/Optimizely.TestContainers.Commerce.Tests/Startup.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,18 @@
55
using Microsoft.AspNetCore.Builder;
66
using Microsoft.AspNetCore.Hosting;
77
using Microsoft.Extensions.DependencyInjection;
8-
using Microsoft.Extensions.Hosting;
98

109
namespace Optimizely.TestContainers.Commerce.Tests;
1110

1211
public class Startup(IWebHostEnvironment webHostingEnvironment)
1312
{
1413
public void ConfigureServices(IServiceCollection services)
1514
{
16-
if (webHostingEnvironment.IsDevelopment())
17-
{
18-
AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(webHostingEnvironment.ContentRootPath, "App_Data"));
15+
AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(webHostingEnvironment.ContentRootPath, "App_Data"));
16+
17+
services.Configure<SchedulerOptions>(options => options.Enabled = false);
1918

20-
services.Configure<SchedulerOptions>(options => options.Enabled = false);
21-
}
19+
services.AddHttpContextAccessor();
2220

2321
services
2422
.AddCmsAspNetIdentity<ApplicationUser>()
@@ -30,11 +28,6 @@ public void ConfigureServices(IServiceCollection services)
3028

3129
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
3230
{
33-
if (env.IsDevelopment())
34-
{
35-
app.UseDeveloperExceptionPage();
36-
}
37-
3831
app.UseStaticFiles();
3932
app.UseRouting();
4033
app.UseAuthentication();

0 commit comments

Comments
 (0)