fix(wallet): fix activity filter updates special case

This commit fixes a special case where the activity filter incremental
updates were reported as new even that they were present in a previous
update.

Updates status-desktop #12120
This commit is contained in:
Stefan 2024-03-07 23:21:19 +02:00 committed by Stefan Dunca
parent c8044bf400
commit e1c7c715aa
2 changed files with 81 additions and 4 deletions

View File

@ -580,3 +580,73 @@ func TestService_IncrementalUpdateFetchWindowNoReset(t *testing.T) {
}, common.NewAndSet(extraExpect{common.NewAndSet(2), nil})) }, common.NewAndSet(extraExpect{common.NewAndSet(2), nil}))
require.Equal(t, 1, eventActivityDoneCount) require.Equal(t, 1, eventActivityDoneCount)
} }
// Simulate and validate a multi-step user flow that was also a regression in the original implementation
func TestService_FilteredIncrementalUpdateResetAndClear(t *testing.T) {
state := setupTestService(t)
defer state.close()
transactionCount := 5
allAddresses, pendings, ch, cleanup := setupTransactions(t, state, transactionCount, []transactions.TestTxSummary{{DontConfirm: true, Timestamp: transactionCount + 1}})
defer cleanup()
// Generate new transaction for step 5
newOffset := transactionCount + 2
newTxs, newFromTrs, newToTrs := transfer.GenerateTestTransfers(t, state.service.db, newOffset, 1)
allAddresses = append(append(allAddresses, newFromTrs...), newToTrs...)
// 1. User visualizes transactions for the first time
sessionID := state.service.StartFilterSession(allAddresses, true, allNetworksFilter(), Filter{}, 4)
require.Greater(t, sessionID, SessionID(0))
defer state.service.StopFilterSession(sessionID)
validateFilteringDone(t, ch, 4, nil, nil)
// 2. User applies a filter for pending transactions
err := state.service.UpdateFilterForSession(sessionID, Filter{Statuses: []Status{PendingAS}}, 4)
require.NoError(t, err)
filterResponseCount := validateFilteringDone(t, ch, 0, nil, nil)
// 3. A pending transaction is added
exp := pendings[0]
err = state.pendingTracker.StoreAndTrackPendingTx(&exp)
require.NoError(t, err)
vFn := getValidateSessionUpdateHasNewOnTopFn(t)
pendingTransactionUpdate, sessionUpdatesCount := validateSessionUpdateEvent(t, ch, &filterResponseCount, 1, vFn)
// 4. User resets the view and the new pending transaction has the new flag
err = state.service.ResetFilterSession(sessionID, 2)
require.NoError(t, err)
// Validate the reset data
eventActivityDoneCount := validateFilteringDone(t, ch, 1, func(payload FilterResponse) {
require.True(t, payload.Activities[0].isNew)
require.Equal(t, int64(transactionCount+1), payload.Activities[0].timestamp)
}, nil)
require.Equal(t, 1, pendingTransactionUpdate)
require.Equal(t, 1, filterResponseCount)
require.Equal(t, 1, sessionUpdatesCount)
require.Equal(t, 1, eventActivityDoneCount)
// 5. A new transaction is downloaded
transfer.InsertTestTransfer(t, state.service.db, newTxs[0].To, &newTxs[0])
// 6. User clears the filter and only the new transaction should have the new flag
err = state.service.UpdateFilterForSession(sessionID, Filter{}, 4)
require.NoError(t, err)
eventActivityDoneCount = validateFilteringDone(t, ch, 4, func(payload FilterResponse) {
require.True(t, payload.Activities[0].isNew)
require.Equal(t, int64(newOffset), payload.Activities[0].timestamp)
require.False(t, payload.Activities[1].isNew)
require.Equal(t, int64(newOffset-1), payload.Activities[1].timestamp)
require.False(t, payload.Activities[2].isNew)
require.Equal(t, int64(newOffset-2), payload.Activities[2].timestamp)
require.False(t, payload.Activities[3].isNew)
require.Equal(t, int64(newOffset-3), payload.Activities[3].timestamp)
}, nil)
require.Equal(t, 1, eventActivityDoneCount)
}

View File

@ -190,8 +190,8 @@ func (s *Service) UpdateFilterForSession(id SessionID, filter Filter, firstPageC
return errors.New("session not found") return errors.New("session not found")
} }
prevEmpty := session.filter.IsEmpty() prevFilterEmpty := session.filter.IsEmpty()
newEmpty := filter.IsEmpty() newFilerEmpty := filter.IsEmpty()
s.sessionsRWMutex.RUnlock() s.sessionsRWMutex.RUnlock()
s.sessionsRWMutex.Lock() s.sessionsRWMutex.Lock()
@ -200,7 +200,7 @@ func (s *Service) UpdateFilterForSession(id SessionID, filter Filter, firstPageC
session.filter = filter session.filter = filter
if prevEmpty && !newEmpty { if prevFilterEmpty && !newFilerEmpty {
// Session is moving from empty to non-empty filter // Session is moving from empty to non-empty filter
// Take a snapshot of the current model // Take a snapshot of the current model
session.noFilterModel = entryIdsToMap(session.model) session.noFilterModel = entryIdsToMap(session.model)
@ -209,7 +209,7 @@ func (s *Service) UpdateFilterForSession(id SessionID, filter Filter, firstPageC
// In this case there is nothing to flag so we request the first page // In this case there is nothing to flag so we request the first page
s.internalFilterForSession(session, firstPageCount) s.internalFilterForSession(session, firstPageCount)
} else if !prevEmpty && newEmpty { } else if !prevFilterEmpty && newFilerEmpty {
// Session is moving from non-empty to empty filter // Session is moving from non-empty to empty filter
// In this case we need to flag all the new entries that are not in the noFilterModel // In this case we need to flag all the new entries that are not in the noFilterModel
s.internalFilter( s.internalFilter(
@ -277,6 +277,13 @@ func (s *Service) ResetFilterSession(id SessionID, firstPageCount int) error {
} }
session.new = nil session.new = nil
if session.noFilterModel != nil {
// Add reported new entries to mark them as seen
for _, a := range newMap {
session.noFilterModel[a.key()] = a
}
}
// Mirror client identities for checking updates // Mirror client identities for checking updates
session.model = mirrorIdentities(entries) session.model = mirrorIdentities(entries)