@@ -6,6 +6,7 @@ import 'package:fake_async/fake_async.dart';
66import 'package:flutter/foundation.dart' ;
77import 'package:http/http.dart' as http;
88import 'package:test/scaffolding.dart' ;
9+ import 'package:zulip/api/backoff.dart' ;
910import 'package:zulip/api/core.dart' ;
1011import 'package:zulip/api/model/events.dart' ;
1112import 'package:zulip/api/model/model.dart' ;
@@ -130,6 +131,27 @@ void main() {
130131 await check (future).throws <AccountNotFoundException >();
131132 }));
132133
134+ test ('GlobalStore.perAccount account is logged out while loading; then succeeds' , () => awaitFakeAsync ((async ) async {
135+ final globalStore = UpdateMachineTestGlobalStore (accounts: [eg.selfAccount]);
136+ globalStore.prepareRegisterQueueResponse = (connection) =>
137+ connection.prepare (
138+ delay: TestGlobalStore .removeAccountDuration + Duration (seconds: 1 ),
139+ json: eg.initialSnapshot ().toJson ());
140+ final connection = globalStore.apiConnectionFromAccount (eg.selfAccount) as FakeApiConnection ;
141+ final future = globalStore.perAccount (eg.selfAccount.id);
142+ check (connection.takeRequests ()).length.equals (1 ); // register request
143+
144+ await logOutAccount (globalStore, eg.selfAccount.id);
145+ check (globalStore.takeDoRemoveAccountCalls ())
146+ .single.equals (eg.selfAccount.id);
147+
148+ await check (future).throws <AccountNotFoundException >();
149+ check (globalStore.takeDoRemoveAccountCalls ()).isEmpty ();
150+ // no poll, server-emoji-data, or register-token requests
151+ check (connection.takeRequests ()).isEmpty ();
152+ check (connection).isOpen.isFalse ();
153+ }));
154+
133155 test ('GlobalStore.perAccount account is logged out while loading; then fails with HTTP status code 401' , () => awaitFakeAsync ((async ) async {
134156 final globalStore = UpdateMachineTestGlobalStore (accounts: [eg.selfAccount]);
135157 globalStore.prepareRegisterQueueResponse = (connection) =>
@@ -148,8 +170,31 @@ void main() {
148170 check (globalStore.takeDoRemoveAccountCalls ()).isEmpty ();
149171 // no poll, server-emoji-data, or register-token requests
150172 check (connection.takeRequests ()).isEmpty ();
151- // TODO(#1354) uncomment
152- // check(connection).isOpen.isFalse();
173+ check (connection).isOpen.isFalse ();
174+ }));
175+
176+ test ('GlobalStore.perAccount account is logged out during transient-error backoff' , () => awaitFakeAsync ((async ) async {
177+ final globalStore = UpdateMachineTestGlobalStore (accounts: [eg.selfAccount]);
178+ globalStore.prepareRegisterQueueResponse = (connection) =>
179+ connection.prepare (
180+ delay: Duration (seconds: 1 ),
181+ httpException: http.ClientException ('Oops' ));
182+ final connection = globalStore.apiConnectionFromAccount (eg.selfAccount) as FakeApiConnection ;
183+ final future = globalStore.perAccount (eg.selfAccount.id);
184+ BackoffMachine .debugDuration = Duration (seconds: 1 );
185+ async .elapse (Duration (milliseconds: 1500 ));
186+ check (connection.takeRequests ()).length.equals (1 ); // register request
187+
188+ assert (TestGlobalStore .removeAccountDuration < Duration (milliseconds: 500 ));
189+ await logOutAccount (globalStore, eg.selfAccount.id);
190+ check (globalStore.takeDoRemoveAccountCalls ())
191+ .single.equals (eg.selfAccount.id);
192+
193+ await check (future).throws <AccountNotFoundException >();
194+ check (globalStore.takeDoRemoveAccountCalls ()).isEmpty ();
195+ // no retry-register, poll, server-emoji-data, or register-token requests
196+ check (connection.takeRequests ()).isEmpty ();
197+ check (connection).isOpen.isFalse ();
153198 }));
154199
155200 // TODO test insertAccount
@@ -251,10 +296,10 @@ void main() {
251296 checkGlobalStore (globalStore, eg.selfAccount.id,
252297 expectAccount: true , expectStore: false );
253298
254- // assert(globalStore.useCachedApiConnections);
299+ assert (globalStore.useCachedApiConnections);
255300 // Cache a connection and get this reference to it,
256301 // so we can check later that it gets closed.
257- // final connection = globalStore.apiConnectionFromAccount(eg.selfAccount) as FakeApiConnection;
302+ final connection = globalStore.apiConnectionFromAccount (eg.selfAccount) as FakeApiConnection ;
258303
259304 globalStore.prepareRegisterQueueResponse = (connection) {
260305 connection.prepare (
@@ -274,14 +319,11 @@ void main() {
274319 expectAccount: false , expectStore: false );
275320 check (notifyCount).equals (1 );
276321
277- // Actually throws a null-check error; that's the bug #1354.
278- // TODO(#1354) should specifically throw AccountNotFoundException
279- await check (loadingFuture).throws ();
322+ await check (loadingFuture).throws <AccountNotFoundException >();
280323 checkGlobalStore (globalStore, eg.selfAccount.id,
281324 expectAccount: false , expectStore: false );
282325 check (notifyCount).equals (1 ); // no extra notify
283- // TODO(#1354) uncomment
284- // check(connection).isOpen.isFalse();
326+ check (connection).isOpen.isFalse ();
285327
286328 check (globalStore.debugNumPerAccountStoresLoading).equals (0 );
287329 });
@@ -1028,10 +1070,7 @@ void main() {
10281070 async .flushTimers ();
10291071 // Reload never succeeds and there are no unhandled errors.
10301072 check (globalStore.perAccountSync (eg.selfAccount.id)).isNull ();
1031- }),
1032- // An unhandled error is actually the bug #1354, so skip for now
1033- // TODO(#1354) unskip
1034- skip: true );
1073+ }));
10351074
10361075 test ('new store is not loaded, gets HTTP 401 error instead' , () => awaitFakeAsync ((async ) async {
10371076 await prepareReload (async ,
0 commit comments