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
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void Setup()
// AggregatingAssetPool takes IEnumerable<IAssetPool> in constructor
}

private AssetResponseDto CreateAsset(string id) => new AssetResponseDto { Id = id, OriginalPath = $"/path/{id}.jpg", Type = AssetTypeEnum.IMAGE, ExifInfo = new ExifResponseDto() };
private AssetResponseDto CreateAsset(string id) => new AssetResponseDto { Id = FixtureHelpers.GuidFor(id), OriginalPath = $"/path/{id}.jpg", Type = AssetTypeEnum.IMAGE, ExifInfo = new ExifResponseDto() };

[Test]
public async Task GetAssetCount_NoPools_ReturnsZero()
Expand Down
28 changes: 14 additions & 14 deletions ImmichFrame.Core.Tests/Logic/Pool/AlbumAssetsPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void Setup()
_mockAccountSettings.SetupGet(s => s.ExcludedAlbums).Returns(new List<Guid>());
}

private AssetResponseDto CreateAsset(string id) => new AssetResponseDto { Id = id, Type = AssetTypeEnum.IMAGE };
private AssetResponseDto CreateAsset(string id) => new AssetResponseDto { Id = FixtureHelpers.GuidFor(id), Type = AssetTypeEnum.IMAGE };

[Test]
public async Task LoadAssets_ReturnsAssetsPresentIIncludedNotExcludedAlbums()
Expand All @@ -50,29 +50,29 @@ public async Task LoadAssets_ReturnsAssetsPresentIIncludedNotExcludedAlbums()
_mockAccountSettings.SetupGet(s => s.Albums).Returns(new List<Guid> { album1Id });
_mockAccountSettings.SetupGet(s => s.ExcludedAlbums).Returns(new List<Guid> { excludedAlbumId });

_mockImmichApi.Setup(api => api.GetAlbumInfoAsync(album1Id, null, null, It.IsAny<CancellationToken>()))
.ReturnsAsync(new AlbumResponseDto { Assets = new List<AssetResponseDto> { assetA, assetB, assetD } });
_mockImmichApi.Setup(api => api.GetAlbumInfoAsync(excludedAlbumId, null, null, It.IsAny<CancellationToken>()))
.ReturnsAsync(new AlbumResponseDto { Assets = new List<AssetResponseDto> { assetB, assetC } });
_mockImmichApi.Setup(api => api.SearchAssetsAsync(It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(album1Id)), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SearchResponseDto { Assets = new SearchAssetResponseDto { Items = new List<AssetResponseDto> { assetA, assetB, assetD }, Total = 3 } });
_mockImmichApi.Setup(api => api.SearchAssetsAsync(It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(excludedAlbumId)), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SearchResponseDto { Assets = new SearchAssetResponseDto { Items = new List<AssetResponseDto> { assetB, assetC }, Total = 2 } });

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

// Assert
Assert.That(result.Count, Is.EqualTo(2));
Assert.That(result.Any(a => a.Id == "A"));
Assert.That(result.Any(a => a.Id == "D"));
_mockImmichApi.Verify(api => api.GetAlbumInfoAsync(album1Id, null, null, It.IsAny<CancellationToken>()), Times.Once);
_mockImmichApi.Verify(api => api.GetAlbumInfoAsync(excludedAlbumId, null, null, It.IsAny<CancellationToken>()), Times.Once);
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("A")));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("D")));
_mockImmichApi.Verify(api => api.SearchAssetsAsync(It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(album1Id)), It.IsAny<CancellationToken>()), Times.Once);
_mockImmichApi.Verify(api => api.SearchAssetsAsync(It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(excludedAlbumId)), It.IsAny<CancellationToken>()), Times.Once);
}

