94 lines
5.0 KiB
C#
94 lines
5.0 KiB
C#
using JobTrackerApi.Models;
|
|
using JobTrackerApi.Services;
|
|
using JobTrackerApi.Tests.TestSupport;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace JobTrackerApi.Tests;
|
|
|
|
public sealed class AvatarRetentionTests
|
|
{
|
|
[Fact]
|
|
public async Task Hourly_retention_removes_old_orphans_but_preserves_current_recent_and_unrelated_files()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "avatar-retention-" + Guid.NewGuid().ToString("N"));
|
|
try
|
|
{
|
|
var paths = Paths(root);
|
|
await using var db = TestHostFactory.CreateInMemoryDb();
|
|
var current = await AvatarStorage.StageAsync(root, "owner", new byte[] { 1 }, default);
|
|
var orphan = await AvatarStorage.StageAsync(root, "owner", new byte[] { 2 }, default);
|
|
var recent = await AvatarStorage.StageAsync(root, "owner", new byte[] { 3 }, default);
|
|
var missingOwner = await AvatarStorage.StageAsync(root, "deleted-owner", new byte[] { 4 }, default);
|
|
var legacyOrphan = await AvatarStorage.StoreAsync(root, "owner", new byte[] { 5 }, "image/webp", default);
|
|
var legacyCurrent = await AvatarStorage.StoreAsync(root, "legacy-owner", new byte[] { 6 }, "image/jpeg", default);
|
|
var unrelated = Path.Combine(Path.GetDirectoryName(orphan[5..])!, "unrelated.png");
|
|
await File.WriteAllTextAsync(unrelated, "keep");
|
|
var now = DateTimeOffset.UtcNow;
|
|
foreach (var file in new[] { current, orphan, missingOwner, legacyOrphan, legacyCurrent }) File.SetLastWriteTimeUtc(file[5..], now.AddDays(-2).UtcDateTime);
|
|
db.Users.Add(new ApplicationUser { Id = "owner", UserName = "owner", AvatarImageDataUrl = current });
|
|
db.Users.Add(new ApplicationUser { Id = "legacy-owner", UserName = "legacy-owner", AvatarImageDataUrl = legacyCurrent });
|
|
await db.SaveChangesAsync();
|
|
|
|
await CvImportRetentionHostedService.SweepAsync(db, paths, now, default);
|
|
|
|
Assert.True(File.Exists(current[5..]));
|
|
Assert.True(File.Exists(recent[5..]));
|
|
Assert.True(File.Exists(unrelated));
|
|
Assert.False(File.Exists(orphan[5..]));
|
|
Assert.False(File.Exists(missingOwner[5..]));
|
|
Assert.False(File.Exists(legacyOrphan[5..]));
|
|
Assert.True(File.Exists(legacyCurrent[5..]));
|
|
}
|
|
finally { if (Directory.Exists(root)) Directory.Delete(root, true); }
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Account_inventory_includes_only_its_own_staged_portraits_even_without_a_current_avatar()
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "avatar-inventory-" + Guid.NewGuid().ToString("N"));
|
|
try
|
|
{
|
|
var paths = Paths(root);
|
|
await using var db = TestHostFactory.CreateInMemoryDb();
|
|
var own = await AvatarStorage.StageAsync(root, "owner", new byte[] { 1 }, default);
|
|
var other = await AvatarStorage.StageAsync(root, "other", new byte[] { 2 }, default);
|
|
db.Users.Add(new ApplicationUser { Id = "owner", UserName = "owner" });
|
|
await db.SaveChangesAsync();
|
|
var inventory = await new AccountOwnedFileInventory(db, paths, Mock.Of<IAttachmentStorage>()).BuildAsync("owner", default);
|
|
var avatar = Assert.Single(inventory.Files, file => file.Category == "avatar");
|
|
Assert.Equal(own[5..], avatar.SourcePath);
|
|
Assert.DoesNotContain(inventory.Files, file => file.SourcePath == other[5..]);
|
|
}
|
|
finally { if (Directory.Exists(root)) Directory.Delete(root, true); }
|
|
}
|
|
|
|
private static AppPaths Paths(string root) => new(
|
|
new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string?> { ["Data:Root"] = root }).Build(),
|
|
Mock.Of<IHostEnvironment>(environment => environment.ContentRootPath == root));
|
|
|
|
[Theory]
|
|
[InlineData("image/png")]
|
|
[InlineData("image/jpeg")]
|
|
[InlineData("image/webp")]
|
|
public async Task Account_inventory_covers_exact_legacy_leftovers_after_portrait_replacement(string contentType)
|
|
{
|
|
var root = Path.Combine(Path.GetTempPath(), "avatar-legacy-inventory-" + Guid.NewGuid().ToString("N"));
|
|
try
|
|
{
|
|
var paths = Paths(root);
|
|
await using var db = TestHostFactory.CreateInMemoryDb();
|
|
var legacy = await AvatarStorage.StoreAsync(root, "owner", new byte[] { 1 }, contentType, default);
|
|
var current = await AvatarStorage.StageAsync(root, "owner", new byte[] { 2 }, default);
|
|
db.Users.Add(new ApplicationUser { Id = "owner", UserName = "owner", AvatarImageDataUrl = current });
|
|
await db.SaveChangesAsync();
|
|
var inventory = await new AccountOwnedFileInventory(db, paths, Mock.Of<IAttachmentStorage>()).BuildAsync("owner", default);
|
|
Assert.Contains(inventory.Files, file => file.SourcePath == legacy[5..]);
|
|
Assert.Single(inventory.Files, file => file.SourcePath == current[5..]);
|
|
}
|
|
finally { if (Directory.Exists(root)) Directory.Delete(root, true); }
|
|
}
|
|
}
|