182 lines
5.9 KiB
C#
182 lines
5.9 KiB
C#
using FluentValidation;
|
|
using FluentValidation.Results;
|
|
using Moq;
|
|
using Prefab.Shared.Catalog;
|
|
using Prefab.Shared.Catalog.Products;
|
|
using Prefab.Web.Gateway.Shared;
|
|
using Shouldly;
|
|
|
|
namespace Prefab.Tests.Unit.Web.Gateway;
|
|
|
|
[Trait(TraitName.Category, TraitCategory.Unit)]
|
|
public sealed class NavMenuServiceShould
|
|
{
|
|
[Fact]
|
|
public async Task ReturnOnlyFeaturedChildrenWhenAvailable()
|
|
{
|
|
var moduleClient = new Mock<IModuleClient>();
|
|
var productClient = new Mock<IProductClient>();
|
|
moduleClient.SetupGet(c => c.Product).Returns(productClient.Object);
|
|
|
|
var featuredChild = CreateNode(
|
|
name: "Featured",
|
|
slug: "featured",
|
|
displayOrder: 2,
|
|
isFeatured: true,
|
|
isLeaf: true);
|
|
|
|
var secondaryChild = CreateNode(
|
|
name: "Secondary",
|
|
slug: "secondary",
|
|
displayOrder: 1,
|
|
isFeatured: false,
|
|
isLeaf: true);
|
|
|
|
var root = CreateNode(
|
|
name: "Root",
|
|
slug: "root",
|
|
displayOrder: 0,
|
|
isFeatured: false,
|
|
isLeaf: false,
|
|
children: new[] { secondaryChild, featuredChild });
|
|
|
|
productClient
|
|
.Setup(p => p.GetCategoryTree(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new GetCategoryTree.Response(new[] { root }));
|
|
|
|
var sut = new NavMenu.Service(moduleClient.Object);
|
|
|
|
var result = await sut.Get(2, CancellationToken.None);
|
|
|
|
result.IsSuccess.ShouldBeTrue();
|
|
var model = result.Value.ShouldNotBeNull();
|
|
model.Depth.ShouldBe(2);
|
|
model.Columns.Count.ShouldBe(1);
|
|
|
|
var column = model.Columns.Single();
|
|
column.Root.Name.ShouldBe("Root");
|
|
column.Root.Url.ShouldBe("/catalog/category?category-slug=root");
|
|
column.Items.Count.ShouldBe(1);
|
|
|
|
var item = column.Items.Single();
|
|
item.Name.ShouldBe("Featured");
|
|
item.Url.ShouldBe("/catalog/products?category-slug=featured");
|
|
column.HasMore.ShouldBeFalse();
|
|
column.SeeAllUrl.ShouldBe("/catalog/category?category-slug=root");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task FallBackToAllChildrenWhenNoFeatured()
|
|
{
|
|
var moduleClient = new Mock<IModuleClient>();
|
|
var productClient = new Mock<IProductClient>();
|
|
moduleClient.SetupGet(c => c.Product).Returns(productClient.Object);
|
|
|
|
var firstChild = CreateNode(
|
|
name: "Alpha",
|
|
slug: "alpha",
|
|
displayOrder: 3,
|
|
isFeatured: false,
|
|
isLeaf: true);
|
|
var secondChild = CreateNode(
|
|
name: "Beta",
|
|
slug: "beta",
|
|
displayOrder: 1,
|
|
isFeatured: false,
|
|
isLeaf: false);
|
|
var thirdChild = CreateNode(
|
|
name: "Gamma",
|
|
slug: "gamma",
|
|
displayOrder: 2,
|
|
isFeatured: false,
|
|
isLeaf: true);
|
|
|
|
var root = CreateNode(
|
|
name: "Root",
|
|
slug: "root",
|
|
displayOrder: 0,
|
|
isFeatured: false,
|
|
isLeaf: false,
|
|
children: new[] { firstChild, secondChild, thirdChild });
|
|
|
|
productClient
|
|
.Setup(p => p.GetCategoryTree(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new GetCategoryTree.Response(new[] { root }));
|
|
|
|
var sut = new NavMenu.Service(moduleClient.Object);
|
|
|
|
var result = await sut.Get(1, CancellationToken.None);
|
|
|
|
result.IsSuccess.ShouldBeTrue();
|
|
var model = result.Value.ShouldNotBeNull();
|
|
model.Depth.ShouldBe(1);
|
|
var names = model.Columns.Single().Items.Select(i => i.Name).ToList();
|
|
names.ShouldBe(["Beta", "Gamma", "Alpha"]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ClampDepthBeforeCallingCatalog()
|
|
{
|
|
var moduleClient = new Mock<IModuleClient>();
|
|
var productClient = new Mock<IProductClient>();
|
|
moduleClient.SetupGet(c => c.Product).Returns(productClient.Object);
|
|
|
|
var root = CreateNode("Root", "root", 0, false, false);
|
|
|
|
productClient
|
|
.Setup(p => p.GetCategoryTree(2, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new GetCategoryTree.Response(new[] { root }));
|
|
|
|
var sut = new NavMenu.Service(moduleClient.Object);
|
|
await sut.Get(10, CancellationToken.None);
|
|
|
|
productClient.Verify(p => p.GetCategoryTree(2, It.IsAny<CancellationToken>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReturnProblemWhenCatalogValidationFails()
|
|
{
|
|
var moduleClient = new Mock<IModuleClient>();
|
|
var productClient = new Mock<IProductClient>();
|
|
moduleClient.SetupGet(c => c.Product).Returns(productClient.Object);
|
|
|
|
productClient
|
|
.Setup(p => p.GetCategoryTree(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
|
.ThrowsAsync(new ValidationException([new ValidationFailure("Depth", "invalid")]));
|
|
|
|
var sut = new NavMenu.Service(moduleClient.Object);
|
|
|
|
var result = await sut.Get(-1, CancellationToken.None);
|
|
|
|
result.IsSuccess.ShouldBeFalse();
|
|
result.Value.ShouldBeNull();
|
|
var problem = result.Problem.ShouldNotBeNull();
|
|
problem.StatusCode.ShouldBe(400);
|
|
problem.Detail.ShouldBe("Request did not satisfy validation rules.");
|
|
problem.Errors.ShouldNotBeNull();
|
|
problem.Errors!.ShouldContainKey("Depth");
|
|
problem.Errors["Depth"].ShouldContain("invalid");
|
|
}
|
|
|
|
private static GetCategoryModels.CategoryNodeDto CreateNode(
|
|
string name,
|
|
string slug,
|
|
int displayOrder,
|
|
bool isFeatured,
|
|
bool isLeaf,
|
|
IReadOnlyList<GetCategoryModels.CategoryNodeDto>? children = null)
|
|
{
|
|
return new GetCategoryModels.CategoryNodeDto(
|
|
Guid.NewGuid(),
|
|
name,
|
|
slug,
|
|
null,
|
|
null,
|
|
null,
|
|
displayOrder,
|
|
isFeatured,
|
|
isLeaf,
|
|
children ?? []);
|
|
}
|
|
}
|