Skip to content
Open
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
38 changes: 22 additions & 16 deletions ImmichFrame.Core.Tests/Logic/Pool/AggregatingAssetPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using NUnit.Framework;
using ImmichFrame.Core.Api; // For AssetResponseDto
using ImmichFrame.Core.Logic.Pool; // For AggregatingAssetPool and IAssetPool (non-generic)
using ImmichFrame.Core.Interfaces;

namespace ImmichFrame.Core.Tests.Logic.Pool
{
Expand All @@ -12,13 +13,18 @@ public class AggregatingAssetPoolTests
private Mock<IAssetPool> _mockPool2;
private MultiAssetPool _aggregatingPool;
private List<IAssetPool> _assetPools;
private Mock<IRequestContext> _mockRequestContext;

[SetUp]
public void Setup()
{
_mockPool1 = new Mock<IAssetPool>();
_mockPool2 = new Mock<IAssetPool>();
_assetPools = new List<IAssetPool>();
_mockRequestContext = new Mock<IRequestContext>();
// Default RequestContext
_mockRequestContext.Setup(x => x.AssetOffset).Returns(0);

// AggregatingAssetPool takes IEnumerable<IAssetPool> in constructor
}

Expand Down Expand Up @@ -55,7 +61,7 @@ public async Task GetAssetCount_MultiplePools_ReturnsSumOfCounts()
public async Task GetAssets_RequestZeroAssets_ReturnsEmptyCollection()
{
_aggregatingPool = new MultiAssetPool(_assetPools); // Use MultiAssetPool
var result = await _aggregatingPool.GetAssets(0, CancellationToken.None);
var result = await _aggregatingPool.GetAssets(0, _mockRequestContext.Object, CancellationToken.None);
Assert.That(result, Is.Empty);
}

Expand All @@ -69,15 +75,15 @@ public async Task GetAssets_TotalLessThanRequested_ReturnsAllAvailableAssets()

_mockPool1.Setup(p => p.GetAssetCount(It.IsAny<CancellationToken>()))
.ReturnsAsync(() => (long)pool1AvailableAssets.Count);
_mockPool1.Setup(p => p.GetAssets(1, It.IsAny<CancellationToken>())) // AggregatingAssetPool.GetNextAsset calls GetAssets(1,...)
_mockPool1.Setup(p => p.GetAssets(1, _mockRequestContext.Object, It.IsAny<CancellationToken>())) // AggregatingAssetPool.GetNextAsset calls GetAssets(1,...)
.ReturnsAsync(() => pool1AvailableAssets.Any()
? new List<AssetResponseDto> { pool1AvailableAssets.Dequeue() }
: new List<AssetResponseDto>());

_assetPools.Add(_mockPool1.Object);
_aggregatingPool = new MultiAssetPool(_assetPools); // Use MultiAssetPool

var result = (await _aggregatingPool.GetAssets(5, CancellationToken.None)).ToList();
var result = (await _aggregatingPool.GetAssets(5, _mockRequestContext.Object, CancellationToken.None)).ToList();
Assert.That(result.Count, Is.EqualTo(2));
Assert.That(result.All(x => allAssetsFromPool1.Contains(x)), Is.True);
Assert.That(allAssetsFromPool1.All(x => result.Contains(x)), Is.True);
Expand All @@ -90,21 +96,21 @@ public async Task GetAssets_TotalMoreThanRequested_AggregatesAssetsFromPools()
var assetP1A2 = CreateAsset("p1a2");
var pool1Queue = new Queue<AssetResponseDto>(new[] { assetP1A1, assetP1A2 });
_mockPool1.Setup(p => p.GetAssetCount(It.IsAny<CancellationToken>())).ReturnsAsync(() => (long)pool1Queue.Count);
_mockPool1.Setup(p => p.GetAssets(1, It.IsAny<CancellationToken>()))
_mockPool1.Setup(p => p.GetAssets(1, _mockRequestContext.Object, It.IsAny<CancellationToken>()))
.ReturnsAsync(() => pool1Queue.Any() ? new List<AssetResponseDto> { pool1Queue.Dequeue() } : new List<AssetResponseDto>());

var assetP2A1 = CreateAsset("p2a1");
var pool2Queue = new Queue<AssetResponseDto>(new[] { assetP2A1 });
_mockPool2.Setup(p => p.GetAssetCount(It.IsAny<CancellationToken>())).ReturnsAsync(() => (long)pool2Queue.Count);
_mockPool2.Setup(p => p.GetAssets(1, It.IsAny<CancellationToken>()))
_mockPool2.Setup(p => p.GetAssets(1, _mockRequestContext.Object, It.IsAny<CancellationToken>()))
.ReturnsAsync(() => pool2Queue.Any() ? new List<AssetResponseDto> { pool2Queue.Dequeue() } : new List<AssetResponseDto>());

_assetPools.Add(_mockPool1.Object);
_assetPools.Add(_mockPool2.Object);
_aggregatingPool = new MultiAssetPool(_assetPools); // Use MultiAssetPool

// Request 3 assets. Pool1 has 2, Pool2 has 1.
var result = (await _aggregatingPool.GetAssets(3, CancellationToken.None)).ToList();
var result = (await _aggregatingPool.GetAssets(3, _mockRequestContext.Object, CancellationToken.None)).ToList();
Assert.That(result.Count, Is.EqualTo(3));
// Check presence of all expected assets, order might vary based on AggregatingAssetPool internal logic
Assert.That(result, Does.Contain(assetP1A1));
Expand All @@ -121,7 +127,7 @@ public async Task GetAssets_PoolReturnsFewerAssetsThanCountSuggests_HandlesGrace

// Pool1 reports 5 assets, but its queue only has 1.
_mockPool1.Setup(p => p.GetAssetCount(It.IsAny<CancellationToken>())).ReturnsAsync(5L);
_mockPool1.Setup(p => p.GetAssets(1, It.IsAny<CancellationToken>()))
_mockPool1.Setup(p => p.GetAssets(1, _mockRequestContext.Object, It.IsAny<CancellationToken>()))
.ReturnsAsync(() =>
{
if (pool1AvailableAssets.Any())
Expand All @@ -138,7 +144,7 @@ public async Task GetAssets_PoolReturnsFewerAssetsThanCountSuggests_HandlesGrace
var originalPool2Assets = new List<AssetResponseDto> { p2a1 };
_mockPool2.Setup(p => p.GetAssetCount(It.IsAny<CancellationToken>()))
.ReturnsAsync(() => (long)pool2AvailableAssets.Count);
_mockPool2.Setup(p => p.GetAssets(1, It.IsAny<CancellationToken>()))
_mockPool2.Setup(p => p.GetAssets(1, _mockRequestContext.Object, It.IsAny<CancellationToken>()))
.ReturnsAsync(() => pool2AvailableAssets.Any() ? new List<AssetResponseDto> { pool2AvailableAssets.Dequeue() } : new List<AssetResponseDto>());

_assetPools.Add(_mockPool1.Object);
Expand All @@ -150,7 +156,7 @@ public async Task GetAssets_PoolReturnsFewerAssetsThanCountSuggests_HandlesGrace
// It will get p1a1 from pool1. Then pool1 is exhausted (its GetAssets(1,..) will return empty).
// Then it will get p2a1 from pool2. Then pool2 is exhausted.
// The loop in AggregatingAssetPool.GetAssets should break when GetNextAsset returns null.
var result = (await _aggregatingPool.GetAssets(5, CancellationToken.None)).ToList();
var result = (await _aggregatingPool.GetAssets(5, _mockRequestContext.Object, CancellationToken.None)).ToList();

Assert.That(result.Count, Is.EqualTo(2));
var expectedTotalAssets = originalPool1Assets.Concat(originalPool2Assets).ToList();
Expand All @@ -168,12 +174,12 @@ public async Task GetNextAssetBehavior_RetrievesAllAssets()

var q1 = new Queue<AssetResponseDto>(new[] { asset1, asset2 });
_mockPool1.Setup(p => p.GetAssetCount(It.IsAny<CancellationToken>())).ReturnsAsync(() => (long)q1.Count);
_mockPool1.Setup(p => p.GetAssets(1, It.IsAny<CancellationToken>()))
_mockPool1.Setup(p => p.GetAssets(1, _mockRequestContext.Object, It.IsAny<CancellationToken>()))
.ReturnsAsync(() => q1.Any() ? new List<AssetResponseDto> { q1.Dequeue() } : new List<AssetResponseDto>());

var q2 = new Queue<AssetResponseDto>(new[] { asset3 });
_mockPool2.Setup(p => p.GetAssetCount(It.IsAny<CancellationToken>())).ReturnsAsync(() => (long)q2.Count);
_mockPool2.Setup(p => p.GetAssets(1, It.IsAny<CancellationToken>()))
_mockPool2.Setup(p => p.GetAssets(1, _mockRequestContext.Object, It.IsAny<CancellationToken>()))
.ReturnsAsync(() => q2.Any() ? new List<AssetResponseDto> { q2.Dequeue() } : new List<AssetResponseDto>());

_assetPools.Add(_mockPool1.Object);
Expand All @@ -182,7 +188,7 @@ public async Task GetNextAssetBehavior_RetrievesAllAssets()

var retrievedAssets = new List<AssetResponseDto>();
AssetResponseDto currentAsset;
while ((currentAsset = (await _aggregatingPool.GetAssets(1, CancellationToken.None)).FirstOrDefault()) != null)
while ((currentAsset = (await _aggregatingPool.GetAssets(1, _mockRequestContext.Object, CancellationToken.None)).FirstOrDefault()) != null)
{
retrievedAssets.Add(currentAsset);
}
Expand All @@ -191,18 +197,18 @@ public async Task GetNextAssetBehavior_RetrievesAllAssets()
Assert.That(retrievedAssets, Does.Contain(asset1));
Assert.That(retrievedAssets, Does.Contain(asset2));
Assert.That(retrievedAssets, Does.Contain(asset3));
Assert.That((await _aggregatingPool.GetAssets(1, CancellationToken.None)).FirstOrDefault(), Is.Null); // All exhausted
Assert.That((await _aggregatingPool.GetAssets(1, _mockRequestContext.Object, CancellationToken.None)).FirstOrDefault(), Is.Null); // All exhausted
}

[Test]
public async Task GetNextAssetBehavior_NoAssets_ReturnsNull()
{
_mockPool1.Setup(p => p.GetAssetCount(It.IsAny<CancellationToken>())).ReturnsAsync(0L);
_mockPool1.Setup(p => p.GetAssets(1, It.IsAny<CancellationToken>())).ReturnsAsync(new List<AssetResponseDto>());
_mockPool1.Setup(p => p.GetAssets(1, _mockRequestContext.Object, It.IsAny<CancellationToken>())).ReturnsAsync(new List<AssetResponseDto>());
_assetPools.Add(_mockPool1.Object);
_aggregatingPool = new MultiAssetPool(_assetPools); // Use MultiAssetPool

Assert.That((await _aggregatingPool.GetAssets(1, CancellationToken.None)).FirstOrDefault(), Is.Null);
Assert.That((await _aggregatingPool.GetAssets(1, _mockRequestContext.Object, CancellationToken.None)).FirstOrDefault(), Is.Null);
}
}
}
}
15 changes: 10 additions & 5 deletions ImmichFrame.Core.Tests/Logic/Pool/AlbumAssetsPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class AlbumAssetsPoolTests
private Mock<ImmichApi> _mockImmichApi;
private Mock<IAccountSettings> _mockAccountSettings;
private AlbumAssetsPool _albumAssetsPool;
private Mock<IRequestContext> _mockRequestContext;

