From 8fe40e9fe31b47725da1255326de4743c6a4feb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Thu, 23 Apr 2026 15:00:55 +0200 Subject: [PATCH] Potential gmail history id fix. --- .../GmailSynchronizerRequestSuccessTests.cs | 45 ++++++++++++++++++- Wino.Core/Synchronizers/GmailSynchronizer.cs | 14 +++++- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/Wino.Core.Tests/Synchronizers/GmailSynchronizerRequestSuccessTests.cs b/Wino.Core.Tests/Synchronizers/GmailSynchronizerRequestSuccessTests.cs index 4e2f2fc8..14492c5d 100644 --- a/Wino.Core.Tests/Synchronizers/GmailSynchronizerRequestSuccessTests.cs +++ b/Wino.Core.Tests/Synchronizers/GmailSynchronizerRequestSuccessTests.cs @@ -19,6 +19,32 @@ namespace Wino.Core.Tests.Synchronizers; public sealed class GmailSynchronizerRequestSuccessTests { + [Fact] + public async Task UpdateAccountSyncIdentifierAsync_EmptyStoredIdentifier_PersistsFirstHistoryCursor() + { + var changeProcessor = new Mock(MockBehavior.Strict); + changeProcessor + .Setup(x => x.UpdateAccountDeltaSynchronizationIdentifierAsync(It.IsAny(), "123")) + .ReturnsAsync("123"); + + var synchronizer = CreateSynchronizer(changeProcessor.Object, synchronizationDeltaIdentifier: string.Empty); + + await InvokeUpdateAccountSyncIdentifierAsync(synchronizer, 123); + + changeProcessor.Verify(x => x.UpdateAccountDeltaSynchronizationIdentifierAsync(It.IsAny(), "123"), Times.Once); + } + + [Fact] + public async Task UpdateAccountSyncIdentifierAsync_OlderHistoryCursor_DoesNotRegressStoredCursor() + { + var changeProcessor = new Mock(MockBehavior.Strict); + var synchronizer = CreateSynchronizer(changeProcessor.Object, synchronizationDeltaIdentifier: "456"); + + await InvokeUpdateAccountSyncIdentifierAsync(synchronizer, 123); + + changeProcessor.Verify(x => x.UpdateAccountDeltaSynchronizationIdentifierAsync(It.IsAny(), It.IsAny()), Times.Never); + } + [Fact] public async Task ProcessSingleNativeRequestResponseAsync_BatchMarkReadRequest_PersistsLocalReadStateForEachMail() { @@ -209,13 +235,15 @@ public sealed class GmailSynchronizerRequestSuccessTests private static GmailSynchronizer CreateSynchronizer( IGmailChangeProcessor changeProcessor, - IGmailSynchronizerErrorHandlerFactory? errorFactory = null) + IGmailSynchronizerErrorHandlerFactory? errorFactory = null, + string? synchronizationDeltaIdentifier = null) { var account = new MailAccount { Id = Guid.NewGuid(), Name = "Gmail", - Address = "user@example.com" + Address = "user@example.com", + SynchronizationDeltaIdentifier = synchronizationDeltaIdentifier }; var authenticator = new Mock(MockBehavior.Loose); @@ -249,4 +277,17 @@ public sealed class GmailSynchronizerRequestSuccessTests task.Should().NotBeNull(); await task!; } + + private static async Task InvokeUpdateAccountSyncIdentifierAsync(GmailSynchronizer synchronizer, ulong historyId) + { + var method = typeof(GmailSynchronizer).GetMethod( + "UpdateAccountSyncIdentifierAsync", + BindingFlags.Instance | BindingFlags.NonPublic); + + method.Should().NotBeNull(); + + var task = method!.Invoke(synchronizer, [historyId]) as Task; + task.Should().NotBeNull(); + await task!; + } } diff --git a/Wino.Core/Synchronizers/GmailSynchronizer.cs b/Wino.Core/Synchronizers/GmailSynchronizer.cs index f6636dea..b9ef8c61 100644 --- a/Wino.Core/Synchronizers/GmailSynchronizer.cs +++ b/Wino.Core/Synchronizers/GmailSynchronizer.cs @@ -1897,9 +1897,19 @@ public class GmailSynchronizer : WinoSynchronizer currentIdentifier); + if (string.IsNullOrWhiteSpace(currentSynchronizationIdentifier)) + return true; + + if (!ulong.TryParse(currentSynchronizationIdentifier, out ulong currentIdentifier)) + { + _logger.Warning("Current Gmail history ID '{HistoryId}' is invalid for {Name}. Replacing it with {NewHistoryId}.", + currentSynchronizationIdentifier, Account.Name, newHistoryId); + return true; + } + + return newHistoryId > currentIdentifier; } private async Task UpdateAccountSyncIdentifierAsync(ulong? historyId)