@@ -79,6 +79,7 @@ final class _FakeGithubApi implements GithubApi {
7979 this .resolveGate,
8080 this .listGate,
8181 this .fetchFailuresRemaining = 0 ,
82+ this .fetchFailure = const GithubFailure (GithubFailureKind .offline),
8283 this .listFailuresRemaining = 0 ,
8384 }) : _bytesByRemotePath = < String , Uint8List > {
8485 for (final MapEntry <String , String > source
@@ -93,6 +94,7 @@ final class _FakeGithubApi implements GithubApi {
9394 final Completer <void >? resolveGate;
9495 final Completer <void >? listGate;
9596 int fetchFailuresRemaining;
97+ final GithubFailure fetchFailure;
9698 int listFailuresRemaining;
9799 final List <(RepositoryLocator , String )> resolveCalls =
98100 < (RepositoryLocator , String )> [];
@@ -150,7 +152,7 @@ final class _FakeGithubApi implements GithubApi {
150152 fetchedRemotePaths.add (entry.remotePath);
151153 if (fetchFailuresRemaining > 0 ) {
152154 fetchFailuresRemaining -= 1 ;
153- throw GithubFailure ( GithubFailureKind .offline, path : entry.remotePath) ;
155+ throw fetchFailure ;
154156 }
155157 return Uint8List .fromList (_bytesByRemotePath[entry.remotePath]! );
156158 }
@@ -696,6 +698,61 @@ void main() {
696698 );
697699 });
698700
701+ testWidgets ('a rate-limited blob retry waits without writing' , (
702+ WidgetTester tester,
703+ ) async {
704+ final RecordingConnection connection = RecordingConnection (
705+ initial: ConnState .ready,
706+ );
707+ final _FakeGithubApi api = _FakeGithubApi (
708+ entries: const < GithubEntry > [_helloEntry],
709+ fetchFailuresRemaining: 1 ,
710+ fetchFailure: const GithubFailure (
711+ GithubFailureKind .rateLimited,
712+ retryAfter: Duration (seconds: 60 ),
713+ ),
714+ );
715+ addTearDown (connection.dispose);
716+
717+ await _openImport (tester, connection, api);
718+ await _browse (tester);
719+ await tester.tap (
720+ find.byKey (const ValueKey <String >('githubSelect_hello.py' )),
721+ );
722+ await tester.pump ();
723+ await tester.tap (find.byKey (kGithubReviewButtonKey));
724+ await tester.pumpAndSettle ();
725+ await tester.tap (find.byKey (kGithubCommitButtonKey));
726+ await tester.pumpAndSettle ();
727+
728+ expect (connection.putFileCalls, isEmpty);
729+ expect (find.textContaining ('60' ), findsOneWidget);
730+ expect (find.text (l10nOf (tester).githubImportRetry), findsNothing);
731+ expect (
732+ tester
733+ .widget <FilledButton >(find.byKey (kGithubCommitButtonKey))
734+ .onPressed,
735+ isNull,
736+ );
737+
738+ await tester.pump (const Duration (seconds: 60 ));
739+ expect (find.text (l10nOf (tester).githubImportRetry), findsOneWidget);
740+ expect (
741+ tester
742+ .widget <FilledButton >(find.byKey (kGithubCommitButtonKey))
743+ .onPressed,
744+ isNotNull,
745+ );
746+
747+ await tester.tap (find.text (l10nOf (tester).githubImportRetry));
748+ await tester.pumpAndSettle ();
749+ expect (connection.putFileCalls, hasLength (1 ));
750+ expect (
751+ find.byKey (const ValueKey <String >('githubImportResult' )),
752+ findsOneWidget,
753+ );
754+ });
755+
699756 testWidgets ('review marks an existing regular-file target before consent' , (
700757 WidgetTester tester,
701758 ) async {
0 commit comments