From e6c69f130d907ba7135924c902b3540f43ea3c93 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Sun, 25 Oct 2026 14:46:58 +0300 Subject: [PATCH] test(pool): PoolEntryLock, shared create, parallel keys, failure and sync throw (#1683) --- .../database/connection_pool_lock_test.dart | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 test/core/database/connection_pool_lock_test.dart diff --git a/test/core/database/connection_pool_lock_test.dart b/test/core/database/connection_pool_lock_test.dart new file mode 100644 index 00000000..c67041de --- /dev/null +++ b/test/core/database/connection_pool_lock_test.dart @@ -0,0 +1,94 @@ +import 'dart:async'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:querya_desktop/core/database/connection_pool_lock.dart'; + +void main() { + test('two callers for one key share a single create()', () async { + final lock = PoolEntryLock(); + final gate = Completer(); + var created = 0; + Future create() { + created++; + return gate.future; + } + + final a = lock.createIfAbsent('k', create); + final b = lock.createIfAbsent('k', create); + gate.complete(7); + + expect(await a, 7); + expect(await b, 7); + expect(created, 1); + }); + + test('different keys are created in parallel', () async { + final lock = PoolEntryLock(); + final gates = {'a': Completer(), 'b': Completer()}; + final started = []; + + final a = lock.createIfAbsent('a', () { + started.add('a'); + return gates['a']!.future; + }); + final b = lock.createIfAbsent('b', () { + started.add('b'); + return gates['b']!.future; + }); + await Future.delayed(Duration.zero); + + // Both creations are running although neither has finished. + expect(started, ['a', 'b']); + gates['b']!.complete('B'); + gates['a']!.complete('A'); + expect(await a, 'A'); + expect(await b, 'B'); + }); + + test('a finished creation is forgotten: the next call creates again', () async { + final lock = PoolEntryLock(); + var created = 0; + Future create() async => ++created; + + expect(await lock.createIfAbsent('k', create), 1); + await Future.delayed(Duration.zero); + expect(await lock.createIfAbsent('k', create), 2); + }); + + test('a failing create() fails every waiter and clears the entry', () async { + final lock = PoolEntryLock(); + final gate = Completer(); + var created = 0; + + final a = lock.createIfAbsent('k', () { + created++; + return gate.future; + }); + final b = lock.createIfAbsent('k', () { + created++; + return gate.future; + }); + final failures = [ + expectLater(a, throwsA(isA())), + expectLater(b, throwsA(isA())), + ]; + gate.completeError(StateError('connect refused')); + await Future.wait(failures); + expect(created, 1); + + await Future.delayed(Duration.zero); + expect(await lock.createIfAbsent('k', () async => 5), 5); + }); + + test('a create() that throws synchronously does not wedge the lock', () async { + final lock = PoolEntryLock(); + + await expectLater( + lock.createIfAbsent('k', () => throw StateError('boom')), + throwsA(isA()), + ); + + expect(await lock.createIfAbsent('k', () async => 1), 1); + expect(await lock.createIfAbsent('other', () async => 2), 2); + }); +}