[Test]
public async Task LoadAssets_NoIncludedAlbums_ReturnsEmpty()
{
_mockAccountSettings.SetupGet(s => s.Albums).Returns(new List<Guid>());
_mockAccountSettings.SetupGet(s => s.ExcludedAlbums).Returns(new List<Guid> { Guid.NewGuid() });
_mockImmichApi.Setup(api => api.GetAlbumInfoAsync(It.IsAny<Guid>(), null, null, It.IsAny<CancellationToken>()))
.ReturnsAsync(new AlbumResponseDto { Assets = new List<AssetResponseDto> { CreateAsset("excluded_only") } });
_mockImmichApi.Setup(api => api.SearchAssetsAsync(It.IsAny<MetadataSearchDto>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SearchResponseDto { Assets = new SearchAssetResponseDto { Items = new List<AssetResponseDto> { CreateAsset("excluded_only") }, Total = 1 } });


var result = (await _albumAssetsPool.GetAssets(25)).ToList();
Expand All @@ -86,12 +86,12 @@ public async Task LoadAssets_NoExcludedAlbums_ReturnsAlbums()
_mockAccountSettings.SetupGet(s => s.Albums).Returns(new List<Guid> { album1Id });
_mockAccountSettings.SetupGet(s => s.ExcludedAlbums).Returns(new List<Guid>()); // Empty excluded

_mockImmichApi.Setup(api => api.GetAlbumInfoAsync(album1Id, null, null, It.IsAny<CancellationToken>()))
.ReturnsAsync(new AlbumResponseDto { Assets = new List<AssetResponseDto> { CreateAsset("A") } });
_mockImmichApi.Setup(api => api.SearchAssetsAsync(It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(album1Id)), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SearchResponseDto { Assets = new SearchAssetResponseDto { Items = new List<AssetResponseDto> { CreateAsset("A") }, Total = 1 } });

var result = (await _albumAssetsPool.GetAssets(25)).ToList();
Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result.Any(a => a.Id == "A"));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("A")));
}

[Test]
Expand Down
19 changes: 10 additions & 9 deletions ImmichFrame.Core.Tests/Logic/Pool/AllAssetsPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public void Setup()
private List<AssetResponseDto> CreateSampleAssets(int count, string idPrefix, AssetTypeEnum type, int? rating = null)
{
return Enumerable.Range(0, count)
.Select(i => new AssetResponseDto { Id = $"{idPrefix}{i}", Type = type, ExifInfo = new ExifResponseDto { Rating = rating } })
.Select(i => new AssetResponseDto { Id = FixtureHelpers.GuidFor($"{idPrefix}{i}"), Type = type, ExifInfo = new ExifResponseDto { Rating = rating } })
.ToList();
}

Expand Down Expand Up @@ -177,25 +177,26 @@ public async Task GetAssets_ExcludesAssetsFromExcludedAlbums()
{
// Arrange
var mainAssets = CreateSampleImageAssets(3, "main"); // main0, main1, main2
var excludedAsset = new AssetResponseDto { Id = "excluded1", Type = AssetTypeEnum.IMAGE };
var excludedAsset = new AssetResponseDto { Id = FixtureHelpers.GuidFor("excluded1"), Type = AssetTypeEnum.IMAGE };
var assetsToReturnFromSearch = new List<AssetResponseDto>(mainAssets) { excludedAsset };

var excludedAlbumId = Guid.NewGuid();
_mockAccountSettings.SetupGet(s => s.ExcludedAlbums).Returns(new List<Guid> { excludedAlbumId });

_mockImmichApi.Setup(api => api.SearchRandomAsync(It.IsAny<RandomSearchDto>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(assetsToReturnFromSearch);
_mockImmichApi.Setup(api => api.GetAlbumInfoAsync(excludedAlbumId, null, null, It.IsAny<CancellationToken>()))
.ReturnsAsync(new AlbumResponseDto { Assets = new List<AssetResponseDto> { excludedAsset }, AssetCount = 1 });
_mockImmichApi.Setup(api => api.SearchAssetsAsync(It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(excludedAlbumId)), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SearchResponseDto { Assets = new SearchAssetResponseDto { Items = new List<AssetResponseDto> { excludedAsset }, Total = 1 } });

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

// Assert
Assert.That(result.Count, Is.EqualTo(3));
Assert.That(result.Any(a => a.Id == "excluded1"), Is.False);
Assert.That(result.All(a => a.Id.StartsWith("main")));
_mockImmichApi.Verify(api => api.GetAlbumInfoAsync(excludedAlbumId, null, null, It.IsAny<CancellationToken>()), Times.Once);
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("excluded1")), Is.False);
var mainIds = mainAssets.Select(a => a.Id);
Assert.That(result.All(a => mainIds.Contains(a.Id)));
_mockImmichApi.Verify(api => api.SearchAssetsAsync(It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(excludedAlbumId)), It.IsAny<CancellationToken>()), Times.Once);
}

[Test]
Expand All @@ -216,7 +217,7 @@ public async Task GetAssets_NullExcludedAlbums_Succeeds()
Assert.That(result.Count, Is.EqualTo(5));
Assert.That(result, Is.EqualTo(allAssets));