[SetUp]
public void Setup()
Expand All @@ -29,6 +30,10 @@ public void Setup()
_mockAccountSettings = new Mock<IAccountSettings>();
_albumAssetsPool = new AlbumAssetsPool(_mockApiCache.Object, _mockImmichApi.Object, _mockAccountSettings.Object);

_mockRequestContext = new Mock<IRequestContext>();
// Default RequestContext
_mockRequestContext.Setup(x => x.AssetOffset).Returns(0);

_mockAccountSettings.SetupGet(s => s.Albums).Returns(new List<Guid>());
_mockAccountSettings.SetupGet(s => s.ExcludedAlbums).Returns(new List<Guid>());
}
Expand Down Expand Up @@ -56,7 +61,7 @@ public async Task LoadAssets_ReturnsAssetsPresentIIncludedNotExcludedAlbums()
.ReturnsAsync(new AlbumResponseDto { Assets = new List<AssetResponseDto> { assetB, assetC } });

// Act
var result = (await _albumAssetsPool.GetAssets(25)).ToList();
var result = (await _albumAssetsPool.GetAssets(25, _mockRequestContext.Object)).ToList();

// Assert
Assert.That(result.Count, Is.EqualTo(2));
Expand All @@ -75,7 +80,7 @@ public async Task LoadAssets_NoIncludedAlbums_ReturnsEmpty()
.ReturnsAsync(new AlbumResponseDto { Assets = new List<AssetResponseDto> { CreateAsset("excluded_only") } });


