Fix media placeholders and image cache

This commit is contained in:
sevenhill
2026-07-06 09:10:31 +03:00
parent 9940f9e43c
commit 61d7aae7eb
10 changed files with 275 additions and 16 deletions
+92
View File
@@ -773,6 +773,40 @@ public sealed class ApiSmokeTests : IDisposable
Assert.StartsWith("preview:", backfilled.ExternalId);
}
[Fact]
public async Task GetMessagesDoesNotBackfillGenericMediaPreviewWhenAttachmentRowIsMissing()
{
using var factory = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.RemoveAll<IHostedService>();
});
});
using var client = factory.CreateClient();
await LoginAsync(client);
var marker = Guid.NewGuid().ToString("N");
var chat = await CreateDirectChatAsync(client, $"generic-media-preview-chat-{marker}", "Generic Media Preview");
await using (var scope = factory.Services.CreateAsyncScope())
{
var db = scope.ServiceProvider.GetRequiredService<QMaxDbContext>();
var stored = await db.Chats.SingleAsync(x => x.Id == chat.Id);
stored.LastMessagePreview = "\u0424\u043e\u0442\u043e";
stored.LastMessageAt = DateTimeOffset.UtcNow;
stored.UnreadCount = 1;
await db.SaveChangesAsync();
}
var messages = await client.GetFromJsonAsync<MessageDto[]>($"/api/chats/{chat.Id}/messages", JsonOptions);
Assert.NotNull(messages);
Assert.DoesNotContain(messages!, x => x.ExternalId?.StartsWith("preview:", StringComparison.Ordinal) == true);
await using var verifyScope = factory.Services.CreateAsyncScope();
var verifyDb = verifyScope.ServiceProvider.GetRequiredService<QMaxDbContext>();
Assert.False(await verifyDb.Messages.AnyAsync(x => x.ChatId == chat.Id));
}
[Fact]
public async Task SyncCreatesMessageRowForFreshPreviewOnlyIncomingUpdate()
{
@@ -836,6 +870,64 @@ public sealed class ApiSmokeTests : IDisposable
}
}
[Fact]
public async Task SyncDoesNotCreateTextOnlyMessageForGenericMediaPreviewOnlyIncomingUpdate()
{
var marker = Guid.NewGuid().ToString("N");
var externalId = $"generic-media-sync-chat-{marker}";
var now = DateTimeOffset.UtcNow;
var bridge = new ResolvingDeleteMaxBridgeClient
{
Updates =
[
new MaxChatUpdate(
externalId,
"Generic Media Sync",
null,
now,
[],
"\u0424\u043e\u0442\u043e",
false,
now,
now.ToOffset(TimeSpan.FromHours(3)).ToString("HH:mm"),
$"https://max.test/chat/{externalId}")
]
};
using var factory = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.RemoveAll<IHostedService>();
services.RemoveAll<IMaxBridgeClient>();
services.AddSingleton<IMaxBridgeClient>(bridge);
});
});
using var client = factory.CreateClient();
await LoginAsync(client);
var chat = await CreateDirectChatAsync(client, externalId, "Generic Media Sync");
await using (var scope = factory.Services.CreateAsyncScope())
{
var db = scope.ServiceProvider.GetRequiredService<QMaxDbContext>();
var stored = await db.Chats.SingleAsync(x => x.Id == chat.Id);
stored.LastMessagePreview = "previous preview";
stored.LastMessageAt = now.AddMinutes(-5);
await db.SaveChangesAsync();
}
var sync = await client.PostAsync("/api/max/sync", null);
await AssertStatusAsync(HttpStatusCode.OK, sync);
await using var verifyScope = factory.Services.CreateAsyncScope();
var verifyDb = verifyScope.ServiceProvider.GetRequiredService<QMaxDbContext>();
var storedChat = await verifyDb.Chats.SingleAsync(x => x.Id == chat.Id);
Assert.Equal("\u041c\u0435\u0434\u0438\u0430", storedChat.LastMessagePreview);
Assert.False(await verifyDb.Messages.AnyAsync(x =>
x.ChatId == chat.Id &&
x.ExternalId != null &&
x.ExternalId.StartsWith("preview:")));
}
[Fact]
public async Task BulkDeleteOutboxRefreshesStaleChatUrlAfterMenuOpenFailure()
{