Files
jobtrackingapp/JobTrackerApi.Tests/AvatarRetentionTests.cs
T

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); }
}
}