Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using Grand.Business.Core.Interfaces.Catalog.Brands;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Storage;
using Grand.Business.Core.Utilities.Catalog;
using Grand.Domain;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
using Grand.Domain.Localization;
using Grand.Domain.Media;
using Grand.Infrastructure.Caching;
using Grand.Infrastructure.Configuration;
using Grand.Mediator;
using Grand.Web.Features.Handlers.Catalog;
using Grand.Web.Features.Models.Catalog;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace Grand.Web.Tests.Features.Handlers.Catalog;

[TestClass]
public class GetHomepageBrandsHandlerTests
{
private GetHomepageBrandsHandler _handler;
private Mock<IBrandService> _brandServiceMock;

[TestInitialize]
public void Init()
{
_brandServiceMock = new Mock<IBrandService>();
_brandServiceMock.Setup(s => s.GetAllBrands(It.IsAny<string>(), It.IsAny<CatalogAccess>(),
It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync((string _, CatalogAccess access, int _, int _) => {
var brands = new List<Brand> { new() { Id = "public", ShowOnHomePage = true } };
if (access.Customer.Groups.Contains("vip"))
brands.Add(new Brand { Id = "vip-only", ShowOnHomePage = true });
return new PagedList<Brand>(brands, 0, int.MaxValue);
});

var translationServiceMock = new Mock<ITranslationService>();
translationServiceMock.Setup(s => s.GetResource(It.IsAny<string>())).Returns("{0}");

var cacheBase = new MemoryCacheBase(new MemoryCache(new MemoryCacheOptions()), new Mock<IMediator>().Object,
new CacheConfig { DefaultCacheTimeMinutes = 1 });

_handler = new GetHomepageBrandsHandler(cacheBase, _brandServiceMock.Object,
new Mock<IPictureService>().Object, translationServiceMock.Object, new MediaSettings());
}

private Task<IList<Web.Models.Catalog.BrandModel>> Handle(params string[] groups)
{
return HandleIn("store-1", "lang-1", groups);
}

private Task<IList<Web.Models.Catalog.BrandModel>> HandleIn(string storeId, string languageId, params string[] groups)
{
var customer = new Customer();
foreach (var group in groups) customer.Groups.Add(group);
return _handler.Handle(new GetHomepageBrands {
Customer = customer,
Store = new Domain.Stores.Store { Id = storeId },
Language = new Language { Id = languageId }
}, CancellationToken.None);
}

[TestMethod]
public async Task Handle_GuestAfterGroupMember_DoesNotSeeGroupLimitedBrand()
{
var vip = await Handle("registered", "vip");
var guest = await Handle("guests");

CollectionAssert.AreEquivalent(new[] { "public", "vip-only" }, vip.Select(x => x.Id).ToArray());
CollectionAssert.AreEquivalent(new[] { "public" }, guest.Select(x => x.Id).ToArray());
}

[TestMethod]
public async Task Handle_GroupMemberAfterGuest_SeesGroupLimitedBrand()
{
await Handle("guests");
var vip = await Handle("registered", "vip");

CollectionAssert.AreEquivalent(new[] { "public", "vip-only" }, vip.Select(x => x.Id).ToArray());
}

[TestMethod]
public async Task Handle_OtherStoreOrLanguage_DoesNotReuseCachedEntry()
{
await HandleIn("store-1", "lang-1", "guests");
await HandleIn("store-2", "lang-1", "guests");
await HandleIn("store-1", "lang-2", "guests");
await HandleIn("store-1", "lang-1", "guests");

_brandServiceMock.Verify(s => s.GetAllBrands(It.IsAny<string>(), It.IsAny<CatalogAccess>(),
It.IsAny<int>(), It.IsAny<int>()), Times.Exactly(3));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using Grand.Business.Core.Interfaces.Catalog.Collections;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Storage;
using Grand.Business.Core.Utilities.Catalog;
using Grand.Domain;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
using Grand.Domain.Localization;
using Grand.Domain.Media;
using Grand.Infrastructure.Caching;
using Grand.Infrastructure.Configuration;
using Grand.Mediator;
using Grand.Web.Features.Handlers.Catalog;
using Grand.Web.Features.Models.Catalog;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace Grand.Web.Tests.Features.Handlers.Catalog;

[TestClass]
public class GetHomepageCollectionsHandlerTests
{
private GetHomepageCollectionsHandler _handler;
private Mock<ICollectionService> _collectionServiceMock;

[TestInitialize]
public void Init()
{
_collectionServiceMock = new Mock<ICollectionService>();
_collectionServiceMock.Setup(s => s.GetAllCollections(It.IsAny<string>(), It.IsAny<CatalogAccess>(),
It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync((string _, CatalogAccess access, int _, int _) => {
var collections = new List<Collection> { new() { Id = "public", ShowOnHomePage = true } };
if (access.Customer.Groups.Contains("vip"))
collections.Add(new Collection { Id = "vip-only", ShowOnHomePage = true });
return new PagedList<Collection>(collections, 0, int.MaxValue);
});

var translationServiceMock = new Mock<ITranslationService>();
translationServiceMock.Setup(s => s.GetResource(It.IsAny<string>())).Returns("{0}");

var cacheBase = new MemoryCacheBase(new MemoryCache(new MemoryCacheOptions()), new Mock<IMediator>().Object,
new CacheConfig { DefaultCacheTimeMinutes = 1 });

_handler = new GetHomepageCollectionsHandler(cacheBase, _collectionServiceMock.Object,
new Mock<IPictureService>().Object, translationServiceMock.Object, new MediaSettings());
}

private Task<IList<Web.Models.Catalog.CollectionModel>> Handle(params string[] groups)
{
return HandleIn("store-1", "lang-1", groups);
}

private Task<IList<Web.Models.Catalog.CollectionModel>> HandleIn(string storeId, string languageId, params string[] groups)
{
var customer = new Customer();
foreach (var group in groups) customer.Groups.Add(group);
return _handler.Handle(new GetHomepageCollections {
Customer = customer,
Store = new Domain.Stores.Store { Id = storeId },
Language = new Language { Id = languageId }
}, CancellationToken.None);
}

[TestMethod]
public async Task Handle_GuestAfterGroupMember_DoesNotSeeGroupLimitedCollection()
{
var vip = await Handle("registered", "vip");
var guest = await Handle("guests");

CollectionAssert.AreEquivalent(new[] { "public", "vip-only" }, vip.Select(x => x.Id).ToArray());
CollectionAssert.AreEquivalent(new[] { "public" }, guest.Select(x => x.Id).ToArray());
}

[TestMethod]
public async Task Handle_GroupMemberAfterGuest_SeesGroupLimitedCollection()
{
await Handle("guests");
var vip = await Handle("registered", "vip");

CollectionAssert.AreEquivalent(new[] { "public", "vip-only" }, vip.Select(x => x.Id).ToArray());
}

[TestMethod]
public async Task Handle_OtherStoreOrLanguage_DoesNotReuseCachedEntry()
{
await HandleIn("store-1", "lang-1", "guests");
await HandleIn("store-2", "lang-1", "guests");
await HandleIn("store-1", "lang-2", "guests");
await HandleIn("store-1", "lang-1", "guests");

_collectionServiceMock.Verify(s => s.GetAllCollections(It.IsAny<string>(), It.IsAny<CatalogAccess>(),
It.IsAny<int>(), It.IsAny<int>()), Times.Exactly(3));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using Grand.Business.Core.Interfaces.Catalog.Brands;
using Grand.Business.Core.Interfaces.Catalog.Categories;
using Grand.Business.Core.Interfaces.Catalog.Collections;
using Grand.Business.Core.Interfaces.Cms;
using Grand.Business.Core.Interfaces.Storage;
using Grand.Business.Core.Utilities.Catalog;
using Grand.Domain;
using Grand.Domain.Blogs;
using Grand.Domain.Catalog;
using Grand.Domain.Common;
using Grand.Domain.Customers;
using Grand.Domain.Localization;
using Grand.Domain.Media;
using Grand.Domain.Pages;
using Grand.Infrastructure.Caching;
using Grand.Infrastructure.Configuration;
using Grand.Mediator;
using Grand.Web.Features.Handlers.Catalog;
using Grand.Web.Features.Models.Catalog;
using Grand.Web.Models.Catalog;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace Grand.Web.Tests.Features.Handlers.Catalog;

[TestClass]
public class GetMenuHandlerTests
{
private GetMenuHandler _handler;
private Mock<IBrandService> _brandServiceMock;
private Mock<ICollectionService> _collectionServiceMock;

[TestInitialize]
public void Init()
{
_brandServiceMock = new Mock<IBrandService>();
_brandServiceMock.Setup(s => s.GetAllBrands(It.IsAny<string>(), It.IsAny<CatalogAccess>(),
It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync((string _, CatalogAccess access, int _, int _) => {
var brands = new List<Brand> { new() { Id = "public", IncludeInMenu = true } };
if (access.Customer.Groups.Contains("vip"))
brands.Add(new Brand { Id = "vip-only", IncludeInMenu = true });
return new PagedList<Brand>(brands, 0, int.MaxValue);
});

_collectionServiceMock = new Mock<ICollectionService>();
_collectionServiceMock.Setup(s => s.GetAllCollections(It.IsAny<string>(), It.IsAny<CatalogAccess>(),
It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync((string _, CatalogAccess access, int _, int _) => {
var collections = new List<Collection> { new() { Id = "public", IncludeInMenu = true } };
if (access.Customer.Groups.Contains("vip"))
collections.Add(new Collection { Id = "vip-only", IncludeInMenu = true });
return new PagedList<Collection>(collections, 0, int.MaxValue);
});

var categoryServiceMock = new Mock<ICategoryService>();
categoryServiceMock.Setup(s => s.GetMenuCategories(It.IsAny<Customer>(), It.IsAny<string>()))
.ReturnsAsync(new List<Category>());

var pageServiceMock = new Mock<IPageService>();
pageServiceMock.Setup(s => s.GetAllPages(It.IsAny<string>(), It.IsAny<bool>()))
.ReturnsAsync(new List<Page>());

var cacheBase = new MemoryCacheBase(new MemoryCache(new MemoryCacheOptions()), new Mock<IMediator>().Object,
new CacheConfig { DefaultCacheTimeMinutes = 1 });

_handler = new GetMenuHandler(categoryServiceMock.Object, _brandServiceMock.Object, pageServiceMock.Object,
cacheBase, _collectionServiceMock.Object, new Mock<IPictureService>().Object, new CatalogSettings(),
new BlogSettings(), new MenuItemSettings(), new MediaSettings());
}

private Task<MenuModel> Handle(params string[] groups)
{
return HandleIn("store-1", "lang-1", groups);
}

private Task<MenuModel> HandleIn(string storeId, string languageId, params string[] groups)
{
var customer = new Customer();
foreach (var group in groups) customer.Groups.Add(group);
return _handler.Handle(new GetMenu {
Customer = customer,
Store = new Domain.Stores.Store { Id = storeId },
Language = new Language { Id = languageId }
}, CancellationToken.None);
}

[TestMethod]
public async Task Handle_GuestAfterGroupMember_DoesNotSeeGroupLimitedBrandsAndCollections()
{
await Handle("registered", "vip");
var guest = await Handle("guests");

CollectionAssert.AreEquivalent(new[] { "public" }, guest.Brands.Select(x => x.Id).ToArray());
CollectionAssert.AreEquivalent(new[] { "public" }, guest.Collections.Select(x => x.Id).ToArray());
}

[TestMethod]
public async Task Handle_GroupMemberAfterGuest_SeesGroupLimitedBrandsAndCollections()
{
await Handle("guests");
var vip = await Handle("registered", "vip");

CollectionAssert.AreEquivalent(new[] { "public", "vip-only" }, vip.Brands.Select(x => x.Id).ToArray());
CollectionAssert.AreEquivalent(new[] { "public", "vip-only" }, vip.Collections.Select(x => x.Id).ToArray());
}

[TestMethod]
public async Task Handle_OtherStoreOrLanguage_DoesNotReuseCachedEntry()
{
await HandleIn("store-1", "lang-1", "guests");
await HandleIn("store-2", "lang-1", "guests");
await HandleIn("store-1", "lang-2", "guests");
await HandleIn("store-1", "lang-1", "guests");

_brandServiceMock.Verify(s => s.GetAllBrands(It.IsAny<string>(), It.IsAny<CatalogAccess>(),
It.IsAny<int>(), It.IsAny<int>()), Times.Exactly(3));
_collectionServiceMock.Verify(s => s.GetAllCollections(It.IsAny<string>(), It.IsAny<CatalogAccess>(),
It.IsAny<int>(), It.IsAny<int>()), Times.Exactly(3));
}
}
12 changes: 8 additions & 4 deletions src/Web/Grand.Web/Events/Cache/CacheKeyConst.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ public static class CacheKeyConst
/// <remarks>
/// {0} : current store ID
/// {1} : language ID
/// {2} : groups of the current user
/// </remarks>
public const string BRAND_HOMEPAGE_KEY = "Grand.brand.navigation.homepage-{0}-{1}.pres";
public const string BRAND_HOMEPAGE_KEY = "Grand.brand.navigation.homepage-{0}-{1}-{2}.pres";

/// <summary>
/// Key for CollectionNavigationModel caching
/// </summary>
/// <remarks>
/// {0} : language id
/// {1} : current store ID
/// {2} : groups of the current user
/// </remarks>
public const string BRAND_NAVIGATION_MENU = "Grand.brand.navigation.menu-{0}-{1}.pres";
public const string BRAND_NAVIGATION_MENU = "Grand.brand.navigation.menu-{0}-{1}-{2}.pres";

/// <summary>
/// Key for CollectionNavigationModel caching
Expand All @@ -47,17 +49,19 @@ public static class CacheKeyConst
/// <remarks>
/// {0} : language id
/// {1} : current store ID
/// {2} : groups of the current user
/// </remarks>
public const string COLLECTION_NAVIGATION_MENU = "Grand.collection.navigation.menu-{0}-{1}.pres";
public const string COLLECTION_NAVIGATION_MENU = "Grand.collection.navigation.menu-{0}-{1}-{2}.pres";

/// <summary>
/// Key for caching of collection displayed on home page
/// </summary>
/// <remarks>
/// {0} : current store ID
/// {1} : language ID
/// {2} : groups of the current user
/// </remarks>
public const string COLLECTION_HOMEPAGE_KEY = "Grand.collection.navigation.homepage-{0}-{1}.pres";
public const string COLLECTION_HOMEPAGE_KEY = "Grand.collection.navigation.homepage-{0}-{1}-{2}.pres";


/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Storage;
using Grand.Business.Core.Utilities.Catalog;
using Grand.Domain.Customers;
using Grand.Domain.Media;
using Grand.Infrastructure.Caching;
using Grand.Web.Events.Cache;
Expand Down Expand Up @@ -37,7 +38,8 @@ public GetHomepageBrandsHandler(

public async Task<IList<BrandModel>> Handle(GetHomepageBrands request, CancellationToken cancellationToken)
{
var brandsCacheKey = string.Format(CacheKeyConst.BRAND_HOMEPAGE_KEY, request.Store.Id, request.Language.Id);
var brandsCacheKey = string.Format(CacheKeyConst.BRAND_HOMEPAGE_KEY, request.Store.Id, request.Language.Id,
string.Join(",", request.Customer.GetCustomerGroupIds()));

var model = await _cacheBase.GetAsync(brandsCacheKey, async () =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Storage;
using Grand.Business.Core.Utilities.Catalog;
using Grand.Domain.Customers;
using Grand.Domain.Media;
using Grand.Infrastructure.Caching;
using Grand.Web.Events.Cache;
Expand Down Expand Up @@ -38,8 +39,8 @@ public GetHomepageCollectionsHandler(
public async Task<IList<CollectionModel>> Handle(GetHomepageCollections request,
CancellationToken cancellationToken)
{
var collectionsCacheKey =
string.Format(CacheKeyConst.COLLECTION_HOMEPAGE_KEY, request.Store.Id, request.Language.Id);
var collectionsCacheKey = string.Format(CacheKeyConst.COLLECTION_HOMEPAGE_KEY, request.Store.Id,
request.Language.Id, string.Join(",", request.Customer.GetCustomerGroupIds()));

var model = await _cacheBase.GetAsync(collectionsCacheKey, async () =>
{
Expand Down
Loading
Loading