// Verify that GetAlbumInfoAsync was never called since ExcludedAlbums is null
_mockImmichApi.Verify(api => api.GetAlbumInfoAsync(It.IsAny<Guid>(), null, null, It.IsAny<CancellationToken>()), Times.Never);
// Verify that no excluded-album lookup happened since ExcludedAlbums is null
_mockImmichApi.Verify(api => api.SearchAssetsAsync(It.IsAny<MetadataSearchDto>(), It.IsAny<CancellationToken>()), Times.Never);
}
}
40 changes: 20 additions & 20 deletions ImmichFrame.Core.Tests/Logic/Pool/CachingApiAssetsPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ private List<AssetResponseDto> CreateSampleAssets()
{
return new List<AssetResponseDto>
{
new AssetResponseDto { Id = "1", Type = AssetTypeEnum.IMAGE, IsArchived = false, ExifInfo = new ExifResponseDto { DateTimeOriginal = DateTime.Now.AddDays(-10), Rating = 5 } },
new AssetResponseDto { Id = "2", Type = AssetTypeEnum.VIDEO, IsArchived = false, ExifInfo = new ExifResponseDto { DateTimeOriginal = DateTime.Now.AddDays(-10) } }, // Video asset
new AssetResponseDto { Id = "3", Type = AssetTypeEnum.IMAGE, IsArchived = true, ExifInfo = new ExifResponseDto { DateTimeOriginal = DateTime.Now.AddDays(-5), Rating = 3 } }, // Potentially filtered by archive status
new AssetResponseDto { Id = "4", Type = AssetTypeEnum.IMAGE, IsArchived = false, ExifInfo = new ExifResponseDto { DateTimeOriginal = DateTime.Now.AddDays(-2), Rating = 5 } },
new AssetResponseDto { Id = "5", Type = AssetTypeEnum.IMAGE, IsArchived = false, ExifInfo = new ExifResponseDto { DateTimeOriginal = DateTime.Now.AddYears(-1), Rating = 1 } },
new AssetResponseDto { Id = FixtureHelpers.GuidFor("1"), Type = AssetTypeEnum.IMAGE, IsArchived = false, ExifInfo = new ExifResponseDto { DateTimeOriginal = DateTime.Now.AddDays(-10), Rating = 5 } },
new AssetResponseDto { Id = FixtureHelpers.GuidFor("2"), Type = AssetTypeEnum.VIDEO, IsArchived = false, ExifInfo = new ExifResponseDto { DateTimeOriginal = DateTime.Now.AddDays(-10) } }, // Video asset
new AssetResponseDto { Id = FixtureHelpers.GuidFor("3"), Type = AssetTypeEnum.IMAGE, IsArchived = true, ExifInfo = new ExifResponseDto { DateTimeOriginal = DateTime.Now.AddDays(-5), Rating = 3 } }, // Potentially filtered by archive status
new AssetResponseDto { Id = FixtureHelpers.GuidFor("4"), Type = AssetTypeEnum.IMAGE, IsArchived = false, ExifInfo = new ExifResponseDto { DateTimeOriginal = DateTime.Now.AddDays(-2), Rating = 5 } },
new AssetResponseDto { Id = FixtureHelpers.GuidFor("5"), Type = AssetTypeEnum.IMAGE, IsArchived = false, ExifInfo = new ExifResponseDto { DateTimeOriginal = DateTime.Now.AddYears(-1), Rating = 1 } },
};
}

Expand Down Expand Up @@ -197,8 +197,8 @@ public async Task ApplyAccountFilters_FiltersArchived()
var result = (await _testPool.GetAssets(5)).ToList(); // Request more than available to get all filtered

// Assert
Assert.That(result.Any(a => a.Id == "2"), Is.False); // Video asset filtered out by default
Assert.That(result.Any(a => a.Id == "3"), Is.False); // Archived asset
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("2")), Is.False); // Video asset filtered out by default
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("3")), Is.False); // Archived asset
Assert.That(result.Count, Is.EqualTo(3)); // 1, 4, 5
}

Expand All @@ -215,7 +215,7 @@ public async Task ApplyAccountFilters_FiltersArchived_WithVideo()
var result = (await _testPool.GetAssets(5)).ToList(); // Request more than available to get all filtered

// Assert
Assert.That(result.Any(a => a.Id == "3"), Is.False);
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("3")), Is.False);
Assert.That(result.Count, Is.EqualTo(4)); // 1, 2, 4, 5
}

Expand All @@ -238,9 +238,9 @@ public async Task ApplyAccountFilters_FiltersImagesUntilDate()
// Expected: Assets "1", "5"
Assert.That(result.All(a => a.ExifInfo?.DateTimeOriginal <= untilDate));
Assert.That(result.Count, Is.EqualTo(2), string.Join(",", result.Select(x => x.Id)));
Assert.That(result.Any(a => a.Id == "1"));
Assert.That(result.Any(a => a.Id == "2"), Is.False); // Video asset
Assert.That(result.Any(a => a.Id == "5"));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("1")));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("2")), Is.False); // Video asset
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("5")));
}

