twonly-app/test/services/retransmission_backoff_test.dart

66 lines
2.1 KiB
Dart

import 'package:clock/clock.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/api/messages.api.dart';
void main() {
final lastRetry = DateTime(2026, 9, 24, 12);
Receipt receipt(int retryCount) => Receipt(
receiptId: 'receipt',
contactId: 1,
message: Uint8List(0),
contactWillSendsReceipt: true,
willBeRetriedByMediaUpload: false,
ackByServerAt: lastRetry,
retryCount: retryCount,
lastRetry: lastRetry,
createdAt: lastRetry,
);
bool dueAfter(
Duration elapsed,
Receipt receipt, {
Duration minimumPause = Duration.zero,
}) => withClock(
Clock.fixed(lastRetry.add(elapsed)),
() => isRetransmissionDue(receipt, minimumPause: minimumPause),
);
test('the first retry is not held back', () {
expect(dueAfter(Duration.zero, receipt(0)), isTrue);
expect(dueAfter(Duration.zero, receipt(1)), isTrue);
});
test('the pause doubles from one hour with every attempt', () {
for (final (retryCount, hours) in [(2, 1), (3, 2), (4, 4), (6, 16)]) {
final pause = Duration(hours: hours);
final r = receipt(retryCount);
expect(
dueAfter(pause - const Duration(minutes: 1), r),
isFalse,
reason: 'retry $retryCount',
);
expect(dueAfter(pause, r), isTrue, reason: 'retry $retryCount');
}
});
test('the pause stops growing at one day', () {
final stuck = receipt(65);
expect(dueAfter(const Duration(hours: 23), stuck), isFalse);
expect(dueAfter(const Duration(hours: 24), stuck), isTrue);
});
test('the pause never drops below the minimum', () {
const sixHours = Duration(hours: 6);
final r = receipt(2);
expect(
dueAfter(const Duration(hours: 5), r, minimumPause: sixHours),
isFalse,
);
expect(dueAfter(sixHours, r, minimumPause: sixHours), isTrue);
// The first retry is not held back by the minimum either.
expect(dueAfter(Duration.zero, receipt(1), minimumPause: sixHours), isTrue);
});
}