Clean up legacy outgoing attachment duplicates

This commit is contained in:
sevenhill
2026-07-05 00:26:18 +03:00
parent b022218a1f
commit b518f22a09
14 changed files with 407 additions and 2 deletions
+95 -2
View File
@@ -712,7 +712,7 @@ public sealed class ApiSmokeTests : IDisposable
{
Message = message,
OriginalFileName = "sent-photo.jpg",
StorageFileName = "sent-photo.jpg",
StorageFileName = $"{Guid.NewGuid():N}-sent-photo.jpg",
ContentType = "image/jpeg",
FileSizeBytes = 1024,
Kind = AttachmentKind.Image
@@ -791,6 +791,94 @@ public sealed class ApiSmokeTests : IDisposable
}
}
[Fact]
public async Task DatabaseCleanupRemovesLegacyWebFileAttachmentDuplicateAndClearsSelfUnread()
{
var databasePath = Path.Combine(Path.GetTempPath(), $"qmax-legacy-web-file-test-{Guid.NewGuid():N}.db");
var dbOptions = new DbContextOptionsBuilder<QMaxDbContext>()
.UseSqlite($"Data Source={databasePath};Pooling=False")
.Options;
var chatId = Guid.NewGuid();
const string attachmentHash = "same-photo-sha";
var legacySentAt = DateTimeOffset.UtcNow.AddMinutes(-40);
try
{
await using (var db = new QMaxDbContext(dbOptions))
{
await db.Database.EnsureCreatedAsync();
db.Chats.Add(new Chat
{
Id = chatId,
Title = "Legacy Photo",
LastMessagePreview = "\u041c\u0435\u0434\u0438\u0430",
LastMessageAt = legacySentAt.AddMinutes(30),
UnreadCount = 2
});
var legacyMessage = new Message
{
ChatId = chatId,
ExternalId = "web-file-123",
Direction = MessageDirection.Outgoing,
SentAt = legacySentAt,
DeliveryState = MessageDeliveryState.Sent
};
legacyMessage.Attachments.Add(new MessageAttachment
{
OriginalFileName = "legacy.jpg",
StorageFileName = $"{Guid.NewGuid():N}.jpg",
ContentType = "image/jpeg",
FileSizeBytes = 10,
Sha256 = attachmentHash,
Kind = AttachmentKind.Image
});
var confirmedMessage = new Message
{
ChatId = chatId,
ExternalId = "domhist:real-photo",
Direction = MessageDirection.Outgoing,
SentAt = legacySentAt.AddMinutes(30),
DeliveryState = MessageDeliveryState.Sent
};
confirmedMessage.Attachments.Add(new MessageAttachment
{
OriginalFileName = "confirmed.jpg",
StorageFileName = $"{Guid.NewGuid():N}.jpg",
ContentType = "image/jpeg",
FileSizeBytes = 10,
Sha256 = attachmentHash,
Kind = AttachmentKind.Image
});
db.Messages.AddRange(legacyMessage, confirmedMessage);
await db.SaveChangesAsync();
}
await using (var cleanupDb = new QMaxDbContext(dbOptions))
{
await QMaxDatabaseCleanup.RemoveLegacyWebFileAttachmentEchoesAsync(cleanupDb);
await QMaxDatabaseCleanup.ClearUnreadCountsForLatestOutgoingChatsAsync(cleanupDb);
}
await using var verifyDb = new QMaxDbContext(dbOptions);
var messages = await verifyDb.Messages
.Where(message => message.ChatId == chatId)
.ToListAsync();
Assert.DoesNotContain(messages, message => message.ExternalId == "web-file-123");
Assert.Contains(messages, message => message.ExternalId == "domhist:real-photo");
var chat = await verifyDb.Chats.FindAsync(chatId);
Assert.NotNull(chat);
Assert.Equal(0, chat!.UnreadCount);
}
finally
{
DeleteSqliteDatabase(databasePath);
}
}
[Fact]
public async Task AdminStatusRequiresPairingCode()
{
@@ -832,9 +920,14 @@ public sealed class ApiSmokeTests : IDisposable
public void Dispose()
{
_factory.Dispose();
DeleteSqliteDatabase(_databasePath);
}
private static void DeleteSqliteDatabase(string databasePath)
{
foreach (var suffix in new[] { "", "-shm", "-wal" })
{
var path = _databasePath + suffix;
var path = databasePath + suffix;
if (File.Exists(path))
{
File.Delete(path);