Retry chat bulk actions after stale menu URL

This commit is contained in:
sevenhill
2026-07-05 21:40:08 +03:00
parent 1cffa1ef8d
commit f57faead53
2 changed files with 55 additions and 1 deletions
+53
View File
@@ -682,6 +682,45 @@ public sealed class ApiSmokeTests : IDisposable
Assert.DoesNotContain(chats!, x => x.Id == chat.Id);
}
[Fact]
public async Task BulkDeleteRefreshesStaleChatUrlAfterMenuOpenFailure()
{
const string externalId = "stale-menu-url-chat";
const string staleUrl = "https://max.test/chat/wrong-chat";
var bridge = new ResolvingDeleteMaxBridgeClient(staleUrl);
using var factory = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
services.RemoveAll<IMaxBridgeClient>();
services.AddSingleton<IMaxBridgeClient>(bridge);
});
});
using var client = factory.CreateClient();
await LoginAsync(client);
var chat = await CreateDirectChatAsync(client, externalId, "Stale Menu Url");
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.WebUrl = staleUrl;
await db.SaveChangesAsync();
}
var response = await client.PostAsJsonAsync(
"/api/chats/delete",
new ChatBulkActionRequest([chat.Id]),
JsonOptions);
await AssertStatusAsync(HttpStatusCode.NoContent, response);
Assert.Equal(externalId, bridge.ResolvedExternalId);
Assert.Equal([staleUrl, $"https://max.test/chat/{externalId}"], bridge.DeleteChatUrls);
var chats = await client.GetFromJsonAsync<ChatDto[]>("/api/chats", JsonOptions);
Assert.DoesNotContain(chats!, x => x.Id == chat.Id);
}
[Fact]
public async Task FailedMaxTextSendMarksLocalMessageFailed()
{
@@ -1457,8 +1496,16 @@ public sealed class ApiSmokeTests : IDisposable
private sealed class ResolvingDeleteMaxBridgeClient : IMaxBridgeClient
{
private readonly string? _staleUrlToReject;
public ResolvingDeleteMaxBridgeClient(string? staleUrlToReject = null)
{
_staleUrlToReject = staleUrlToReject;
}
public string? ResolvedExternalId { get; private set; }
public string? DeleteChatUrl { get; private set; }
public List<string?> DeleteChatUrls { get; } = [];
public Task<MaxBridgeStatus> GetStatusAsync(CancellationToken cancellationToken)
{
@@ -1508,7 +1555,13 @@ public sealed class ApiSmokeTests : IDisposable
public Task<MaxActionResult> DeleteChatAsync(string externalChatId, string? chatUrl, CancellationToken cancellationToken)
{
DeleteChatUrls.Add(chatUrl);
DeleteChatUrl = chatUrl;
if (string.Equals(chatUrl, _staleUrlToReject, StringComparison.Ordinal))
{
return Task.FromResult(new MaxActionResult(false, "MAX chat menu was not opened."));
}
return Task.FromResult(string.IsNullOrWhiteSpace(chatUrl)
? new MaxActionResult(false, "MAX chat was not found or did not open.")
: new MaxActionResult(true, null));