var result = (await _albumAssetsPool.GetAssets(25)).ToList();
var result = (await _albumAssetsPool.GetAssets(25, _mockRequestContext.Object)).ToList();
Assert.That(result, Is.Empty);
}

Expand All @@ -89,7 +94,7 @@ public async Task LoadAssets_NoExcludedAlbums_ReturnsAlbums()
_mockImmichApi.Setup(api => api.GetAlbumInfoAsync(album1Id, null, null, It.IsAny<CancellationToken>()))
.ReturnsAsync(new AlbumResponseDto { Assets = new List<AssetResponseDto> { CreateAsset("A") } });

var result = (await _albumAssetsPool.GetAssets(25)).ToList();
var result = (await _albumAssetsPool.GetAssets(25, _mockRequestContext.Object)).ToList();
Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result.Any(a => a.Id == "A"));
}
Expand All @@ -99,7 +104,7 @@ public async Task LoadAssets_NullAlbums_ReturnsEmpty()
{
_mockAccountSettings.SetupGet(s => s.Albums).Returns((List<Guid>)null);

var result = (await _albumAssetsPool.GetAssets(25)).ToList();
var result = (await _albumAssetsPool.GetAssets(25, _mockRequestContext.Object)).ToList();
Assert.That(result, Is.Empty);

// the absence of an error, whereas before a null pointer exception would be thrown, indicates success.
Expand All @@ -110,7 +115,7 @@ public async Task LoadAssets_NullExcludedAlbums_Succeeds()
{
_mockAccountSettings.SetupGet(s => s.ExcludedAlbums).Returns((List<Guid>)null);

var result = (await _albumAssetsPool.GetAssets(25)).ToList();
var result = (await _albumAssetsPool.GetAssets(25, _mockRequestContext.Object)).ToList();
Assert.That(result, Is.Empty);

// the absence of an error, whereas before a null pointer exception would be thrown, indicates success.
Expand Down
15 changes: 10 additions & 5 deletions ImmichFrame.Core.Tests/Logic/Pool/AllAssetsPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class AllAssetsPoolTests
private Mock<IApiCache> _mockApiCache;
private Mock<ImmichApi> _mockImmichApi;
private Mock<IAccountSettings> _mockAccountSettings;
private Mock<IRequestContext> _mockRequestContext;
private AllAssetsPool _allAssetsPool;

[SetUp]
Expand All @@ -20,6 +21,7 @@ public void Setup()
_mockApiCache = new Mock<IApiCache>();
_mockImmichApi = new Mock<ImmichApi>(null, null);
_mockAccountSettings = new Mock<IAccountSettings>();
_mockRequestContext = new Mock<IRequestContext>();
_allAssetsPool = new AllAssetsPool(_mockApiCache.Object, _mockImmichApi.Object, _mockAccountSettings.Object);

// Default account settings
Expand All @@ -43,6 +45,9 @@ public void Setup()
It.IsAny<Func<Task<IEnumerable<AssetResponseDto>>>>()
))
.Returns<string, Func<Task<IEnumerable<AssetResponseDto>>>>(async (key, factory) => await factory());

