diff --git a/src/Business/Grand.Business.Storage/Services/PictureService.cs b/src/Business/Grand.Business.Storage/Services/PictureService.cs index 57253c867..e3594919c 100644 --- a/src/Business/Grand.Business.Storage/Services/PictureService.cs +++ b/src/Business/Grand.Business.Storage/Services/PictureService.cs @@ -158,8 +158,11 @@ protected virtual async Task GetThumbPhysicalPath(string thumbFileName) /// Local picture thumb path protected virtual string GetThumbUrl(string thumbFileName, string storeLocation = null) { - storeLocation = !string.IsNullOrEmpty(storeLocation) ? storeLocation : ""; - return _mediaFileStore.Combine(storeLocation, ImageThumbPath, thumbFileName); + var thumbPath = _mediaFileStore.Combine(ImageThumbPath, thumbFileName); + if (string.IsNullOrEmpty(storeLocation)) + return thumbPath; + + return new Uri(new Uri(storeLocation), thumbPath).ToString(); } /// diff --git a/src/Tests/Grand.Business.Storage.Tests/Services/PictureServiceTests.cs b/src/Tests/Grand.Business.Storage.Tests/Services/PictureServiceTests.cs index 86c1a7206..75575bbff 100644 --- a/src/Tests/Grand.Business.Storage.Tests/Services/PictureServiceTests.cs +++ b/src/Tests/Grand.Business.Storage.Tests/Services/PictureServiceTests.cs @@ -13,6 +13,23 @@ namespace Grand.Business.Storage.Tests.Services; +/// +/// Exposes the protected GetThumbUrl method for testing. +/// +public class TestablePictureService( + IRepository pictureRepository, + ILogger logger, + IMediator mediator, + ICacheBase cacheBase, + IMediaFileStore mediaFileStore, + MediaSettings mediaSettings, + StorageSettings storageSettings) + : PictureService(pictureRepository, logger, mediator, cacheBase, mediaFileStore, mediaSettings, storageSettings) +{ + public string GetThumbUrlPublic(string thumbFileName, string storeLocation = null) + => GetThumbUrl(thumbFileName, storeLocation); +} + [TestClass] public class PictureServiceTests { @@ -22,6 +39,7 @@ public class PictureServiceTests private Mock _mediatorMock; private Mock> _repoMock; private PictureService _service; + private TestablePictureService _testablePictureService; private MediaSettings _settings; private StorageSettings _storagesettings; @@ -42,6 +60,8 @@ public void Init() _storagesettings = new StorageSettings(); _service = new PictureService(_repoMock.Object, _logerMock.Object, _mediatorMock.Object, _cacheMock.Object, _mediaFileStoreMock.Object, _settings, _storagesettings); + _testablePictureService = new TestablePictureService(_repoMock.Object, _logerMock.Object, + _mediatorMock.Object, _cacheMock.Object, _mediaFileStoreMock.Object, _settings, _storagesettings); } [TestMethod] @@ -139,4 +159,28 @@ public async Task DeletePicture_ValidArguments_InvokeRepositoryAndPublishEvent() _repoMock.Verify(c => c.DeleteAsync(It.IsAny()), Times.Once); _mediatorMock.Verify(c => c.Publish(It.IsAny>(), default), Times.Once); } + + [TestMethod] + public void GetThumbUrl_WithAbsoluteStoreLocation_ReturnsValidAbsoluteUrl() + { + const string storeLocation = "https://example.com/"; + const string thumbFileName = "sample_123.jpeg"; + + var result = _testablePictureService.GetThumbUrlPublic(thumbFileName, storeLocation); + + Assert.IsTrue(result.StartsWith("https://"), $"Expected URL to start with 'https://' but got: {result}"); + Assert.IsTrue(result.Contains(thumbFileName), $"Expected URL to contain thumb filename but got: {result}"); + Assert.IsFalse(result.StartsWith("/https://"), $"URL must not have a leading slash before the scheme: {result}"); + } + + [TestMethod] + public void GetThumbUrl_WithNoStoreLocation_ReturnsRelativePath() + { + const string thumbFileName = "sample_123.jpeg"; + + var result = _testablePictureService.GetThumbUrlPublic(thumbFileName); + + Assert.IsTrue(result.StartsWith("/"), $"Expected relative path to start with '/' but got: {result}"); + Assert.IsTrue(result.Contains(thumbFileName), $"Expected path to contain thumb filename but got: {result}"); + } } \ No newline at end of file