Files
2025-10-27 17:39:18 -04:00

148 lines
5.5 KiB
C#

using Prefab.Catalog.Domain.Entities;
using Prefab.Catalog.Domain.Services;
using Shouldly;
namespace Prefab.Tests.Unit.Modules.Catalog.Domain.Services;
[Trait(TraitName.Category, TraitCategory.Unit)]
public sealed class VariantResolverShould
{
private readonly StubUniqueChecker _checker = new();
[Fact]
public async Task ResolveVariantFromAxisSelections()
{
var context = await BuildModelWithVariantsAsync();
var resolver = new VariantResolver();
var selections = new[]
{
new OptionSelection(context.ColorOption.Id, context.RedValue.Id, null),
new OptionSelection(context.DepthOption.Id, context.Depth24Value.Id, null)
};
var result = resolver.Resolve(context.Model, selections);
result.IsSuccess.ShouldBeTrue();
result.Variant.ShouldBe(context.Red24Variant);
}
[Fact]
public async Task ReturnErrorWhenAxisSelectionMissing()
{
var context = await BuildModelWithVariantsAsync();
var resolver = new VariantResolver();
var selections = new[]
{
new OptionSelection(context.ColorOption.Id, context.RedValue.Id, null)
};
var result = resolver.Resolve(context.Model, selections);
result.IsSuccess.ShouldBeFalse();
result.Errors.ShouldContain(e => e.Contains("axis", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public async Task ResolveBySkuWhenProvided()
{
var context = await BuildModelWithVariantsAsync();
var resolver = new VariantResolver();
var result = resolver.Resolve(context.Model, Array.Empty<OptionSelection>(), context.Blue34Variant.Sku);
result.IsSuccess.ShouldBeTrue();
result.Variant.ShouldBe(context.Blue34Variant);
}
private async Task<ModelContext> BuildModelWithVariantsAsync()
{
var model = await Product.CreateModel($"variant-model-{Guid.NewGuid():N}", $"variant-model-{Guid.NewGuid():N}", null, _checker);
var colorOption = await OptionDefinition.CreateChoice(model, "color", "Color", _checker, isVariantAxis: true);
var redValue = colorOption.AddValue("red", "Red", 0m, PriceDeltaKind.Absolute);
var blueValue = colorOption.AddValue("blue", "Blue", 0m, PriceDeltaKind.Absolute);
var depthOption = await OptionDefinition.CreateChoice(model, "depth", "Depth", _checker, isVariantAxis: true);
var depth24Value = depthOption.AddValue("depth_24", "24\"", 0m, PriceDeltaKind.Absolute);
var depth34Value = depthOption.AddValue("depth_34", "34\"", 0m, PriceDeltaKind.Absolute);
var red24Variant = await Product.CreateVariant(model.Id, $"SKU-{Guid.NewGuid():N}", "Red 24", 20m, _checker);
red24Variant.AxisValues.Add(new VariantAxisValue
{
ProductVariant = red24Variant,
ProductVariantId = red24Variant.Id,
OptionDefinition = colorOption,
OptionDefinitionId = colorOption.Id,
OptionValue = redValue,
OptionValueId = redValue.Id
});
red24Variant.AxisValues.Add(new VariantAxisValue
{
ProductVariant = red24Variant,
ProductVariantId = red24Variant.Id,
OptionDefinition = depthOption,
OptionDefinitionId = depthOption.Id,
OptionValue = depth24Value,
OptionValueId = depth24Value.Id
});
var blue34Variant = await Product.CreateVariant(model.Id, $"SKU-{Guid.NewGuid():N}", "Blue 34", 22m, _checker);
blue34Variant.AxisValues.Add(new VariantAxisValue
{
ProductVariant = blue34Variant,
ProductVariantId = blue34Variant.Id,
OptionDefinition = colorOption,
OptionDefinitionId = colorOption.Id,
OptionValue = blueValue,
OptionValueId = blueValue.Id
});
blue34Variant.AxisValues.Add(new VariantAxisValue
{
ProductVariant = blue34Variant,
ProductVariantId = blue34Variant.Id,
OptionDefinition = depthOption,
OptionDefinitionId = depthOption.Id,
OptionValue = depth34Value,
OptionValueId = depth34Value.Id
});
model.AttachVariant(red24Variant);
model.AttachVariant(blue34Variant);
return new ModelContext(
model,
colorOption,
depthOption,
redValue,
depth24Value,
blue34Variant,
red24Variant,
depth34Value);
}
private sealed record ModelContext(
Product Model,
OptionDefinition ColorOption,
OptionDefinition DepthOption,
OptionValue RedValue,
OptionValue Depth24Value,
Product Blue34Variant,
Product Red24Variant,
OptionValue Depth34Value);
private sealed class StubUniqueChecker : IUniqueChecker
{
public Task<bool> CategoryNameIsUnique(string name, CancellationToken cancellationToken = default) => Task.FromResult(true);
public Task<bool> ProductModelNameIsUnique(string name, CancellationToken cancellationToken = default) => Task.FromResult(true);
public Task<bool> ProductSlugIsUnique(string? slug, CancellationToken cancellationToken = default) => Task.FromResult(true);
public Task<bool> ProductSkuIsUnique(string sku, CancellationToken cancellationToken = default) => Task.FromResult(true);
public Task<bool> OptionCodeIsUniqueForProduct(Guid productId, string code, CancellationToken cancellationToken = default) => Task.FromResult(true);
}
}