// Default RequestContext
_mockRequestContext.Setup(x => x.AssetOffset).Returns(0);
}

private List<AssetResponseDto> CreateSampleAssets(int count, string idPrefix, AssetTypeEnum type, int? rating = null)
Expand Down Expand Up @@ -111,7 +116,7 @@ public async Task GetAssets_CallsSearchRandomAsync_WithCorrectParameters_OnlyIma
.ReturnsAsync(returnedAssets.Where(a => a.Type == AssetTypeEnum.IMAGE).ToList());

// Act
var assets = await _allAssetsPool.GetAssets(requestedImageCount);
var assets = await _allAssetsPool.GetAssets(requestedImageCount, _mockRequestContext.Object);

// Assert
Assert.That(assets.Count(), Is.EqualTo(requestedImageCount));
Expand Down Expand Up @@ -142,7 +147,7 @@ public async Task GetAssets_CallsSearchRandomAsync_WithCorrectParameters_ImagesA
.ReturnsAsync(returnedAssets.ToList());

// Act
var assets = await _allAssetsPool.GetAssets(requestedImageCount + requestedVideoCount);
var assets = await _allAssetsPool.GetAssets(requestedImageCount + requestedVideoCount, _mockRequestContext.Object);

// Assert
Assert.That(assets.Count(), Is.EqualTo(requestedImageCount + requestedVideoCount));
Expand All @@ -165,7 +170,7 @@ public async Task GetAssets_AppliesDateFilters_FromDays()
_mockImmichApi.Setup(api => api.SearchRandomAsync(It.IsAny<RandomSearchDto>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new List<AssetResponseDto>());

await _allAssetsPool.GetAssets(5);
await _allAssetsPool.GetAssets(5, _mockRequestContext.Object);

_mockImmichApi.Verify(api => api.SearchRandomAsync(
It.Is<RandomSearchDto>(dto => dto.TakenAfter.HasValue && dto.TakenAfter.Value.Date == expectedFromDate.Date),
Expand All @@ -189,7 +194,7 @@ public async Task GetAssets_ExcludesAssetsFromExcludedAlbums()
.ReturnsAsync(new AlbumResponseDto { Assets = new List<AssetResponseDto> { excludedAsset }, AssetCount = 1 });

// Act
var result = (await _allAssetsPool.GetAssets(4)).ToList();
var result = (await _allAssetsPool.GetAssets(4, _mockRequestContext.Object)).ToList();

// Assert
Assert.That(result.Count, Is.EqualTo(3));
Expand All @@ -210,7 +215,7 @@ public async Task GetAssets_NullExcludedAlbums_Succeeds()
.ReturnsAsync(allAssets);

// Act
var result = (await _allAssetsPool.GetAssets(5)).ToList();
var result = (await _allAssetsPool.GetAssets(5, _mockRequestContext.Object)).ToList();

// Assert
Assert.That(result.Count, Is.EqualTo(5));
Expand Down
Loading