[Test]
Expand All @@ -262,8 +262,8 @@ public async Task ApplyAccountFilters_FiltersImagesFromDate()
// Expected: Asset "3", "4"
Assert.That(result.All(a => a.ExifInfo?.DateTimeOriginal >= fromDate));
Assert.That(result.Count, Is.EqualTo(2), string.Join(",", result.Select(x => x.Id)));
Assert.That(result.Any(a => a.Id == "3"));
Assert.That(result.Any(a => a.Id == "4"));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("3")));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("4")));
}

[Test]
Expand All @@ -286,8 +286,8 @@ public async Task ApplyAccountFilters_FiltersImagesFromDays()
// Expected: Asset "3", "4"
Assert.That(result.All(a => a.ExifInfo?.DateTimeOriginal >= fromDate));
Assert.That(result.Count, Is.EqualTo(2), string.Join(",", result.Select(x => x.Id)));
Assert.That(result.Any(a => a.Id == "3"));
Assert.That(result.Any(a => a.Id == "4"));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("3")));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("4")));
}

[Test]
Expand All @@ -307,8 +307,8 @@ public async Task ApplyAccountFilters_FiltersRating()
// Expected: Asset "1", "4" (both rating 5)
Assert.That(result.All(a => a.ExifInfo?.Rating == 5));
Assert.That(result.Count, Is.EqualTo(2), string.Join(",", result.Select(x => x.Id)));
Assert.That(result.Any(a => a.Id == "1"));
Assert.That(result.Any(a => a.Id == "4"));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("1")));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("4")));
}

[Test]
Expand All @@ -329,8 +329,8 @@ public async Task ApplyAccountFilters_CombinedFilters()
// Assert
// Expected: Assets "1", "4"
Assert.That(result.Count, Is.EqualTo(2));
Assert.That(result.Any(a => a.Id == "1"));
Assert.That(result.Any(a => a.Id == "4"));
Assert.That(result.Any(a => a.Id == "3" || a.Id == "5" || a.Id == "2"), Is.False);
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("1")));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("4")));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("3") || a.Id == FixtureHelpers.GuidFor("5") || a.Id == FixtureHelpers.GuidFor("2")), Is.False);
}
}
6 changes: 3 additions & 3 deletions ImmichFrame.Core.Tests/Logic/Pool/FavoriteAssetsPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void Setup()
_favoriteAssetsPool = new TestableFavoriteAssetsPool(_mockApiCache.Object, _mockImmichApi.Object, _mockAccountSettings.Object);
}

private AssetResponseDto CreateAsset(string id, AssetTypeEnum type = AssetTypeEnum.IMAGE) => new AssetResponseDto { Id = id, Type = type };
private AssetResponseDto CreateAsset(string id, AssetTypeEnum type = AssetTypeEnum.IMAGE) => new AssetResponseDto { Id = FixtureHelpers.GuidFor(id), Type = type };
private SearchResponseDto CreateSearchResult(List<AssetResponseDto> assets, int total) =>
new SearchResponseDto { Assets = new SearchAssetResponseDto { Items = assets, Total = total } };

Expand All @@ -60,8 +60,8 @@ public async Task LoadAssets_CallsSearchAssetsAsync_WithFavoriteTrue_AndPaginate

// Assert
Assert.That(result.Count, Is.EqualTo(batchSize + 50));
Assert.That(result.Any(a => a.Id == "fav_p1_0"));
Assert.That(result.Any(a => a.Id == "fav_p2_49"));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("fav_p1_0")));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("fav_p2_49")));

_mockImmichApi.Verify(api => api.SearchAssetsAsync(
It.Is<MetadataSearchDto>(dto =>
Expand Down
9 changes: 9 additions & 0 deletions ImmichFrame.Core.Tests/Logic/Pool/FixtureHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Logging;
namespace ImmichFrame.Core.Tests.Logic.Pool;

public static class FixtureHelpers
{
// v3 changed asset/album/person ids from string to Guid. Tests still want
// readable, stable ids (e.g. "p1_0") for arranging mocks and asserting
// membership, so we deterministically derive a Guid from a seed string:
// the same seed always maps to the same Guid.
public static Guid GuidFor(string seed) =>
new(MD5.HashData(Encoding.UTF8.GetBytes(seed)));

public static ILogger<T> TestLogger<T>()
{
var loggerFactory = LoggerFactory.Create(builder =>
Expand Down
Loading
Loading