[app_dart] Push GitHub webhooks to PubSub (#2068)

diff --git a/app_dart/README.md b/app_dart/README.md
index 38e13eb..5339528 100644
--- a/app_dart/README.md
+++ b/app_dart/README.md
@@ -35,7 +35,7 @@
 To update the JSON serialization generated code, run:
 
 ```sh
-$ dart pub run build_runner build
+$ dart run build_runner build
 ```
 
 Any updates should be checked into source control.
@@ -48,8 +48,8 @@
    the protocol buffer compiler (`protoc`). Once installed, update your `PATH`
    to include the path to the `protoc` binary.
 
-   On Linux, you may be able to use `sudo apt-get install protocol-compiler` to install.
-   On macOS, you may be able to use `brew install protobuf`
+   On Linux, use `sudo apt-get install protocol-compiler` to install.
+   On macOS, use `brew install protobuf`
 
 2. Install the [`protoc_plugin`](https://pub.dev/packages/protoc_plugin) Dart
    package. Once installed, update your `PATH` to include the path to the
@@ -59,10 +59,7 @@
 3. Run the following command:
 
    ```sh
-   $ protoc \
-       --plugin=/path/to/protoc_plugin/bin/ \
-       --dart_out=. \
-       lib/src/path/to/file.proto
+   $ protoc --dart_out=. lib/src/model/proto/**/*.proto
    ```
 
 4. Remove the unused generated files:
diff --git a/app_dart/bin/server.dart b/app_dart/bin/server.dart
index 6a741ee..e3e8b05 100644
--- a/app_dart/bin/server.dart
+++ b/app_dart/bin/server.dart
@@ -71,6 +71,11 @@
       ),
       '/api/github-webhook-pullrequest': GithubWebhook(
         config: config,
+        pubsub: const PubSub(),
+      ),
+      '/api/github/webhook-subscription': GithubWebhookSubscription(
+        config: config,
+        cache: cache,
         branchService: branchService,
         githubChecksService: githubChecksService,
         scheduler: scheduler,
diff --git a/app_dart/lib/cocoon_service.dart b/app_dart/lib/cocoon_service.dart
index 0b28dc0..a3c0fa7 100644
--- a/app_dart/lib/cocoon_service.dart
+++ b/app_dart/lib/cocoon_service.dart
@@ -16,6 +16,7 @@
 export 'src/request_handlers/get_green_commits.dart';
 export 'src/request_handlers/github_rate_limit_status.dart';
 export 'src/request_handlers/github_webhook.dart';
+export 'src/request_handlers/github/webhook_subscription.dart';
 export 'src/request_handlers/postsubmit_luci_subscription.dart';
 export 'src/request_handlers/presubmit_luci_subscription.dart';
 export 'src/request_handlers/push_build_status_to_github.dart';
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.pb.dart b/app_dart/lib/src/model/proto/internal/github_webhook.pb.dart
new file mode 100644
index 0000000..4f4fd7c
--- /dev/null
+++ b/app_dart/lib/src/model/proto/internal/github_webhook.pb.dart
@@ -0,0 +1,82 @@
+///
+//  Generated code. Do not modify.
+//  source: lib/src/model/proto/internal/github_webhook.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class GithubWebhookMessage extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GithubWebhookMessage',
+      createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'event')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'payload')
+    ..hasRequiredFields = false;
+
+  GithubWebhookMessage._() : super();
+  factory GithubWebhookMessage({
+    $core.String? event,
+    $core.String? payload,
+  }) {
+    final _result = create();
+    if (event != null) {
+      _result.event = event;
+    }
+    if (payload != null) {
+      _result.payload = payload;
+    }
+    return _result;
+  }
+  factory GithubWebhookMessage.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory GithubWebhookMessage.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  GithubWebhookMessage clone() => GithubWebhookMessage()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  GithubWebhookMessage copyWith(void Function(GithubWebhookMessage) updates) =>
+      super.copyWith((message) => updates(message as GithubWebhookMessage))
+          as GithubWebhookMessage; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static GithubWebhookMessage create() => GithubWebhookMessage._();
+  GithubWebhookMessage createEmptyInstance() => create();
+  static $pb.PbList<GithubWebhookMessage> createRepeated() => $pb.PbList<GithubWebhookMessage>();
+  @$core.pragma('dart2js:noInline')
+  static GithubWebhookMessage getDefault() =>
+      _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GithubWebhookMessage>(create);
+  static GithubWebhookMessage? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get event => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set event($core.String v) {
+    $_setString(0, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasEvent() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearEvent() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get payload => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set payload($core.String v) {
+    $_setString(1, v);
+  }
+
+  @$pb.TagNumber(2)
+  $core.bool hasPayload() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearPayload() => clearField(2);
+}
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.proto b/app_dart/lib/src/model/proto/internal/github_webhook.proto
new file mode 100644
index 0000000..ef0f4fc
--- /dev/null
+++ b/app_dart/lib/src/model/proto/internal/github_webhook.proto
@@ -0,0 +1,13 @@
+// Copyright 2019 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+// TODO(chillers): Use this for the generated models.
+message GithubWebhookMessage {
+    // TODO(chillers)
+    optional string event = 1;
+    // JSON encoded webhook payload from GitHub.
+    optional string payload = 2;
+}
\ No newline at end of file
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pb.dart b/app_dart/lib/src/model/proto/internal/scheduler.pb.dart
index 6a2a588..a17500b 100644
--- a/app_dart/lib/src/model/proto/internal/scheduler.pb.dart
+++ b/app_dart/lib/src/model/proto/internal/scheduler.pb.dart
@@ -3,7 +3,7 @@
 //  source: lib/src/model/proto/internal/scheduler.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart b/app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart
index 648c0ce..6fce85b 100644
--- a/app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart
+++ b/app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart
@@ -3,7 +3,7 @@
 //  source: lib/src/model/proto/internal/scheduler.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 // ignore_for_file: UNDEFINED_SHOWN_NAME
 import 'dart:core' as $core;
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart b/app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart
index 6a382b0..b2db732 100644
--- a/app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart
+++ b/app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart
@@ -3,7 +3,7 @@
 //  source: lib/src/model/proto/internal/scheduler.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart b/app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart
index ddb748e..a16fa83 100644
--- a/app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart
+++ b/app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart
@@ -3,6 +3,6 @@
 //  source: lib/src/model/proto/internal/scheduler.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 export 'scheduler.pb.dart';
diff --git a/app_dart/lib/src/model/proto/protos.dart b/app_dart/lib/src/model/proto/protos.dart
index 338120e..ae7cdee 100644
--- a/app_dart/lib/src/model/proto/protos.dart
+++ b/app_dart/lib/src/model/proto/protos.dart
@@ -3,5 +3,6 @@
 // found in the LICENSE file.
 
 export 'internal/build_status_response.pb.dart';
+export 'internal/github_webhook.pb.dart';
 export 'internal/key.pb.dart';
 export 'internal/scheduler.pb.dart';
diff --git a/app_dart/lib/src/request_handlers/github/webhook_subscription.dart b/app_dart/lib/src/request_handlers/github/webhook_subscription.dart
new file mode 100644
index 0000000..48d9b86
--- /dev/null
+++ b/app_dart/lib/src/request_handlers/github/webhook_subscription.dart
@@ -0,0 +1,650 @@
+// Copyright 2019 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:github/github.dart';
+import 'package:github/hooks.dart';
+import 'package:meta/meta.dart';
+
+import '../../../protos.dart' as pb;
+import '../../model/github/checks.dart' as cocoon_checks;
+import '../../request_handling/body.dart';
+import '../../request_handling/exceptions.dart';
+import '../../request_handling/subscription_handler.dart';
+import '../../service/branch_service.dart';
+import '../../service/config.dart';
+import '../../service/datastore.dart';
+import '../../service/github_checks_service.dart';
+import '../../service/logging.dart';
+import '../../service/scheduler.dart';
+
+// Filenames which are not actually tests.
+const List<String> kNotActuallyATest = <String>[
+  'packages/flutter/lib/src/gestures/hit_test.dart',
+];
+
+/// List of repos that require check for labels and tests.
+const Set<String> kNeedsCheckLabelsAndTests = <String>{
+  'flutter/engine',
+  'flutter/flutter',
+  'flutter/packages',
+  'flutter/plugins',
+};
+
+final RegExp kEngineTestRegExp = RegExp(r'(tests?|benchmarks?)\.(dart|java|mm|m|cc)$');
+final List<String> kNeedsTestsLabels = <String>['needs tests'];
+
+/// Subscription for processing GitHub webhooks.
+///
+/// The PubSub subscription is set up here:
+/// https://cloud.google.com/cloudpubsub/subscription/detail/github-webhooks-sub?project=flutter-dashboard&tab=overview
+///
+/// This endpoint enables Cocoon to recover from outages.
+///
+/// This endpoint takes in a POST request with the GitHub event JSON.
+// TODO(chillers): There's potential now to split this into seprate subscriptions
+// for various activities (such as infra vs releases). This would mitigate
+// breakages across Cocoon.
+@immutable
+class GithubWebhookSubscription extends SubscriptionHandler {
+  /// Creates a subscription for processing GitHub webhooks.
+  const GithubWebhookSubscription({
+    required super.cache,
+    required super.config,
+    required this.scheduler,
+    this.githubChecksService,
+    this.datastoreProvider = DatastoreService.defaultProvider,
+    required this.branchService,
+    super.authProvider,
+  }) : super(topicName: 'github-webhooks');
+
+  /// Cocoon scheduler to trigger tasks against changes from GitHub.
+  final Scheduler scheduler;
+
+  /// To provide build status updates to GitHub pull requests.
+  final GithubChecksService? githubChecksService;
+
+  final DatastoreServiceProvider datastoreProvider;
+  final BranchService branchService;
+
+  @override
+  Future<Body> post() async {
+    if (message.data == null || message.data!.isEmpty) {
+      log.warning('GitHub webhook message was empty. No-oping');
+      return Body.empty;
+    }
+
+    final pb.GithubWebhookMessage webhook = pb.GithubWebhookMessage.fromJson(message.data!);
+
+    log.fine('Processing ${webhook.event}');
+    log.finest(webhook.payload);
+    switch (webhook.event) {
+      case 'pull_request':
+        await _handlePullRequest(webhook.payload);
+        break;
+      case 'check_run':
+        final Map<String, dynamic> event = jsonDecode(webhook.payload) as Map<String, dynamic>;
+        final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(event);
+        if (await scheduler.processCheckRun(checkRunEvent) == false) {
+          throw InternalServerError('Failed to process $checkRunEvent');
+        }
+        break;
+      case 'create':
+        final CreateEvent createEvent = CreateEvent.fromJson(json.decode(webhook.payload) as Map<String, dynamic>);
+        await branchService.handleCreateRequest(createEvent);
+        if (createEvent.repository?.slug() == Config.flutterSlug) {
+          await branchService.branchFlutterRecipes(createEvent.ref!);
+        }
+        break;
+    }
+
+    return Body.empty;
+  }
+
+  Future<void> _handlePullRequest(
+    String rawRequest,
+  ) async {
+    final PullRequestEvent? pullRequestEvent = await _getPullRequestEvent(rawRequest);
+    if (pullRequestEvent == null) {
+      throw const BadRequestException('Expected pull request event.');
+    }
+    final String? eventAction = pullRequestEvent.action;
+    final PullRequest pr = pullRequestEvent.pullRequest!;
+
+    // See the API reference:
+    // https://developer.github.com/v3/activity/events/types/#pullrequestevent
+    // which unfortunately is a bit light on explanations.
+    log.fine('Processing $eventAction for ${pr.htmlUrl}');
+    switch (eventAction) {
+      case 'closed':
+        // If it was closed without merging, cancel any outstanding tryjobs.
+        // We'll leave unfinished jobs if it was merged since we care about those
+        // results.
+        if (!pr.merged!) {
+          await scheduler.cancelPreSubmitTargets(pullRequest: pr, reason: 'Pull request closed');
+        } else {
+          // Merged pull requests can be added to CI.
+          await scheduler.addPullRequest(pr);
+        }
+        break;
+      case 'edited':
+        // Editing a PR should not trigger new jobs, but may update whether
+        // it has tests.
+        await _checkForLabelsAndTests(pullRequestEvent);
+        break;
+      case 'opened':
+      case 'reopened':
+        // These cases should trigger LUCI jobs.
+        await _checkForLabelsAndTests(pullRequestEvent);
+        await _scheduleIfMergeable(pullRequestEvent);
+        await _tryReleaseApproval(pullRequestEvent);
+        break;
+      case 'labeled':
+        break;
+      case 'synchronize':
+        // This indicates the PR has new commits. We need to cancel old jobs
+        // and schedule new ones.
+        await _scheduleIfMergeable(pullRequestEvent);
+        break;
+      // Ignore the rest of the events.
+      case 'ready_for_review':
+      case 'unlabeled':
+      case 'assigned':
+      case 'locked':
+      case 'review_request_removed':
+      case 'review_requested':
+      case 'unassigned':
+      case 'unlocked':
+        break;
+    }
+  }
+
+  /// This method assumes that jobs should be cancelled if they are already
+  /// runnning.
+  Future<void> _scheduleIfMergeable(
+    PullRequestEvent pullRequestEvent,
+  ) async {
+    final PullRequest pr = pullRequestEvent.pullRequest!;
+    final RepositorySlug slug = pullRequestEvent.repository!.slug();
+
+    log.info(
+        'Scheduling tasks if mergeable(${pr.mergeable}): owner=${slug.owner} repo=${slug.name} and pr=${pr.number}');
+
+    // The mergeable flag may be null. False indicates there's a merge conflict,
+    // null indicates unknown. Err on the side of allowing the job to run.
+    if (pr.mergeable == false) {
+      final RepositorySlug slug = pullRequestEvent.repository!.slug();
+      final GitHub gitHubClient = await config.createGitHubClient(pullRequest: pr);
+      final String body = config.mergeConflictPullRequestMessage;
+      if (!await _alreadyCommented(gitHubClient, pr, body)) {
+        await gitHubClient.issues.createComment(slug, pr.number!, body);
+      }
+
+      return;
+    }
+
+    await scheduler.triggerPresubmitTargets(pullRequest: pr);
+  }
+
+  /// Release tooling generates cherrypick pull requests that should be granted an approval.
+  Future<void> _tryReleaseApproval(
+    PullRequestEvent pullRequestEvent,
+  ) async {
+    final PullRequest pr = pullRequestEvent.pullRequest!;
+    final RepositorySlug slug = pullRequestEvent.repository!.slug();
+
+    final String defaultBranch = Config.defaultBranch(slug);
+    final String? branch = pr.base?.ref;
+    if (branch == null || branch.contains(defaultBranch)) {
+      // This isn't a release branch PR
+      return;
+    }
+
+    final List<String> releaseAccounts = await config.releaseAccounts;
+    if (releaseAccounts.contains(pr.user?.login) == false) {
+      // The author isn't in the list of release accounts, do nothing
+      return;
+    }
+
+    final GitHub gitHubClient = config.createGitHubClientWithToken(await config.githubOAuthToken);
+    final CreatePullRequestReview review = CreatePullRequestReview(slug.owner, slug.name, pr.number!, 'APPROVE');
+    await gitHubClient.pullRequests.createReview(slug, review);
+  }
+
+  Future<void> _checkForLabelsAndTests(PullRequestEvent pullRequestEvent) async {
+    final PullRequest pr = pullRequestEvent.pullRequest!;
+    final String? eventAction = pullRequestEvent.action;
+    final String repo = pr.base!.repo!.fullName.toLowerCase();
+    if (kNeedsCheckLabelsAndTests.contains(repo)) {
+      final GitHub gitHubClient = await config.createGitHubClient(pullRequest: pr);
+      try {
+        await _validateRefs(gitHubClient, pr);
+        if (repo == 'flutter/flutter') {
+          await _applyFrameworkRepoLabels(gitHubClient, eventAction, pr);
+        } else if (repo == 'flutter/engine') {
+          await _applyEngineRepoLabels(gitHubClient, eventAction, pr);
+        } else if (repo == 'flutter/plugins' || repo == 'flutter/packages') {
+          await _applyPackageTestChecks(gitHubClient, eventAction, pr);
+        }
+      } finally {
+        gitHubClient.dispose();
+      }
+    }
+  }
+
+  Future<void> _applyFrameworkRepoLabels(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
+    if (pr.user!.login == 'engine-flutter-autoroll') {
+      return;
+    }
+
+    final RepositorySlug slug = pr.base!.repo!.slug();
+    log.info('Applying framework repo labels for: owner=${slug.owner} repo=${slug.name} and pr=${pr.number}');
+    final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
+
+    final Set<String> labels = <String>{};
+    bool hasTests = false;
+    bool needsTests = false;
+
+    await for (PullRequestFile file in files) {
+      // When null, do not assume 0 lines have been added.
+      final String filename = file.filename!;
+      final int linesAdded = file.additionsCount ?? 1;
+      final int linesDeleted = file.deletionsCount ?? 0;
+      final int linesTotal = file.changesCount ?? linesDeleted + linesAdded;
+      final bool addedCode = linesAdded > 0 || linesDeleted != linesTotal;
+
+      if (addedCode &&
+          !filename.contains('AUTHORS') &&
+          !filename.contains('pubspec.yaml') &&
+          !filename.contains('.ci.yaml') &&
+          !filename.contains('.cirrus.yml') &&
+          !filename.contains('.github') &&
+          !filename.endsWith('.md') &&
+          !filename.contains('CODEOWNERS') &&
+          !filename.startsWith('dev/bots/')) {
+        needsTests = !_allChangesAreCodeComments(file);
+      }
+
+      // Check to see if tests were submitted with this PR.
+      if (_isATest(filename)) {
+        hasTests = true;
+      }
+      labels.addAll(getLabelsForFrameworkPath(filename));
+    }
+
+    if (pr.user!.login == 'fluttergithubbot') {
+      needsTests = false;
+      labels.addAll(<String>['team', 'tech-debt', 'team: flakes']);
+    }
+
+    if (labels.isNotEmpty) {
+      await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, labels.toList());
+    }
+
+    // We do not need to add test labels if this is an auto roller author.
+    if (config.rollerAccounts.contains(pr.user!.login)) {
+      return;
+    }
+
+    if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
+      final String body = config.missingTestsPullRequestMessage;
+      if (!await _alreadyCommented(gitHubClient, pr, body)) {
+        await gitHubClient.issues.createComment(slug, pr.number!, body);
+      }
+    }
+  }
+
+  bool _isATest(String filename) {
+    if (kNotActuallyATest.any(filename.endsWith)) {
+      return false;
+    }
+    // Check for Objective-C tests which end in either "Tests.m" or "Test.m"
+    // in the "dev" directory.
+    final RegExp objectiveCTestRegex = RegExp(r'.*dev\/.*Test[s]?\.m$');
+    return filename.endsWith('_test.dart') ||
+        filename.endsWith('.expect') ||
+        filename.contains('test_fixes') ||
+        filename.startsWith('dev/bots/analyze.dart') ||
+        filename.startsWith('dev/bots/test.dart') ||
+        filename.startsWith('dev/devicelab/bin/tasks') ||
+        filename.startsWith('dev/devicelab/lib/tasks') ||
+        filename.startsWith('dev/devicelab/lib/tasks') ||
+        objectiveCTestRegex.hasMatch(filename);
+  }
+
+  /// Returns the set of labels applicable to a file in the framework repo.
+  static Set<String> getLabelsForFrameworkPath(String filepath) {
+    final Set<String> labels = <String>{};
+    if (filepath.endsWith('pubspec.yaml')) {
+      // These get updated by a script, and are updated en masse.
+      labels.add('team');
+      return labels;
+    }
+
+    if (filepath.endsWith('fix_data.yaml') || filepath.endsWith('.expect') || filepath.contains('test_fixes')) {
+      // Dart fixes
+      labels.add('team');
+      labels.add('tech-debt');
+    }
+
+    const Map<String, List<String>> pathPrefixLabels = <String, List<String>>{
+      'bin/internal/engine.version': <String>['engine'],
+      'dev/': <String>['team'],
+      'examples/': <String>['d: examples', 'team'],
+      'examples/api/': <String>['d: examples', 'team', 'd: api docs', 'documentation'],
+      'examples/flutter_gallery/': <String>['d: examples', 'team', 'team: gallery'],
+      'packages/flutter_tools/': <String>['tool'],
+      'packages/flutter_tools/lib/src/ios/': <String>['platform-ios'],
+      'packages/flutter/': <String>['framework'],
+      'packages/flutter_driver/': <String>['framework', 'a: tests'],
+      'packages/flutter_localizations/': <String>['a: internationalization'],
+      'packages/flutter_goldens/': <String>['framework', 'a: tests', 'team'],
+      'packages/flutter_goldens_client/': <String>['framework', 'a: tests', 'team'],
+      'packages/flutter_test/': <String>['framework', 'a: tests'],
+      'packages/fuchsia_remote_debug_protocol/': <String>['tool'],
+      'packages/integration_test/': <String>['integration_test'],
+    };
+    const Map<String, List<String>> pathContainsLabels = <String, List<String>>{
+      'accessibility': <String>['a: accessibility'],
+      'animation': <String>['a: animation'],
+      'cupertino': <String>['f: cupertino'],
+      'focus': <String>['f: focus'],
+      'gestures': <String>['f: gestures'],
+      'material': <String>['f: material design'],
+      'navigator': <String>['f: routes'],
+      'route': <String>['f: routes'],
+      'scroll': <String>['f: scrolling'],
+      'semantics': <String>['a: accessibility'],
+      'sliver': <String>['f: scrolling'],
+      'text': <String>['a: text input'],
+      'viewport': <String>['f: scrolling'],
+    };
+
+    pathPrefixLabels.forEach((String path, List<String> pathLabels) {
+      if (filepath.startsWith(path)) {
+        labels.addAll(pathLabels);
+      }
+    });
+    pathContainsLabels.forEach((String path, List<String> pathLabels) {
+      if (filepath.contains(path)) {
+        labels.addAll(pathLabels);
+      }
+    });
+    return labels;
+  }
+
+  /// Returns the set of labels applicable to a file in the engine repo.
+  static Set<String> getLabelsForEnginePath(String filepath) {
+    const Map<String, List<String>> pathPrefixLabels = <String, List<String>>{
+      'shell/platform/android': <String>['platform-android'],
+      'shell/platform/embedder': <String>['embedder'],
+      'shell/platform/darwin/common': <String>['platform-ios', 'platform-macos'],
+      'shell/platform/darwin/ios': <String>['platform-ios'],
+      'shell/platform/darwin/macos': <String>['platform-macos'],
+      'shell/platform/fuchsia': <String>['platform-fuchsia'],
+      'shell/platform/linux': <String>['platform-linux'],
+      'shell/platform/windows': <String>['platform-windows'],
+      'lib/web_ui': <String>['platform-web'],
+      'web_sdk': <String>['platform-web'],
+    };
+    final Set<String> labels = <String>{};
+    pathPrefixLabels.forEach((String path, List<String> pathLabels) {
+      if (filepath.startsWith(path)) {
+        labels.addAll(pathLabels);
+      }
+    });
+    return labels;
+  }
+
+  Future<void> _applyEngineRepoLabels(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
+    // Do not apply the test labels for the autoroller accounts.
+    if (pr.user!.login == 'skia-flutter-autoroll') {
+      return;
+    }
+
+    final RepositorySlug slug = pr.base!.repo!.slug();
+    final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
+    final Set<String> labels = <String>{};
+    bool hasTests = false;
+    bool needsTests = false;
+
+    await for (PullRequestFile file in files) {
+      final String filename = file.filename!.toLowerCase();
+      if (filename.endsWith('.dart') ||
+          filename.endsWith('.mm') ||
+          filename.endsWith('.m') ||
+          filename.endsWith('.java') ||
+          filename.endsWith('.cc')) {
+        needsTests = true;
+      }
+
+      if (kEngineTestRegExp.hasMatch(filename)) {
+        hasTests = true;
+      }
+
+      labels.addAll(getLabelsForEnginePath(filename));
+    }
+
+    if (labels.isNotEmpty) {
+      await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, labels.toList());
+    }
+
+    // We do not need to add test labels if this is an auto roller author.
+    if (config.rollerAccounts.contains(pr.user!.login)) {
+      return;
+    }
+
+    if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
+      final String body = config.missingTestsPullRequestMessage;
+      if (!await _alreadyCommented(gitHubClient, pr, body)) {
+        await gitHubClient.issues.createComment(slug, pr.number!, body);
+        await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, kNeedsTestsLabels);
+      }
+    }
+  }
+
+  // Runs automated test checks for both flutter/packages and flutter/plugins.
+  Future<void> _applyPackageTestChecks(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
+    final RepositorySlug slug = pr.base!.repo!.slug();
+    final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
+    bool hasTests = false;
+    bool needsTests = false;
+
+    await for (PullRequestFile file in files) {
+      final String filename = file.filename!;
+
+      // When null, do not assume 0 lines have been added.
+      final int linesAdded = file.additionsCount ?? 1;
+      final int linesDeleted = file.deletionsCount ?? 0;
+      final int linesTotal = file.changesCount ?? linesDeleted + linesAdded;
+      final bool addedCode = linesAdded > 0 || linesDeleted != linesTotal;
+
+      if (addedCode &&
+          !filename.endsWith('AUTHORS') &&
+          !filename.endsWith('CODEOWNERS') &&
+          !filename.endsWith('pubspec.yaml') &&
+          !filename.endsWith('.ci.yaml') &&
+          !filename.endsWith('.cirrus.yml') &&
+          !filename.contains('.ci/') &&
+          !filename.contains('.github/') &&
+          !filename.endsWith('.md')) {
+        needsTests = !_allChangesAreCodeComments(file);
+      }
+      // See https://github.com/flutter/flutter/wiki/Plugin-Tests for discussion
+      // of various plugin test types and locations.
+      if (filename.endsWith('_test.dart') ||
+          // Native iOS/macOS tests.
+          filename.contains('RunnerTests/') ||
+          filename.contains('RunnerUITests/') ||
+          // Native Android tests.
+          filename.contains('android/src/test/') ||
+          filename.contains('androidTest/') ||
+          // Native Linux tests.
+          filename.endsWith('_test.cc') ||
+          // Native Windows tests.
+          filename.endsWith('_test.cpp')) {
+        hasTests = true;
+      }
+    }
+
+    // We do not need to add test labels if this is an auto roller author.
+    if (config.rollerAccounts.contains(pr.user!.login)) {
+      return;
+    }
+
+    if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
+      final String body = config.missingTestsPullRequestMessage;
+      if (!await _alreadyCommented(gitHubClient, pr, body)) {
+        await gitHubClient.issues.createComment(slug, pr.number!, body);
+        await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, kNeedsTestsLabels);
+      }
+    }
+  }
+
+  /// Validate the base and head refs of the PR.
+  Future<void> _validateRefs(
+    GitHub gitHubClient,
+    PullRequest pr,
+  ) async {
+    final RepositorySlug slug = pr.base!.repo!.slug();
+    String body;
+    const List<String> releaseChannels = <String>[
+      'stable',
+      'beta',
+      'dev',
+    ];
+    // Close PRs that use a release branch as a source.
+    if (releaseChannels.contains(pr.head!.ref)) {
+      body = config.wrongHeadBranchPullRequestMessage(pr.head!.ref!);
+      if (!await _alreadyCommented(gitHubClient, pr, body)) {
+        await gitHubClient.pullRequests.edit(
+          slug,
+          pr.number!,
+          state: 'closed',
+        );
+        await gitHubClient.issues.createComment(slug, pr.number!, body);
+      }
+      return;
+    }
+    final String defaultBranchName = Config.defaultBranch(pr.base!.repo!.slug());
+    final String baseName = pr.base!.ref!;
+    if (baseName == defaultBranchName) {
+      return;
+    }
+    if (_isReleaseBranch(pr)) {
+      body = config.releaseBranchPullRequestMessage;
+      if (!await _alreadyCommented(gitHubClient, pr, body)) {
+        await gitHubClient.issues.createComment(slug, pr.number!, body);
+      }
+      return;
+    }
+
+    // Assume this PR should be based against config.defaultBranch.
+    body = _getWrongBaseComment(base: baseName, defaultBranch: defaultBranchName);
+    if (!await _alreadyCommented(gitHubClient, pr, body)) {
+      await gitHubClient.pullRequests.edit(
+        slug,
+        pr.number!,
+        base: Config.defaultBranch(slug),
+      );
+      await gitHubClient.issues.createComment(slug, pr.number!, body);
+    }
+  }
+
+  bool _isReleaseBranch(PullRequest pr) {
+    final String defaultBranchName = Config.defaultBranch(pr.base!.repo!.slug());
+    final String baseName = pr.base!.ref!;
+
+    if (baseName == defaultBranchName) {
+      return false;
+    }
+    // Check if branch name confroms to the format flutter-x.x-candidate.x,
+    // A pr with conforming branch name is likely to be intended
+    // for a release branch, whereas a pr with non conforming name is likely
+    // caused by user misoperations, in which case bot
+    // will suggest open pull request against default branch instead.
+    final RegExp candidateTest = RegExp(r'flutter-\d+\.\d+-candidate\.\d+');
+    if (candidateTest.hasMatch(baseName) && candidateTest.hasMatch(pr.head!.ref!)) {
+      return true;
+    }
+    return false;
+  }
+
+  Future<bool> _alreadyCommented(
+    GitHub gitHubClient,
+    PullRequest pr,
+    String message,
+  ) async {
+    final Stream<IssueComment> comments = gitHubClient.issues.listCommentsByIssue(pr.base!.repo!.slug(), pr.number!);
+    await for (IssueComment comment in comments) {
+      if (comment.body != null && comment.body!.contains(message)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  String _getWrongBaseComment({
+    required String base,
+    required String defaultBranch,
+  }) {
+    final String messageTemplate = config.wrongBaseBranchPullRequestMessage;
+    return messageTemplate.replaceAll('{{target_branch}}', base).replaceAll('{{default_branch}}', defaultBranch);
+  }
+
+  Future<PullRequestEvent?> _getPullRequestEvent(String request) async {
+    try {
+      return PullRequestEvent.fromJson(json.decode(request) as Map<String, dynamic>);
+    } on FormatException {
+      return null;
+    }
+  }
+
+  /// Returns true if the changes to [file] are all code comments.
+  ///
+  /// If that cannot be determined with confidence, returns false. False
+  /// negatives (e.g., for /* */-style multi-line comments) should be expected.
+  bool _allChangesAreCodeComments(PullRequestFile file) {
+    final int? linesAdded = file.additionsCount;
+    final int? linesDeleted = file.deletionsCount;
+    final String? patch = file.patch;
+    // If information is missing, err or the side of assuming it's a non-comment
+    // change.
+    if (linesAdded == null || linesDeleted == null || patch == null) {
+      return false;
+    }
+
+    // Ensure that the file is a language reconized by the check below.
+    const Set<String> codeExtensions = <String>{
+      'cc',
+      'cpp',
+      'dart',
+      'java',
+      'kt',
+      'm',
+      'mm',
+      'swift',
+    };
+    final String filename = file.filename!;
+    final String? extension = filename.contains('.') ? filename.split('.').last.toLowerCase() : null;
+    if (extension == null || !codeExtensions.contains(extension)) {
+      return false;
+    }
+
+    // Only handles single-line comments; identifying multi-line comments
+    // would require the full file and non-trivial parsing. Also doesn't handle
+    // end-of-line comments (e.g., "int x = 0; // Info about x").
+    final RegExp commentRegex = RegExp(r'[+-]\s*//');
+    for (String line in patch.split('\n')) {
+      if (!line.startsWith('+') && !line.startsWith('-')) {
+        continue;
+      }
+      if (!commentRegex.hasMatch(line)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/app_dart/lib/src/request_handlers/github_webhook.dart b/app_dart/lib/src/request_handlers/github_webhook.dart
index cf1b87d..5ea32e6 100644
--- a/app_dart/lib/src/request_handlers/github_webhook.dart
+++ b/app_dart/lib/src/request_handlers/github_webhook.dart
@@ -4,593 +4,52 @@
 
 import 'dart:async';
 import 'dart:convert';
-
-import 'package:cocoon_service/src/service/branch_service.dart';
 import 'package:crypto/crypto.dart';
-import 'package:github/github.dart'
-    show CreatePullRequestReview, GitHub, IssueComment, PullRequest, PullRequestFile, RepositorySlug;
-import 'package:github/hooks.dart';
-
-import '../model/github/checks.dart' as cocoon_checks;
+import '../model/proto/protos.dart' as pb;
 import '../request_handling/body.dart';
 import '../request_handling/exceptions.dart';
+import '../request_handling/pubsub.dart';
 import '../request_handling/request_handler.dart';
-import '../service/config.dart';
-import '../service/datastore.dart';
-import '../service/github_checks_service.dart';
-import '../service/logging.dart';
-import '../service/scheduler.dart';
 
-// Filenames which are not actually tests.
-const List<String> kNotActuallyATest = <String>[
-  'packages/flutter/lib/src/gestures/hit_test.dart',
-];
-
-/// List of repos that require check for labels and tests.
-const Set<String> kNeedsCheckLabelsAndTests = <String>{
-  'flutter/engine',
-  'flutter/flutter',
-  'flutter/packages',
-  'flutter/plugins',
-};
-
-final RegExp kEngineTestRegExp = RegExp(r'(tests?|benchmarks?)\.(dart|java|mm|m|cc)$');
-final List<String> kNeedsTestsLabels = <String>['needs tests'];
-
+/// The [RequestHandler] for processing GitHub webhooks and publishing valid events to PubSub.
+///
+/// Requests are only published as a [GithubWebhookMessage] iff they contain:
+///   1. Event type from the header `X-GitHub-Event`
+///   2. Event payload that was HMAC authenticated
 class GithubWebhook extends RequestHandler<Body> {
   GithubWebhook({
     required super.config,
-    required this.scheduler,
-    this.githubChecksService,
-    this.datastoreProvider = DatastoreService.defaultProvider,
-    required this.branchService,
+    required this.pubsub,
   });
 
-  /// Cocoon scheduler to trigger tasks against changes from GitHub.
-  final Scheduler scheduler;
-
-  /// Github checks service. Used to provide build status to github.
-  final GithubChecksService? githubChecksService;
-
-  final DatastoreServiceProvider datastoreProvider;
-  final BranchService branchService;
+  final PubSub pubsub;
 
   @override
   Future<Body> post() async {
-    final String? gitHubEvent = request!.headers.value('X-GitHub-Event');
+    final String? event = request!.headers.value('X-GitHub-Event');
 
-    if (gitHubEvent == null || request!.headers.value('X-Hub-Signature') == null) {
+    if (event == null || request!.headers.value('X-Hub-Signature') == null) {
       throw const BadRequestException('Missing required headers.');
     }
     final List<int> requestBytes = await request!.expand((_) => _).toList();
     final String? hmacSignature = request!.headers.value('X-Hub-Signature');
-    if (!await _validateRequest(hmacSignature, requestBytes)) {
-      throw const Forbidden();
-    }
+    await _validateRequest(hmacSignature, requestBytes);
 
-    try {
-      final String stringRequest = utf8.decode(requestBytes);
-      log.fine('Processing $gitHubEvent');
-      log.finest(stringRequest);
-      switch (gitHubEvent) {
-        case 'pull_request':
-          await _handlePullRequest(stringRequest);
-          break;
-        case 'check_run':
-          final Map<String, dynamic> event = jsonDecode(stringRequest) as Map<String, dynamic>;
-          final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(event);
-          if (await scheduler.processCheckRun(checkRunEvent) == false) {
-            throw InternalServerError('Failed to process $checkRunEvent');
-          }
-          break;
-        case 'create':
-          final CreateEvent createEvent = _getCreateEvent(stringRequest)!;
-          await branchService.handleCreateRequest(createEvent);
-          if (createEvent.repository?.slug() == Config.flutterSlug) {
-            await branchService.branchFlutterRecipes(createEvent.ref!);
-          }
-          break;
-      }
+    final String requestString = utf8.decode(requestBytes);
 
-      return Body.empty;
-    } on FormatException {
-      throw const BadRequestException('Could not process input data.');
-    } on InternalServerError {
-      rethrow;
-    }
+    // TODO(chillers): This won't work until using proto.
+    final pb.GithubWebhookMessage message = pb.GithubWebhookMessage.create()
+      ..event = event
+      ..payload = requestString;
+    await pubsub.publish('github-wehbooks', message);
+
+    return Body.empty;
   }
 
-  Future<void> _handlePullRequest(
-    String rawRequest,
-  ) async {
-    final PullRequestEvent? pullRequestEvent = await _getPullRequestEvent(rawRequest);
-    if (pullRequestEvent == null) {
-      throw const BadRequestException('Expected pull request event.');
-    }
-    final String? eventAction = pullRequestEvent.action;
-    final PullRequest pr = pullRequestEvent.pullRequest!;
-
-    // See the API reference:
-    // https://developer.github.com/v3/activity/events/types/#pullrequestevent
-    // which unfortunately is a bit light on explanations.
-    log.fine('Processing $eventAction for ${pr.htmlUrl}');
-    switch (eventAction) {
-      case 'closed':
-        // If it was closed without merging, cancel any outstanding tryjobs.
-        // We'll leave unfinished jobs if it was merged since we care about those
-        // results.
-        if (!pr.merged!) {
-          await scheduler.cancelPreSubmitTargets(pullRequest: pr, reason: 'Pull request closed');
-        } else {
-          // Merged pull requests can be added to CI.
-          await scheduler.addPullRequest(pr);
-        }
-        break;
-      case 'edited':
-        // Editing a PR should not trigger new jobs, but may update whether
-        // it has tests.
-        await _checkForLabelsAndTests(pullRequestEvent);
-        break;
-      case 'opened':
-      case 'reopened':
-        // These cases should trigger LUCI jobs.
-        await _checkForLabelsAndTests(pullRequestEvent);
-        await _scheduleIfMergeable(pullRequestEvent);
-        await _tryReleaseApproval(pullRequestEvent);
-        break;
-      case 'labeled':
-        break;
-      case 'synchronize':
-        // This indicates the PR has new commits. We need to cancel old jobs
-        // and schedule new ones.
-        await _scheduleIfMergeable(pullRequestEvent);
-        break;
-      // Ignore the rest of the events.
-      case 'ready_for_review':
-      case 'unlabeled':
-      case 'assigned':
-      case 'locked':
-      case 'review_request_removed':
-      case 'review_requested':
-      case 'unassigned':
-      case 'unlocked':
-        break;
-    }
-  }
-
-  /// This method assumes that jobs should be cancelled if they are already
-  /// runnning.
-  Future<void> _scheduleIfMergeable(
-    PullRequestEvent pullRequestEvent,
-  ) async {
-    final PullRequest pr = pullRequestEvent.pullRequest!;
-    final RepositorySlug slug = pullRequestEvent.repository!.slug();
-
-    log.info(
-        'Scheduling tasks if mergeable(${pr.mergeable}): owner=${slug.owner} repo=${slug.name} and pr=${pr.number}');
-
-    // The mergeable flag may be null. False indicates there's a merge conflict,
-    // null indicates unknown. Err on the side of allowing the job to run.
-    if (pr.mergeable == false) {
-      final RepositorySlug slug = pullRequestEvent.repository!.slug();
-      final GitHub gitHubClient = await config.createGitHubClient(pullRequest: pr);
-      final String body = config.mergeConflictPullRequestMessage;
-      if (!await _alreadyCommented(gitHubClient, pr, body)) {
-        await gitHubClient.issues.createComment(slug, pr.number!, body);
-      }
-
-      return;
-    }
-
-    await scheduler.triggerPresubmitTargets(pullRequest: pr);
-  }
-
-  /// Release tooling generates cherrypick pull requests that should be granted an approval.
-  Future<void> _tryReleaseApproval(
-    PullRequestEvent pullRequestEvent,
-  ) async {
-    final PullRequest pr = pullRequestEvent.pullRequest!;
-    final RepositorySlug slug = pullRequestEvent.repository!.slug();
-
-    final String defaultBranch = Config.defaultBranch(slug);
-    final String? branch = pr.base?.ref;
-    if (branch == null || branch.contains(defaultBranch)) {
-      // This isn't a release branch PR
-      return;
-    }
-
-    final List<String> releaseAccounts = await config.releaseAccounts;
-    if (releaseAccounts.contains(pr.user?.login) == false) {
-      // The author isn't in the list of release accounts, do nothing
-      return;
-    }
-
-    final GitHub gitHubClient = config.createGitHubClientWithToken(await config.githubOAuthToken);
-    final CreatePullRequestReview review = CreatePullRequestReview(slug.owner, slug.name, pr.number!, 'APPROVE');
-    await gitHubClient.pullRequests.createReview(slug, review);
-  }
-
-  Future<void> _checkForLabelsAndTests(PullRequestEvent pullRequestEvent) async {
-    final PullRequest pr = pullRequestEvent.pullRequest!;
-    final String? eventAction = pullRequestEvent.action;
-    final String repo = pr.base!.repo!.fullName.toLowerCase();
-    if (kNeedsCheckLabelsAndTests.contains(repo)) {
-      final GitHub gitHubClient = await config.createGitHubClient(pullRequest: pr);
-      try {
-        await _validateRefs(gitHubClient, pr);
-        if (repo == 'flutter/flutter') {
-          await _applyFrameworkRepoLabels(gitHubClient, eventAction, pr);
-        } else if (repo == 'flutter/engine') {
-          await _applyEngineRepoLabels(gitHubClient, eventAction, pr);
-        } else if (repo == 'flutter/plugins' || repo == 'flutter/packages') {
-          await _applyPackageTestChecks(gitHubClient, eventAction, pr);
-        }
-      } finally {
-        gitHubClient.dispose();
-      }
-    }
-  }
-
-  Future<void> _applyFrameworkRepoLabels(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
-    if (pr.user!.login == 'engine-flutter-autoroll') {
-      return;
-    }
-
-    final RepositorySlug slug = pr.base!.repo!.slug();
-    log.info('Applying framework repo labels for: owner=${slug.owner} repo=${slug.name} and pr=${pr.number}');
-    final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
-
-    final Set<String> labels = <String>{};
-    bool hasTests = false;
-    bool needsTests = false;
-
-    await for (PullRequestFile file in files) {
-      // When null, do not assume 0 lines have been added.
-      final String filename = file.filename!;
-      final int linesAdded = file.additionsCount ?? 1;
-      final int linesDeleted = file.deletionsCount ?? 0;
-      final int linesTotal = file.changesCount ?? linesDeleted + linesAdded;
-      final bool addedCode = linesAdded > 0 || linesDeleted != linesTotal;
-
-      if (addedCode &&
-          !filename.contains('AUTHORS') &&
-          !filename.contains('pubspec.yaml') &&
-          !filename.contains('.ci.yaml') &&
-          !filename.contains('.cirrus.yml') &&
-          !filename.contains('.github') &&
-          !filename.endsWith('.md') &&
-          !filename.contains('CODEOWNERS') &&
-          !filename.startsWith('dev/bots/')) {
-        needsTests = !_allChangesAreCodeComments(file);
-      }
-
-      // Check to see if tests were submitted with this PR.
-      if (_isATest(filename)) {
-        hasTests = true;
-      }
-      labels.addAll(getLabelsForFrameworkPath(filename));
-    }
-
-    if (pr.user!.login == 'fluttergithubbot') {
-      needsTests = false;
-      labels.addAll(<String>['team', 'tech-debt', 'team: flakes']);
-    }
-
-    if (labels.isNotEmpty) {
-      await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, labels.toList());
-    }
-
-    // We do not need to add test labels if this is an auto roller author.
-    if (config.rollerAccounts.contains(pr.user!.login)) {
-      return;
-    }
-
-    if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
-      final String body = config.missingTestsPullRequestMessage;
-      if (!await _alreadyCommented(gitHubClient, pr, body)) {
-        await gitHubClient.issues.createComment(slug, pr.number!, body);
-      }
-    }
-  }
-
-  bool _isATest(String filename) {
-    if (kNotActuallyATest.any(filename.endsWith)) {
-      return false;
-    }
-    // Check for Objective-C tests which end in either "Tests.m" or "Test.m"
-    // in the "dev" directory.
-    final RegExp objectiveCTestRegex = RegExp(r'.*dev\/.*Test[s]?\.m$');
-    return filename.endsWith('_test.dart') ||
-        filename.endsWith('.expect') ||
-        filename.contains('test_fixes') ||
-        filename.startsWith('dev/bots/analyze.dart') ||
-        filename.startsWith('dev/bots/test.dart') ||
-        filename.startsWith('dev/devicelab/bin/tasks') ||
-        filename.startsWith('dev/devicelab/lib/tasks') ||
-        objectiveCTestRegex.hasMatch(filename);
-  }
-
-  /// Returns the set of labels applicable to a file in the framework repo.
-  static Set<String> getLabelsForFrameworkPath(String filepath) {
-    final Set<String> labels = <String>{};
-    if (filepath.endsWith('pubspec.yaml')) {
-      // These get updated by a script, and are updated en masse.
-      labels.add('team');
-      return labels;
-    }
-
-    if (filepath.endsWith('fix_data.yaml') || filepath.endsWith('.expect') || filepath.contains('test_fixes')) {
-      // Dart fixes
-      labels.add('team');
-      labels.add('tech-debt');
-    }
-
-    const Map<String, List<String>> pathPrefixLabels = <String, List<String>>{
-      'bin/internal/engine.version': <String>['engine'],
-      'dev/': <String>['team'],
-      'examples/': <String>['d: examples', 'team'],
-      'examples/api/': <String>['d: examples', 'team', 'd: api docs', 'documentation'],
-      'examples/flutter_gallery/': <String>['d: examples', 'team', 'team: gallery'],
-      'packages/flutter_tools/': <String>['tool'],
-      'packages/flutter_tools/lib/src/ios/': <String>['platform-ios'],
-      'packages/flutter/': <String>['framework'],
-      'packages/flutter_driver/': <String>['framework', 'a: tests'],
-      'packages/flutter_localizations/': <String>['a: internationalization'],
-      'packages/flutter_goldens/': <String>['framework', 'a: tests', 'team'],
-      'packages/flutter_goldens_client/': <String>['framework', 'a: tests', 'team'],
-      'packages/flutter_test/': <String>['framework', 'a: tests'],
-      'packages/fuchsia_remote_debug_protocol/': <String>['tool'],
-      'packages/integration_test/': <String>['integration_test'],
-    };
-    const Map<String, List<String>> pathContainsLabels = <String, List<String>>{
-      'accessibility': <String>['a: accessibility'],
-      'animation': <String>['a: animation'],
-      'cupertino': <String>['f: cupertino'],
-      'focus': <String>['f: focus'],
-      'gestures': <String>['f: gestures'],
-      'material': <String>['f: material design'],
-      'navigator': <String>['f: routes'],
-      'route': <String>['f: routes'],
-      'scroll': <String>['f: scrolling'],
-      'semantics': <String>['a: accessibility'],
-      'sliver': <String>['f: scrolling'],
-      'text': <String>['a: text input'],
-      'viewport': <String>['f: scrolling'],
-    };
-
-    pathPrefixLabels.forEach((String path, List<String> pathLabels) {
-      if (filepath.startsWith(path)) {
-        labels.addAll(pathLabels);
-      }
-    });
-    pathContainsLabels.forEach((String path, List<String> pathLabels) {
-      if (filepath.contains(path)) {
-        labels.addAll(pathLabels);
-      }
-    });
-    return labels;
-  }
-
-  /// Returns the set of labels applicable to a file in the engine repo.
-  static Set<String> getLabelsForEnginePath(String filepath) {
-    const Map<String, List<String>> pathPrefixLabels = <String, List<String>>{
-      'shell/platform/android': <String>['platform-android'],
-      'shell/platform/embedder': <String>['embedder'],
-      'shell/platform/darwin/common': <String>['platform-ios', 'platform-macos'],
-      'shell/platform/darwin/ios': <String>['platform-ios'],
-      'shell/platform/darwin/macos': <String>['platform-macos'],
-      'shell/platform/fuchsia': <String>['platform-fuchsia'],
-      'shell/platform/linux': <String>['platform-linux'],
-      'shell/platform/windows': <String>['platform-windows'],
-      'lib/web_ui': <String>['platform-web'],
-      'web_sdk': <String>['platform-web'],
-    };
-    final Set<String> labels = <String>{};
-    pathPrefixLabels.forEach((String path, List<String> pathLabels) {
-      if (filepath.startsWith(path)) {
-        labels.addAll(pathLabels);
-      }
-    });
-    return labels;
-  }
-
-  Future<void> _applyEngineRepoLabels(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
-    // Do not apply the test labels for the autoroller accounts.
-    if (pr.user!.login == 'skia-flutter-autoroll') {
-      return;
-    }
-
-    final RepositorySlug slug = pr.base!.repo!.slug();
-    final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
-    final Set<String> labels = <String>{};
-    bool hasTests = false;
-    bool needsTests = false;
-
-    await for (PullRequestFile file in files) {
-      final String filename = file.filename!.toLowerCase();
-      if (filename.endsWith('.dart') ||
-          filename.endsWith('.mm') ||
-          filename.endsWith('.m') ||
-          filename.endsWith('.java') ||
-          filename.endsWith('.cc')) {
-        needsTests = true;
-      }
-
-      if (kEngineTestRegExp.hasMatch(filename)) {
-        hasTests = true;
-      }
-
-      labels.addAll(getLabelsForEnginePath(filename));
-    }
-
-    if (labels.isNotEmpty) {
-      await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, labels.toList());
-    }
-
-    // We do not need to add test labels if this is an auto roller author.
-    if (config.rollerAccounts.contains(pr.user!.login)) {
-      return;
-    }
-
-    if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
-      final String body = config.missingTestsPullRequestMessage;
-      if (!await _alreadyCommented(gitHubClient, pr, body)) {
-        await gitHubClient.issues.createComment(slug, pr.number!, body);
-        await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, kNeedsTestsLabels);
-      }
-    }
-  }
-
-  // Runs automated test checks for both flutter/packages and flutter/plugins.
-  Future<void> _applyPackageTestChecks(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
-    final RepositorySlug slug = pr.base!.repo!.slug();
-    final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
-    bool hasTests = false;
-    bool needsTests = false;
-
-    await for (PullRequestFile file in files) {
-      final String filename = file.filename!;
-
-      // When null, do not assume 0 lines have been added.
-      final int linesAdded = file.additionsCount ?? 1;
-      final int linesDeleted = file.deletionsCount ?? 0;
-      final int linesTotal = file.changesCount ?? linesDeleted + linesAdded;
-      final bool addedCode = linesAdded > 0 || linesDeleted != linesTotal;
-
-      if (addedCode &&
-          !filename.endsWith('AUTHORS') &&
-          !filename.endsWith('CODEOWNERS') &&
-          !filename.endsWith('pubspec.yaml') &&
-          !filename.endsWith('.ci.yaml') &&
-          !filename.endsWith('.cirrus.yml') &&
-          !filename.contains('.ci/') &&
-          !filename.contains('.github/') &&
-          !filename.endsWith('.md')) {
-        needsTests = !_allChangesAreCodeComments(file);
-      }
-      // See https://github.com/flutter/flutter/wiki/Plugin-Tests for discussion
-      // of various plugin test types and locations.
-      if (filename.endsWith('_test.dart') ||
-          // Native iOS/macOS tests.
-          filename.contains('RunnerTests/') ||
-          filename.contains('RunnerUITests/') ||
-          // Native Android tests.
-          filename.contains('android/src/test/') ||
-          filename.contains('androidTest/') ||
-          // Native Linux tests.
-          filename.endsWith('_test.cc') ||
-          // Native Windows tests.
-          filename.endsWith('_test.cpp')) {
-        hasTests = true;
-      }
-    }
-
-    // We do not need to add test labels if this is an auto roller author.
-    if (config.rollerAccounts.contains(pr.user!.login)) {
-      return;
-    }
-
-    if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
-      final String body = config.missingTestsPullRequestMessage;
-      if (!await _alreadyCommented(gitHubClient, pr, body)) {
-        await gitHubClient.issues.createComment(slug, pr.number!, body);
-        await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, kNeedsTestsLabels);
-      }
-    }
-  }
-
-  /// Validate the base and head refs of the PR.
-  Future<void> _validateRefs(
-    GitHub gitHubClient,
-    PullRequest pr,
-  ) async {
-    final RepositorySlug slug = pr.base!.repo!.slug();
-    String body;
-    const List<String> releaseChannels = <String>[
-      'stable',
-      'beta',
-      'dev',
-    ];
-    // Close PRs that use a release branch as a source.
-    if (releaseChannels.contains(pr.head!.ref)) {
-      body = config.wrongHeadBranchPullRequestMessage(pr.head!.ref!);
-      if (!await _alreadyCommented(gitHubClient, pr, body)) {
-        await gitHubClient.pullRequests.edit(
-          slug,
-          pr.number!,
-          state: 'closed',
-        );
-        await gitHubClient.issues.createComment(slug, pr.number!, body);
-      }
-      return;
-    }
-    final String defaultBranchName = Config.defaultBranch(pr.base!.repo!.slug());
-    final String baseName = pr.base!.ref!;
-    if (baseName == defaultBranchName) {
-      return;
-    }
-    if (_isReleaseBranch(pr)) {
-      body = config.releaseBranchPullRequestMessage;
-      if (!await _alreadyCommented(gitHubClient, pr, body)) {
-        await gitHubClient.issues.createComment(slug, pr.number!, body);
-      }
-      return;
-    }
-
-    // Assume this PR should be based against config.defaultBranch.
-    body = _getWrongBaseComment(base: baseName, defaultBranch: defaultBranchName);
-    if (!await _alreadyCommented(gitHubClient, pr, body)) {
-      await gitHubClient.pullRequests.edit(
-        slug,
-        pr.number!,
-        base: Config.defaultBranch(slug),
-      );
-      await gitHubClient.issues.createComment(slug, pr.number!, body);
-    }
-  }
-
-  bool _isReleaseBranch(PullRequest pr) {
-    final String defaultBranchName = Config.defaultBranch(pr.base!.repo!.slug());
-    final String baseName = pr.base!.ref!;
-
-    if (baseName == defaultBranchName) {
-      return false;
-    }
-    // Check if branch name confroms to the format flutter-x.x-candidate.x,
-    // A pr with conforming branch name is likely to be intended
-    // for a release branch, whereas a pr with non conforming name is likely
-    // caused by user misoperations, in which case bot
-    // will suggest open pull request against default branch instead.
-    final RegExp candidateTest = RegExp(r'flutter-\d+\.\d+-candidate\.\d+');
-    if (candidateTest.hasMatch(baseName) && candidateTest.hasMatch(pr.head!.ref!)) {
-      return true;
-    }
-    return false;
-  }
-
-  Future<bool> _alreadyCommented(
-    GitHub gitHubClient,
-    PullRequest pr,
-    String message,
-  ) async {
-    final Stream<IssueComment> comments = gitHubClient.issues.listCommentsByIssue(pr.base!.repo!.slug(), pr.number!);
-    await for (IssueComment comment in comments) {
-      if (comment.body != null && comment.body!.contains(message)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  String _getWrongBaseComment({
-    required String base,
-    required String defaultBranch,
-  }) {
-    final String messageTemplate = config.wrongBaseBranchPullRequestMessage;
-    return messageTemplate.replaceAll('{{target_branch}}', base).replaceAll('{{default_branch}}', defaultBranch);
-  }
-
-  Future<bool> _validateRequest(
+  /// Ensures the signature provided for the given payload matches what is expected.
+  ///
+  /// The expected key is the sha1 hash of the payload using the private key of the GitHub app.
+  Future<void> _validateRequest(
     String? signature,
     List<int> requestBody,
   ) async {
@@ -599,71 +58,8 @@
     final Hmac hmac = Hmac(sha1, key);
     final Digest digest = hmac.convert(requestBody);
     final String bodySignature = 'sha1=$digest';
-    return bodySignature == signature;
-  }
-
-  Future<PullRequestEvent?> _getPullRequestEvent(String request) async {
-    try {
-      return PullRequestEvent.fromJson(json.decode(request) as Map<String, dynamic>);
-    } on FormatException {
-      return null;
-    }
-  }
-
-  /// Returns true if the changes to [file] are all code comments.
-  ///
-  /// If that cannot be determined with confidence, returns false. False
-  /// negatives (e.g., for /* */-style multi-line comments) should be expected.
-  bool _allChangesAreCodeComments(PullRequestFile file) {
-    final int? linesAdded = file.additionsCount;
-    final int? linesDeleted = file.deletionsCount;
-    final String? patch = file.patch;
-    // If information is missing, err or the side of assuming it's a non-comment
-    // change.
-    if (linesAdded == null || linesDeleted == null || patch == null) {
-      return false;
-    }
-
-    // Ensure that the file is a language reconized by the check below.
-    const Set<String> codeExtensions = <String>{
-      'cc',
-      'cpp',
-      'dart',
-      'java',
-      'kt',
-      'm',
-      'mm',
-      'swift',
-    };
-    final String filename = file.filename!;
-    final String? extension = filename.contains('.') ? filename.split('.').last.toLowerCase() : null;
-    if (extension == null || !codeExtensions.contains(extension)) {
-      return false;
-    }
-
-    // Only handles single-line comments; identifying multi-line comments
-    // would require the full file and non-trivial parsing. Also doesn't handle
-    // end-of-line comments (e.g., "int x = 0; // Info about x").
-    final RegExp commentRegex = RegExp(r'[+-]\s*//');
-    for (String line in patch.split('\n')) {
-      if (!line.startsWith('+') && !line.startsWith('-')) {
-        continue;
-      }
-      if (!commentRegex.hasMatch(line)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  CreateEvent? _getCreateEvent(String request) {
-    try {
-      return CreateEvent.fromJson(json.decode(request) as Map<String, dynamic>);
-    } on FormatException {
-      return null;
-    } catch (e) {
-      log.severe('Unexpected exception was encountered while decoding json webhook msg for branch creation: $e');
-      return null;
+    if (bodySignature != signature) {
+      throw const Forbidden('X-Hub-Signature does not match expected value');
     }
   }
 }
diff --git a/app_dart/lib/src/service/config.dart b/app_dart/lib/src/service/config.dart
index d01b328..94fa1de 100644
--- a/app_dart/lib/src/service/config.dart
+++ b/app_dart/lib/src/service/config.dart
@@ -30,9 +30,11 @@
 const String kDefaultBranchName = 'master';
 
 /// Name of an example release base branch name.
+// TODO(chillers): Delete this as it's only used for tests.
 const String kReleaseBaseRef = 'flutter-2.12-candidate.4';
 
 /// Name of an example release head branch name.
+// TODO(chillers): Delete this as it's only used for tests.
 const String kReleaseHeadRef = 'cherrypicks-flutter-2.12-candidate.4';
 
 class Config {
diff --git a/app_dart/test/model/push_message_test.dart b/app_dart/test/model/push_message_test.dart
index a998fc1..7a16a0d 100644
--- a/app_dart/test/model/push_message_test.dart
+++ b/app_dart/test/model/push_message_test.dart
@@ -14,6 +14,7 @@
       json.decode(buildPushMessageJson) as Map<String, dynamic>,
     );
 
+    // TODO(chillers): This fails in EMEA timezones. Prefer using UTC instead of local.
     expect(data.build!.createdTimestamp!.year, 2019);
     expect(data.build!.createdTimestamp!.month, 8);
     expect(data.build!.createdTimestamp!.day, 5);
diff --git a/app_dart/test/request_handlers/github/webhook_subscription_test.dart b/app_dart/test/request_handlers/github/webhook_subscription_test.dart
new file mode 100644
index 0000000..57fc915
--- /dev/null
+++ b/app_dart/test/request_handlers/github/webhook_subscription_test.dart
@@ -0,0 +1,2382 @@
+// Copyright 2019 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:cocoon_service/src/model/appengine/commit.dart';
+import 'package:cocoon_service/src/model/luci/buildbucket.dart';
+import 'package:cocoon_service/src/request_handlers/github/webhook_subscription.dart';
+import 'package:cocoon_service/src/service/cache_service.dart';
+import 'package:cocoon_service/src/service/config.dart';
+import 'package:cocoon_service/src/service/datastore.dart';
+
+import 'package:github/github.dart' hide Branch;
+import 'package:googleapis/bigquery/v2.dart';
+import 'package:mockito/mockito.dart';
+import 'package:test/test.dart';
+
+import '../../src/datastore/fake_config.dart';
+import '../../src/datastore/fake_datastore.dart';
+import '../../src/request_handling/fake_http.dart';
+import '../../src/request_handling/subscription_tester.dart';
+import '../../src/service/fake_buildbucket.dart';
+import '../../src/service/fake_github_service.dart';
+import '../../src/service/fake_scheduler.dart';
+import '../../src/utilities/entity_generators.dart';
+import '../../src/utilities/mocks.dart';
+import '../../src/utilities/webhook_generators.dart';
+
+void main() {
+  late GithubWebhookSubscription webhook;
+  late FakeBuildBucketClient fakeBuildBucketClient;
+  late FakeConfig config;
+  late FakeDatastoreDB db;
+  late FakeGithubService githubService;
+  late FakeHttpRequest request;
+  late FakeScheduler scheduler;
+  late MockBranchService branchService;
+  late MockGitHub gitHubClient;
+  late MockGithubChecksUtil mockGithubChecksUtil;
+  late MockGithubChecksService mockGithubChecksService;
+  late MockIssuesService issuesService;
+  late MockPullRequestsService pullRequestsService;
+  late SubscriptionTester tester;
+
+  setUp(() {
+    request = FakeHttpRequest();
+    db = FakeDatastoreDB();
+    gitHubClient = MockGitHub();
+    githubService = FakeGithubService();
+    final MockTabledataResource tabledataResource = MockTabledataResource();
+    when(tabledataResource.insertAll(any, any, any, any)).thenAnswer((_) async => TableDataInsertAllResponse());
+    config = FakeConfig(
+      dbValue: db,
+      githubClient: gitHubClient,
+      githubService: githubService,
+      githubOAuthTokenValue: 'githubOAuthKey',
+      missingTestsPullRequestMessageValue: 'missingTestPullRequestMessage',
+      releaseBranchPullRequestMessageValue: 'releaseBranchPullRequestMessage',
+      rollerAccountsValue: const <String>{
+        'skia-flutter-autoroll',
+        'engine-flutter-autoroll',
+        'dependabot',
+      },
+      tabledataResource: tabledataResource,
+      wrongHeadBranchPullRequestMessageValue: 'wrongHeadBranchPullRequestMessage',
+      wrongBaseBranchPullRequestMessageValue: '{{target_branch}} -> {{default_branch}}',
+    );
+    branchService = MockBranchService();
+    issuesService = MockIssuesService();
+    when(issuesService.addLabelsToIssue(any, any, any)).thenAnswer((_) async => <IssueLabel>[]);
+    when(issuesService.createComment(any, any, any)).thenAnswer((_) async => IssueComment());
+    when(issuesService.listCommentsByIssue(any, any))
+        .thenAnswer((_) => Stream<IssueComment>.fromIterable(<IssueComment>[IssueComment()]));
+    pullRequestsService = MockPullRequestsService();
+    when(pullRequestsService.listFiles(Config.flutterSlug, any))
+        .thenAnswer((_) => const Stream<PullRequestFile>.empty());
+    when(pullRequestsService.edit(any, any, title: anyNamed('title'), state: anyNamed('state'), base: anyNamed('base')))
+        .thenAnswer((_) async => PullRequest());
+    fakeBuildBucketClient = FakeBuildBucketClient();
+    mockGithubChecksUtil = MockGithubChecksUtil();
+    scheduler = FakeScheduler(
+      config: config,
+      buildbucket: fakeBuildBucketClient,
+      githubChecksUtil: mockGithubChecksUtil,
+    );
+    tester = SubscriptionTester(request: request);
+
+    mockGithubChecksService = MockGithubChecksService();
+    when(gitHubClient.issues).thenReturn(issuesService);
+    when(gitHubClient.pullRequests).thenReturn(pullRequestsService);
+    when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))).thenAnswer((_) async {
+      return CheckRun.fromJson(const <String, dynamic>{
+        'id': 1,
+        'started_at': '2020-05-10T02:49:31Z',
+        'check_suite': <String, dynamic>{'id': 2}
+      });
+    });
+
+    webhook = GithubWebhookSubscription(
+      config: config,
+      cache: CacheService(inMemory: true),
+      datastoreProvider: (_) => DatastoreService(config.db, 5),
+      githubChecksService: mockGithubChecksService,
+      scheduler: scheduler,
+      branchService: branchService,
+    );
+  });
+
+  group('github webhook pull_request event', () {
+    test('Closes PR opened from dev', () async {
+      const int issueNumber = 123;
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        headRef: 'dev',
+      );
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/flutter/blah.dart',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(pullRequestsService.edit(
+        Config.flutterSlug,
+        issueNumber,
+        state: 'closed',
+      )).called(1);
+
+      verify(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.wrongHeadBranchPullRequestMessageValue)),
+      )).called(1);
+    });
+
+    test('Acts on opened against dev', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        baseRef: 'dev',
+      );
+
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/flutter/blah.dart',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(pullRequestsService.edit(
+        Config.flutterSlug,
+        issueNumber,
+        base: kDefaultBranchName,
+      )).called(1);
+
+      verify(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains('dev -> master')),
+      )).called(1);
+    });
+
+    test('Acts on opened against master when default is main', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        baseRef: 'master',
+        slug: Config.engineSlug,
+      );
+
+      when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/flutter/blah.dart',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(pullRequestsService.edit(
+        Config.engineSlug,
+        issueNumber,
+        base: 'main',
+      )).called(1);
+
+      verify(issuesService.createComment(
+        Config.engineSlug,
+        issueNumber,
+        argThat(contains('master -> main')),
+      )).called(1);
+    });
+
+    // We already schedule checks when a draft is opened, don't need to re-test
+    // just because it was marked ready for review
+    test('Does nothing on ready_for_review', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'ready_for_review',
+        number: issueNumber,
+      );
+      bool batchRequestCalled = false;
+
+      Future<BatchResponse> _getBatchResponse() async {
+        batchRequestCalled = true;
+        fail('Marking a draft ready for review should not trigger new builds');
+      }
+
+      fakeBuildBucketClient.batchResponse = _getBatchResponse;
+
+      await tester.post(webhook);
+
+      expect(batchRequestCalled, isFalse);
+    });
+
+    test('Triggers builds when opening a draft PR', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        isDraft: true,
+      );
+      bool batchRequestCalled = false;
+
+      Future<BatchResponse> _getBatchResponse() async {
+        batchRequestCalled = true;
+        return BatchResponse(
+          responses: <Response>[
+            Response(
+              searchBuilds: SearchBuildsResponse(
+                builds: <Build>[
+                  generateBuild(999, name: 'Linux', status: Status.ended),
+                ],
+              ),
+            ),
+            Response(
+              searchBuilds: SearchBuildsResponse(
+                builds: <Build>[
+                  generateBuild(998, name: 'Linux', status: Status.ended),
+                ],
+              ),
+            ),
+          ],
+        );
+      }
+
+      fakeBuildBucketClient.batchResponse = _getBatchResponse;
+
+      await tester.post(webhook);
+
+      expect(batchRequestCalled, isTrue);
+    });
+
+    test('Does nothing against cherry pick PR', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        baseRef: 'flutter-1.20-candidate.7',
+        headRef: 'cherrypicks-flutter-1.20-candidate.7',
+      );
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/flutter/blah.dart',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(pullRequestsService.edit(
+        Config.flutterSlug,
+        issueNumber,
+        base: kDefaultBranchName,
+      ));
+
+      verifyNever(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.wrongBaseBranchPullRequestMessage)),
+      ));
+    });
+
+    group('getLabelsForFrameworkPath', () {
+      test('Only the team label is applied to pubspec.yaml', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_tools/pubspec.yaml'),
+            <String>{'team'});
+      });
+
+      test('Tool label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_tools/hot_reload.dart'),
+            contains('tool'));
+        expect(
+            GithubWebhookSubscription.getLabelsForFrameworkPath(
+                'packages/fuchsia_remote_debug_protocol/hot_reload.dart'),
+            contains('tool'));
+      });
+
+      test('iOS label applied', () {
+        expect(
+          GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_tools/lib/src/ios/devices.dart'),
+          <String>{'platform-ios', 'tool'},
+        );
+      });
+
+      test('Engine label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('bin/internal/engine.version'), contains('engine'));
+      });
+
+      test('Framework label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/widget.dart'),
+            contains('framework'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_test/lib/tester.dart'),
+            contains('framework'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_driver/lib/driver.dart'),
+            contains('framework'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_goldens/lib/flutter_goldens.dart'),
+            contains('framework'));
+        expect(
+            GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_goldens_client/lib/skia_client.dart'),
+            contains('framework'));
+      });
+
+      test('Material label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('foo/bar/baz/material/design.dart'),
+            contains('f: material design'));
+      });
+
+      test('Cupertino label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('foo/bar/baz/cupertino/design.dart'),
+            contains('f: cupertino'));
+      });
+
+      test('i18n label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_localizations/allo.dart'),
+            contains('a: internationalization'));
+      });
+
+      test('Tests label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_test/lib/tester.dart'),
+            contains('a: tests'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_driver/lib/driver.dart'),
+            contains('a: tests'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_goldens/lib/flutter_goldens.dart'),
+            contains('a: tests'));
+        expect(
+            GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_goldens_client/lib/skia_client.dart'),
+            contains('a: tests'));
+      });
+
+      test('a11y label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('foo/bar/baz/semantics/voiceover.dart'),
+            contains('a: accessibility'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('foo/bar/baz/accessibility/voiceover.dart'),
+            contains('a: accessibility'));
+      });
+
+      test('Examples label applied', () {
+        expect(
+            GithubWebhookSubscription.getLabelsForFrameworkPath('examples/foo/bar/baz.dart'), contains('d: examples'));
+      });
+
+      test('API Docs label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('examples/api/bar/baz.dart'),
+            <String>['d: examples', 'team', 'd: api docs', 'documentation']);
+      });
+
+      test('Gallery label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('examples/flutter_gallery/lib/gallery.dart'),
+            contains('team: gallery'));
+      });
+
+      test('Team label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('dev/foo/bar/baz.dart'), contains('team'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('examples/foo/bar/baz.dart'), contains('team'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_goldens/lib/flutter_goldens.dart'),
+            contains('team'));
+        expect(
+            GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter_goldens_client/lib/skia_client.dart'),
+            contains('team'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/fix_data.yaml'),
+            contains('team'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/test_fixes'), contains('team'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/test_fixes/material.expect'),
+            contains('team'));
+      });
+
+      test('tech-debt label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/fix_data.yaml'),
+            contains('tech-debt'));
+        expect(
+            GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/test_fixes'), contains('tech-debt'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/test_fixes/material.expect'),
+            contains('tech-debt'));
+      });
+
+      test('gestures label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/gestures'),
+            contains('f: gestures'));
+      });
+
+      test('focus label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/focus_node.dart'),
+            contains('f: focus'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/focus_scope.dart'),
+            contains('f: focus'));
+        expect(
+            GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/focus_manager.dart'),
+            contains('f: focus'));
+      });
+
+      test('routes label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/router.dart'),
+            contains('f: routes'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/routes.dart'),
+            contains('f: routes'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/navigator.dart'),
+            contains('f: routes'));
+      });
+
+      test('text input label applied', () {
+        expect(
+            GithubWebhookSubscription.getLabelsForFrameworkPath(
+                'dev/integration_tests/web_e2e_tests/test_driver/text_editing_integration.dart'),
+            contains('a: text input'));
+        expect(
+            GithubWebhookSubscription.getLabelsForFrameworkPath(
+                'packages/flutter/lib/src/widgets/text_editing_action.dart'),
+            contains('a: text input'));
+      });
+
+      test('animation label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/animation'),
+            contains('a: animation'));
+      });
+
+      test('scrolling label applied', () {
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/sliver.dart'),
+            contains('f: scrolling'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/material/scrollbar.dart'),
+            contains('f: scrolling'));
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/flutter/lib/src/rendering/viewport.dart'),
+            contains('f: scrolling'));
+      });
+
+      test('integration_test label is/isn\'t applied', () {
+        // Label does not apply to integration tests outside of the
+        // integration_test package.
+        expect(
+            GithubWebhookSubscription.getLabelsForFrameworkPath(
+                'dev/integration_tests/web_e2e_tests/test_driver/text_editing_integration.dart'),
+            <String>{'team', 'a: text input'});
+        // Label applies to integration_test package
+        expect(GithubWebhookSubscription.getLabelsForFrameworkPath('packages/integration_test/lib/common.dart'),
+            contains('integration_test'));
+      });
+    });
+
+    group('getLabelsForEnginePath', () {
+      test('No label is applied to paths with no applicable label', () {
+        expect(GithubWebhookSubscription.getLabelsForEnginePath('nonsense/path/foo.cc'), isEmpty);
+      });
+
+      test('platform-android applied for Android embedder', () {
+        expect(GithubWebhookSubscription.getLabelsForEnginePath('shell/platform/android/RIsForRhubarbPie.java'),
+            contains('platform-android'));
+      });
+
+      test('platform-ios and platform-macos applied for common Darwin code', () {
+        expect(GithubWebhookSubscription.getLabelsForEnginePath('shell/platform/darwin/common/ThinkDifferent.mm'),
+            containsAll(<String>['platform-ios', 'platform-macos']));
+      });
+
+      test('platform-ios and platform-ios applied for iOS embedder', () {
+        expect(GithubWebhookSubscription.getLabelsForEnginePath('shell/platform/darwin/ios/BackButton.mm'),
+            contains('platform-ios'));
+      });
+
+      test('platform-macos applied for macOS embedder', () {
+        expect(GithubWebhookSubscription.getLabelsForEnginePath('shell/platform/darwin/macos/PhysicalEscapeKey.mm'),
+            contains('platform-macos'));
+      });
+
+      test('platform-fuchsia applied for fuchsia embedder', () {
+        expect(GithubWebhookSubscription.getLabelsForEnginePath('shell/platform/fuchsia/spell_checker.cc'),
+            contains('platform-fuchsia'));
+      });
+
+      test('platform-linux applied for linux embedder', () {
+        expect(GithubWebhookSubscription.getLabelsForEnginePath('shell/platform/linux/systemd_integration.cc'),
+            contains('platform-linux'));
+      });
+
+      test('platform-windows applied for windows embedder', () {
+        expect(GithubWebhookSubscription.getLabelsForEnginePath('shell/platform/windows/start_menu.cc'),
+            contains('platform-windows'));
+      });
+
+      test('platform-web applied for web paths', () {
+        expect(
+            GithubWebhookSubscription.getLabelsForEnginePath('lib/web_ui/shadow_dom.dart'), contains('platform-web'));
+        expect(GithubWebhookSubscription.getLabelsForEnginePath('web_sdk/'), contains('platform-web'));
+      });
+    });
+
+    test('release PRs are approved', () async {
+      const int issueNumber = 123;
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        // Base is where the PR will merge into
+        baseRef: 'flutter-2.13-candidate.0',
+        login: 'dart-flutter-releaser',
+      );
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber))
+          .thenAnswer((_) => const Stream<PullRequestFile>.empty());
+      when(pullRequestsService.createReview(Config.flutterSlug, any))
+          .thenAnswer((_) async => PullRequestReview(id: 123, user: User()));
+
+      await tester.post(webhook);
+
+      final List<dynamic> reviews = verify(pullRequestsService.createReview(Config.flutterSlug, captureAny)).captured;
+      expect(reviews.length, 1);
+      final CreatePullRequestReview review = reviews.single as CreatePullRequestReview;
+      expect(review.event, 'APPROVE');
+    });
+
+    test('fake release PRs are not approved', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        // Base is where the PR will merge into
+        baseRef: 'master',
+        // Head is the branch from the fork
+        headRef: 'flutter-2.13-candidate.0',
+        login: 'dart-flutter-releaser',
+      );
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber))
+          .thenAnswer((_) => const Stream<PullRequestFile>.empty());
+      when(pullRequestsService.createReview(Config.flutterSlug, any))
+          .thenAnswer((_) async => PullRequestReview(id: 123, user: User()));
+
+      await tester.post(webhook);
+
+      verifyNever(pullRequestsService.createReview(Config.flutterSlug, captureAny));
+    });
+
+    test('release PRs are not approved for outsider PRs', () async {
+      const int issueNumber = 123;
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        headRef: 'flutter-2.13-candidate.0',
+      );
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber))
+          .thenAnswer((_) => const Stream<PullRequestFile>.empty());
+      when(pullRequestsService.createReview(Config.flutterSlug, any))
+          .thenAnswer((_) async => PullRequestReview(id: 123, user: User()));
+
+      await tester.post(webhook);
+
+      verifyNever(pullRequestsService.createReview(Config.flutterSlug, any));
+    });
+
+    test('Framework labels PRs, comment if no tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/flutter/blah.dart',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        Config.flutterSlug,
+        issueNumber,
+        <String>['framework'],
+      )).called(1);
+
+      verify(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      )).called(1);
+    });
+
+    group("Auto-roller accounts do not label Framework PR with test label or comment.", () {
+      Set<String> inputs = {
+        'skia-flutter-autoroll',
+        'dependabot',
+      };
+
+      for (String element in inputs) {
+        test('Framework does not label PR with no tests label if author is $element', () async {
+          const int issueNumber = 123;
+
+          tester.message = generateGithubWebhookMessage(
+            action: 'opened',
+            number: issueNumber,
+            login: element,
+          );
+
+          final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+          when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+            (_) => Stream<PullRequestFile>.value(
+              PullRequestFile()..filename = 'packages/flutter/blah.dart',
+            ),
+          );
+
+          when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
+            (_) => Stream<IssueComment>.value(
+              IssueComment()..body = 'some other comment',
+            ),
+          );
+
+          await tester.post(webhook);
+
+          verify(issuesService.addLabelsToIssue(
+            slug,
+            issueNumber,
+            <String>['framework'],
+          )).called(1);
+
+          verifyNever(issuesService.createComment(
+            slug,
+            issueNumber,
+            argThat(contains(config.missingTestsPullRequestMessageValue)),
+          ));
+        });
+      }
+    });
+
+    test('Framework does not label PR with no tests label if author is engine-flutter-autoroll', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        login: 'engine-flutter-autoroll',
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/flutter/blah.dart',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.addLabelsToIssue(
+        slug,
+        issueNumber,
+        <String>['framework'],
+      ));
+
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework labels PRs, comment if no tests including hit_test.dart file', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()
+            ..additionsCount = 10
+            ..changesCount = 10
+            ..filename = 'packages/flutter/lib/src/gestures/hit_test.dart',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        slug,
+        issueNumber,
+        <String>['framework', 'f: gestures'],
+      )).called(1);
+
+      verify(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      )).called(1);
+    });
+
+    test('Framework labels PRs, no dart files', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/flutter/blah.md',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        slug,
+        issueNumber,
+        <String>['framework'],
+      )).called(1);
+
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        any,
+      ));
+    });
+
+    test('Framework labels PRs, no comment if tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/flutter/semantics_test.dart',
+          PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
+          PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
+          PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart',
+          PullRequestFile()..filename = 'dev/bots/test.dart',
+          PullRequestFile()..filename = 'dev/devicelab/bin/tasks/analyzer_benchmark.dart',
+          PullRequestFile()..filename = 'bin/internal/engine.version',
+          PullRequestFile()..filename = 'packages/flutter/lib/src/cupertino/blah.dart',
+          PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart',
+          PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        slug,
+        issueNumber,
+        <String>[
+          'framework',
+          'a: accessibility',
+          'tool',
+          'a: tests',
+          'd: examples',
+          'team',
+          'team: gallery',
+          'engine',
+          'f: cupertino',
+          'f: material design',
+          'a: internationalization',
+        ],
+      )).called(1);
+
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework labels dart fix PRs, no comment if tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/flutter/test_fixes/material.dart',
+          PullRequestFile()..filename = 'packages/flutter/test_fixes/material.expect',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        slug,
+        issueNumber,
+        <String>['team', 'tech-debt', 'framework', 'f: material design'],
+      )).called(1);
+
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework labels bot PR, no comment', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        login: 'fluttergithubbot',
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
+          PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        slug,
+        issueNumber,
+        <String>['tool', 'framework', 'a: tests', 'team', 'tech-debt', 'team: flakes'],
+      )).called(1);
+
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework labels deletion only PR, no test request', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()
+            ..filename = 'packages/flutter/blah.dart'
+            ..deletionsCount = 20
+            ..additionsCount = 0
+            ..changesCount = 20,
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        slug,
+        issueNumber,
+        <String>['framework'],
+      )).called(1);
+
+      // The PR here is only deleting code, so no test comment.
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('PR with additions and deletions is commented and labeled', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()
+            ..filename = 'packages/flutter/blah.dart'
+            ..deletionsCount = 20
+            ..additionsCount = 1
+            ..changesCount = 21,
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        slug,
+        issueNumber,
+        <String>['framework'],
+      )).called(1);
+
+      verify(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      )).called(1);
+    });
+
+    test('Framework no comment if code has only devicelab test', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/flutter_tools/lib/src/ios/devices.dart',
+          PullRequestFile()..filename = 'dev/devicelab/lib/tasks/plugin_tests.dart',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework no comment if only dev bots or devicelab changed', () async {
+      const int issueNumber = 123;
+      tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber);
+
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'dev/bots/test.dart',
+          PullRequestFile()..filename = 'dev/devicelab/bin/tasks/analyzer_benchmark.dart',
+          PullRequestFile()..filename = 'dev/devicelab/lib/tasks/plugin_tests.dart',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework no test comment if Objective-C test changed', () async {
+      const int issueNumber = 123;
+      tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber);
+
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          // Example of real behavior code change.
+          PullRequestFile()
+            ..filename = 'packages/flutter_tools/templates/app_shared/macos.tmpl/Runner/Base.lproj/MainMenu.xib',
+          // Example of Objective-C test.
+          PullRequestFile()..filename = 'dev/integration_tests/flutter_gallery/macos/RunnerTests/RunnerTests.m',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework no comment if only AUTHORS changed', () async {
+      const int issueNumber = 123;
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'AUTHORS',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework no comment if only ci.yaml and cirrus.yml changed', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = '.ci.yaml',
+          PullRequestFile()..filename = '.cirrus.yml',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework no comment if only CODEOWNERS changed', () async {
+      const int issueNumber = 123;
+      tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber);
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'CODEOWNERS',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework no comment if only comments changed', () async {
+      const int issueNumber = 123;
+      tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber);
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      const String patch = '''
+@@ -128,7 +128,7 @@
+
+/// Insert interesting comment here.
+///
+-/// More details here, but some of them are wrong.
++/// These are the right details!
+void foo() {
+  int bar = 0;
+  String baz = '';
+''';
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()
+            ..filename = 'packages/foo/lib/foo.dart'
+            ..additionsCount = 1
+            ..deletionsCount = 1
+            ..changesCount = 2
+            ..patch = patch,
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework labels PRs, no comment if tests (dev/bots/test.dart)', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'dev/bots/test.dart',
+          PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
+          PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
+          PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart',
+          PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart',
+          PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework labels PRs, no comment if tests (dev/bots/analyze.dart)', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'dev/bots/analyze.dart',
+          PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
+          PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
+          PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart',
+          PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart',
+          PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Framework labels PRs, apply label but no comment when rolling engine version', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        baseRef: kReleaseBaseRef,
+        headRef: kReleaseHeadRef,
+      );
+
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()
+            ..filename = 'bin/internal/engine.version'
+            ..deletionsCount = 20
+            ..additionsCount = 1
+            ..changesCount = 21,
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        Config.flutterSlug,
+        issueNumber,
+        <String>['engine'],
+      )).called(1);
+
+      verifyNever(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Engine labels PRs, comment and labels if no tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.engineSlug,
+      );
+      final RepositorySlug slug = RepositorySlug('flutter', 'engine');
+
+      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        slug,
+        issueNumber,
+        <String>['platform-ios'],
+      )).called(1);
+
+      verify(issuesService.createComment(
+        slug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      )).called(1);
+
+      verify(issuesService.addLabelsToIssue(
+        slug,
+        issueNumber,
+        <String>['needs tests'],
+      )).called(1);
+    });
+
+    group("Auto-roller accounts do not label Engine PR with test label or comment.", () {
+      Set<String> inputs = {
+        'engine-flutter-autoroll',
+        'dependabot',
+      };
+
+      for (String element in inputs) {
+        test('Engine does not label PR for no tests if author is $element', () async {
+          const int issueNumber = 123;
+
+          tester.message = generateGithubWebhookMessage(
+            action: 'opened',
+            number: issueNumber,
+            slug: Config.engineSlug,
+            login: element,
+          );
+
+          when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
+            (_) => Stream<PullRequestFile>.value(
+              PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
+            ),
+          );
+
+          when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer(
+            (_) => Stream<IssueComment>.value(
+              IssueComment()..body = 'some other comment',
+            ),
+          );
+
+          await tester.post(webhook);
+
+          verify(issuesService.addLabelsToIssue(
+            Config.engineSlug,
+            issueNumber,
+            <String>['platform-ios'],
+          )).called(1);
+
+          verifyNever(issuesService.createComment(
+            Config.engineSlug,
+            issueNumber,
+            argThat(contains(config.missingTestsPullRequestMessageValue)),
+          ));
+
+          verifyNever(issuesService.addLabelsToIssue(
+            Config.engineSlug,
+            issueNumber,
+            <String>['needs tests'],
+          ));
+        });
+      }
+    });
+
+    test('Engine does not label PR for no tests if author is skia-flutter-autoroll', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.engineSlug,
+        login: 'skia-flutter-autoroll',
+      );
+
+      when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.addLabelsToIssue(
+        Config.engineSlug,
+        issueNumber,
+        <String>['platform-ios'],
+      ));
+
+      verifyNever(issuesService.createComment(
+        Config.engineSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+
+      verifyNever(issuesService.addLabelsToIssue(
+        Config.engineSlug,
+        issueNumber,
+        <String>['needs tests'],
+      ));
+    });
+
+    test('Engine labels PRs, no code files', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        baseRef: 'main',
+        slug: Config.engineSlug,
+      );
+
+      when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'DEPS',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.addLabelsToIssue(
+        Config.engineSlug,
+        issueNumber,
+        any,
+      ));
+
+      verifyNever(issuesService.createComment(
+        Config.engineSlug,
+        issueNumber,
+        any,
+      ));
+    });
+
+    test('Engine labels PRs, no comment if Java tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.engineSlug,
+      );
+
+      when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'shell/platform/android/io/flutter/Blah.java',
+          PullRequestFile()..filename = 'shell/platform/android/test/io/flutter/BlahTest.java',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        Config.engineSlug,
+        issueNumber,
+        <String>[
+          'platform-android',
+        ],
+      )).called(1);
+
+      verifyNever(issuesService.createComment(
+        Config.engineSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Engine labels PRs, no comment if cc tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.engineSlug,
+      );
+
+      when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'fml/blah.cc',
+          PullRequestFile()..filename = 'fml/blah_unittests.cc',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.addLabelsToIssue(
+        Config.engineSlug,
+        issueNumber,
+        any,
+      ));
+
+      verifyNever(issuesService.createComment(
+        Config.engineSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Engine labels PRs, no comment if cc becnhmarks', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.engineSlug,
+      );
+
+      when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'fml/blah.cc',
+          PullRequestFile()..filename = 'fml/blah_benchmarks.cc',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.addLabelsToIssue(
+        Config.engineSlug,
+        issueNumber,
+        any,
+      ));
+
+      verifyNever(issuesService.createComment(
+        Config.engineSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Engine labels PRs, no comments if pr is for release branches', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        baseRef: kReleaseBaseRef,
+        headRef: kReleaseHeadRef,
+        slug: Config.engineSlug,
+      );
+      when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        Config.engineSlug,
+        issueNumber,
+        <String>['platform-ios'],
+      )).called(1);
+
+      verifyNever(issuesService.createComment(
+        Config.engineSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('No labels when only pubspec.yaml changes', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/flutter/pubspec.yaml',
+          PullRequestFile()..filename = 'packages/flutter_tools/pubspec.yaml',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        Config.flutterSlug,
+        issueNumber,
+        <String>['team'],
+      )).called(1);
+
+      verifyNever(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Plugins comments and labels if no tests and no patch info', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      )).called(1);
+
+      verify(issuesService.addLabelsToIssue(
+        Config.pluginsSlug,
+        issueNumber,
+        <String>['needs tests'],
+      )).called(1);
+    });
+
+    group('Plugins does not comment and label if author is an autoroller account.', () {
+      Set<String> inputs = {
+        'engine-flutter-autoroll',
+        'skia-flutter-autoroll',
+        'dependabot',
+      };
+
+      for (String element in inputs) {
+        test('Plugins does not comment and label if author is $element.', () async {
+          const int issueNumber = 123;
+
+          tester.message = generateGithubWebhookMessage(
+            action: 'opened',
+            number: issueNumber,
+            slug: Config.pluginsSlug,
+            login: element,
+          );
+
+          when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+            (_) => Stream<PullRequestFile>.value(
+              PullRequestFile()..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m',
+            ),
+          );
+
+          when(issuesService.listCommentsByIssue(Config.pluginsSlug, issueNumber)).thenAnswer(
+            (_) => Stream<IssueComment>.value(
+              IssueComment()..body = 'some other comment',
+            ),
+          );
+
+          await tester.post(webhook);
+
+          verifyNever(issuesService.createComment(
+            Config.pluginsSlug,
+            issueNumber,
+            argThat(contains(config.missingTestsPullRequestMessageValue)),
+          ));
+
+          verifyNever(issuesService.addLabelsToIssue(
+            Config.pluginsSlug,
+            issueNumber,
+            <String>['needs tests'],
+          ));
+        });
+      }
+    });
+
+    test('Plugins apply no label or comment if pr is for release branches', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        baseRef: kReleaseBaseRef,
+        headRef: kReleaseHeadRef,
+        slug: Config.pluginsSlug,
+      );
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+
+      verifyNever(issuesService.addLabelsToIssue(
+        Config.pluginsSlug,
+        issueNumber,
+        any,
+      ));
+    });
+
+    test('Plugins comments and labels for code change', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+      const String patch = '''
+@@ -128,8 +128,8 @@
+  NSString* foo = "";
+  int bar = 0;
+
+-  // Some incorrect code:
+-  int baz = 7 / bar;
++  // Better code:
++  int baz = 7 * bar;
+  return baz;
+}
+
+''';
+
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()
+            ..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m'
+            ..additionsCount = 2
+            ..deletionsCount = 2
+            ..changesCount = 4
+            ..patch = patch,
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      )).called(1);
+
+      verify(issuesService.addLabelsToIssue(
+        Config.pluginsSlug,
+        issueNumber,
+        <String>['needs tests'],
+      )).called(1);
+    });
+
+    test('Plugins comments and labels for code removal with comment addition', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+      const String patch = '''
+@@ -128,7 +128,7 @@
+  int foo = 0;
+
+  int bar = 0;
+-  int baz = 0;
++  // int baz = 0;
+
+  // More code here:
+
+''';
+
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()
+            ..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m'
+            ..additionsCount = 1
+            ..deletionsCount = 1
+            ..changesCount = 2
+            ..patch = patch,
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      )).called(1);
+
+      verify(issuesService.addLabelsToIssue(
+        Config.pluginsSlug,
+        issueNumber,
+        <String>['needs tests'],
+      )).called(1);
+    });
+
+    test('Plugins does not comment for comment-only changes', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+      const String patch = '''
+@@ -128,7 +128,7 @@
+
+/// Insert interesting comment here.
+///
+-/// More details here, but some of them are wrong.
++/// These are the right details!
+void foo() {
+  int bar = 0;
+  String baz = '';
+''';
+
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()
+            ..filename = 'packages/foo/lib/foo.dart'
+            ..additionsCount = 1
+            ..deletionsCount = 1
+            ..changesCount = 2
+            ..patch = patch,
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Plugins does not comment if Dart tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
+          PullRequestFile()..filename = 'packages/foo/test/foo_test.dart',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Plugins does not comment if Android unit tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/foo/foo_android/src/main/Foo.java',
+          PullRequestFile()..filename = 'packages/foo/foo_android/src/test/FooTest.java',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Plugins does not comment if Android UI tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/foo/foo_android/src/main/Foo.java',
+          PullRequestFile()..filename = 'packages/foo/foo_android/example/android/app/src/androidTest/FooTest.java',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Plugins does not comment if iOS/macOS unit tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m',
+          PullRequestFile()..filename = 'packages/foo/foo_ios/example/ios/RunnerTests/FooTests.m',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Plugins does not comment if iOS/macOS UI tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m',
+          PullRequestFile()..filename = 'packages/foo/foo_ios/example/ios/RunnerUITests/FooTests.m',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Plugins does not comment if Windows tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/foo/foo_windows/windows/foo.cpp',
+          PullRequestFile()..filename = 'packages/foo/foo_windows/windows/test/foo_test.cpp',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Plugins does not comment if Linux tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.pluginsSlug,
+      );
+      when(pullRequestsService.listFiles(Config.pluginsSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/foo/foo_linux/linux/foo.cc',
+          PullRequestFile()..filename = 'packages/foo/foo_linux/linux/test/foo_test.cc',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.pluginsSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Packages comments and labels if no tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.packagesSlug,
+      );
+      when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.packagesSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.createComment(
+        Config.packagesSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      )).called(1);
+
+      verify(issuesService.addLabelsToIssue(
+        Config.packagesSlug,
+        issueNumber,
+        <String>['needs tests'],
+      )).called(1);
+    });
+
+    test('Packages do not comment or label if pr is for release branches', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        baseRef: kReleaseBaseRef,
+        headRef: kReleaseHeadRef,
+        slug: Config.packagesSlug,
+      );
+
+      when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.packagesSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = 'some other comment',
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.packagesSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+
+      verifyNever(issuesService.addLabelsToIssue(
+        Config.packagesSlug,
+        issueNumber,
+        any,
+      ));
+    });
+
+    test('Packages does not comment if Dart tests', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        slug: Config.packagesSlug,
+      );
+
+      when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
+          PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
+          PullRequestFile()..filename = 'packages/foo/test/foo_test.dart',
+        ]),
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.packagesSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Schedule tasks when pull request is closed and merged', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'closed',
+        number: issueNumber,
+        merged: true,
+      );
+
+      expect(db.values.values.whereType<Commit>().length, 0);
+      await tester.post(webhook);
+      expect(db.values.values.whereType<Commit>().length, 1);
+    });
+
+    test('Does not comment about needing tests on draft pull requests.', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        isDraft: true,
+      );
+
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber))
+          .thenAnswer((_) => Stream<PullRequestFile>.value(
+                PullRequestFile()..filename = 'some_change.dart',
+              ));
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Will not spawn comments if they have already been made.', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+      );
+
+      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<PullRequestFile>.value(
+          PullRequestFile()..filename = 'packages/flutter/blah.dart',
+        ),
+      );
+
+      when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
+        (_) => Stream<IssueComment>.value(
+          IssueComment()..body = config.missingTestsPullRequestMessageValue,
+        ),
+      );
+
+      await tester.post(webhook);
+
+      verify(issuesService.addLabelsToIssue(
+        Config.flutterSlug,
+        issueNumber,
+        <String>['framework'],
+      )).called(1);
+
+      verifyNever(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        argThat(contains(config.missingTestsPullRequestMessageValue)),
+      ));
+    });
+
+    test('Skips labeling or commenting on autorolls', () async {
+      const int issueNumber = 123;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'opened',
+        number: issueNumber,
+        login: 'engine-flutter-autoroll',
+      );
+
+      await tester.post(webhook);
+
+      verifyNever(issuesService.createComment(
+        any,
+        issueNumber,
+        any,
+      ));
+    });
+
+    test('Comments on PR but does not schedule builds for unmergeable PRs', () async {
+      const int issueNumber = 12345;
+      tester.message = generateGithubWebhookMessage(
+        action: 'synchronize',
+        number: issueNumber,
+        // This PR is unmergeable (probably merge conflict)
+        mergeable: false,
+      );
+
+      await tester.post(webhook);
+      verify(issuesService.createComment(
+        Config.flutterSlug,
+        issueNumber,
+        config.mergeConflictPullRequestMessage,
+      ));
+    });
+
+    test('When synchronized, cancels existing builds and schedules new ones', () async {
+      const int issueNumber = 12345;
+      bool batchRequestCalled = false;
+      Future<BatchResponse> _getBatchResponse() async {
+        batchRequestCalled = true;
+        return BatchResponse(
+          responses: <Response>[
+            Response(
+              searchBuilds: SearchBuildsResponse(
+                builds: <Build>[
+                  generateBuild(999, name: 'Linux', status: Status.ended),
+                ],
+              ),
+            ),
+            Response(
+              searchBuilds: SearchBuildsResponse(
+                builds: <Build>[
+                  generateBuild(998, name: 'Linux', status: Status.ended),
+                ],
+              ),
+            ),
+          ],
+        );
+      }
+
+      fakeBuildBucketClient.batchResponse = _getBatchResponse;
+
+      tester.message = generateGithubWebhookMessage(
+        action: 'synchronize',
+        number: issueNumber,
+      );
+
+      final MockRepositoriesService mockRepositoriesService = MockRepositoriesService();
+      when(gitHubClient.repositories).thenReturn(mockRepositoriesService);
+
+      await tester.post(webhook);
+      expect(batchRequestCalled, isTrue);
+    });
+
+    group('BuildBucket', () {
+      const int issueNumber = 123;
+
+      Future<void> _testActions(String action) async {
+        when(issuesService.listLabelsByIssue(any, issueNumber)).thenAnswer((_) {
+          return Stream<IssueLabel>.fromIterable(<IssueLabel>[
+            IssueLabel()..name = 'Random Label',
+          ]);
+        });
+
+        fakeBuildBucketClient.batchResponse = () => Future<BatchResponse>.value(
+              const BatchResponse(
+                responses: <Response>[
+                  Response(
+                    searchBuilds: SearchBuildsResponse(
+                      builds: <Build>[],
+                    ),
+                  ),
+                  Response(
+                    searchBuilds: SearchBuildsResponse(
+                      builds: <Build>[],
+                    ),
+                  ),
+                ],
+              ),
+            );
+
+        tester.message = generateGithubWebhookMessage(
+          action: action,
+          number: 1,
+        );
+
+        await tester.post(webhook);
+      }
+
+      test('Edited Action works properly', () async {
+        await _testActions('edited');
+      });
+
+      test('Opened Action works properly', () async {
+        await _testActions('opened');
+      });
+
+      test('Ready_for_review Action works properly', () async {
+        await _testActions('ready_for_review');
+      });
+
+      test('Reopened Action works properly', () async {
+        await _testActions('reopened');
+      });
+
+      test('Labeled Action works properly', () async {
+        await _testActions('labeled');
+      });
+
+      test('Synchronize Action works properly', () async {
+        await _testActions('synchronize');
+      });
+
+      test('Comments on PR but does not schedule builds for unmergeable PRs', () async {
+        when(issuesService.listCommentsByIssue(any, any)).thenAnswer((_) => Stream<IssueComment>.value(IssueComment()));
+        tester.message = generateGithubWebhookMessage(
+          action: 'synchronize',
+          number: issueNumber,
+          // This PR is unmergeable (probably merge conflict)
+          mergeable: false,
+        );
+        await tester.post(webhook);
+        verify(issuesService.createComment(
+          Config.flutterSlug,
+          issueNumber,
+          config.mergeConflictPullRequestMessage,
+        ));
+      });
+
+      test('When synchronized, cancels existing builds and schedules new ones', () async {
+        fakeBuildBucketClient.batchResponse = () => Future<BatchResponse>.value(
+              BatchResponse(
+                responses: <Response>[
+                  Response(
+                    searchBuilds: SearchBuildsResponse(
+                      builds: <Build>[
+                        generateBuild(999, name: 'Linux', status: Status.ended),
+                      ],
+                    ),
+                  ),
+                  Response(
+                    searchBuilds: SearchBuildsResponse(
+                      builds: <Build>[
+                        generateBuild(998, name: 'Linux', status: Status.ended),
+                      ],
+                    ),
+                  ),
+                ],
+              ),
+            );
+
+        tester.message = generateGithubWebhookMessage(
+          action: 'synchronize',
+          number: issueNumber,
+        );
+        final MockRepositoriesService mockRepositoriesService = MockRepositoriesService();
+        when(gitHubClient.repositories).thenReturn(mockRepositoriesService);
+
+        await tester.post(webhook);
+      });
+    });
+  });
+
+  group('github webhook create branch event', () {
+    test('process create branch event', () async {
+      tester.message = generateCreateBranchMessage('flutter-2.12-candidate.4', Config.flutterSlug.fullName);
+      await tester.post(webhook);
+
+      verify(branchService.branchFlutterRecipes('flutter-2.12-candidate.4'));
+    });
+
+    test('do not create recipe branches on non-flutter/flutter branches', () async {
+      tester.message = generateCreateBranchMessage('flutter-2.12-candidate.4', Config.engineSlug.fullName);
+      await tester.post(webhook);
+
+      verifyNever(branchService.branchFlutterRecipes(any));
+    });
+  });
+
+  group('github webhook check_run event', () {
+    test('processes check run event', () async {
+      tester.message = generateCheckRunEvent();
+
+      await tester.post(webhook);
+    });
+
+    test('processes completed check run event', () async {
+      tester.message = generateCheckRunEvent(
+        action: 'completed',
+        numberOfPullRequests: 0,
+      );
+
+      await tester.post(webhook);
+    });
+  });
+}
diff --git a/app_dart/test/request_handlers/github_webhook_test.dart b/app_dart/test/request_handlers/github_webhook_test.dart
index 9ff10c8..cf84d2e 100644
--- a/app_dart/test/request_handlers/github_webhook_test.dart
+++ b/app_dart/test/request_handlers/github_webhook_test.dart
@@ -6,46 +6,23 @@
 import 'dart:typed_data';
 
 import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
+import 'package:cocoon_service/protos.dart';
 import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
 
 import 'package:crypto/crypto.dart';
-import 'package:github/github.dart' hide Branch;
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:mockito/mockito.dart';
 import 'package:test/test.dart';
 
 import '../src/datastore/fake_config.dart';
-import '../src/datastore/fake_datastore.dart';
 import '../src/request_handling/fake_http.dart';
+import '../src/request_handling/fake_pubsub.dart';
 import '../src/request_handling/request_handler_tester.dart';
-import '../src/service/fake_buildbucket.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/service/fake_scheduler.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/mocks.dart';
-import '../src/utilities/webhook_generators.dart';
 
 void main() {
   late GithubWebhook webhook;
-  late FakeBuildBucketClient fakeBuildBucketClient;
   late FakeConfig config;
-  late FakeDatastoreDB db;
-  late FakeGithubService githubService;
   late FakeHttpRequest request;
-  late FakeScheduler scheduler;
-  late MockBranchService branchService;
-  late MockGitHub gitHubClient;
-  late MockGithubChecksUtil mockGithubChecksUtil;
-  late MockGithubChecksService mockGithubChecksService;
-  late MockIssuesService issuesService;
-  late MockPullRequestsService pullRequestsService;
+  late FakePubSub pubsub;
   late RequestHandlerTester tester;
-  const String serviceAccountEmail = 'test@test';
-  ServiceAccountInfo? serviceAccountInfo;
-
   const String keyString = 'not_a_real_key';
 
   String getHmac(Uint8List list, Uint8List key) {
@@ -54,2672 +31,50 @@
   }
 
   setUp(() {
-    serviceAccountInfo = const ServiceAccountInfo(email: serviceAccountEmail);
     request = FakeHttpRequest();
-    db = FakeDatastoreDB();
-    gitHubClient = MockGitHub();
-    githubService = FakeGithubService();
-    final MockTabledataResource tabledataResource = MockTabledataResource();
-    when(tabledataResource.insertAll(any, any, any, any)).thenAnswer((_) async => TableDataInsertAllResponse());
-    config = FakeConfig(
-      dbValue: db,
-      deviceLabServiceAccountValue: serviceAccountInfo,
-      githubService: githubService,
-      tabledataResource: tabledataResource,
-      githubClient: gitHubClient,
-    );
-    branchService = MockBranchService();
-    issuesService = MockIssuesService();
-    when(issuesService.addLabelsToIssue(any, any, any)).thenAnswer((_) async => <IssueLabel>[]);
-    when(issuesService.createComment(any, any, any)).thenAnswer((_) async => IssueComment());
-    when(issuesService.listCommentsByIssue(any, any))
-        .thenAnswer((_) => Stream<IssueComment>.fromIterable(<IssueComment>[IssueComment()]));
-    pullRequestsService = MockPullRequestsService();
-    when(pullRequestsService.listFiles(Config.flutterSlug, any))
-        .thenAnswer((_) => const Stream<PullRequestFile>.empty());
-    when(pullRequestsService.edit(any, any, title: anyNamed('title'), state: anyNamed('state'), base: anyNamed('base')))
-        .thenAnswer((_) async => PullRequest());
-    fakeBuildBucketClient = FakeBuildBucketClient();
-    mockGithubChecksUtil = MockGithubChecksUtil();
-    scheduler = FakeScheduler(
-      config: config,
-      buildbucket: fakeBuildBucketClient,
-      githubChecksUtil: mockGithubChecksUtil,
-    );
     tester = RequestHandlerTester(request: request);
 
-    mockGithubChecksService = MockGithubChecksService();
-    when(gitHubClient.issues).thenReturn(issuesService);
-    when(gitHubClient.pullRequests).thenReturn(pullRequestsService);
-    when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))).thenAnswer((_) async {
-      return CheckRun.fromJson(const <String, dynamic>{
-        'id': 1,
-        'started_at': '2020-05-10T02:49:31Z',
-        'check_suite': <String, dynamic>{'id': 2}
-      });
-    });
+    config = FakeConfig(
+      webhookKeyValue: keyString,
+    );
+    pubsub = FakePubSub();
 
     webhook = GithubWebhook(
       config: config,
-      datastoreProvider: (_) => DatastoreService(config.db, 5),
-      githubChecksService: mockGithubChecksService,
-      scheduler: scheduler,
-      branchService: branchService,
+      pubsub: pubsub,
     );
-
-    config.wrongHeadBranchPullRequestMessageValue = 'wrongHeadBranchPullRequestMessage';
-    config.wrongBaseBranchPullRequestMessageValue = '{{target_branch}} -> {{default_branch}}';
-    config.releaseBranchPullRequestMessageValue = 'releaseBranchPullRequestMessage';
-    config.missingTestsPullRequestMessageValue = 'missingTestPullRequestMessage';
-    config.githubOAuthTokenValue = 'githubOAuthKey';
-    config.webhookKeyValue = keyString;
-    config.githubClient = gitHubClient;
-    config.deviceLabServiceAccountValue = const ServiceAccountInfo(email: serviceAccountEmail);
-    config.rollerAccountsValue = const <String>{
-      'skia-flutter-autoroll',
-      'engine-flutter-autoroll',
-      'dependabot',
-    };
   });
 
-  group('github webhook pull_request event', () {
-    test('Rejects non-POST methods with methodNotAllowed', () async {
-      expect(tester.get(webhook), throwsA(isA<MethodNotAllowed>()));
-    });
-
-    test('Rejects missing headers', () async {
-      expect(tester.post(webhook), throwsA(isA<BadRequestException>()));
-    });
-
-    test('Rejects invalid hmac', () async {
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.headers.set('X-Hub-Signature', 'bar');
-      request.body = 'Hello, World!';
-      expect(tester.post(webhook), throwsA(isA<Forbidden>()));
-    });
-
-    test('Rejects malformed unicode', () async {
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.bodyBytes = Uint8List.fromList(<int>[0xc3, 0x28]);
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(request.bodyBytes, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      expect(tester.post(webhook), throwsA(isA<BadRequestException>()));
-    });
-
-    test('Rejects non-json', () async {
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = 'Hello, World!';
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      expect(tester.post(webhook), throwsA(isA<BadRequestException>()));
-    });
-
-    test('Closes PR opened from dev', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        headRef: 'dev',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/flutter/blah.dart',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(pullRequestsService.edit(
-        Config.flutterSlug,
-        issueNumber,
-        state: 'closed',
-      )).called(1);
-
-      verify(issuesService.createComment(
-        Config.flutterSlug,
-        issueNumber,
-        argThat(contains(config.wrongHeadBranchPullRequestMessageValue)),
-      )).called(1);
-    });
-
-    test('Acts on opened against dev', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, 'dev');
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/flutter/blah.dart',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(pullRequestsService.edit(
-        slug,
-        issueNumber,
-        base: kDefaultBranchName,
-      )).called(1);
-
-      verify(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains('dev -> master')),
-      )).called(1);
-    });
-
-    test('Acts on opened against master when default is main', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        'master',
-        repoName: 'engine',
-        repoFullName: 'flutter/engine',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-      final RepositorySlug slug = RepositorySlug('flutter', 'engine');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/flutter/blah.dart',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(pullRequestsService.edit(
-        slug,
-        issueNumber,
-        base: 'main',
-      )).called(1);
-
-      verify(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains('master -> main')),
-      )).called(1);
-    });
-
-    // We already schedule checks when a draft is opened, don't need to re-test
-    // just because it was marked ready for review
-    test('Does nothing on ready_for_review', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'ready_for_review',
-        issueNumber,
-        kDefaultBranchName,
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-      bool batchRequestCalled = false;
-
-      Future<BatchResponse> _getBatchResponse() async {
-        batchRequestCalled = true;
-        fail('Marking a draft ready for review should not trigger new builds');
-      }
-
-      fakeBuildBucketClient.batchResponse = _getBatchResponse;
-
-      await tester.post(webhook);
-
-      expect(batchRequestCalled, isFalse);
-    });
-
-    test('Triggers builds when opening a draft PR', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        isDraft: true,
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-      bool batchRequestCalled = false;
-
-      Future<BatchResponse> _getBatchResponse() async {
-        batchRequestCalled = true;
-        return BatchResponse(
-          responses: <Response>[
-            Response(
-              searchBuilds: SearchBuildsResponse(
-                builds: <Build>[
-                  generateBuild(999, name: 'Linux', status: Status.ended),
-                ],
-              ),
-            ),
-            Response(
-              searchBuilds: SearchBuildsResponse(
-                builds: <Build>[
-                  generateBuild(998, name: 'Linux', status: Status.ended),
-                ],
-              ),
-            ),
-          ],
-        );
-      }
-
-      fakeBuildBucketClient.batchResponse = _getBatchResponse;
-
-      await tester.post(webhook);
-
-      expect(batchRequestCalled, isTrue);
-    });
-
-    test('Does nothing against cherry pick PR', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        'flutter-1.20-candidate.7',
-        headRef: 'cherrypicks-flutter-1.20-candidate.7',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/flutter/blah.dart',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(pullRequestsService.edit(
-        slug,
-        issueNumber,
-        base: kDefaultBranchName,
-      ));
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.wrongBaseBranchPullRequestMessage)),
-      ));
-    });
-
-    group('getLabelsForFrameworkPath', () {
-      test('Only the team label is applied to pubspec.yaml', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_tools/pubspec.yaml'), <String>{'team'});
-      });
-
-      test('Tool label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_tools/hot_reload.dart'), contains('tool'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/fuchsia_remote_debug_protocol/hot_reload.dart'),
-            contains('tool'));
-      });
-
-      test('iOS label applied', () {
-        expect(
-          GithubWebhook.getLabelsForFrameworkPath('packages/flutter_tools/lib/src/ios/devices.dart'),
-          <String>{'platform-ios', 'tool'},
-        );
-      });
-
-      test('Engine label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('bin/internal/engine.version'), contains('engine'));
-      });
-
-      test('Framework label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/widget.dart'), contains('framework'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_test/lib/tester.dart'), contains('framework'));
-        expect(
-            GithubWebhook.getLabelsForFrameworkPath('packages/flutter_driver/lib/driver.dart'), contains('framework'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_goldens/lib/flutter_goldens.dart'),
-            contains('framework'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_goldens_client/lib/skia_client.dart'),
-            contains('framework'));
-      });
-
-      test('Material label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('foo/bar/baz/material/design.dart'),
-            contains('f: material design'));
-      });
-
-      test('Cupertino label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('foo/bar/baz/cupertino/design.dart'), contains('f: cupertino'));
-      });
-
-      test('i18n label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_localizations/allo.dart'),
-            contains('a: internationalization'));
-      });
-
-      test('Tests label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_test/lib/tester.dart'), contains('a: tests'));
-        expect(
-            GithubWebhook.getLabelsForFrameworkPath('packages/flutter_driver/lib/driver.dart'), contains('a: tests'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_goldens/lib/flutter_goldens.dart'),
-            contains('a: tests'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_goldens_client/lib/skia_client.dart'),
-            contains('a: tests'));
-      });
-
-      test('a11y label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('foo/bar/baz/semantics/voiceover.dart'),
-            contains('a: accessibility'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('foo/bar/baz/accessibility/voiceover.dart'),
-            contains('a: accessibility'));
-      });
-
-      test('Examples label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('examples/foo/bar/baz.dart'), contains('d: examples'));
-      });
-
-      test('API Docs label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('examples/api/bar/baz.dart'),
-            <String>['d: examples', 'team', 'd: api docs', 'documentation']);
-      });
-
-      test('Gallery label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('examples/flutter_gallery/lib/gallery.dart'),
-            contains('team: gallery'));
-      });
-
-      test('Team label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('dev/foo/bar/baz.dart'), contains('team'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('examples/foo/bar/baz.dart'), contains('team'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_goldens/lib/flutter_goldens.dart'),
-            contains('team'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter_goldens_client/lib/skia_client.dart'),
-            contains('team'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/fix_data.yaml'), contains('team'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/test_fixes'), contains('team'));
-        expect(
-            GithubWebhook.getLabelsForFrameworkPath('packages/flutter/test_fixes/material.expect'), contains('team'));
-      });
-
-      test('tech-debt label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/fix_data.yaml'), contains('tech-debt'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/test_fixes'), contains('tech-debt'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/test_fixes/material.expect'),
-            contains('tech-debt'));
-      });
-
-      test('gestures label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/gestures'), contains('f: gestures'));
-      });
-
-      test('focus label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/focus_node.dart'),
-            contains('f: focus'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/focus_scope.dart'),
-            contains('f: focus'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/focus_manager.dart'),
-            contains('f: focus'));
-      });
-
-      test('routes label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/router.dart'),
-            contains('f: routes'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/routes.dart'),
-            contains('f: routes'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/navigator.dart'),
-            contains('f: routes'));
-      });
-
-      test('text input label applied', () {
-        expect(
-            GithubWebhook.getLabelsForFrameworkPath(
-                'dev/integration_tests/web_e2e_tests/test_driver/text_editing_integration.dart'),
-            contains('a: text input'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/text_editing_action.dart'),
-            contains('a: text input'));
-      });
-
-      test('animation label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/animation'), contains('a: animation'));
-      });
-
-      test('scrolling label applied', () {
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/widgets/sliver.dart'),
-            contains('f: scrolling'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/material/scrollbar.dart'),
-            contains('f: scrolling'));
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/flutter/lib/src/rendering/viewport.dart'),
-            contains('f: scrolling'));
-      });
-
-      test('integration_test label is/isn\'t applied', () {
-        // Label does not apply to integration tests outside of the
-        // integration_test package.
-        expect(
-            GithubWebhook.getLabelsForFrameworkPath(
-                'dev/integration_tests/web_e2e_tests/test_driver/text_editing_integration.dart'),
-            <String>{'team', 'a: text input'});
-        // Label applies to integration_test package
-        expect(GithubWebhook.getLabelsForFrameworkPath('packages/integration_test/lib/common.dart'),
-            contains('integration_test'));
-      });
-    });
-
-    group('getLabelsForEnginePath', () {
-      test('No label is applied to paths with no applicable label', () {
-        expect(GithubWebhook.getLabelsForEnginePath('nonsense/path/foo.cc'), isEmpty);
-      });
-
-      test('platform-android applied for Android embedder', () {
-        expect(GithubWebhook.getLabelsForEnginePath('shell/platform/android/RIsForRhubarbPie.java'),
-            contains('platform-android'));
-      });
-
-      test('platform-ios and platform-macos applied for common Darwin code', () {
-        expect(GithubWebhook.getLabelsForEnginePath('shell/platform/darwin/common/ThinkDifferent.mm'),
-            containsAll(<String>['platform-ios', 'platform-macos']));
-      });
-
-      test('platform-ios and platform-ios applied for iOS embedder', () {
-        expect(
-            GithubWebhook.getLabelsForEnginePath('shell/platform/darwin/ios/BackButton.mm'), contains('platform-ios'));
-      });
-
-      test('platform-macos applied for macOS embedder', () {
-        expect(GithubWebhook.getLabelsForEnginePath('shell/platform/darwin/macos/PhysicalEscapeKey.mm'),
-            contains('platform-macos'));
-      });
-
-      test('platform-fuchsia applied for fuchsia embedder', () {
-        expect(GithubWebhook.getLabelsForEnginePath('shell/platform/fuchsia/spell_checker.cc'),
-            contains('platform-fuchsia'));
-      });
-
-      test('platform-linux applied for linux embedder', () {
-        expect(GithubWebhook.getLabelsForEnginePath('shell/platform/linux/systemd_integration.cc'),
-            contains('platform-linux'));
-      });
-
-      test('platform-windows applied for windows embedder', () {
-        expect(
-            GithubWebhook.getLabelsForEnginePath('shell/platform/windows/start_menu.cc'), contains('platform-windows'));
-      });
-
-      test('platform-web applied for web paths', () {
-        expect(GithubWebhook.getLabelsForEnginePath('lib/web_ui/shadow_dom.dart'), contains('platform-web'));
-        expect(GithubWebhook.getLabelsForEnginePath('web_sdk/'), contains('platform-web'));
-      });
-    });
-
-    test('release PRs are approved', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        // Base is where the PR will merge into
-        'flutter-2.13-candidate.0',
-        // Head is the branch from the fork
-        headRef: 'master',
-        login: 'dart-flutter-releaser',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer((_) => const Stream<PullRequestFile>.empty());
-      when(pullRequestsService.createReview(slug, any))
-          .thenAnswer((_) async => PullRequestReview(id: 123, user: User()));
-
-      await tester.post(webhook);
-
-      final List<dynamic> reviews = verify(pullRequestsService.createReview(slug, captureAny)).captured;
-      expect(reviews.length, 1);
-      final CreatePullRequestReview review = reviews.single as CreatePullRequestReview;
-      expect(review.event, 'APPROVE');
-    });
-
-    test('fake release PRs are not approved', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        // Base is where the PR will merge into
-        'master',
-        // Head is the branch from the fork
-        headRef: 'flutter-2.13-candidate.0',
-        login: 'dart-flutter-releaser',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-      when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber))
-          .thenAnswer((_) => const Stream<PullRequestFile>.empty());
-      when(pullRequestsService.createReview(Config.flutterSlug, any))
-          .thenAnswer((_) async => PullRequestReview(id: 123, user: User()));
-
-      await tester.post(webhook);
-
-      verifyNever(pullRequestsService.createReview(Config.flutterSlug, captureAny));
-    });
-
-    test('release PRs are not approved for outsider PRs', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, 'flutter-2.13-candidate.0');
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer((_) => const Stream<PullRequestFile>.empty());
-      when(pullRequestsService.createReview(slug, any))
-          .thenAnswer((_) async => PullRequestReview(id: 123, user: User()));
-
-      await tester.post(webhook);
-
-      verifyNever(pullRequestsService.createReview(slug, any));
-    });
-
-    test('Framework labels PRs, comment if no tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/flutter/blah.dart',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['framework'],
-      )).called(1);
-
-      verify(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      )).called(1);
-    });
-
-    group("Auto-roller accounts do not label Framework PR with test label or comment.", () {
-      Set<String> inputs = {
-        'skia-flutter-autoroll',
-        'dependabot',
-      };
-
-      for (String element in inputs) {
-        test('Framework does not label PR with no tests label if author is $element', () async {
-          const int issueNumber = 123;
-          request.headers.set('X-GitHub-Event', 'pull_request');
-          request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName, login: element);
-          final Uint8List body = utf8.encode(request.body!) as Uint8List;
-          final Uint8List key = utf8.encode(keyString) as Uint8List;
-          final String hmac = getHmac(body, key);
-          request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-          final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-          when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-            (_) => Stream<PullRequestFile>.value(
-              PullRequestFile()..filename = 'packages/flutter/blah.dart',
-            ),
-          );
-
-          when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-            (_) => Stream<IssueComment>.value(
-              IssueComment()..body = 'some other comment',
-            ),
-          );
-
-          await tester.post(webhook);
-
-          verify(issuesService.addLabelsToIssue(
-            slug,
-            issueNumber,
-            <String>['framework'],
-          )).called(1);
-
-          verifyNever(issuesService.createComment(
-            slug,
-            issueNumber,
-            argThat(contains(config.missingTestsPullRequestMessageValue)),
-          ));
-        });
-      }
-    });
-
-    test('Framework does not label PR with no tests label if author is engine-flutter-autoroll', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body =
-          generatePullRequestEvent('opened', issueNumber, kDefaultBranchName, login: 'engine-flutter-autoroll');
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/flutter/blah.dart',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['framework'],
-      ));
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework labels PRs, comment if no tests including hit_test.dart file', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()
-            ..additionsCount = 10
-            ..changesCount = 10
-            ..filename = 'packages/flutter/lib/src/gestures/hit_test.dart',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['framework', 'f: gestures'],
-      )).called(1);
-
-      verify(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      )).called(1);
-    });
-
-    test('Framework labels PRs, no dart files', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/flutter/blah.md',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['framework'],
-      )).called(1);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        any,
-      ));
-    });
-
-    test('Framework labels PRs, no comment if tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/flutter/semantics_test.dart',
-          PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
-          PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
-          PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart',
-          PullRequestFile()..filename = 'dev/bots/test.dart',
-          PullRequestFile()..filename = 'dev/devicelab/bin/tasks/analyzer_benchmark.dart',
-          PullRequestFile()..filename = 'bin/internal/engine.version',
-          PullRequestFile()..filename = 'packages/flutter/lib/src/cupertino/blah.dart',
-          PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart',
-          PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>[
-          'framework',
-          'a: accessibility',
-          'tool',
-          'a: tests',
-          'd: examples',
-          'team',
-          'team: gallery',
-          'engine',
-          'f: cupertino',
-          'f: material design',
-          'a: internationalization',
-        ],
-      )).called(1);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework labels dart fix PRs, no comment if tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/flutter/test_fixes/material.dart',
-          PullRequestFile()..filename = 'packages/flutter/test_fixes/material.expect',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['team', 'tech-debt', 'framework', 'f: material design'],
-      )).called(1);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework labels bot PR, no comment', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName, login: 'fluttergithubbot');
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
-          PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['tool', 'framework', 'a: tests', 'team', 'tech-debt', 'team: flakes'],
-      )).called(1);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework labels deletion only PR, no test request', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()
-            ..filename = 'packages/flutter/blah.dart'
-            ..deletionsCount = 20
-            ..additionsCount = 0
-            ..changesCount = 20,
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['framework'],
-      )).called(1);
-
-      // The PR here is only deleting code, so no test comment.
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('PR with additions and deletions is commented and labeled', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()
-            ..filename = 'packages/flutter/blah.dart'
-            ..deletionsCount = 20
-            ..additionsCount = 1
-            ..changesCount = 21,
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['framework'],
-      )).called(1);
-
-      verify(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      )).called(1);
-    });
-
-    test('Framework no comment if code has only devicelab test', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/flutter_tools/lib/src/ios/devices.dart',
-          PullRequestFile()..filename = 'dev/devicelab/lib/tasks/plugin_tests.dart',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework no comment if only dev bots or devicelab changed', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'dev/bots/test.dart',
-          PullRequestFile()..filename = 'dev/devicelab/bin/tasks/analyzer_benchmark.dart',
-          PullRequestFile()..filename = 'dev/devicelab/lib/tasks/plugin_tests.dart',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework no test comment if Objective-C test changed', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          // Example of real behavior code change.
-          PullRequestFile()
-            ..filename = 'packages/flutter_tools/templates/app_shared/macos.tmpl/Runner/Base.lproj/MainMenu.xib',
-          // Example of Objective-C test.
-          PullRequestFile()..filename = 'dev/integration_tests/flutter_gallery/macos/RunnerTests/RunnerTests.m',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework no comment if only AUTHORS changed', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'AUTHORS',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework no comment if only ci.yaml and cirrus.yml changed', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = '.ci.yaml',
-          PullRequestFile()..filename = '.cirrus.yml',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework no comment if only CODEOWNERS changed', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'CODEOWNERS',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework no comment if only comments changed', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      const String patch = '''
-@@ -128,7 +128,7 @@
-
-/// Insert interesting comment here.
-///
--/// More details here, but some of them are wrong.
-+/// These are the right details!
-void foo() {
-  int bar = 0;
-  String baz = '';
-''';
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()
-            ..filename = 'packages/foo/lib/foo.dart'
-            ..additionsCount = 1
-            ..deletionsCount = 1
-            ..changesCount = 2
-            ..patch = patch,
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework labels PRs, no comment if tests (dev/bots/test.dart)', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'dev/bots/test.dart',
-          PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
-          PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
-          PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart',
-          PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart',
-          PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework labels PRs, no comment if tests (dev/bots/analyze.dart)', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'dev/bots/analyze.dart',
-          PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
-          PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
-          PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart',
-          PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart',
-          PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Framework labels PRs, apply label but no comment when rolling engine version', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kReleaseBaseRef,
-        headRef: kReleaseHeadRef,
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()
-            ..filename = 'bin/internal/engine.version'
-            ..deletionsCount = 20
-            ..additionsCount = 1
-            ..changesCount = 21,
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['engine'],
-      )).called(1);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Engine labels PRs, comment and labels if no tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'engine',
-        repoFullName: 'flutter/engine',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'engine');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['platform-ios'],
-      )).called(1);
-
-      verify(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      )).called(1);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['needs tests'],
-      )).called(1);
-    });
-
-    group("Auto-roller accounts do not label Engine PR with test label or comment.", () {
-      Set<String> inputs = {
-        'engine-flutter-autoroll',
-        'dependabot',
-      };
-
-      for (String element in inputs) {
-        test('Engine does not label PR for no tests if author is $element', () async {
-          const int issueNumber = 123;
-          request.headers.set('X-GitHub-Event', 'pull_request');
-          request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName,
-              repoName: 'engine', repoFullName: 'flutter/engine', login: element);
-
-          final Uint8List body = utf8.encode(request.body!) as Uint8List;
-          final Uint8List key = utf8.encode(keyString) as Uint8List;
-          final String hmac = getHmac(body, key);
-          request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-          final RepositorySlug slug = RepositorySlug('flutter', 'engine');
-
-          when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-            (_) => Stream<PullRequestFile>.value(
-              PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
-            ),
-          );
-
-          when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-            (_) => Stream<IssueComment>.value(
-              IssueComment()..body = 'some other comment',
-            ),
-          );
-
-          await tester.post(webhook);
-
-          verify(issuesService.addLabelsToIssue(
-            slug,
-            issueNumber,
-            <String>['platform-ios'],
-          )).called(1);
-
-          verifyNever(issuesService.createComment(
-            slug,
-            issueNumber,
-            argThat(contains(config.missingTestsPullRequestMessageValue)),
-          ));
-
-          verifyNever(issuesService.addLabelsToIssue(
-            slug,
-            issueNumber,
-            <String>['needs tests'],
-          ));
-        });
-      }
-    });
-
-    test('Engine does not label PR for no tests if author is skia-flutter-autoroll', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName,
-          repoName: 'engine', repoFullName: 'flutter/engine', login: 'skia-flutter-autoroll');
-
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'engine');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['platform-ios'],
-      ));
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-
-      verifyNever(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['needs tests'],
-      ));
-    });
-
-    test('Engine labels PRs, no code files', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        'main',
-        repoName: 'engine',
-        repoFullName: 'flutter/engine',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'engine');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'DEPS',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        any,
-      ));
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        any,
-      ));
-    });
-
-    test('Engine labels PRs, no comment if Java tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'engine',
-        repoFullName: 'flutter/engine',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'engine');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'shell/platform/android/io/flutter/Blah.java',
-          PullRequestFile()..filename = 'shell/platform/android/test/io/flutter/BlahTest.java',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>[
-          'platform-android',
-        ],
-      )).called(1);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Engine labels PRs, no comment if cc tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'engine',
-        repoFullName: 'flutter/engine',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'engine');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'fml/blah.cc',
-          PullRequestFile()..filename = 'fml/blah_unittests.cc',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        any,
-      ));
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Engine labels PRs, no comment if cc becnhmarks', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'engine',
-        repoFullName: 'flutter/engine',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'engine');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'fml/blah.cc',
-          PullRequestFile()..filename = 'fml/blah_benchmarks.cc',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        any,
-      ));
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Engine labels PRs, no comments if pr is for release branches', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kReleaseBaseRef,
-        headRef: kReleaseHeadRef,
-        repoName: 'engine',
-        repoFullName: 'flutter/engine',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'engine');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['platform-ios'],
-      )).called(1);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('No labels when only pubspec.yaml changes', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/flutter/pubspec.yaml',
-          PullRequestFile()..filename = 'packages/flutter_tools/pubspec.yaml',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['team'],
-      )).called(1);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Plugins comments and labels if no tests and no patch info', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      )).called(1);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['needs tests'],
-      )).called(1);
-    });
-
-    group('Plugins does not comment and label if author is an autoroller account.', () {
-      Set<String> inputs = {
-        'engine-flutter-autoroll',
-        'skia-flutter-autoroll',
-        'dependabot',
-      };
-
-      for (String element in inputs) {
-        test('Plugins does not comment and label if author is $element.', () async {
-          const int issueNumber = 123;
-          request.headers.set('X-GitHub-Event', 'pull_request');
-          request.body = generatePullRequestEvent(
-            'opened',
-            issueNumber,
-            kDefaultBranchName,
-            repoName: 'plugins',
-            repoFullName: 'flutter/plugins',
-            login: element,
-          );
-          final Uint8List body = utf8.encode(request.body!) as Uint8List;
-          final Uint8List key = utf8.encode(keyString) as Uint8List;
-          final String hmac = getHmac(body, key);
-          request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-          final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-          when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-            (_) => Stream<PullRequestFile>.value(
-              PullRequestFile()..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m',
-            ),
-          );
-
-          when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-            (_) => Stream<IssueComment>.value(
-              IssueComment()..body = 'some other comment',
-            ),
-          );
-
-          await tester.post(webhook);
-
-          verifyNever(issuesService.createComment(
-            slug,
-            issueNumber,
-            argThat(contains(config.missingTestsPullRequestMessageValue)),
-          ));
-
-          verifyNever(issuesService.addLabelsToIssue(
-            slug,
-            issueNumber,
-            <String>['needs tests'],
-          ));
-        });
-      }
-    });
-
-    test('Plugins apply no label or comment if pr is for release branches', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kReleaseBaseRef,
-        headRef: kReleaseHeadRef,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-
-      verifyNever(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        any,
-      ));
-    });
-
-    test('Plugins comments and labels for code change', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      const String patch = '''
-@@ -128,8 +128,8 @@
-  NSString* foo = "";
-  int bar = 0;
-
--  // Some incorrect code:
--  int baz = 7 / bar;
-+  // Better code:
-+  int baz = 7 * bar;
-  return baz;
-}
-
-''';
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()
-            ..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m'
-            ..additionsCount = 2
-            ..deletionsCount = 2
-            ..changesCount = 4
-            ..patch = patch,
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      )).called(1);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['needs tests'],
-      )).called(1);
-    });
-
-    test('Plugins comments and labels for code removal with comment addition', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      const String patch = '''
-@@ -128,7 +128,7 @@
-  int foo = 0;
-
-  int bar = 0;
--  int baz = 0;
-+  // int baz = 0;
-
-  // More code here:
-
-''';
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()
-            ..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m'
-            ..additionsCount = 1
-            ..deletionsCount = 1
-            ..changesCount = 2
-            ..patch = patch,
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      )).called(1);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['needs tests'],
-      )).called(1);
-    });
-
-    test('Plugins does not comment for comment-only changes', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      const String patch = '''
-@@ -128,7 +128,7 @@
-
-/// Insert interesting comment here.
-///
--/// More details here, but some of them are wrong.
-+/// These are the right details!
-void foo() {
-  int bar = 0;
-  String baz = '';
-''';
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()
-            ..filename = 'packages/foo/lib/foo.dart'
-            ..additionsCount = 1
-            ..deletionsCount = 1
-            ..changesCount = 2
-            ..patch = patch,
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Plugins does not comment if Dart tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
-          PullRequestFile()..filename = 'packages/foo/test/foo_test.dart',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Plugins does not comment if Android unit tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/foo/foo_android/src/main/Foo.java',
-          PullRequestFile()..filename = 'packages/foo/foo_android/src/test/FooTest.java',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Plugins does not comment if Android UI tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/foo/foo_android/src/main/Foo.java',
-          PullRequestFile()..filename = 'packages/foo/foo_android/example/android/app/src/androidTest/FooTest.java',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Plugins does not comment if iOS/macOS unit tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m',
-          PullRequestFile()..filename = 'packages/foo/foo_ios/example/ios/RunnerTests/FooTests.m',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Plugins does not comment if iOS/macOS UI tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/foo/foo_ios/ios/Classes/Foo.m',
-          PullRequestFile()..filename = 'packages/foo/foo_ios/example/ios/RunnerUITests/FooTests.m',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Plugins does not comment if Windows tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/foo/foo_windows/windows/foo.cpp',
-          PullRequestFile()..filename = 'packages/foo/foo_windows/windows/test/foo_test.cpp',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Plugins does not comment if Linux tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'plugins',
-        repoFullName: 'flutter/plugins',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'plugins');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/foo/foo_linux/linux/foo.cc',
-          PullRequestFile()..filename = 'packages/foo/foo_linux/linux/test/foo_test.cc',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Packages comments and labels if no tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'packages',
-        repoFullName: 'flutter/packages',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'packages');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      )).called(1);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['needs tests'],
-      )).called(1);
-    });
-
-    test('Packages do not comment or label if pr is for release branches', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kReleaseBaseRef,
-        headRef: kReleaseHeadRef,
-        repoName: 'packages',
-        repoFullName: 'flutter/packages',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'packages');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = 'some other comment',
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-
-      verifyNever(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        any,
-      ));
-    });
-
-    test('Packages does not comment if Dart tests', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        repoName: 'packages',
-        repoFullName: 'flutter/packages',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'packages');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
-          PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
-          PullRequestFile()..filename = 'packages/foo/test/foo_test.dart',
-        ]),
-      );
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Schedule tasks when pull request is closed and merged', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('closed', issueNumber, kDefaultBranchName, merged: true);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-      expect(db.values.values.whereType<Commit>().length, 0);
-      await tester.post(webhook);
-      expect(db.values.values.whereType<Commit>().length, 1);
-    });
-
-    test('Does not comment about needing tests on draft pull requests.', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        isDraft: true,
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer((_) => Stream<PullRequestFile>.value(
-            PullRequestFile()..filename = 'some_change.dart',
-          ));
-
-      await tester.post(webhook);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Will not spawn comments if they have already been made.', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent('opened', issueNumber, kDefaultBranchName);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
-        (_) => Stream<PullRequestFile>.value(
-          PullRequestFile()..filename = 'packages/flutter/blah.dart',
-        ),
-      );
-
-      when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
-        (_) => Stream<IssueComment>.value(
-          IssueComment()..body = config.missingTestsPullRequestMessageValue,
-        ),
-      );
-
-      await tester.post(webhook);
-
-      verify(issuesService.addLabelsToIssue(
-        slug,
-        issueNumber,
-        <String>['framework'],
-      )).called(1);
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        argThat(contains(config.missingTestsPullRequestMessageValue)),
-      ));
-    });
-
-    test('Skips labeling or commenting on autorolls', () async {
-      const int issueNumber = 123;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      request.body = generatePullRequestEvent(
-        'opened',
-        issueNumber,
-        kDefaultBranchName,
-        login: 'engine-flutter-autoroll',
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-      await tester.post(webhook);
-
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      verifyNever(issuesService.createComment(
-        slug,
-        issueNumber,
-        any,
-      ));
-    });
-
-    test('Comments on PR but does not schedule builds for unmergeable PRs', () async {
-      const int issueNumber = 12345;
-      request.body = generatePullRequestEvent(
-        'synchronize',
-        issueNumber,
-        kDefaultBranchName,
-        includeCqLabel: true,
-        // This PR is unmergeable (probably merge conflict)
-        isMergeable: false,
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
-      await tester.post(webhook);
-      verify(issuesService.createComment(slug, issueNumber, config.mergeConflictPullRequestMessage));
-    });
-
-    test('When synchronized, cancels existing builds and schedules new ones', () async {
-      const int issueNumber = 12345;
-      bool batchRequestCalled = false;
-      Future<BatchResponse> _getBatchResponse() async {
-        batchRequestCalled = true;
-        return BatchResponse(
-          responses: <Response>[
-            Response(
-              searchBuilds: SearchBuildsResponse(
-                builds: <Build>[
-                  generateBuild(999, name: 'Linux', status: Status.ended),
-                ],
-              ),
-            ),
-            Response(
-              searchBuilds: SearchBuildsResponse(
-                builds: <Build>[
-                  generateBuild(998, name: 'Linux', status: Status.ended),
-                ],
-              ),
-            ),
-          ],
-        );
-      }
-
-      fakeBuildBucketClient.batchResponse = _getBatchResponse;
-
-      request.body = generatePullRequestEvent('synchronize', issueNumber, kDefaultBranchName, includeCqLabel: true);
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      request.headers.set('X-GitHub-Event', 'pull_request');
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      final MockRepositoriesService mockRepositoriesService = MockRepositoriesService();
-      when(gitHubClient.repositories).thenReturn(mockRepositoriesService);
-
-      await tester.post(webhook);
-      expect(batchRequestCalled, isTrue);
-    });
-
-    group('BuildBucket', () {
-      const int issueNumber = 123;
-
-      setUp(() async {
-        request.headers.set('X-GitHub-Event', 'pull_request');
-      });
-
-      Future<void> _testActions(String action) async {
-        when(issuesService.listLabelsByIssue(any, issueNumber)).thenAnswer((_) {
-          return Stream<IssueLabel>.fromIterable(<IssueLabel>[
-            IssueLabel()..name = 'Random Label',
-          ]);
-        });
-
-        fakeBuildBucketClient.batchResponse = () => Future<BatchResponse>.value(
-              const BatchResponse(
-                responses: <Response>[
-                  Response(
-                    searchBuilds: SearchBuildsResponse(
-                      builds: <Build>[],
-                    ),
-                  ),
-                  Response(
-                    searchBuilds: SearchBuildsResponse(
-                      builds: <Build>[],
-                    ),
-                  ),
-                ],
-              ),
-            );
-
-        request.body = generatePullRequestEvent(action, 1, 'master');
-
-        final Uint8List body = utf8.encode(request.body!) as Uint8List;
-        final Uint8List key = utf8.encode(keyString) as Uint8List;
-        final String hmac = getHmac(body, key);
-        request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-        await tester.post(webhook);
-      }
-
-      test('Edited Action works properly', () async {
-        await _testActions('edited');
-      });
-
-      test('Opened Action works properly', () async {
-        await _testActions('opened');
-      });
-
-      test('Ready_for_review Action works properly', () async {
-        await _testActions('ready_for_review');
-      });
-
-      test('Reopened Action works properly', () async {
-        await _testActions('reopened');
-      });
-
-      test('Labeled Action works properly', () async {
-        await _testActions('labeled');
-      });
-
-      test('Synchronize Action works properly', () async {
-        await _testActions('synchronize');
-      });
-
-      test('Comments on PR but does not schedule builds for unmergeable PRs', () async {
-        when(issuesService.listCommentsByIssue(any, any)).thenAnswer((_) => Stream<IssueComment>.value(IssueComment()));
-        request.body = generatePullRequestEvent(
-          'synchronize',
-          issueNumber,
-          kDefaultBranchName,
-          includeCqLabel: true,
-          // This PR is unmergeable (probably merge conflict)
-          isMergeable: false,
-        );
-        final Uint8List body = utf8.encode(request.body!) as Uint8List;
-        final Uint8List key = utf8.encode(keyString) as Uint8List;
-        final String hmac = getHmac(body, key);
-        request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
-        await tester.post(webhook);
-        verify(issuesService.createComment(Config.flutterSlug, issueNumber, config.mergeConflictPullRequestMessage));
-      });
-
-      test('When synchronized, cancels existing builds and schedules new ones', () async {
-        fakeBuildBucketClient.batchResponse = () => Future<BatchResponse>.value(
-              BatchResponse(
-                responses: <Response>[
-                  Response(
-                    searchBuilds: SearchBuildsResponse(
-                      builds: <Build>[
-                        generateBuild(999, name: 'Linux', status: Status.ended),
-                      ],
-                    ),
-                  ),
-                  Response(
-                    searchBuilds: SearchBuildsResponse(
-                      builds: <Build>[
-                        generateBuild(998, name: 'Linux', status: Status.ended),
-                      ],
-                    ),
-                  ),
-                ],
-              ),
-            );
-
-        request.body = generatePullRequestEvent('synchronize', issueNumber, kDefaultBranchName, includeCqLabel: true);
-        final Uint8List body = utf8.encode(request.body!) as Uint8List;
-        final Uint8List key = utf8.encode(keyString) as Uint8List;
-        final String hmac = getHmac(body, key);
-        request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-        final MockRepositoriesService mockRepositoriesService = MockRepositoriesService();
-        when(gitHubClient.repositories).thenReturn(mockRepositoriesService);
-
-        await tester.post(webhook);
-      });
-    });
+  test('Rejects non-POST methods with methodNotAllowed', () async {
+    expect(tester.get(webhook), throwsA(isA<MethodNotAllowed>()));
+    expect(pubsub.messages, isEmpty);
   });
 
-  group('github webhook create branch event', () {
-    test('process create branch event', () async {
-      request.headers.set('X-GitHub-Event', 'create');
-      request.body =
-          jsonEncode(generateCreateBranchEvent('flutter-2.12-candidate.4', Config.flutterSlug.fullName).toJson());
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      await tester.post(webhook);
-
-      verify(branchService.branchFlutterRecipes('flutter-2.12-candidate.4'));
-    });
-
-    test('do not create recipe branches on non-flutter/flutter branches', () async {
-      request.headers.set('X-GitHub-Event', 'create');
-      request.body =
-          jsonEncode(generateCreateBranchEvent('flutter-2.12-candidate.4', Config.engineSlug.fullName).toJson());
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      await tester.post(webhook);
-
-      verifyNever(branchService.branchFlutterRecipes(any));
-    });
+  test('Rejects missing headers', () async {
+    expect(tester.post(webhook), throwsA(isA<BadRequestException>()));
+    expect(pubsub.messages, isEmpty);
   });
 
-  group('github webhook check_run event', () {
-    setUp(() {
-      request.headers.set('X-GitHub-Event', 'check_run');
-    });
+  test('Rejects invalid hmac', () async {
+    request.headers.set('X-GitHub-Event', 'pull_request');
+    request.headers.set('X-Hub-Signature', 'bar');
+    request.body = 'Hello, World!';
+    expect(tester.post(webhook), throwsA(isA<Forbidden>()));
+    expect(pubsub.messages, isEmpty);
+  });
 
-    test('processes check run event', () async {
-      request.body = generateCheckRunEvent();
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      await tester.post(webhook);
-    });
+  test('Publishes message', () async {
+    request.headers.set('X-GitHub-Event', 'pull_request');
+    request.body = '{}';
+    final Uint8List body = utf8.encode(request.body!) as Uint8List;
+    final Uint8List key = utf8.encode(keyString) as Uint8List;
+    final String hmac = getHmac(body, key);
+    request.headers.set('X-Hub-Signature', 'sha1=$hmac');
+    await tester.post(webhook);
 
-    test('processes completed check run event', () async {
-      request.body = generateCheckRunEvent(
-        action: 'completed',
-        numberOfPullRequests: 0,
-      );
-      final Uint8List body = utf8.encode(request.body!) as Uint8List;
-      final Uint8List key = utf8.encode(keyString) as Uint8List;
-      final String hmac = getHmac(body, key);
-      request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-      await tester.post(webhook);
-    });
+    expect(pubsub.messages, hasLength(1));
+    final GithubWebhookMessage message = pubsub.messages.single as GithubWebhookMessage;
+    expect(message.event, 'pull_request');
+    expect(message.payload, '{}');
   });
 }
diff --git a/app_dart/test/src/utilities/mocks.mocks.dart b/app_dart/test/src/utilities/mocks.mocks.dart
index c40b068..0ff5fb3 100644
--- a/app_dart/test/src/utilities/mocks.mocks.dart
+++ b/app_dart/test/src/utilities/mocks.mocks.dart
@@ -1,7 +1,8 @@
-// Mocks generated by Mockito 5.2.0 from annotations
+// Mocks generated by Mockito 5.3.0 from annotations
 // in cocoon_service/test/src/utilities/mocks.dart.
 // Do not manually edit this file.
 
+// ignore_for_file: no_leading_underscores_for_library_prefixes
 import 'dart:async' as _i17;
 import 'dart:convert' as _i16;
 import 'dart:io' as _i15;
@@ -55,200 +56,395 @@
 // ignore_for_file: prefer_const_constructors
 // ignore_for_file: unnecessary_parenthesis
 // ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
 
-class _FakeClient_0 extends _i1.Fake implements _i2.Client {}
+class _FakeClient_0 extends _i1.SmartFake implements _i2.Client {
+  _FakeClient_0(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeConfig_1 extends _i1.Fake implements _i3.Config {}
+class _FakeConfig_1 extends _i1.SmartFake implements _i3.Config {
+  _FakeConfig_1(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeAccessToken_2 extends _i1.Fake implements _i4.AccessToken {}
+class _FakeAccessToken_2 extends _i1.SmartFake implements _i4.AccessToken {
+  _FakeAccessToken_2(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeAccessClientProvider_3 extends _i1.Fake implements _i5.AccessClientProvider {}
+class _FakeAccessClientProvider_3 extends _i1.SmartFake implements _i5.AccessClientProvider {
+  _FakeAccessClientProvider_3(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeTabledataResource_4 extends _i1.Fake implements _i6.TabledataResource {}
+class _FakeTabledataResource_4 extends _i1.SmartFake implements _i6.TabledataResource {
+  _FakeTabledataResource_4(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeJobsResource_5 extends _i1.Fake implements _i6.JobsResource {}
+class _FakeJobsResource_5 extends _i1.SmartFake implements _i6.JobsResource {
+  _FakeJobsResource_5(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGerritService_6 extends _i1.Fake implements _i7.GerritService {}
+class _FakeGerritService_6 extends _i1.SmartFake implements _i7.GerritService {
+  _FakeGerritService_6(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeRetryOptions_7 extends _i1.Fake implements _i8.RetryOptions {}
+class _FakeRetryOptions_7 extends _i1.SmartFake implements _i8.RetryOptions {
+  _FakeRetryOptions_7(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeBuild_8 extends _i1.Fake implements _i9.Build {}
+class _FakeBuild_8 extends _i1.SmartFake implements _i9.Build {
+  _FakeBuild_8(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeSearchBuildsResponse_9 extends _i1.Fake implements _i9.SearchBuildsResponse {}
+class _FakeSearchBuildsResponse_9 extends _i1.SmartFake implements _i9.SearchBuildsResponse {
+  _FakeSearchBuildsResponse_9(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeBatchResponse_10 extends _i1.Fake implements _i9.BatchResponse {}
+class _FakeBatchResponse_10 extends _i1.SmartFake implements _i9.BatchResponse {
+  _FakeBatchResponse_10(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeListBuildersResponse_11 extends _i1.Fake implements _i9.ListBuildersResponse {}
+class _FakeListBuildersResponse_11 extends _i1.SmartFake implements _i9.ListBuildersResponse {
+  _FakeListBuildersResponse_11(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGitHub_12 extends _i1.Fake implements _i10.GitHub {}
+class _FakeGitHub_12 extends _i1.SmartFake implements _i10.GitHub {
+  _FakeGitHub_12(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeIssue_13 extends _i1.Fake implements _i10.Issue {}
+class _FakeIssue_13 extends _i1.SmartFake implements _i10.Issue {
+  _FakeIssue_13(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeIssueComment_14 extends _i1.Fake implements _i10.IssueComment {}
+class _FakeIssueComment_14 extends _i1.SmartFake implements _i10.IssueComment {
+  _FakeIssueComment_14(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeIssueLabel_15 extends _i1.Fake implements _i10.IssueLabel {}
+class _FakeIssueLabel_15 extends _i1.SmartFake implements _i10.IssueLabel {
+  _FakeIssueLabel_15(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeMilestone_16 extends _i1.Fake implements _i10.Milestone {}
+class _FakeMilestone_16 extends _i1.SmartFake implements _i10.Milestone {
+  _FakeMilestone_16(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGithubChecksUtil_17 extends _i1.Fake implements _i11.GithubChecksUtil {}
+class _FakeGithubChecksUtil_17 extends _i1.SmartFake implements _i11.GithubChecksUtil {
+  _FakeGithubChecksUtil_17(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeCheckRunConclusion_18 extends _i1.Fake implements _i10.CheckRunConclusion {}
+class _FakeCheckRunConclusion_18 extends _i1.SmartFake implements _i10.CheckRunConclusion {
+  _FakeCheckRunConclusion_18(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeCheckRunStatus_19 extends _i1.Fake implements _i10.CheckRunStatus {}
+class _FakeCheckRunStatus_19 extends _i1.SmartFake implements _i10.CheckRunStatus {
+  _FakeCheckRunStatus_19(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeCheckSuite_20 extends _i1.Fake implements _i10.CheckSuite {}
+class _FakeCheckSuite_20 extends _i1.SmartFake implements _i10.CheckSuite {
+  _FakeCheckSuite_20(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeCheckRun_21 extends _i1.Fake implements _i10.CheckRun {}
+class _FakeCheckRun_21 extends _i1.SmartFake implements _i10.CheckRun {
+  _FakeCheckRun_21(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakePullRequest_22 extends _i1.Fake implements _i10.PullRequest {}
+class _FakePullRequest_22 extends _i1.SmartFake implements _i10.PullRequest {
+  _FakePullRequest_22(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGitReference_23 extends _i1.Fake implements _i10.GitReference {}
+class _FakeGitReference_23 extends _i1.SmartFake implements _i10.GitReference {
+  _FakeGitReference_23(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeRateLimit_24 extends _i1.Fake implements _i10.RateLimit {}
+class _FakeRateLimit_24 extends _i1.SmartFake implements _i10.RateLimit {
+  _FakeRateLimit_24(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGitBlob_25 extends _i1.Fake implements _i10.GitBlob {}
+class _FakeGitBlob_25 extends _i1.SmartFake implements _i10.GitBlob {
+  _FakeGitBlob_25(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGitCommit_26 extends _i1.Fake implements _i10.GitCommit {}
+class _FakeGitCommit_26 extends _i1.SmartFake implements _i10.GitCommit {
+  _FakeGitCommit_26(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGitTag_27 extends _i1.Fake implements _i10.GitTag {}
+class _FakeGitTag_27 extends _i1.SmartFake implements _i10.GitTag {
+  _FakeGitTag_27(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGitTree_28 extends _i1.Fake implements _i10.GitTree {}
+class _FakeGitTree_28 extends _i1.SmartFake implements _i10.GitTree {
+  _FakeGitTree_28(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeDefaultPolicies_29 extends _i1.Fake implements _i12.DefaultPolicies {}
+class _FakeDefaultPolicies_29 extends _i1.SmartFake implements _i12.DefaultPolicies {
+  _FakeDefaultPolicies_29(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeLink_30 extends _i1.Fake implements _i12.Link {}
+class _FakeLink_30 extends _i1.SmartFake implements _i12.Link {
+  _FakeLink_30(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGraphQLCache_31 extends _i1.Fake implements _i13.GraphQLCache {}
+class _FakeGraphQLCache_31 extends _i1.SmartFake implements _i13.GraphQLCache {
+  _FakeGraphQLCache_31(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeQueryManager_32 extends _i1.Fake implements _i12.QueryManager {}
+class _FakeQueryManager_32 extends _i1.SmartFake implements _i12.QueryManager {
+  _FakeQueryManager_32(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGraphQLClient_33 extends _i1.Fake implements _i14.GraphQLClient {}
+class _FakeGraphQLClient_33 extends _i1.SmartFake implements _i14.GraphQLClient {
+  _FakeGraphQLClient_33(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeObservableQuery_34<TParsed> extends _i1.Fake implements _i12.ObservableQuery<TParsed> {}
+class _FakeObservableQuery_34<TParsed> extends _i1.SmartFake implements _i12.ObservableQuery<TParsed> {
+  _FakeObservableQuery_34(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeQueryResult_35<TParsed extends Object?> extends _i1.Fake implements _i12.QueryResult<TParsed> {}
+class _FakeQueryResult_35<TParsed extends Object?> extends _i1.SmartFake implements _i12.QueryResult<TParsed> {
+  _FakeQueryResult_35(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeDuration_36 extends _i1.Fake implements Duration {}
+class _FakeDuration_36 extends _i1.SmartFake implements Duration {
+  _FakeDuration_36(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeHttpClientRequest_37 extends _i1.Fake implements _i15.HttpClientRequest {}
+class _FakeHttpClientRequest_37 extends _i1.SmartFake implements _i15.HttpClientRequest {
+  _FakeHttpClientRequest_37(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeUri_38 extends _i1.Fake implements Uri {}
+class _FakeUri_38 extends _i1.SmartFake implements Uri {
+  _FakeUri_38(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeHttpHeaders_39 extends _i1.Fake implements _i15.HttpHeaders {}
+class _FakeHttpHeaders_39 extends _i1.SmartFake implements _i15.HttpHeaders {
+  _FakeHttpHeaders_39(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeHttpClientResponse_40 extends _i1.Fake implements _i15.HttpClientResponse {}
+class _FakeHttpClientResponse_40 extends _i1.SmartFake implements _i15.HttpClientResponse {
+  _FakeHttpClientResponse_40(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeEncoding_41 extends _i1.Fake implements _i16.Encoding {}
+class _FakeEncoding_41 extends _i1.SmartFake implements _i16.Encoding {
+  _FakeEncoding_41(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeSocket_42 extends _i1.Fake implements _i15.Socket {}
+class _FakeSocket_42 extends _i1.SmartFake implements _i15.Socket {
+  _FakeSocket_42(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeStreamSubscription_43<T> extends _i1.Fake implements _i17.StreamSubscription<T> {}
+class _FakeStreamSubscription_43<T> extends _i1.SmartFake implements _i17.StreamSubscription<T> {
+  _FakeStreamSubscription_43(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeJobCancelResponse_44 extends _i1.Fake implements _i6.JobCancelResponse {}
+class _FakeJobCancelResponse_44 extends _i1.SmartFake implements _i6.JobCancelResponse {
+  _FakeJobCancelResponse_44(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeJob_45 extends _i1.Fake implements _i6.Job {}
+class _FakeJob_45 extends _i1.SmartFake implements _i6.Job {
+  _FakeJob_45(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGetQueryResultsResponse_46 extends _i1.Fake implements _i6.GetQueryResultsResponse {}
+class _FakeGetQueryResultsResponse_46 extends _i1.SmartFake implements _i6.GetQueryResultsResponse {
+  _FakeGetQueryResultsResponse_46(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeJobList_47 extends _i1.Fake implements _i6.JobList {}
+class _FakeJobList_47 extends _i1.SmartFake implements _i6.JobList {
+  _FakeJobList_47(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeQueryResponse_48 extends _i1.Fake implements _i6.QueryResponse {}
+class _FakeQueryResponse_48 extends _i1.SmartFake implements _i6.QueryResponse {
+  _FakeQueryResponse_48(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeBuildBucketClient_49 extends _i1.Fake implements _i18.BuildBucketClient {}
+class _FakeBuildBucketClient_49 extends _i1.SmartFake implements _i18.BuildBucketClient {
+  _FakeBuildBucketClient_49(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakePubSub_50 extends _i1.Fake implements _i19.PubSub {}
+class _FakePubSub_50 extends _i1.SmartFake implements _i19.PubSub {
+  _FakePubSub_50(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeClientContext_51 extends _i1.Fake implements _i20.ClientContext {}
+class _FakeClientContext_51 extends _i1.SmartFake implements _i20.ClientContext {
+  _FakeClientContext_51(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeProcess_52 extends _i1.Fake implements _i15.Process {}
+class _FakeProcess_52 extends _i1.SmartFake implements _i15.Process {
+  _FakeProcess_52(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeProcessResult_53 extends _i1.Fake implements _i15.ProcessResult {}
+class _FakeProcessResult_53 extends _i1.SmartFake implements _i15.ProcessResult {
+  _FakeProcessResult_53(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakePullRequestMerge_54 extends _i1.Fake implements _i10.PullRequestMerge {}
+class _FakePullRequestMerge_54 extends _i1.SmartFake implements _i10.PullRequestMerge {
+  _FakePullRequestMerge_54(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakePullRequestComment_55 extends _i1.Fake implements _i10.PullRequestComment {}
+class _FakePullRequestComment_55 extends _i1.SmartFake implements _i10.PullRequestComment {
+  _FakePullRequestComment_55(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakePullRequestReview_56 extends _i1.Fake implements _i10.PullRequestReview {}
+class _FakePullRequestReview_56 extends _i1.SmartFake implements _i10.PullRequestReview {
+  _FakePullRequestReview_56(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeRepository_57 extends _i1.Fake implements _i10.Repository {}
+class _FakeRepository_57 extends _i1.SmartFake implements _i10.Repository {
+  _FakeRepository_57(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeLicenseDetails_58 extends _i1.Fake implements _i10.LicenseDetails {}
+class _FakeLicenseDetails_58 extends _i1.SmartFake implements _i10.LicenseDetails {
+  _FakeLicenseDetails_58(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeLanguageBreakdown_59 extends _i1.Fake implements _i10.LanguageBreakdown {}
+class _FakeLanguageBreakdown_59 extends _i1.SmartFake implements _i10.LanguageBreakdown {
+  _FakeLanguageBreakdown_59(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeBranch_60 extends _i1.Fake implements _i10.Branch {}
+class _FakeBranch_60 extends _i1.SmartFake implements _i10.Branch {
+  _FakeBranch_60(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeCommitComment_61 extends _i1.Fake implements _i10.CommitComment {}
+class _FakeCommitComment_61 extends _i1.SmartFake implements _i10.CommitComment {
+  _FakeCommitComment_61(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeRepositoryCommit_62 extends _i1.Fake implements _i10.RepositoryCommit {}
+class _FakeRepositoryCommit_62 extends _i1.SmartFake implements _i10.RepositoryCommit {
+  _FakeRepositoryCommit_62(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGitHubComparison_63 extends _i1.Fake implements _i10.GitHubComparison {}
+class _FakeGitHubComparison_63 extends _i1.SmartFake implements _i10.GitHubComparison {
+  _FakeGitHubComparison_63(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGitHubFile_64 extends _i1.Fake implements _i10.GitHubFile {}
+class _FakeGitHubFile_64 extends _i1.SmartFake implements _i10.GitHubFile {
+  _FakeGitHubFile_64(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeRepositoryContents_65 extends _i1.Fake implements _i10.RepositoryContents {}
+class _FakeRepositoryContents_65 extends _i1.SmartFake implements _i10.RepositoryContents {
+  _FakeRepositoryContents_65(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeContentCreation_66 extends _i1.Fake implements _i10.ContentCreation {}
+class _FakeContentCreation_66 extends _i1.SmartFake implements _i10.ContentCreation {
+  _FakeContentCreation_66(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeHook_67 extends _i1.Fake implements _i10.Hook {}
+class _FakeHook_67 extends _i1.SmartFake implements _i10.Hook {
+  _FakeHook_67(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakePublicKey_68 extends _i1.Fake implements _i10.PublicKey {}
+class _FakePublicKey_68 extends _i1.SmartFake implements _i10.PublicKey {
+  _FakePublicKey_68(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeRepositoryPages_69 extends _i1.Fake implements _i10.RepositoryPages {}
+class _FakeRepositoryPages_69 extends _i1.SmartFake implements _i10.RepositoryPages {
+  _FakeRepositoryPages_69(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakePageBuild_70 extends _i1.Fake implements _i10.PageBuild {}
+class _FakePageBuild_70 extends _i1.SmartFake implements _i10.PageBuild {
+  _FakePageBuild_70(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeRelease_71 extends _i1.Fake implements _i10.Release {}
+class _FakeRelease_71 extends _i1.SmartFake implements _i10.Release {
+  _FakeRelease_71(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeReleaseAsset_72 extends _i1.Fake implements _i10.ReleaseAsset {}
+class _FakeReleaseAsset_72 extends _i1.SmartFake implements _i10.ReleaseAsset {
+  _FakeReleaseAsset_72(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeContributorParticipation_73 extends _i1.Fake implements _i10.ContributorParticipation {}
+class _FakeContributorParticipation_73 extends _i1.SmartFake implements _i10.ContributorParticipation {
+  _FakeContributorParticipation_73(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeRepositoryStatus_74 extends _i1.Fake implements _i10.RepositoryStatus {}
+class _FakeRepositoryStatus_74 extends _i1.SmartFake implements _i10.RepositoryStatus {
+  _FakeRepositoryStatus_74(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeCombinedRepositoryStatus_75 extends _i1.Fake implements _i10.CombinedRepositoryStatus {}
+class _FakeCombinedRepositoryStatus_75 extends _i1.SmartFake implements _i10.CombinedRepositoryStatus {
+  _FakeCombinedRepositoryStatus_75(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeReleaseNotes_76 extends _i1.Fake implements _i10.ReleaseNotes {}
+class _FakeReleaseNotes_76 extends _i1.SmartFake implements _i10.ReleaseNotes {
+  _FakeReleaseNotes_76(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeTableDataInsertAllResponse_77 extends _i1.Fake implements _i6.TableDataInsertAllResponse {}
+class _FakeTableDataInsertAllResponse_77 extends _i1.SmartFake implements _i6.TableDataInsertAllResponse {
+  _FakeTableDataInsertAllResponse_77(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeTableDataList_78 extends _i1.Fake implements _i6.TableDataList {}
+class _FakeTableDataList_78 extends _i1.SmartFake implements _i6.TableDataList {
+  _FakeTableDataList_78(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeUser_79 extends _i1.Fake implements _i10.User {}
+class _FakeUser_79 extends _i1.SmartFake implements _i10.User {
+  _FakeUser_79(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeCurrentUser_80 extends _i1.Fake implements _i10.CurrentUser {}
+class _FakeCurrentUser_80 extends _i1.SmartFake implements _i10.CurrentUser {
+  _FakeCurrentUser_80(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeEntry_81<T> extends _i1.Fake implements _i21.Entry<T> {}
+class _FakeEntry_81<T> extends _i1.SmartFake implements _i21.Entry<T> {
+  _FakeEntry_81(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeCache_82<T> extends _i1.Fake implements _i21.Cache<T> {}
+class _FakeCache_82<T> extends _i1.SmartFake implements _i21.Cache<T> {
+  _FakeCache_82(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeActivityService_83 extends _i1.Fake implements _i10.ActivityService {}
+class _FakeActivityService_83 extends _i1.SmartFake implements _i10.ActivityService {
+  _FakeActivityService_83(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeAuthorizationsService_84 extends _i1.Fake implements _i10.AuthorizationsService {}
+class _FakeAuthorizationsService_84 extends _i1.SmartFake implements _i10.AuthorizationsService {
+  _FakeAuthorizationsService_84(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGistsService_85 extends _i1.Fake implements _i10.GistsService {}
+class _FakeGistsService_85 extends _i1.SmartFake implements _i10.GistsService {
+  _FakeGistsService_85(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeGitService_86 extends _i1.Fake implements _i10.GitService {}
+class _FakeGitService_86 extends _i1.SmartFake implements _i10.GitService {
+  _FakeGitService_86(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeIssuesService_87 extends _i1.Fake implements _i10.IssuesService {}
+class _FakeIssuesService_87 extends _i1.SmartFake implements _i10.IssuesService {
+  _FakeIssuesService_87(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeMiscService_88 extends _i1.Fake implements _i10.MiscService {}
+class _FakeMiscService_88 extends _i1.SmartFake implements _i10.MiscService {
+  _FakeMiscService_88(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeOrganizationsService_89 extends _i1.Fake implements _i10.OrganizationsService {}
+class _FakeOrganizationsService_89 extends _i1.SmartFake implements _i10.OrganizationsService {
+  _FakeOrganizationsService_89(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakePullRequestsService_90 extends _i1.Fake implements _i10.PullRequestsService {}
+class _FakePullRequestsService_90 extends _i1.SmartFake implements _i10.PullRequestsService {
+  _FakePullRequestsService_90(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeRepositoriesService_91 extends _i1.Fake implements _i10.RepositoriesService {}
+class _FakeRepositoriesService_91 extends _i1.SmartFake implements _i10.RepositoriesService {
+  _FakeRepositoriesService_91(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeSearchService_92 extends _i1.Fake implements _i10.SearchService {}
+class _FakeSearchService_92 extends _i1.SmartFake implements _i10.SearchService {
+  _FakeSearchService_92(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeUrlShortenerService_93 extends _i1.Fake implements _i10.UrlShortenerService {}
+class _FakeUrlShortenerService_93 extends _i1.SmartFake implements _i10.UrlShortenerService {
+  _FakeUrlShortenerService_93(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeUsersService_94 extends _i1.Fake implements _i10.UsersService {}
+class _FakeUsersService_94 extends _i1.SmartFake implements _i10.UsersService {
+  _FakeUsersService_94(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeChecksService_95 extends _i1.Fake implements _i10.ChecksService {}
+class _FakeChecksService_95 extends _i1.SmartFake implements _i10.ChecksService {
+  _FakeChecksService_95(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
-class _FakeResponse_96 extends _i1.Fake implements _i2.Response {}
+class _FakeResponse_96 extends _i1.SmartFake implements _i2.Response {
+  _FakeResponse_96(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
+}
 
 /// A class which mocks [AccessClientProvider].
 ///
@@ -262,7 +458,9 @@
   _i17.Future<_i2.Client> createAccessClient(
           {List<String>? scopes = const [r'https://www.googleapis.com/auth/cloud-platform']}) =>
       (super.noSuchMethod(Invocation.method(#createAccessClient, [], {#scopes: scopes}),
-          returnValue: Future<_i2.Client>.value(_FakeClient_0())) as _i17.Future<_i2.Client>);
+              returnValue: _i17.Future<_i2.Client>.value(
+                  _FakeClient_0(this, Invocation.method(#createAccessClient, [], {#scopes: scopes}))))
+          as _i17.Future<_i2.Client>);
 }
 
 /// A class which mocks [AccessTokenService].
@@ -274,10 +472,14 @@
   }
 
   @override
-  _i3.Config get config => (super.noSuchMethod(Invocation.getter(#config), returnValue: _FakeConfig_1()) as _i3.Config);
+  _i3.Config get config =>
+      (super.noSuchMethod(Invocation.getter(#config), returnValue: _FakeConfig_1(this, Invocation.getter(#config)))
+          as _i3.Config);
   @override
   _i17.Future<_i4.AccessToken> createAccessToken() => (super.noSuchMethod(Invocation.method(#createAccessToken, []),
-      returnValue: Future<_i4.AccessToken>.value(_FakeAccessToken_2())) as _i17.Future<_i4.AccessToken>);
+          returnValue:
+              _i17.Future<_i4.AccessToken>.value(_FakeAccessToken_2(this, Invocation.method(#createAccessToken, []))))
+      as _i17.Future<_i4.AccessToken>);
 }
 
 /// A class which mocks [BigqueryService].
@@ -289,28 +491,31 @@
   }
 
   @override
-  _i5.AccessClientProvider get accessClientProvider =>
-      (super.noSuchMethod(Invocation.getter(#accessClientProvider), returnValue: _FakeAccessClientProvider_3())
-          as _i5.AccessClientProvider);
+  _i5.AccessClientProvider get accessClientProvider => (super.noSuchMethod(Invocation.getter(#accessClientProvider),
+          returnValue: _FakeAccessClientProvider_3(this, Invocation.getter(#accessClientProvider)))
+      as _i5.AccessClientProvider);
   @override
   _i17.Future<_i6.TabledataResource> defaultTabledata() => (super.noSuchMethod(Invocation.method(#defaultTabledata, []),
-          returnValue: Future<_i6.TabledataResource>.value(_FakeTabledataResource_4()))
+          returnValue: _i17.Future<_i6.TabledataResource>.value(
+              _FakeTabledataResource_4(this, Invocation.method(#defaultTabledata, []))))
       as _i17.Future<_i6.TabledataResource>);
   @override
   _i17.Future<_i6.JobsResource> defaultJobs() => (super.noSuchMethod(Invocation.method(#defaultJobs, []),
-      returnValue: Future<_i6.JobsResource>.value(_FakeJobsResource_5())) as _i17.Future<_i6.JobsResource>);
+          returnValue:
+              _i17.Future<_i6.JobsResource>.value(_FakeJobsResource_5(this, Invocation.method(#defaultJobs, []))))
+      as _i17.Future<_i6.JobsResource>);
   @override
   _i17.Future<List<_i23.BuilderStatistic>> listBuilderStatistic(String? projectId,
           {int? limit = 100, String? bucket = r'prod'}) =>
       (super.noSuchMethod(Invocation.method(#listBuilderStatistic, [projectId], {#limit: limit, #bucket: bucket}),
-              returnValue: Future<List<_i23.BuilderStatistic>>.value(<_i23.BuilderStatistic>[]))
+              returnValue: _i17.Future<List<_i23.BuilderStatistic>>.value(<_i23.BuilderStatistic>[]))
           as _i17.Future<List<_i23.BuilderStatistic>>);
   @override
   _i17.Future<List<_i23.BuilderRecord>> listRecentBuildRecordsForBuilder(String? projectId,
           {String? builder, int? limit}) =>
       (super.noSuchMethod(
               Invocation.method(#listRecentBuildRecordsForBuilder, [projectId], {#builder: builder, #limit: limit}),
-              returnValue: Future<List<_i23.BuilderRecord>>.value(<_i23.BuilderRecord>[]))
+              returnValue: _i17.Future<List<_i23.BuilderRecord>>.value(<_i23.BuilderRecord>[]))
           as _i17.Future<List<_i23.BuilderRecord>>);
 }
 
@@ -323,25 +528,29 @@
   }
 
   @override
-  _i3.Config get config => (super.noSuchMethod(Invocation.getter(#config), returnValue: _FakeConfig_1()) as _i3.Config);
+  _i3.Config get config =>
+      (super.noSuchMethod(Invocation.getter(#config), returnValue: _FakeConfig_1(this, Invocation.getter(#config)))
+          as _i3.Config);
   @override
-  _i7.GerritService get gerritService =>
-      (super.noSuchMethod(Invocation.getter(#gerritService), returnValue: _FakeGerritService_6()) as _i7.GerritService);
+  _i7.GerritService get gerritService => (super.noSuchMethod(Invocation.getter(#gerritService),
+      returnValue: _FakeGerritService_6(this, Invocation.getter(#gerritService))) as _i7.GerritService);
   @override
-  _i8.RetryOptions get retryOptions =>
-      (super.noSuchMethod(Invocation.getter(#retryOptions), returnValue: _FakeRetryOptions_7()) as _i8.RetryOptions);
+  _i8.RetryOptions get retryOptions => (super.noSuchMethod(Invocation.getter(#retryOptions),
+      returnValue: _FakeRetryOptions_7(this, Invocation.getter(#retryOptions))) as _i8.RetryOptions);
   @override
   _i17.Future<void> handleCreateRequest(_i25.CreateEvent? createEvent) =>
       (super.noSuchMethod(Invocation.method(#handleCreateRequest, [createEvent]),
-          returnValue: Future<void>.value(), returnValueForMissingStub: Future<void>.value()) as _i17.Future<void>);
+          returnValue: _i17.Future<void>.value(),
+          returnValueForMissingStub: _i17.Future<void>.value()) as _i17.Future<void>);
   @override
   _i17.Future<void> branchFlutterRecipes(String? branch) =>
       (super.noSuchMethod(Invocation.method(#branchFlutterRecipes, [branch]),
-          returnValue: Future<void>.value(), returnValueForMissingStub: Future<void>.value()) as _i17.Future<void>);
+          returnValue: _i17.Future<void>.value(),
+          returnValueForMissingStub: _i17.Future<void>.value()) as _i17.Future<void>);
   @override
-  _i17.Future<List<Map<String, String>>> getStableBetaDevBranches({_i10.GitHub? github, _i10.RepositorySlug? slug}) =>
-      (super.noSuchMethod(Invocation.method(#getStableBetaDevBranches, [], {#github: github, #slug: slug}),
-              returnValue: Future<List<Map<String, String>>>.value(<Map<String, String>>[]))
+  _i17.Future<List<Map<String, String>>> getReleaseBranches({_i10.GitHub? github, _i10.RepositorySlug? slug}) =>
+      (super.noSuchMethod(Invocation.method(#getReleaseBranches, [], {#github: github, #slug: slug}),
+              returnValue: _i17.Future<List<Map<String, String>>>.value(<Map<String, String>>[]))
           as _i17.Future<List<Map<String, String>>>);
 }
 
@@ -361,39 +570,49 @@
   String get buildBucketBuilderUri =>
       (super.noSuchMethod(Invocation.getter(#buildBucketBuilderUri), returnValue: '') as String);
   @override
-  _i2.Client get httpClient =>
-      (super.noSuchMethod(Invocation.getter(#httpClient), returnValue: _FakeClient_0()) as _i2.Client);
+  _i2.Client get httpClient => (super.noSuchMethod(Invocation.getter(#httpClient),
+      returnValue: _FakeClient_0(this, Invocation.getter(#httpClient))) as _i2.Client);
   @override
   _i17.Future<_i9.Build> scheduleBuild(_i9.ScheduleBuildRequest? request,
           {String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds'}) =>
       (super.noSuchMethod(Invocation.method(#scheduleBuild, [request], {#buildBucketUri: buildBucketUri}),
-          returnValue: Future<_i9.Build>.value(_FakeBuild_8())) as _i17.Future<_i9.Build>);
+              returnValue: _i17.Future<_i9.Build>.value(
+                  _FakeBuild_8(this, Invocation.method(#scheduleBuild, [request], {#buildBucketUri: buildBucketUri}))))
+          as _i17.Future<_i9.Build>);
   @override
   _i17.Future<_i9.SearchBuildsResponse> searchBuilds(_i9.SearchBuildsRequest? request,
           {String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds'}) =>
       (super.noSuchMethod(Invocation.method(#searchBuilds, [request], {#buildBucketUri: buildBucketUri}),
-              returnValue: Future<_i9.SearchBuildsResponse>.value(_FakeSearchBuildsResponse_9()))
+              returnValue: _i17.Future<_i9.SearchBuildsResponse>.value(_FakeSearchBuildsResponse_9(
+                  this, Invocation.method(#searchBuilds, [request], {#buildBucketUri: buildBucketUri}))))
           as _i17.Future<_i9.SearchBuildsResponse>);
   @override
   _i17.Future<_i9.BatchResponse> batch(_i9.BatchRequest? request,
           {String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds'}) =>
       (super.noSuchMethod(Invocation.method(#batch, [request], {#buildBucketUri: buildBucketUri}),
-          returnValue: Future<_i9.BatchResponse>.value(_FakeBatchResponse_10())) as _i17.Future<_i9.BatchResponse>);
+              returnValue: _i17.Future<_i9.BatchResponse>.value(
+                  _FakeBatchResponse_10(this, Invocation.method(#batch, [request], {#buildBucketUri: buildBucketUri}))))
+          as _i17.Future<_i9.BatchResponse>);
   @override
   _i17.Future<_i9.Build> cancelBuild(_i9.CancelBuildRequest? request,
           {String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds'}) =>
       (super.noSuchMethod(Invocation.method(#cancelBuild, [request], {#buildBucketUri: buildBucketUri}),
-          returnValue: Future<_i9.Build>.value(_FakeBuild_8())) as _i17.Future<_i9.Build>);
+              returnValue: _i17.Future<_i9.Build>.value(
+                  _FakeBuild_8(this, Invocation.method(#cancelBuild, [request], {#buildBucketUri: buildBucketUri}))))
+          as _i17.Future<_i9.Build>);
   @override
   _i17.Future<_i9.Build> getBuild(_i9.GetBuildRequest? request,
           {String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds'}) =>
       (super.noSuchMethod(Invocation.method(#getBuild, [request], {#buildBucketUri: buildBucketUri}),
-          returnValue: Future<_i9.Build>.value(_FakeBuild_8())) as _i17.Future<_i9.Build>);
+              returnValue: _i17.Future<_i9.Build>.value(
+                  _FakeBuild_8(this, Invocation.method(#getBuild, [request], {#buildBucketUri: buildBucketUri}))))
+          as _i17.Future<_i9.Build>);
   @override
   _i17.Future<_i9.ListBuildersResponse> listBuilders(_i9.ListBuildersRequest? request,
           {String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builders'}) =>
       (super.noSuchMethod(Invocation.method(#listBuilders, [request], {#buildBucketUri: buildBucketUri}),
-              returnValue: Future<_i9.ListBuildersResponse>.value(_FakeListBuildersResponse_11()))
+              returnValue: _i17.Future<_i9.ListBuildersResponse>.value(_FakeListBuildersResponse_11(
+                  this, Invocation.method(#listBuilders, [request], {#buildBucketUri: buildBucketUri}))))
           as _i17.Future<_i9.ListBuildersResponse>);
   @override
   void close() => super.noSuchMethod(Invocation.method(#close, []), returnValueForMissingStub: null);
@@ -416,13 +635,14 @@
   @override
   _i17.Future<_i27.Uint8List> get([_i17.Future<_i27.Uint8List?> Function()? create, Duration? ttl]) =>
       (super.noSuchMethod(Invocation.method(#get, [create, ttl]),
-          returnValue: Future<_i27.Uint8List>.value(_i27.Uint8List(0))) as _i17.Future<_i27.Uint8List>);
+          returnValue: _i17.Future<_i27.Uint8List>.value(_i27.Uint8List(0))) as _i17.Future<_i27.Uint8List>);
   @override
   _i17.Future<void> purge({int? retries = 0}) => (super.noSuchMethod(Invocation.method(#purge, [], {#retries: retries}),
-      returnValue: Future<void>.value(), returnValueForMissingStub: Future<void>.value()) as _i17.Future<void>);
+      returnValue: _i17.Future<void>.value(),
+      returnValueForMissingStub: _i17.Future<void>.value()) as _i17.Future<void>);
   @override
   _i17.Future<_i27.Uint8List?> set(_i27.Uint8List? value, [Duration? ttl]) =>
-      (super.noSuchMethod(Invocation.method(#set, [value, ttl]), returnValue: Future<_i27.Uint8List?>.value())
+      (super.noSuchMethod(Invocation.method(#set, [value, ttl]), returnValue: _i17.Future<_i27.Uint8List?>.value())
           as _i17.Future<_i27.Uint8List?>);
 }
 
@@ -436,7 +656,8 @@
 
   @override
   _i10.GitHub get github =>
-      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12()) as _i10.GitHub);
+      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12(this, Invocation.getter(#github)))
+          as _i10.GitHub);
   @override
   _i17.Stream<_i10.Issue> listAll(
           {int? milestoneNumber,
@@ -456,7 +677,7 @@
             #perPage: perPage,
             #labels: labels
           }),
-          returnValue: Stream<_i10.Issue>.empty()) as _i17.Stream<_i10.Issue>);
+          returnValue: _i17.Stream<_i10.Issue>.empty()) as _i17.Stream<_i10.Issue>);
   @override
   _i17.Stream<_i10.Issue> listByUser(
           {int? milestoneNumber,
@@ -476,7 +697,7 @@
             #perPage: perPage,
             #labels: labels
           }),
-          returnValue: Stream<_i10.Issue>.empty()) as _i17.Stream<_i10.Issue>);
+          returnValue: _i17.Stream<_i10.Issue>.empty()) as _i17.Stream<_i10.Issue>);
   @override
   _i17.Stream<_i10.Issue> listByOrg(String? org,
           {int? milestoneNumber,
@@ -498,7 +719,7 @@
             #perPage: perPage,
             #labels: labels
           }),
-          returnValue: Stream<_i10.Issue>.empty()) as _i17.Stream<_i10.Issue>);
+          returnValue: _i17.Stream<_i10.Issue>.empty()) as _i17.Stream<_i10.Issue>);
   @override
   _i17.Stream<_i10.Issue> listByRepo(_i10.RepositorySlug? slug,
           {int? milestoneNumber,
@@ -520,109 +741,127 @@
             #perPage: perPage,
             #labels: labels
           }),
-          returnValue: Stream<_i10.Issue>.empty()) as _i17.Stream<_i10.Issue>);
+          returnValue: _i17.Stream<_i10.Issue>.empty()) as _i17.Stream<_i10.Issue>);
   @override
   _i17.Stream<_i10.Reaction> listReactions(_i10.RepositorySlug? slug, int? issueNumber, {_i10.ReactionType? content}) =>
       (super.noSuchMethod(Invocation.method(#listReactions, [slug, issueNumber], {#content: content}),
-          returnValue: Stream<_i10.Reaction>.empty()) as _i17.Stream<_i10.Reaction>);
+          returnValue: _i17.Stream<_i10.Reaction>.empty()) as _i17.Stream<_i10.Reaction>);
   @override
   _i17.Future<_i10.Issue> edit(_i10.RepositorySlug? slug, int? issueNumber, _i10.IssueRequest? issue) =>
       (super.noSuchMethod(Invocation.method(#edit, [slug, issueNumber, issue]),
-          returnValue: Future<_i10.Issue>.value(_FakeIssue_13())) as _i17.Future<_i10.Issue>);
+          returnValue: _i17.Future<_i10.Issue>.value(
+              _FakeIssue_13(this, Invocation.method(#edit, [slug, issueNumber, issue])))) as _i17.Future<_i10.Issue>);
   @override
-  _i17.Future<_i10.Issue> get(_i10.RepositorySlug? slug, int? issueNumber) =>
-      (super.noSuchMethod(Invocation.method(#get, [slug, issueNumber]),
-          returnValue: Future<_i10.Issue>.value(_FakeIssue_13())) as _i17.Future<_i10.Issue>);
+  _i17.Future<_i10.Issue> get(_i10.RepositorySlug? slug, int? issueNumber) => (super.noSuchMethod(
+          Invocation.method(#get, [slug, issueNumber]),
+          returnValue: _i17.Future<_i10.Issue>.value(_FakeIssue_13(this, Invocation.method(#get, [slug, issueNumber]))))
+      as _i17.Future<_i10.Issue>);
   @override
-  _i17.Future<_i10.Issue> create(_i10.RepositorySlug? slug, _i10.IssueRequest? issue) =>
-      (super.noSuchMethod(Invocation.method(#create, [slug, issue]),
-          returnValue: Future<_i10.Issue>.value(_FakeIssue_13())) as _i17.Future<_i10.Issue>);
+  _i17.Future<_i10.Issue> create(_i10.RepositorySlug? slug, _i10.IssueRequest? issue) => (super.noSuchMethod(
+          Invocation.method(#create, [slug, issue]),
+          returnValue: _i17.Future<_i10.Issue>.value(_FakeIssue_13(this, Invocation.method(#create, [slug, issue]))))
+      as _i17.Future<_i10.Issue>);
   @override
   _i17.Stream<_i10.User> listAssignees(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listAssignees, [slug]), returnValue: Stream<_i10.User>.empty())
+      (super.noSuchMethod(Invocation.method(#listAssignees, [slug]), returnValue: _i17.Stream<_i10.User>.empty())
           as _i17.Stream<_i10.User>);
   @override
   _i17.Future<bool> isAssignee(_i10.RepositorySlug? slug, String? repoName) =>
-      (super.noSuchMethod(Invocation.method(#isAssignee, [slug, repoName]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#isAssignee, [slug, repoName]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.IssueComment> listCommentsByIssue(_i10.RepositorySlug? slug, int? issueNumber) =>
       (super.noSuchMethod(Invocation.method(#listCommentsByIssue, [slug, issueNumber]),
-          returnValue: Stream<_i10.IssueComment>.empty()) as _i17.Stream<_i10.IssueComment>);
+          returnValue: _i17.Stream<_i10.IssueComment>.empty()) as _i17.Stream<_i10.IssueComment>);
   @override
-  _i17.Stream<_i10.IssueComment> listCommentsByRepo(_i10.RepositorySlug? slug) => (super
-          .noSuchMethod(Invocation.method(#listCommentsByRepo, [slug]), returnValue: Stream<_i10.IssueComment>.empty())
-      as _i17.Stream<_i10.IssueComment>);
+  _i17.Stream<_i10.IssueComment> listCommentsByRepo(_i10.RepositorySlug? slug) =>
+      (super.noSuchMethod(Invocation.method(#listCommentsByRepo, [slug]),
+          returnValue: _i17.Stream<_i10.IssueComment>.empty()) as _i17.Stream<_i10.IssueComment>);
   @override
-  _i17.Future<_i10.IssueComment> getComment(_i10.RepositorySlug? slug, int? id) =>
-      (super.noSuchMethod(Invocation.method(#getComment, [slug, id]),
-          returnValue: Future<_i10.IssueComment>.value(_FakeIssueComment_14())) as _i17.Future<_i10.IssueComment>);
+  _i17.Future<_i10.IssueComment> getComment(_i10.RepositorySlug? slug, int? id) => (super.noSuchMethod(
+      Invocation.method(#getComment, [slug, id]),
+      returnValue: _i17.Future<_i10.IssueComment>.value(
+          _FakeIssueComment_14(this, Invocation.method(#getComment, [slug, id])))) as _i17.Future<_i10.IssueComment>);
   @override
   _i17.Future<_i10.IssueComment> createComment(_i10.RepositorySlug? slug, int? issueNumber, String? body) =>
       (super.noSuchMethod(Invocation.method(#createComment, [slug, issueNumber, body]),
-          returnValue: Future<_i10.IssueComment>.value(_FakeIssueComment_14())) as _i17.Future<_i10.IssueComment>);
+              returnValue: _i17.Future<_i10.IssueComment>.value(
+                  _FakeIssueComment_14(this, Invocation.method(#createComment, [slug, issueNumber, body]))))
+          as _i17.Future<_i10.IssueComment>);
   @override
   _i17.Future<_i10.IssueComment> updateComment(_i10.RepositorySlug? slug, int? id, String? body) =>
       (super.noSuchMethod(Invocation.method(#updateComment, [slug, id, body]),
-          returnValue: Future<_i10.IssueComment>.value(_FakeIssueComment_14())) as _i17.Future<_i10.IssueComment>);
+              returnValue: _i17.Future<_i10.IssueComment>.value(
+                  _FakeIssueComment_14(this, Invocation.method(#updateComment, [slug, id, body]))))
+          as _i17.Future<_i10.IssueComment>);
   @override
   _i17.Future<bool> deleteComment(_i10.RepositorySlug? slug, int? id) =>
-      (super.noSuchMethod(Invocation.method(#deleteComment, [slug, id]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#deleteComment, [slug, id]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.IssueLabel> listLabels(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listLabels, [slug]), returnValue: Stream<_i10.IssueLabel>.empty())
+      (super.noSuchMethod(Invocation.method(#listLabels, [slug]), returnValue: _i17.Stream<_i10.IssueLabel>.empty())
           as _i17.Stream<_i10.IssueLabel>);
   @override
-  _i17.Future<_i10.IssueLabel> getLabel(_i10.RepositorySlug? slug, String? name) =>
-      (super.noSuchMethod(Invocation.method(#getLabel, [slug, name]),
-          returnValue: Future<_i10.IssueLabel>.value(_FakeIssueLabel_15())) as _i17.Future<_i10.IssueLabel>);
+  _i17.Future<_i10.IssueLabel> getLabel(_i10.RepositorySlug? slug, String? name) => (super.noSuchMethod(
+          Invocation.method(#getLabel, [slug, name]),
+          returnValue:
+              _i17.Future<_i10.IssueLabel>.value(_FakeIssueLabel_15(this, Invocation.method(#getLabel, [slug, name]))))
+      as _i17.Future<_i10.IssueLabel>);
   @override
   _i17.Future<_i10.IssueLabel> createLabel(_i10.RepositorySlug? slug, String? name, String? color) =>
       (super.noSuchMethod(Invocation.method(#createLabel, [slug, name, color]),
-          returnValue: Future<_i10.IssueLabel>.value(_FakeIssueLabel_15())) as _i17.Future<_i10.IssueLabel>);
+              returnValue: _i17.Future<_i10.IssueLabel>.value(
+                  _FakeIssueLabel_15(this, Invocation.method(#createLabel, [slug, name, color]))))
+          as _i17.Future<_i10.IssueLabel>);
   @override
   _i17.Future<_i10.IssueLabel> editLabel(_i10.RepositorySlug? slug, String? name, String? color) =>
       (super.noSuchMethod(Invocation.method(#editLabel, [slug, name, color]),
-          returnValue: Future<_i10.IssueLabel>.value(_FakeIssueLabel_15())) as _i17.Future<_i10.IssueLabel>);
+              returnValue: _i17.Future<_i10.IssueLabel>.value(
+                  _FakeIssueLabel_15(this, Invocation.method(#editLabel, [slug, name, color]))))
+          as _i17.Future<_i10.IssueLabel>);
   @override
   _i17.Future<bool> deleteLabel(_i10.RepositorySlug? slug, String? name) =>
-      (super.noSuchMethod(Invocation.method(#deleteLabel, [slug, name]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#deleteLabel, [slug, name]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.IssueLabel> listLabelsByIssue(_i10.RepositorySlug? slug, int? issueNumber) =>
       (super.noSuchMethod(Invocation.method(#listLabelsByIssue, [slug, issueNumber]),
-          returnValue: Stream<_i10.IssueLabel>.empty()) as _i17.Stream<_i10.IssueLabel>);
+          returnValue: _i17.Stream<_i10.IssueLabel>.empty()) as _i17.Stream<_i10.IssueLabel>);
   @override
   _i17.Future<List<_i10.IssueLabel>> addLabelsToIssue(
           _i10.RepositorySlug? slug, int? issueNumber, List<String>? labels) =>
       (super.noSuchMethod(Invocation.method(#addLabelsToIssue, [slug, issueNumber, labels]),
-          returnValue: Future<List<_i10.IssueLabel>>.value(<_i10.IssueLabel>[])) as _i17.Future<List<_i10.IssueLabel>>);
+              returnValue: _i17.Future<List<_i10.IssueLabel>>.value(<_i10.IssueLabel>[]))
+          as _i17.Future<List<_i10.IssueLabel>>);
   @override
   _i17.Future<List<_i10.IssueLabel>> replaceLabelsForIssue(
           _i10.RepositorySlug? slug, int? issueNumber, List<String>? labels) =>
       (super.noSuchMethod(Invocation.method(#replaceLabelsForIssue, [slug, issueNumber, labels]),
-          returnValue: Future<List<_i10.IssueLabel>>.value(<_i10.IssueLabel>[])) as _i17.Future<List<_i10.IssueLabel>>);
+              returnValue: _i17.Future<List<_i10.IssueLabel>>.value(<_i10.IssueLabel>[]))
+          as _i17.Future<List<_i10.IssueLabel>>);
   @override
   _i17.Future<bool> removeLabelForIssue(_i10.RepositorySlug? slug, int? issueNumber, String? label) =>
       (super.noSuchMethod(Invocation.method(#removeLabelForIssue, [slug, issueNumber, label]),
-          returnValue: Future<bool>.value(false)) as _i17.Future<bool>);
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
   @override
   _i17.Future<bool> removeAllLabelsForIssue(_i10.RepositorySlug? slug, int? issueNumber) =>
       (super.noSuchMethod(Invocation.method(#removeAllLabelsForIssue, [slug, issueNumber]),
-          returnValue: Future<bool>.value(false)) as _i17.Future<bool>);
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.Milestone> listMilestones(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listMilestones, [slug]), returnValue: Stream<_i10.Milestone>.empty())
+      (super.noSuchMethod(Invocation.method(#listMilestones, [slug]), returnValue: _i17.Stream<_i10.Milestone>.empty())
           as _i17.Stream<_i10.Milestone>);
   @override
   _i17.Future<_i10.Milestone> createMilestone(_i10.RepositorySlug? slug, _i10.CreateMilestone? request) =>
       (super.noSuchMethod(Invocation.method(#createMilestone, [slug, request]),
-          returnValue: Future<_i10.Milestone>.value(_FakeMilestone_16())) as _i17.Future<_i10.Milestone>);
+              returnValue: _i17.Future<_i10.Milestone>.value(
+                  _FakeMilestone_16(this, Invocation.method(#createMilestone, [slug, request]))))
+          as _i17.Future<_i10.Milestone>);
   @override
   _i17.Future<bool> deleteMilestone(_i10.RepositorySlug? slug, int? number) =>
-      (super.noSuchMethod(Invocation.method(#deleteMilestone, [slug, number]), returnValue: Future<bool>.value(false))
-          as _i17.Future<bool>);
+      (super.noSuchMethod(Invocation.method(#deleteMilestone, [slug, number]),
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
 }
 
 /// A class which mocks [GithubChecksService].
@@ -634,14 +873,15 @@
   }
 
   @override
-  _i3.Config get config => (super.noSuchMethod(Invocation.getter(#config), returnValue: _FakeConfig_1()) as _i3.Config);
+  _i3.Config get config =>
+      (super.noSuchMethod(Invocation.getter(#config), returnValue: _FakeConfig_1(this, Invocation.getter(#config)))
+          as _i3.Config);
   @override
   set config(_i3.Config? _config) =>
       super.noSuchMethod(Invocation.setter(#config, _config), returnValueForMissingStub: null);
   @override
-  _i11.GithubChecksUtil get githubChecksUtil =>
-      (super.noSuchMethod(Invocation.getter(#githubChecksUtil), returnValue: _FakeGithubChecksUtil_17())
-          as _i11.GithubChecksUtil);
+  _i11.GithubChecksUtil get githubChecksUtil => (super.noSuchMethod(Invocation.getter(#githubChecksUtil),
+      returnValue: _FakeGithubChecksUtil_17(this, Invocation.getter(#githubChecksUtil))) as _i11.GithubChecksUtil);
   @override
   set githubChecksUtil(_i11.GithubChecksUtil? _githubChecksUtil) =>
       super.noSuchMethod(Invocation.setter(#githubChecksUtil, _githubChecksUtil), returnValueForMissingStub: null);
@@ -649,23 +889,25 @@
   _i17.Future<void> handleCheckSuite(
           _i10.PullRequest? pullRequest, _i25.CheckSuiteEvent? checkSuiteEvent, _i29.Scheduler? scheduler) =>
       (super.noSuchMethod(Invocation.method(#handleCheckSuite, [pullRequest, checkSuiteEvent, scheduler]),
-          returnValue: Future<void>.value(), returnValueForMissingStub: Future<void>.value()) as _i17.Future<void>);
+          returnValue: _i17.Future<void>.value(),
+          returnValueForMissingStub: _i17.Future<void>.value()) as _i17.Future<void>);
   @override
   _i17.Future<bool> updateCheckStatus(_i30.BuildPushMessage? buildPushMessage, _i31.LuciBuildService? luciBuildService,
           _i10.RepositorySlug? slug) =>
       (super.noSuchMethod(Invocation.method(#updateCheckStatus, [buildPushMessage, luciBuildService, slug]),
-          returnValue: Future<bool>.value(false)) as _i17.Future<bool>);
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
   @override
   String getGithubSummary(String? summary) =>
       (super.noSuchMethod(Invocation.method(#getGithubSummary, [summary]), returnValue: '') as String);
   @override
   _i10.CheckRunConclusion conclusionForResult(_i30.Result? result) =>
-      (super.noSuchMethod(Invocation.method(#conclusionForResult, [result]), returnValue: _FakeCheckRunConclusion_18())
+      (super.noSuchMethod(Invocation.method(#conclusionForResult, [result]),
+              returnValue: _FakeCheckRunConclusion_18(this, Invocation.method(#conclusionForResult, [result])))
           as _i10.CheckRunConclusion);
   @override
-  _i10.CheckRunStatus statusForResult(_i30.Status? status) =>
-      (super.noSuchMethod(Invocation.method(#statusForResult, [status]), returnValue: _FakeCheckRunStatus_19())
-          as _i10.CheckRunStatus);
+  _i10.CheckRunStatus statusForResult(_i30.Status? status) => (super.noSuchMethod(
+      Invocation.method(#statusForResult, [status]),
+      returnValue: _FakeCheckRunStatus_19(this, Invocation.method(#statusForResult, [status]))) as _i10.CheckRunStatus);
 }
 
 /// A class which mocks [GithubChecksUtil].
@@ -680,12 +922,14 @@
   _i17.Future<Map<String, _i10.CheckRun>> allCheckRuns(
           _i10.GitHub? gitHubClient, _i25.CheckSuiteEvent? checkSuiteEvent) =>
       (super.noSuchMethod(Invocation.method(#allCheckRuns, [gitHubClient, checkSuiteEvent]),
-              returnValue: Future<Map<String, _i10.CheckRun>>.value(<String, _i10.CheckRun>{}))
+              returnValue: _i17.Future<Map<String, _i10.CheckRun>>.value(<String, _i10.CheckRun>{}))
           as _i17.Future<Map<String, _i10.CheckRun>>);
   @override
   _i17.Future<_i10.CheckSuite> getCheckSuite(_i10.GitHub? gitHubClient, _i10.RepositorySlug? slug, int? checkSuiteId) =>
       (super.noSuchMethod(Invocation.method(#getCheckSuite, [gitHubClient, slug, checkSuiteId]),
-          returnValue: Future<_i10.CheckSuite>.value(_FakeCheckSuite_20())) as _i17.Future<_i10.CheckSuite>);
+              returnValue: _i17.Future<_i10.CheckSuite>.value(
+                  _FakeCheckSuite_20(this, Invocation.method(#getCheckSuite, [gitHubClient, slug, checkSuiteId]))))
+          as _i17.Future<_i10.CheckSuite>);
   @override
   _i17.Future<void> updateCheckRun(_i3.Config? cocoonConfig, _i10.RepositorySlug? slug, _i10.CheckRun? checkRun,
           {_i10.CheckRunStatus? status = _i10.CheckRunStatus.queued,
@@ -695,18 +939,22 @@
       (super.noSuchMethod(
           Invocation.method(#updateCheckRun, [cocoonConfig, slug, checkRun],
               {#status: status, #conclusion: conclusion, #detailsUrl: detailsUrl, #output: output}),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i17.Future<void>);
+          returnValue: _i17.Future<void>.value(),
+          returnValueForMissingStub: _i17.Future<void>.value()) as _i17.Future<void>);
   @override
   _i17.Future<_i10.CheckRun> getCheckRun(_i3.Config? cocoonConfig, _i10.RepositorySlug? slug, int? id) =>
       (super.noSuchMethod(Invocation.method(#getCheckRun, [cocoonConfig, slug, id]),
-          returnValue: Future<_i10.CheckRun>.value(_FakeCheckRun_21())) as _i17.Future<_i10.CheckRun>);
+              returnValue: _i17.Future<_i10.CheckRun>.value(
+                  _FakeCheckRun_21(this, Invocation.method(#getCheckRun, [cocoonConfig, slug, id]))))
+          as _i17.Future<_i10.CheckRun>);
   @override
   _i17.Future<_i10.CheckRun> createCheckRun(
           _i3.Config? cocoonConfig, _i10.RepositorySlug? slug, String? sha, String? name,
           {_i10.CheckRunOutput? output}) =>
       (super.noSuchMethod(Invocation.method(#createCheckRun, [cocoonConfig, slug, sha, name], {#output: output}),
-          returnValue: Future<_i10.CheckRun>.value(_FakeCheckRun_21())) as _i17.Future<_i10.CheckRun>);
+              returnValue: _i17.Future<_i10.CheckRun>.value(_FakeCheckRun_21(
+                  this, Invocation.method(#createCheckRun, [cocoonConfig, slug, sha, name], {#output: output}))))
+          as _i17.Future<_i10.CheckRun>);
 }
 
 /// A class which mocks [GithubService].
@@ -719,17 +967,18 @@
 
   @override
   _i10.GitHub get github =>
-      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12()) as _i10.GitHub);
+      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12(this, Invocation.getter(#github)))
+          as _i10.GitHub);
   @override
   _i17.Future<List<_i10.RepositoryCommit>> listCommits(
           _i10.RepositorySlug? slug, String? branch, int? lastCommitTimestampMills) =>
       (super.noSuchMethod(Invocation.method(#listCommits, [slug, branch, lastCommitTimestampMills]),
-              returnValue: Future<List<_i10.RepositoryCommit>>.value(<_i10.RepositoryCommit>[]))
+              returnValue: _i17.Future<List<_i10.RepositoryCommit>>.value(<_i10.RepositoryCommit>[]))
           as _i17.Future<List<_i10.RepositoryCommit>>);
   @override
   _i17.Future<List<_i10.PullRequest>> listPullRequests(_i10.RepositorySlug? slug, String? branch) =>
       (super.noSuchMethod(Invocation.method(#listPullRequests, [slug, branch]),
-              returnValue: Future<List<_i10.PullRequest>>.value(<_i10.PullRequest>[]))
+              returnValue: _i17.Future<List<_i10.PullRequest>>.value(<_i10.PullRequest>[]))
           as _i17.Future<List<_i10.PullRequest>>);
   @override
   _i17.Future<_i10.PullRequest> createPullRequest(_i10.RepositorySlug? slug,
@@ -738,21 +987,29 @@
           String? commitMessage,
           _i10.GitReference? baseRef,
           List<_i10.CreateGitTreeEntry>? entries}) =>
-      (super.noSuchMethod(
-          Invocation.method(#createPullRequest, [slug],
-              {#title: title, #body: body, #commitMessage: commitMessage, #baseRef: baseRef, #entries: entries}),
-          returnValue: Future<_i10.PullRequest>.value(_FakePullRequest_22())) as _i17.Future<_i10.PullRequest>);
+      (super.noSuchMethod(Invocation.method(#createPullRequest, [slug], {#title: title, #body: body, #commitMessage: commitMessage, #baseRef: baseRef, #entries: entries}),
+          returnValue: _i17.Future<_i10.PullRequest>.value(_FakePullRequest_22(
+              this,
+              Invocation.method(#createPullRequest, [
+                slug
+              ], {
+                #title: title,
+                #body: body,
+                #commitMessage: commitMessage,
+                #baseRef: baseRef,
+                #entries: entries
+              })))) as _i17.Future<_i10.PullRequest>);
   @override
   _i17.Future<void> assignReviewer(_i10.RepositorySlug? slug, {int? pullRequestNumber, String? reviewer}) =>
       (super.noSuchMethod(
           Invocation.method(#assignReviewer, [slug], {#pullRequestNumber: pullRequestNumber, #reviewer: reviewer}),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i17.Future<void>);
+          returnValue: _i17.Future<void>.value(),
+          returnValueForMissingStub: _i17.Future<void>.value()) as _i17.Future<void>);
   @override
   _i17.Future<List<_i10.Issue>> listIssues(_i10.RepositorySlug? slug,
           {List<String>? labels, String? state = r'open'}) =>
       (super.noSuchMethod(Invocation.method(#listIssues, [slug], {#labels: labels, #state: state}),
-          returnValue: Future<List<_i10.Issue>>.value(<_i10.Issue>[])) as _i17.Future<List<_i10.Issue>>);
+          returnValue: _i17.Future<List<_i10.Issue>>.value(<_i10.Issue>[])) as _i17.Future<List<_i10.Issue>>);
   @override
   _i17.Future<_i10.Issue>? getIssue(_i10.RepositorySlug? slug, {int? issueNumber}) =>
       (super.noSuchMethod(Invocation.method(#getIssue, [slug], {#issueNumber: issueNumber}))
@@ -760,38 +1017,47 @@
   @override
   _i17.Future<void> assignIssue(_i10.RepositorySlug? slug, {int? issueNumber, String? assignee}) =>
       (super.noSuchMethod(Invocation.method(#assignIssue, [slug], {#issueNumber: issueNumber, #assignee: assignee}),
-          returnValue: Future<void>.value(), returnValueForMissingStub: Future<void>.value()) as _i17.Future<void>);
+          returnValue: _i17.Future<void>.value(),
+          returnValueForMissingStub: _i17.Future<void>.value()) as _i17.Future<void>);
   @override
   _i17.Future<_i10.Issue> createIssue(_i10.RepositorySlug? slug,
           {String? title, String? body, List<String>? labels, String? assignee}) =>
       (super.noSuchMethod(
           Invocation.method(#createIssue, [slug], {#title: title, #body: body, #labels: labels, #assignee: assignee}),
-          returnValue: Future<_i10.Issue>.value(_FakeIssue_13())) as _i17.Future<_i10.Issue>);
+          returnValue: _i17.Future<_i10.Issue>.value(_FakeIssue_13(
+              this,
+              Invocation.method(
+                  #createIssue, [slug], {#title: title, #body: body, #labels: labels, #assignee: assignee})))) as _i17
+          .Future<_i10.Issue>);
   @override
   _i17.Future<_i10.IssueComment?> createComment(_i10.RepositorySlug? slug, {int? issueNumber, String? body}) =>
       (super.noSuchMethod(Invocation.method(#createComment, [slug], {#issueNumber: issueNumber, #body: body}),
-          returnValue: Future<_i10.IssueComment?>.value()) as _i17.Future<_i10.IssueComment?>);
+          returnValue: _i17.Future<_i10.IssueComment?>.value()) as _i17.Future<_i10.IssueComment?>);
   @override
   _i17.Future<List<_i10.IssueLabel>> replaceLabelsForIssue(_i10.RepositorySlug? slug,
           {int? issueNumber, List<String>? labels}) =>
       (super.noSuchMethod(
-          Invocation.method(#replaceLabelsForIssue, [slug], {#issueNumber: issueNumber, #labels: labels}),
-          returnValue: Future<List<_i10.IssueLabel>>.value(<_i10.IssueLabel>[])) as _i17.Future<List<_i10.IssueLabel>>);
+              Invocation.method(#replaceLabelsForIssue, [slug], {#issueNumber: issueNumber, #labels: labels}),
+              returnValue: _i17.Future<List<_i10.IssueLabel>>.value(<_i10.IssueLabel>[]))
+          as _i17.Future<List<_i10.IssueLabel>>);
   @override
   _i17.Future<List<String>> listFiles(_i10.PullRequest? pullRequest) =>
       (super.noSuchMethod(Invocation.method(#listFiles, [pullRequest]),
-          returnValue: Future<List<String>>.value(<String>[])) as _i17.Future<List<String>>);
+          returnValue: _i17.Future<List<String>>.value(<String>[])) as _i17.Future<List<String>>);
   @override
   _i17.Future<String> getFileContent(_i10.RepositorySlug? slug, String? path) =>
-      (super.noSuchMethod(Invocation.method(#getFileContent, [slug, path]), returnValue: Future<String>.value(''))
+      (super.noSuchMethod(Invocation.method(#getFileContent, [slug, path]), returnValue: _i17.Future<String>.value(''))
           as _i17.Future<String>);
   @override
   _i17.Future<_i10.GitReference> getReference(_i10.RepositorySlug? slug, String? ref) =>
       (super.noSuchMethod(Invocation.method(#getReference, [slug, ref]),
-          returnValue: Future<_i10.GitReference>.value(_FakeGitReference_23())) as _i17.Future<_i10.GitReference>);
+              returnValue: _i17.Future<_i10.GitReference>.value(
+                  _FakeGitReference_23(this, Invocation.method(#getReference, [slug, ref]))))
+          as _i17.Future<_i10.GitReference>);
   @override
   _i17.Future<_i10.RateLimit> getRateLimit() => (super.noSuchMethod(Invocation.method(#getRateLimit, []),
-      returnValue: Future<_i10.RateLimit>.value(_FakeRateLimit_24())) as _i17.Future<_i10.RateLimit>);
+          returnValue: _i17.Future<_i10.RateLimit>.value(_FakeRateLimit_24(this, Invocation.method(#getRateLimit, []))))
+      as _i17.Future<_i10.RateLimit>);
 }
 
 /// A class which mocks [GitService].
@@ -804,60 +1070,80 @@
 
   @override
   _i10.GitHub get github =>
-      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12()) as _i10.GitHub);
+      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12(this, Invocation.getter(#github)))
+          as _i10.GitHub);
   @override
-  _i17.Future<_i10.GitBlob> getBlob(_i10.RepositorySlug? slug, String? sha) =>
-      (super.noSuchMethod(Invocation.method(#getBlob, [slug, sha]),
-          returnValue: Future<_i10.GitBlob>.value(_FakeGitBlob_25())) as _i17.Future<_i10.GitBlob>);
+  _i17.Future<_i10.GitBlob> getBlob(_i10.RepositorySlug? slug, String? sha) => (super.noSuchMethod(
+          Invocation.method(#getBlob, [slug, sha]),
+          returnValue: _i17.Future<_i10.GitBlob>.value(_FakeGitBlob_25(this, Invocation.method(#getBlob, [slug, sha]))))
+      as _i17.Future<_i10.GitBlob>);
   @override
   _i17.Future<_i10.GitBlob> createBlob(_i10.RepositorySlug? slug, _i10.CreateGitBlob? blob) =>
       (super.noSuchMethod(Invocation.method(#createBlob, [slug, blob]),
-          returnValue: Future<_i10.GitBlob>.value(_FakeGitBlob_25())) as _i17.Future<_i10.GitBlob>);
+              returnValue:
+                  _i17.Future<_i10.GitBlob>.value(_FakeGitBlob_25(this, Invocation.method(#createBlob, [slug, blob]))))
+          as _i17.Future<_i10.GitBlob>);
   @override
-  _i17.Future<_i10.GitCommit> getCommit(_i10.RepositorySlug? slug, String? sha) =>
-      (super.noSuchMethod(Invocation.method(#getCommit, [slug, sha]),
-          returnValue: Future<_i10.GitCommit>.value(_FakeGitCommit_26())) as _i17.Future<_i10.GitCommit>);
+  _i17.Future<_i10.GitCommit> getCommit(_i10.RepositorySlug? slug, String? sha) => (super.noSuchMethod(
+          Invocation.method(#getCommit, [slug, sha]),
+          returnValue:
+              _i17.Future<_i10.GitCommit>.value(_FakeGitCommit_26(this, Invocation.method(#getCommit, [slug, sha]))))
+      as _i17.Future<_i10.GitCommit>);
   @override
   _i17.Future<_i10.GitCommit> createCommit(_i10.RepositorySlug? slug, _i10.CreateGitCommit? commit) =>
       (super.noSuchMethod(Invocation.method(#createCommit, [slug, commit]),
-          returnValue: Future<_i10.GitCommit>.value(_FakeGitCommit_26())) as _i17.Future<_i10.GitCommit>);
+              returnValue: _i17.Future<_i10.GitCommit>.value(
+                  _FakeGitCommit_26(this, Invocation.method(#createCommit, [slug, commit]))))
+          as _i17.Future<_i10.GitCommit>);
   @override
   _i17.Future<_i10.GitReference> getReference(_i10.RepositorySlug? slug, String? ref) =>
       (super.noSuchMethod(Invocation.method(#getReference, [slug, ref]),
-          returnValue: Future<_i10.GitReference>.value(_FakeGitReference_23())) as _i17.Future<_i10.GitReference>);
+              returnValue: _i17.Future<_i10.GitReference>.value(
+                  _FakeGitReference_23(this, Invocation.method(#getReference, [slug, ref]))))
+          as _i17.Future<_i10.GitReference>);
   @override
   _i17.Stream<_i10.GitReference> listReferences(_i10.RepositorySlug? slug, {String? type}) =>
       (super.noSuchMethod(Invocation.method(#listReferences, [slug], {#type: type}),
-          returnValue: Stream<_i10.GitReference>.empty()) as _i17.Stream<_i10.GitReference>);
+          returnValue: _i17.Stream<_i10.GitReference>.empty()) as _i17.Stream<_i10.GitReference>);
   @override
   _i17.Future<_i10.GitReference> createReference(_i10.RepositorySlug? slug, String? ref, String? sha) =>
       (super.noSuchMethod(Invocation.method(#createReference, [slug, ref, sha]),
-          returnValue: Future<_i10.GitReference>.value(_FakeGitReference_23())) as _i17.Future<_i10.GitReference>);
+              returnValue: _i17.Future<_i10.GitReference>.value(
+                  _FakeGitReference_23(this, Invocation.method(#createReference, [slug, ref, sha]))))
+          as _i17.Future<_i10.GitReference>);
   @override
   _i17.Future<_i10.GitReference> editReference(_i10.RepositorySlug? slug, String? ref, String? sha,
           {bool? force = false}) =>
       (super.noSuchMethod(Invocation.method(#editReference, [slug, ref, sha], {#force: force}),
-          returnValue: Future<_i10.GitReference>.value(_FakeGitReference_23())) as _i17.Future<_i10.GitReference>);
+              returnValue: _i17.Future<_i10.GitReference>.value(
+                  _FakeGitReference_23(this, Invocation.method(#editReference, [slug, ref, sha], {#force: force}))))
+          as _i17.Future<_i10.GitReference>);
   @override
   _i17.Future<bool> deleteReference(_i10.RepositorySlug? slug, String? ref) =>
-      (super.noSuchMethod(Invocation.method(#deleteReference, [slug, ref]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#deleteReference, [slug, ref]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
-  _i17.Future<_i10.GitTag> getTag(_i10.RepositorySlug? slug, String? sha) =>
-      (super.noSuchMethod(Invocation.method(#getTag, [slug, sha]),
-          returnValue: Future<_i10.GitTag>.value(_FakeGitTag_27())) as _i17.Future<_i10.GitTag>);
+  _i17.Future<_i10.GitTag> getTag(_i10.RepositorySlug? slug, String? sha) => (super.noSuchMethod(
+          Invocation.method(#getTag, [slug, sha]),
+          returnValue: _i17.Future<_i10.GitTag>.value(_FakeGitTag_27(this, Invocation.method(#getTag, [slug, sha]))))
+      as _i17.Future<_i10.GitTag>);
   @override
-  _i17.Future<_i10.GitTag> createTag(_i10.RepositorySlug? slug, _i10.CreateGitTag? tag) =>
-      (super.noSuchMethod(Invocation.method(#createTag, [slug, tag]),
-          returnValue: Future<_i10.GitTag>.value(_FakeGitTag_27())) as _i17.Future<_i10.GitTag>);
+  _i17.Future<_i10.GitTag> createTag(_i10.RepositorySlug? slug, _i10.CreateGitTag? tag) => (super.noSuchMethod(
+          Invocation.method(#createTag, [slug, tag]),
+          returnValue: _i17.Future<_i10.GitTag>.value(_FakeGitTag_27(this, Invocation.method(#createTag, [slug, tag]))))
+      as _i17.Future<_i10.GitTag>);
   @override
   _i17.Future<_i10.GitTree> getTree(_i10.RepositorySlug? slug, String? sha, {bool? recursive = false}) =>
       (super.noSuchMethod(Invocation.method(#getTree, [slug, sha], {#recursive: recursive}),
-          returnValue: Future<_i10.GitTree>.value(_FakeGitTree_28())) as _i17.Future<_i10.GitTree>);
+              returnValue: _i17.Future<_i10.GitTree>.value(
+                  _FakeGitTree_28(this, Invocation.method(#getTree, [slug, sha], {#recursive: recursive}))))
+          as _i17.Future<_i10.GitTree>);
   @override
   _i17.Future<_i10.GitTree> createTree(_i10.RepositorySlug? slug, _i10.CreateGitTree? tree) =>
       (super.noSuchMethod(Invocation.method(#createTree, [slug, tree]),
-          returnValue: Future<_i10.GitTree>.value(_FakeGitTree_28())) as _i17.Future<_i10.GitTree>);
+              returnValue:
+                  _i17.Future<_i10.GitTree>.value(_FakeGitTree_28(this, Invocation.method(#createTree, [slug, tree]))))
+          as _i17.Future<_i10.GitTree>);
 }
 
 /// A class which mocks [GraphQLClient].
@@ -869,20 +1155,22 @@
   }
 
   @override
-  _i12.DefaultPolicies get defaultPolicies =>
-      (super.noSuchMethod(Invocation.getter(#defaultPolicies), returnValue: _FakeDefaultPolicies_29())
-          as _i12.DefaultPolicies);
+  _i12.DefaultPolicies get defaultPolicies => (super.noSuchMethod(Invocation.getter(#defaultPolicies),
+      returnValue: _FakeDefaultPolicies_29(this, Invocation.getter(#defaultPolicies))) as _i12.DefaultPolicies);
   @override
   set defaultPolicies(_i12.DefaultPolicies? _defaultPolicies) =>
       super.noSuchMethod(Invocation.setter(#defaultPolicies, _defaultPolicies), returnValueForMissingStub: null);
   @override
-  _i12.Link get link => (super.noSuchMethod(Invocation.getter(#link), returnValue: _FakeLink_30()) as _i12.Link);
+  _i12.Link get link =>
+      (super.noSuchMethod(Invocation.getter(#link), returnValue: _FakeLink_30(this, Invocation.getter(#link)))
+          as _i12.Link);
   @override
   _i13.GraphQLCache get cache =>
-      (super.noSuchMethod(Invocation.getter(#cache), returnValue: _FakeGraphQLCache_31()) as _i13.GraphQLCache);
+      (super.noSuchMethod(Invocation.getter(#cache), returnValue: _FakeGraphQLCache_31(this, Invocation.getter(#cache)))
+          as _i13.GraphQLCache);
   @override
-  _i12.QueryManager get queryManager =>
-      (super.noSuchMethod(Invocation.getter(#queryManager), returnValue: _FakeQueryManager_32()) as _i12.QueryManager);
+  _i12.QueryManager get queryManager => (super.noSuchMethod(Invocation.getter(#queryManager),
+      returnValue: _FakeQueryManager_32(this, Invocation.getter(#queryManager))) as _i12.QueryManager);
   @override
   set queryManager(_i12.QueryManager? _queryManager) =>
       super.noSuchMethod(Invocation.setter(#queryManager, _queryManager), returnValueForMissingStub: null);
@@ -895,36 +1183,48 @@
       (super.noSuchMethod(
           Invocation.method(#copyWith, [],
               {#link: link, #cache: cache, #defaultPolicies: defaultPolicies, #alwaysRebroadcast: alwaysRebroadcast}),
-          returnValue: _FakeGraphQLClient_33()) as _i14.GraphQLClient);
+          returnValue: _FakeGraphQLClient_33(
+              this,
+              Invocation.method(#copyWith, [], {
+                #link: link,
+                #cache: cache,
+                #defaultPolicies: defaultPolicies,
+                #alwaysRebroadcast: alwaysRebroadcast
+              }))) as _i14.GraphQLClient);
   @override
   _i12.ObservableQuery<TParsed> watchQuery<TParsed>(_i12.WatchQueryOptions<TParsed>? options) =>
-      (super.noSuchMethod(Invocation.method(#watchQuery, [options]), returnValue: _FakeObservableQuery_34<TParsed>())
+      (super.noSuchMethod(Invocation.method(#watchQuery, [options]),
+              returnValue: _FakeObservableQuery_34<TParsed>(this, Invocation.method(#watchQuery, [options])))
           as _i12.ObservableQuery<TParsed>);
   @override
   _i12.ObservableQuery<TParsed> watchMutation<TParsed>(_i12.WatchQueryOptions<TParsed>? options) =>
-      (super.noSuchMethod(Invocation.method(#watchMutation, [options]), returnValue: _FakeObservableQuery_34<TParsed>())
+      (super.noSuchMethod(Invocation.method(#watchMutation, [options]),
+              returnValue: _FakeObservableQuery_34<TParsed>(this, Invocation.method(#watchMutation, [options])))
           as _i12.ObservableQuery<TParsed>);
   @override
   _i17.Future<_i12.QueryResult<TParsed>> query<TParsed>(_i12.QueryOptions<TParsed>? options) =>
       (super.noSuchMethod(Invocation.method(#query, [options]),
-              returnValue: Future<_i12.QueryResult<TParsed>>.value(_FakeQueryResult_35<TParsed>()))
+              returnValue: _i17.Future<_i12.QueryResult<TParsed>>.value(
+                  _FakeQueryResult_35<TParsed>(this, Invocation.method(#query, [options]))))
           as _i17.Future<_i12.QueryResult<TParsed>>);
   @override
   _i17.Future<_i12.QueryResult<TParsed>> mutate<TParsed>(_i12.MutationOptions<TParsed>? options) =>
       (super.noSuchMethod(Invocation.method(#mutate, [options]),
-              returnValue: Future<_i12.QueryResult<TParsed>>.value(_FakeQueryResult_35<TParsed>()))
+              returnValue: _i17.Future<_i12.QueryResult<TParsed>>.value(
+                  _FakeQueryResult_35<TParsed>(this, Invocation.method(#mutate, [options]))))
           as _i17.Future<_i12.QueryResult<TParsed>>);
   @override
   _i17.Stream<_i12.QueryResult<TParsed>> subscribe<TParsed>(_i12.SubscriptionOptions<TParsed>? options) =>
       (super.noSuchMethod(Invocation.method(#subscribe, [options]),
-          returnValue: Stream<_i12.QueryResult<TParsed>>.empty()) as _i17.Stream<_i12.QueryResult<TParsed>>);
+          returnValue: _i17.Stream<_i12.QueryResult<TParsed>>.empty()) as _i17.Stream<_i12.QueryResult<TParsed>>);
   @override
   _i17.Future<_i12.QueryResult<TParsed>> fetchMore<TParsed>(_i12.FetchMoreOptions? fetchMoreOptions,
           {_i12.QueryOptions<TParsed>? originalOptions, _i12.QueryResult<TParsed>? previousResult}) =>
       (super.noSuchMethod(
               Invocation.method(
                   #fetchMore, [fetchMoreOptions], {#originalOptions: originalOptions, #previousResult: previousResult}),
-              returnValue: Future<_i12.QueryResult<TParsed>>.value(_FakeQueryResult_35<TParsed>()))
+              returnValue: _i17.Future<_i12.QueryResult<TParsed>>.value(_FakeQueryResult_35<TParsed>(
+                  this, Invocation.method(#fetchMore, [fetchMoreOptions], {#originalOptions: originalOptions, #previousResult: previousResult}))))
           as _i17.Future<_i12.QueryResult<TParsed>>);
   @override
   Map<String, dynamic>? readQuery(_i12.Request? request, {bool? optimistic = true}) =>
@@ -957,8 +1257,8 @@
   }
 
   @override
-  Duration get idleTimeout =>
-      (super.noSuchMethod(Invocation.getter(#idleTimeout), returnValue: _FakeDuration_36()) as Duration);
+  Duration get idleTimeout => (super.noSuchMethod(Invocation.getter(#idleTimeout),
+      returnValue: _FakeDuration_36(this, Invocation.getter(#idleTimeout))) as Duration);
   @override
   set idleTimeout(Duration? _idleTimeout) =>
       super.noSuchMethod(Invocation.setter(#idleTimeout, _idleTimeout), returnValueForMissingStub: null);
@@ -997,67 +1297,77 @@
   @override
   _i17.Future<_i15.HttpClientRequest> open(String? method, String? host, int? port, String? path) =>
       (super.noSuchMethod(Invocation.method(#open, [method, host, port, path]),
-              returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
+              returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+                  _FakeHttpClientRequest_37(this, Invocation.method(#open, [method, host, port, path]))))
           as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> openUrl(String? method, Uri? url) =>
       (super.noSuchMethod(Invocation.method(#openUrl, [method, url]),
-              returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
+              returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+                  _FakeHttpClientRequest_37(this, Invocation.method(#openUrl, [method, url]))))
           as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> get(String? host, int? port, String? path) =>
       (super.noSuchMethod(Invocation.method(#get, [host, port, path]),
-              returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
+              returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+                  _FakeHttpClientRequest_37(this, Invocation.method(#get, [host, port, path]))))
           as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> getUrl(Uri? url) => (super.noSuchMethod(Invocation.method(#getUrl, [url]),
-          returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
-      as _i17.Future<_i15.HttpClientRequest>);
+      returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+          _FakeHttpClientRequest_37(this, Invocation.method(#getUrl, [url])))) as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> post(String? host, int? port, String? path) =>
       (super.noSuchMethod(Invocation.method(#post, [host, port, path]),
-              returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
+              returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+                  _FakeHttpClientRequest_37(this, Invocation.method(#post, [host, port, path]))))
           as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> postUrl(Uri? url) => (super.noSuchMethod(Invocation.method(#postUrl, [url]),
-          returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
-      as _i17.Future<_i15.HttpClientRequest>);
+      returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+          _FakeHttpClientRequest_37(this, Invocation.method(#postUrl, [url])))) as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> put(String? host, int? port, String? path) =>
       (super.noSuchMethod(Invocation.method(#put, [host, port, path]),
-              returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
+              returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+                  _FakeHttpClientRequest_37(this, Invocation.method(#put, [host, port, path]))))
           as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> putUrl(Uri? url) => (super.noSuchMethod(Invocation.method(#putUrl, [url]),
-          returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
-      as _i17.Future<_i15.HttpClientRequest>);
+      returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+          _FakeHttpClientRequest_37(this, Invocation.method(#putUrl, [url])))) as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> delete(String? host, int? port, String? path) =>
       (super.noSuchMethod(Invocation.method(#delete, [host, port, path]),
-              returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
+              returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+                  _FakeHttpClientRequest_37(this, Invocation.method(#delete, [host, port, path]))))
           as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> deleteUrl(Uri? url) => (super.noSuchMethod(Invocation.method(#deleteUrl, [url]),
-          returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
+          returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+              _FakeHttpClientRequest_37(this, Invocation.method(#deleteUrl, [url]))))
       as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> patch(String? host, int? port, String? path) =>
       (super.noSuchMethod(Invocation.method(#patch, [host, port, path]),
-              returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
+              returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+                  _FakeHttpClientRequest_37(this, Invocation.method(#patch, [host, port, path]))))
           as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> patchUrl(Uri? url) => (super.noSuchMethod(Invocation.method(#patchUrl, [url]),
-          returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
+          returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+              _FakeHttpClientRequest_37(this, Invocation.method(#patchUrl, [url]))))
       as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> head(String? host, int? port, String? path) =>
       (super.noSuchMethod(Invocation.method(#head, [host, port, path]),
-              returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
+              returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+                  _FakeHttpClientRequest_37(this, Invocation.method(#head, [host, port, path]))))
           as _i17.Future<_i15.HttpClientRequest>);
   @override
   _i17.Future<_i15.HttpClientRequest> headUrl(Uri? url) => (super.noSuchMethod(Invocation.method(#headUrl, [url]),
-          returnValue: Future<_i15.HttpClientRequest>.value(_FakeHttpClientRequest_37()))
-      as _i17.Future<_i15.HttpClientRequest>);
+      returnValue: _i17.Future<_i15.HttpClientRequest>.value(
+          _FakeHttpClientRequest_37(this, Invocation.method(#headUrl, [url])))) as _i17.Future<_i15.HttpClientRequest>);
   @override
   void addCredentials(Uri? url, String? realm, _i15.HttpClientCredentials? credentials) => super
       .noSuchMethod(Invocation.method(#addCredentials, [url, realm, credentials]), returnValueForMissingStub: null);
@@ -1107,27 +1417,29 @@
   @override
   String get method => (super.noSuchMethod(Invocation.getter(#method), returnValue: '') as String);
   @override
-  Uri get uri => (super.noSuchMethod(Invocation.getter(#uri), returnValue: _FakeUri_38()) as Uri);
+  Uri get uri =>
+      (super.noSuchMethod(Invocation.getter(#uri), returnValue: _FakeUri_38(this, Invocation.getter(#uri))) as Uri);
   @override
-  _i15.HttpHeaders get headers =>
-      (super.noSuchMethod(Invocation.getter(#headers), returnValue: _FakeHttpHeaders_39()) as _i15.HttpHeaders);
+  _i15.HttpHeaders get headers => (super.noSuchMethod(Invocation.getter(#headers),
+      returnValue: _FakeHttpHeaders_39(this, Invocation.getter(#headers))) as _i15.HttpHeaders);
   @override
   List<_i15.Cookie> get cookies =>
       (super.noSuchMethod(Invocation.getter(#cookies), returnValue: <_i15.Cookie>[]) as List<_i15.Cookie>);
   @override
   _i17.Future<_i15.HttpClientResponse> get done => (super.noSuchMethod(Invocation.getter(#done),
-          returnValue: Future<_i15.HttpClientResponse>.value(_FakeHttpClientResponse_40()))
+          returnValue:
+              _i17.Future<_i15.HttpClientResponse>.value(_FakeHttpClientResponse_40(this, Invocation.getter(#done))))
       as _i17.Future<_i15.HttpClientResponse>);
   @override
-  _i16.Encoding get encoding =>
-      (super.noSuchMethod(Invocation.getter(#encoding), returnValue: _FakeEncoding_41()) as _i16.Encoding);
+  _i16.Encoding get encoding => (super.noSuchMethod(Invocation.getter(#encoding),
+      returnValue: _FakeEncoding_41(this, Invocation.getter(#encoding))) as _i16.Encoding);
   @override
   set encoding(_i16.Encoding? _encoding) =>
       super.noSuchMethod(Invocation.setter(#encoding, _encoding), returnValueForMissingStub: null);
   @override
   _i17.Future<_i15.HttpClientResponse> close() => (super.noSuchMethod(Invocation.method(#close, []),
-          returnValue: Future<_i15.HttpClientResponse>.value(_FakeHttpClientResponse_40()))
-      as _i17.Future<_i15.HttpClientResponse>);
+      returnValue: _i17.Future<_i15.HttpClientResponse>.value(
+          _FakeHttpClientResponse_40(this, Invocation.method(#close, [])))) as _i17.Future<_i15.HttpClientResponse>);
   @override
   void abort([Object? exception, StackTrace? stackTrace]) =>
       super.noSuchMethod(Invocation.method(#abort, [exception, stackTrace]), returnValueForMissingStub: null);
@@ -1150,11 +1462,12 @@
       super.noSuchMethod(Invocation.method(#addError, [error, stackTrace]), returnValueForMissingStub: null);
   @override
   _i17.Future<dynamic> addStream(_i17.Stream<List<int>>? stream) =>
-      (super.noSuchMethod(Invocation.method(#addStream, [stream]), returnValue: Future<dynamic>.value())
+      (super.noSuchMethod(Invocation.method(#addStream, [stream]), returnValue: _i17.Future<dynamic>.value())
           as _i17.Future<dynamic>);
   @override
   _i17.Future<dynamic> flush() =>
-      (super.noSuchMethod(Invocation.method(#flush, []), returnValue: Future<dynamic>.value()) as _i17.Future<dynamic>);
+      (super.noSuchMethod(Invocation.method(#flush, []), returnValue: _i17.Future<dynamic>.value())
+          as _i17.Future<dynamic>);
 }
 
 /// A class which mocks [HttpClientResponse].
@@ -1185,8 +1498,8 @@
       (super.noSuchMethod(Invocation.getter(#redirects), returnValue: <_i15.RedirectInfo>[])
           as List<_i15.RedirectInfo>);
   @override
-  _i15.HttpHeaders get headers =>
-      (super.noSuchMethod(Invocation.getter(#headers), returnValue: _FakeHttpHeaders_39()) as _i15.HttpHeaders);
+  _i15.HttpHeaders get headers => (super.noSuchMethod(Invocation.getter(#headers),
+      returnValue: _FakeHttpHeaders_39(this, Invocation.getter(#headers))) as _i15.HttpHeaders);
   @override
   List<_i15.Cookie> get cookies =>
       (super.noSuchMethod(Invocation.getter(#cookies), returnValue: <_i15.Cookie>[]) as List<_i15.Cookie>);
@@ -1194,154 +1507,162 @@
   bool get isBroadcast => (super.noSuchMethod(Invocation.getter(#isBroadcast), returnValue: false) as bool);
   @override
   _i17.Future<int> get length =>
-      (super.noSuchMethod(Invocation.getter(#length), returnValue: Future<int>.value(0)) as _i17.Future<int>);
+      (super.noSuchMethod(Invocation.getter(#length), returnValue: _i17.Future<int>.value(0)) as _i17.Future<int>);
   @override
   _i17.Future<bool> get isEmpty =>
-      (super.noSuchMethod(Invocation.getter(#isEmpty), returnValue: Future<bool>.value(false)) as _i17.Future<bool>);
+      (super.noSuchMethod(Invocation.getter(#isEmpty), returnValue: _i17.Future<bool>.value(false))
+          as _i17.Future<bool>);
   @override
   _i17.Future<List<int>> get first =>
-      (super.noSuchMethod(Invocation.getter(#first), returnValue: Future<List<int>>.value(<int>[]))
+      (super.noSuchMethod(Invocation.getter(#first), returnValue: _i17.Future<List<int>>.value(<int>[]))
           as _i17.Future<List<int>>);
   @override
   _i17.Future<List<int>> get last =>
-      (super.noSuchMethod(Invocation.getter(#last), returnValue: Future<List<int>>.value(<int>[]))
+      (super.noSuchMethod(Invocation.getter(#last), returnValue: _i17.Future<List<int>>.value(<int>[]))
           as _i17.Future<List<int>>);
   @override
   _i17.Future<List<int>> get single =>
-      (super.noSuchMethod(Invocation.getter(#single), returnValue: Future<List<int>>.value(<int>[]))
+      (super.noSuchMethod(Invocation.getter(#single), returnValue: _i17.Future<List<int>>.value(<int>[]))
           as _i17.Future<List<int>>);
   @override
   _i17.Future<_i15.HttpClientResponse> redirect([String? method, Uri? url, bool? followLoops]) =>
       (super.noSuchMethod(Invocation.method(#redirect, [method, url, followLoops]),
-              returnValue: Future<_i15.HttpClientResponse>.value(_FakeHttpClientResponse_40()))
+              returnValue: _i17.Future<_i15.HttpClientResponse>.value(
+                  _FakeHttpClientResponse_40(this, Invocation.method(#redirect, [method, url, followLoops]))))
           as _i17.Future<_i15.HttpClientResponse>);
   @override
   _i17.Future<_i15.Socket> detachSocket() => (super.noSuchMethod(Invocation.method(#detachSocket, []),
-      returnValue: Future<_i15.Socket>.value(_FakeSocket_42())) as _i17.Future<_i15.Socket>);
+          returnValue: _i17.Future<_i15.Socket>.value(_FakeSocket_42(this, Invocation.method(#detachSocket, []))))
+      as _i17.Future<_i15.Socket>);
   @override
   _i17.Stream<List<int>> asBroadcastStream(
           {void Function(_i17.StreamSubscription<List<int>>)? onListen,
           void Function(_i17.StreamSubscription<List<int>>)? onCancel}) =>
       (super.noSuchMethod(Invocation.method(#asBroadcastStream, [], {#onListen: onListen, #onCancel: onCancel}),
-          returnValue: Stream<List<int>>.empty()) as _i17.Stream<List<int>>);
+          returnValue: _i17.Stream<List<int>>.empty()) as _i17.Stream<List<int>>);
   @override
   _i17.StreamSubscription<List<int>> listen(void Function(List<int>)? onData,
           {Function? onError, void Function()? onDone, bool? cancelOnError}) =>
       (super.noSuchMethod(
-          Invocation.method(#listen, [onData], {#onError: onError, #onDone: onDone, #cancelOnError: cancelOnError}),
-          returnValue: _FakeStreamSubscription_43<List<int>>()) as _i17.StreamSubscription<List<int>>);
+              Invocation.method(#listen, [onData], {#onError: onError, #onDone: onDone, #cancelOnError: cancelOnError}),
+              returnValue: _FakeStreamSubscription_43<List<int>>(
+                  this,
+                  Invocation.method(
+                      #listen, [onData], {#onError: onError, #onDone: onDone, #cancelOnError: cancelOnError})))
+          as _i17.StreamSubscription<List<int>>);
   @override
   _i17.Stream<List<int>> where(bool Function(List<int>)? test) =>
-      (super.noSuchMethod(Invocation.method(#where, [test]), returnValue: Stream<List<int>>.empty())
+      (super.noSuchMethod(Invocation.method(#where, [test]), returnValue: _i17.Stream<List<int>>.empty())
           as _i17.Stream<List<int>>);
   @override
   _i17.Stream<S> map<S>(S Function(List<int>)? convert) =>
-      (super.noSuchMethod(Invocation.method(#map, [convert]), returnValue: Stream<S>.empty()) as _i17.Stream<S>);
+      (super.noSuchMethod(Invocation.method(#map, [convert]), returnValue: _i17.Stream<S>.empty()) as _i17.Stream<S>);
   @override
   _i17.Stream<E> asyncMap<E>(_i17.FutureOr<E> Function(List<int>)? convert) =>
-      (super.noSuchMethod(Invocation.method(#asyncMap, [convert]), returnValue: Stream<E>.empty()) as _i17.Stream<E>);
+      (super.noSuchMethod(Invocation.method(#asyncMap, [convert]), returnValue: _i17.Stream<E>.empty())
+          as _i17.Stream<E>);
   @override
   _i17.Stream<E> asyncExpand<E>(_i17.Stream<E>? Function(List<int>)? convert) =>
-      (super.noSuchMethod(Invocation.method(#asyncExpand, [convert]), returnValue: Stream<E>.empty())
+      (super.noSuchMethod(Invocation.method(#asyncExpand, [convert]), returnValue: _i17.Stream<E>.empty())
           as _i17.Stream<E>);
   @override
   _i17.Stream<List<int>> handleError(Function? onError, {bool Function(dynamic)? test}) =>
       (super.noSuchMethod(Invocation.method(#handleError, [onError], {#test: test}),
-          returnValue: Stream<List<int>>.empty()) as _i17.Stream<List<int>>);
+          returnValue: _i17.Stream<List<int>>.empty()) as _i17.Stream<List<int>>);
   @override
   _i17.Stream<S> expand<S>(Iterable<S> Function(List<int>)? convert) =>
-      (super.noSuchMethod(Invocation.method(#expand, [convert]), returnValue: Stream<S>.empty()) as _i17.Stream<S>);
+      (super.noSuchMethod(Invocation.method(#expand, [convert]), returnValue: _i17.Stream<S>.empty())
+          as _i17.Stream<S>);
   @override
   _i17.Future<dynamic> pipe(_i17.StreamConsumer<List<int>>? streamConsumer) =>
-      (super.noSuchMethod(Invocation.method(#pipe, [streamConsumer]), returnValue: Future<dynamic>.value())
+      (super.noSuchMethod(Invocation.method(#pipe, [streamConsumer]), returnValue: _i17.Future<dynamic>.value())
           as _i17.Future<dynamic>);
   @override
   _i17.Stream<S> transform<S>(_i17.StreamTransformer<List<int>, S>? streamTransformer) =>
-      (super.noSuchMethod(Invocation.method(#transform, [streamTransformer]), returnValue: Stream<S>.empty())
+      (super.noSuchMethod(Invocation.method(#transform, [streamTransformer]), returnValue: _i17.Stream<S>.empty())
           as _i17.Stream<S>);
   @override
   _i17.Future<List<int>> reduce(List<int> Function(List<int>, List<int>)? combine) =>
-      (super.noSuchMethod(Invocation.method(#reduce, [combine]), returnValue: Future<List<int>>.value(<int>[]))
+      (super.noSuchMethod(Invocation.method(#reduce, [combine]), returnValue: _i17.Future<List<int>>.value(<int>[]))
           as _i17.Future<List<int>>);
   @override
   _i17.Future<S> fold<S>(S? initialValue, S Function(S, List<int>)? combine) =>
-      (super.noSuchMethod(Invocation.method(#fold, [initialValue, combine]), returnValue: Future<S>.value(null))
+      (super.noSuchMethod(Invocation.method(#fold, [initialValue, combine]), returnValue: _i17.Future<S>.value(null))
           as _i17.Future<S>);
   @override
   _i17.Future<String> join([String? separator = r'']) =>
-      (super.noSuchMethod(Invocation.method(#join, [separator]), returnValue: Future<String>.value(''))
+      (super.noSuchMethod(Invocation.method(#join, [separator]), returnValue: _i17.Future<String>.value(''))
           as _i17.Future<String>);
   @override
   _i17.Future<bool> contains(Object? needle) =>
-      (super.noSuchMethod(Invocation.method(#contains, [needle]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#contains, [needle]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Future<dynamic> forEach(void Function(List<int>)? action) =>
-      (super.noSuchMethod(Invocation.method(#forEach, [action]), returnValue: Future<dynamic>.value())
+      (super.noSuchMethod(Invocation.method(#forEach, [action]), returnValue: _i17.Future<dynamic>.value())
           as _i17.Future<dynamic>);
   @override
   _i17.Future<bool> every(bool Function(List<int>)? test) =>
-      (super.noSuchMethod(Invocation.method(#every, [test]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#every, [test]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Future<bool> any(bool Function(List<int>)? test) =>
-      (super.noSuchMethod(Invocation.method(#any, [test]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#any, [test]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Stream<R> cast<R>() =>
-      (super.noSuchMethod(Invocation.method(#cast, []), returnValue: Stream<R>.empty()) as _i17.Stream<R>);
+      (super.noSuchMethod(Invocation.method(#cast, []), returnValue: _i17.Stream<R>.empty()) as _i17.Stream<R>);
   @override
-  _i17.Future<List<List<int>>> toList() =>
-      (super.noSuchMethod(Invocation.method(#toList, []), returnValue: Future<List<List<int>>>.value(<List<int>>[]))
-          as _i17.Future<List<List<int>>>);
+  _i17.Future<List<List<int>>> toList() => (super.noSuchMethod(Invocation.method(#toList, []),
+      returnValue: _i17.Future<List<List<int>>>.value(<List<int>>[])) as _i17.Future<List<List<int>>>);
   @override
   _i17.Future<Set<List<int>>> toSet() =>
-      (super.noSuchMethod(Invocation.method(#toSet, []), returnValue: Future<Set<List<int>>>.value(<List<int>>{}))
+      (super.noSuchMethod(Invocation.method(#toSet, []), returnValue: _i17.Future<Set<List<int>>>.value(<List<int>>{}))
           as _i17.Future<Set<List<int>>>);
   @override
   _i17.Future<E> drain<E>([E? futureValue]) =>
-      (super.noSuchMethod(Invocation.method(#drain, [futureValue]), returnValue: Future<E>.value(null))
+      (super.noSuchMethod(Invocation.method(#drain, [futureValue]), returnValue: _i17.Future<E>.value(null))
           as _i17.Future<E>);
   @override
   _i17.Stream<List<int>> take(int? count) =>
-      (super.noSuchMethod(Invocation.method(#take, [count]), returnValue: Stream<List<int>>.empty())
+      (super.noSuchMethod(Invocation.method(#take, [count]), returnValue: _i17.Stream<List<int>>.empty())
           as _i17.Stream<List<int>>);
   @override
   _i17.Stream<List<int>> takeWhile(bool Function(List<int>)? test) =>
-      (super.noSuchMethod(Invocation.method(#takeWhile, [test]), returnValue: Stream<List<int>>.empty())
+      (super.noSuchMethod(Invocation.method(#takeWhile, [test]), returnValue: _i17.Stream<List<int>>.empty())
           as _i17.Stream<List<int>>);
   @override
   _i17.Stream<List<int>> skip(int? count) =>
-      (super.noSuchMethod(Invocation.method(#skip, [count]), returnValue: Stream<List<int>>.empty())
+      (super.noSuchMethod(Invocation.method(#skip, [count]), returnValue: _i17.Stream<List<int>>.empty())
           as _i17.Stream<List<int>>);
   @override
   _i17.Stream<List<int>> skipWhile(bool Function(List<int>)? test) =>
-      (super.noSuchMethod(Invocation.method(#skipWhile, [test]), returnValue: Stream<List<int>>.empty())
+      (super.noSuchMethod(Invocation.method(#skipWhile, [test]), returnValue: _i17.Stream<List<int>>.empty())
           as _i17.Stream<List<int>>);
   @override
   _i17.Stream<List<int>> distinct([bool Function(List<int>, List<int>)? equals]) =>
-      (super.noSuchMethod(Invocation.method(#distinct, [equals]), returnValue: Stream<List<int>>.empty())
+      (super.noSuchMethod(Invocation.method(#distinct, [equals]), returnValue: _i17.Stream<List<int>>.empty())
           as _i17.Stream<List<int>>);
   @override
   _i17.Future<List<int>> firstWhere(bool Function(List<int>)? test, {List<int> Function()? orElse}) =>
       (super.noSuchMethod(Invocation.method(#firstWhere, [test], {#orElse: orElse}),
-          returnValue: Future<List<int>>.value(<int>[])) as _i17.Future<List<int>>);
+          returnValue: _i17.Future<List<int>>.value(<int>[])) as _i17.Future<List<int>>);
   @override
   _i17.Future<List<int>> lastWhere(bool Function(List<int>)? test, {List<int> Function()? orElse}) =>
       (super.noSuchMethod(Invocation.method(#lastWhere, [test], {#orElse: orElse}),
-          returnValue: Future<List<int>>.value(<int>[])) as _i17.Future<List<int>>);
+          returnValue: _i17.Future<List<int>>.value(<int>[])) as _i17.Future<List<int>>);
   @override
   _i17.Future<List<int>> singleWhere(bool Function(List<int>)? test, {List<int> Function()? orElse}) =>
       (super.noSuchMethod(Invocation.method(#singleWhere, [test], {#orElse: orElse}),
-          returnValue: Future<List<int>>.value(<int>[])) as _i17.Future<List<int>>);
+          returnValue: _i17.Future<List<int>>.value(<int>[])) as _i17.Future<List<int>>);
   @override
   _i17.Future<List<int>> elementAt(int? index) =>
-      (super.noSuchMethod(Invocation.method(#elementAt, [index]), returnValue: Future<List<int>>.value(<int>[]))
+      (super.noSuchMethod(Invocation.method(#elementAt, [index]), returnValue: _i17.Future<List<int>>.value(<int>[]))
           as _i17.Future<List<int>>);
   @override
   _i17.Stream<List<int>> timeout(Duration? timeLimit, {void Function(_i17.EventSink<List<int>>)? onTimeout}) =>
       (super.noSuchMethod(Invocation.method(#timeout, [timeLimit], {#onTimeout: onTimeout}),
-          returnValue: Stream<List<int>>.empty()) as _i17.Stream<List<int>>);
+          returnValue: _i17.Stream<List<int>>.empty()) as _i17.Stream<List<int>>);
 }
 
 /// A class which mocks [JobsResource].
@@ -1355,16 +1676,20 @@
   @override
   _i17.Future<_i6.JobCancelResponse> cancel(String? projectId, String? jobId, {String? location, String? $fields}) =>
       (super.noSuchMethod(Invocation.method(#cancel, [projectId, jobId], {#location: location, #$fields: $fields}),
-              returnValue: Future<_i6.JobCancelResponse>.value(_FakeJobCancelResponse_44()))
+              returnValue: _i17.Future<_i6.JobCancelResponse>.value(_FakeJobCancelResponse_44(
+                  this, Invocation.method(#cancel, [projectId, jobId], {#location: location, #$fields: $fields}))))
           as _i17.Future<_i6.JobCancelResponse>);
   @override
   _i17.Future<void> delete(String? projectId, String? jobId, {String? location, String? $fields}) =>
       (super.noSuchMethod(Invocation.method(#delete, [projectId, jobId], {#location: location, #$fields: $fields}),
-          returnValue: Future<void>.value(), returnValueForMissingStub: Future<void>.value()) as _i17.Future<void>);
+          returnValue: _i17.Future<void>.value(),
+          returnValueForMissingStub: _i17.Future<void>.value()) as _i17.Future<void>);
   @override
   _i17.Future<_i6.Job> get(String? projectId, String? jobId, {String? location, String? $fields}) =>
       (super.noSuchMethod(Invocation.method(#get, [projectId, jobId], {#location: location, #$fields: $fields}),
-          returnValue: Future<_i6.Job>.value(_FakeJob_45())) as _i17.Future<_i6.Job>);
+              returnValue: _i17.Future<_i6.Job>.value(_FakeJob_45(
+                  this, Invocation.method(#get, [projectId, jobId], {#location: location, #$fields: $fields}))))
+          as _i17.Future<_i6.Job>);
   @override
   _i17.Future<_i6.GetQueryResultsResponse> getQueryResults(String? projectId, String? jobId,
           {String? location,
@@ -1374,6 +1699,19 @@
           int? timeoutMs,
           String? $fields}) =>
       (super.noSuchMethod(
+          Invocation.method(#getQueryResults, [
+            projectId,
+            jobId
+          ], {
+            #location: location,
+            #maxResults: maxResults,
+            #pageToken: pageToken,
+            #startIndex: startIndex,
+            #timeoutMs: timeoutMs,
+            #$fields: $fields
+          }),
+          returnValue: _i17.Future<_i6.GetQueryResultsResponse>.value(_FakeGetQueryResultsResponse_46(
+              this,
               Invocation.method(#getQueryResults, [
                 projectId,
                 jobId
@@ -1384,18 +1722,23 @@
                 #startIndex: startIndex,
                 #timeoutMs: timeoutMs,
                 #$fields: $fields
-              }),
-              returnValue: Future<_i6.GetQueryResultsResponse>.value(_FakeGetQueryResultsResponse_46()))
-          as _i17.Future<_i6.GetQueryResultsResponse>);
+              })))) as _i17.Future<_i6.GetQueryResultsResponse>);
   @override
   _i17.Future<_i6.Job> insert(_i6.Job? request, String? projectId,
           {String? $fields,
           _i6.UploadOptions? uploadOptions = _i6.UploadOptions.defaultOptions,
           _i6.Media? uploadMedia}) =>
-      (super.noSuchMethod(
-          Invocation.method(#insert, [request, projectId],
-              {#$fields: $fields, #uploadOptions: uploadOptions, #uploadMedia: uploadMedia}),
-          returnValue: Future<_i6.Job>.value(_FakeJob_45())) as _i17.Future<_i6.Job>);
+      (super.noSuchMethod(Invocation.method(#insert, [request, projectId], {#$fields: $fields, #uploadOptions: uploadOptions, #uploadMedia: uploadMedia}),
+          returnValue: _i17.Future<_i6.Job>.value(_FakeJob_45(
+              this,
+              Invocation.method(#insert, [
+                request,
+                projectId
+              ], {
+                #$fields: $fields,
+                #uploadOptions: uploadOptions,
+                #uploadMedia: uploadMedia
+              })))) as _i17.Future<_i6.Job>);
   @override
   _i17.Future<_i6.JobList> list(String? projectId,
           {bool? allUsers,
@@ -1421,11 +1764,27 @@
             #stateFilter: stateFilter,
             #$fields: $fields
           }),
-          returnValue: Future<_i6.JobList>.value(_FakeJobList_47())) as _i17.Future<_i6.JobList>);
+          returnValue: _i17.Future<_i6.JobList>.value(_FakeJobList_47(
+              this,
+              Invocation.method(#list, [
+                projectId
+              ], {
+                #allUsers: allUsers,
+                #maxCreationTime: maxCreationTime,
+                #maxResults: maxResults,
+                #minCreationTime: minCreationTime,
+                #pageToken: pageToken,
+                #parentJobId: parentJobId,
+                #projection: projection,
+                #stateFilter: stateFilter,
+                #$fields: $fields
+              })))) as _i17.Future<_i6.JobList>);
   @override
   _i17.Future<_i6.QueryResponse> query(_i6.QueryRequest? request, String? projectId, {String? $fields}) =>
       (super.noSuchMethod(Invocation.method(#query, [request, projectId], {#$fields: $fields}),
-          returnValue: Future<_i6.QueryResponse>.value(_FakeQueryResponse_48())) as _i17.Future<_i6.QueryResponse>);
+              returnValue: _i17.Future<_i6.QueryResponse>.value(
+                  _FakeQueryResponse_48(this, Invocation.method(#query, [request, projectId], {#$fields: $fields}))))
+          as _i17.Future<_i6.QueryResponse>);
 }
 
 /// A class which mocks [LuciBuildService].
@@ -1437,55 +1796,56 @@
   }
 
   @override
-  _i18.BuildBucketClient get buildBucketClient =>
-      (super.noSuchMethod(Invocation.getter(#buildBucketClient), returnValue: _FakeBuildBucketClient_49())
-          as _i18.BuildBucketClient);
+  _i18.BuildBucketClient get buildBucketClient => (super.noSuchMethod(Invocation.getter(#buildBucketClient),
+      returnValue: _FakeBuildBucketClient_49(this, Invocation.getter(#buildBucketClient))) as _i18.BuildBucketClient);
   @override
   set buildBucketClient(_i18.BuildBucketClient? _buildBucketClient) =>
       super.noSuchMethod(Invocation.setter(#buildBucketClient, _buildBucketClient), returnValueForMissingStub: null);
   @override
-  _i3.Config get config => (super.noSuchMethod(Invocation.getter(#config), returnValue: _FakeConfig_1()) as _i3.Config);
+  _i3.Config get config =>
+      (super.noSuchMethod(Invocation.getter(#config), returnValue: _FakeConfig_1(this, Invocation.getter(#config)))
+          as _i3.Config);
   @override
   set config(_i3.Config? _config) =>
       super.noSuchMethod(Invocation.setter(#config, _config), returnValueForMissingStub: null);
   @override
-  _i11.GithubChecksUtil get githubChecksUtil =>
-      (super.noSuchMethod(Invocation.getter(#githubChecksUtil), returnValue: _FakeGithubChecksUtil_17())
-          as _i11.GithubChecksUtil);
+  _i11.GithubChecksUtil get githubChecksUtil => (super.noSuchMethod(Invocation.getter(#githubChecksUtil),
+      returnValue: _FakeGithubChecksUtil_17(this, Invocation.getter(#githubChecksUtil))) as _i11.GithubChecksUtil);
   @override
   set githubChecksUtil(_i11.GithubChecksUtil? _githubChecksUtil) =>
       super.noSuchMethod(Invocation.setter(#githubChecksUtil, _githubChecksUtil), returnValueForMissingStub: null);
   @override
-  _i7.GerritService get gerritService =>
-      (super.noSuchMethod(Invocation.getter(#gerritService), returnValue: _FakeGerritService_6()) as _i7.GerritService);
+  _i7.GerritService get gerritService => (super.noSuchMethod(Invocation.getter(#gerritService),
+      returnValue: _FakeGerritService_6(this, Invocation.getter(#gerritService))) as _i7.GerritService);
   @override
   set gerritService(_i7.GerritService? _gerritService) =>
       super.noSuchMethod(Invocation.setter(#gerritService, _gerritService), returnValueForMissingStub: null);
   @override
   _i19.PubSub get pubsub =>
-      (super.noSuchMethod(Invocation.getter(#pubsub), returnValue: _FakePubSub_50()) as _i19.PubSub);
+      (super.noSuchMethod(Invocation.getter(#pubsub), returnValue: _FakePubSub_50(this, Invocation.getter(#pubsub)))
+          as _i19.PubSub);
   @override
   _i17.Future<List<List<_i9.Request>>> shard(List<_i9.Request>? requests, int? max) =>
       (super.noSuchMethod(Invocation.method(#shard, [requests, max]),
-              returnValue: Future<List<List<_i9.Request>>>.value(<List<_i9.Request>>[]))
+              returnValue: _i17.Future<List<List<_i9.Request>>>.value(<List<_i9.Request>>[]))
           as _i17.Future<List<List<_i9.Request>>>);
   @override
   _i17.Future<Iterable<_i9.Build>> getTryBuilds(_i10.RepositorySlug? slug, String? sha, String? builderName) =>
       (super.noSuchMethod(Invocation.method(#getTryBuilds, [slug, sha, builderName]),
-          returnValue: Future<Iterable<_i9.Build>>.value(<_i9.Build>[])) as _i17.Future<Iterable<_i9.Build>>);
+          returnValue: _i17.Future<Iterable<_i9.Build>>.value(<_i9.Build>[])) as _i17.Future<Iterable<_i9.Build>>);
   @override
   _i17.Future<Iterable<_i9.Build>> getProdBuilds(_i10.RepositorySlug? slug, String? commitSha, String? builderName) =>
       (super.noSuchMethod(Invocation.method(#getProdBuilds, [slug, commitSha, builderName]),
-          returnValue: Future<Iterable<_i9.Build>>.value(<_i9.Build>[])) as _i17.Future<Iterable<_i9.Build>>);
+          returnValue: _i17.Future<Iterable<_i9.Build>>.value(<_i9.Build>[])) as _i17.Future<Iterable<_i9.Build>>);
   @override
   _i17.Future<Iterable<_i9.Build>> getBuilds(_i10.RepositorySlug? slug, String? commitSha, String? builderName,
           String? bucket, Map<String, List<String>>? tags) =>
       (super.noSuchMethod(Invocation.method(#getBuilds, [slug, commitSha, builderName, bucket, tags]),
-          returnValue: Future<Iterable<_i9.Build>>.value(<_i9.Build>[])) as _i17.Future<Iterable<_i9.Build>>);
+          returnValue: _i17.Future<Iterable<_i9.Build>>.value(<_i9.Build>[])) as _i17.Future<Iterable<_i9.Build>>);
   @override
   _i17.Future<Map<String?, _i9.Build?>> tryBuildsForPullRequest(_i10.PullRequest? pullRequest) =>
       (super.noSuchMethod(Invocation.method(#tryBuildsForPullRequest, [pullRequest]),
-              returnValue: Future<Map<String?, _i9.Build?>>.value(<String?, _i9.Build?>{}))
+              returnValue: _i17.Future<Map<String?, _i9.Build?>>.value(<String?, _i9.Build?>{}))
           as _i17.Future<Map<String?, _i9.Build?>>);
   @override
   _i17.Future<List<_i33.Target>> scheduleTryBuilds(
@@ -1493,41 +1853,43 @@
       (super.noSuchMethod(
           Invocation.method(#scheduleTryBuilds, [],
               {#targets: targets, #pullRequest: pullRequest, #checkSuiteEvent: checkSuiteEvent}),
-          returnValue: Future<List<_i33.Target>>.value(<_i33.Target>[])) as _i17.Future<List<_i33.Target>>);
+          returnValue: _i17.Future<List<_i33.Target>>.value(<_i33.Target>[])) as _i17.Future<List<_i33.Target>>);
   @override
   _i17.Future<void> cancelBuilds(_i10.PullRequest? pullRequest, String? reason) =>
       (super.noSuchMethod(Invocation.method(#cancelBuilds, [pullRequest, reason]),
-          returnValue: Future<void>.value(), returnValueForMissingStub: Future<void>.value()) as _i17.Future<void>);
+          returnValue: _i17.Future<void>.value(),
+          returnValueForMissingStub: _i17.Future<void>.value()) as _i17.Future<void>);
   @override
   _i17.Future<List<_i9.Build?>> failedBuilds(_i10.PullRequest? pullRequest, List<_i33.Target>? targets) =>
       (super.noSuchMethod(Invocation.method(#failedBuilds, [pullRequest, targets]),
-          returnValue: Future<List<_i9.Build?>>.value(<_i9.Build?>[])) as _i17.Future<List<_i9.Build?>>);
+          returnValue: _i17.Future<List<_i9.Build?>>.value(<_i9.Build?>[])) as _i17.Future<List<_i9.Build?>>);
   @override
   _i17.Future<bool> rescheduleBuild(
           {String? commitSha, String? builderName, _i30.BuildPushMessage? buildPushMessage}) =>
       (super.noSuchMethod(
           Invocation.method(#rescheduleBuild, [],
               {#commitSha: commitSha, #builderName: builderName, #buildPushMessage: buildPushMessage}),
-          returnValue: Future<bool>.value(false)) as _i17.Future<bool>);
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
   @override
   _i17.Future<bool> rescheduleUsingCheckRunEvent(_i34.CheckRunEvent? checkRunEvent) =>
       (super.noSuchMethod(Invocation.method(#rescheduleUsingCheckRunEvent, [checkRunEvent]),
-          returnValue: Future<bool>.value(false)) as _i17.Future<bool>);
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
   @override
-  _i17.Future<_i9.Build> getTryBuildById(String? id, {String? fields}) =>
-      (super.noSuchMethod(Invocation.method(#getTryBuildById, [id], {#fields: fields}),
-          returnValue: Future<_i9.Build>.value(_FakeBuild_8())) as _i17.Future<_i9.Build>);
+  _i17.Future<_i9.Build> getTryBuildById(String? id, {String? fields}) => (super.noSuchMethod(
+      Invocation.method(#getTryBuildById, [id], {#fields: fields}),
+      returnValue: _i17.Future<_i9.Build>.value(
+          _FakeBuild_8(this, Invocation.method(#getTryBuildById, [id], {#fields: fields})))) as _i17.Future<_i9.Build>);
   @override
   _i17.Future<Set<String>> getAvailableBuilderSet({String? project = r'flutter', String? bucket = r'prod'}) =>
       (super.noSuchMethod(Invocation.method(#getAvailableBuilderSet, [], {#project: project, #bucket: bucket}),
-          returnValue: Future<Set<String>>.value(<String>{})) as _i17.Future<Set<String>>);
+          returnValue: _i17.Future<Set<String>>.value(<String>{})) as _i17.Future<Set<String>>);
   @override
   _i17.Future<void> schedulePostsubmitBuilds(
           {_i35.Commit? commit, List<_i36.Tuple<_i33.Target, _i37.Task, int>>? toBeScheduled}) =>
       (super.noSuchMethod(
           Invocation.method(#schedulePostsubmitBuilds, [], {#commit: commit, #toBeScheduled: toBeScheduled}),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i17.Future<void>);
+          returnValue: _i17.Future<void>.value(),
+          returnValueForMissingStub: _i17.Future<void>.value()) as _i17.Future<void>);
   @override
   _i17.Future<bool> checkRerunBuilder(
           {_i35.Commit? commit,
@@ -1545,7 +1907,7 @@
             #tags: tags,
             #ignoreChecks: ignoreChecks
           }),
-          returnValue: Future<bool>.value(false)) as _i17.Future<bool>);
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
 }
 
 /// A class which mocks [LuciService].
@@ -1558,21 +1920,21 @@
   }
 
   @override
-  _i18.BuildBucketClient get buildBucketClient =>
-      (super.noSuchMethod(Invocation.getter(#buildBucketClient), returnValue: _FakeBuildBucketClient_49())
-          as _i18.BuildBucketClient);
+  _i18.BuildBucketClient get buildBucketClient => (super.noSuchMethod(Invocation.getter(#buildBucketClient),
+      returnValue: _FakeBuildBucketClient_49(this, Invocation.getter(#buildBucketClient))) as _i18.BuildBucketClient);
   @override
-  _i3.Config get config => (super.noSuchMethod(Invocation.getter(#config), returnValue: _FakeConfig_1()) as _i3.Config);
+  _i3.Config get config =>
+      (super.noSuchMethod(Invocation.getter(#config), returnValue: _FakeConfig_1(this, Invocation.getter(#config)))
+          as _i3.Config);
   @override
-  _i20.ClientContext get clientContext =>
-      (super.noSuchMethod(Invocation.getter(#clientContext), returnValue: _FakeClientContext_51())
-          as _i20.ClientContext);
+  _i20.ClientContext get clientContext => (super.noSuchMethod(Invocation.getter(#clientContext),
+      returnValue: _FakeClientContext_51(this, Invocation.getter(#clientContext))) as _i20.ClientContext);
   @override
   _i17.Future<Map<_i39.BranchLuciBuilder, Map<String, List<_i39.LuciTask>>>> getBranchRecentTasks(
           {List<_i39.LuciBuilder>? builders, bool? requireTaskName = false}) =>
       (super.noSuchMethod(
               Invocation.method(#getBranchRecentTasks, [], {#builders: builders, #requireTaskName: requireTaskName}),
-              returnValue: Future<Map<_i39.BranchLuciBuilder, Map<String, List<_i39.LuciTask>>>>.value(
+              returnValue: _i17.Future<Map<_i39.BranchLuciBuilder, Map<String, List<_i39.LuciTask>>>>.value(
                   <_i39.BranchLuciBuilder, Map<String, List<_i39.LuciTask>>>{}))
           as _i17.Future<Map<_i39.BranchLuciBuilder, Map<String, List<_i39.LuciTask>>>>);
   @override
@@ -1583,19 +1945,18 @@
   _i17.Future<List<_i9.Build>> getBuildsForBuilderList(List<_i39.LuciBuilder>? builders,
           {bool? requireTaskName = false}) =>
       (super.noSuchMethod(Invocation.method(#getBuildsForBuilderList, [builders], {#requireTaskName: requireTaskName}),
-          returnValue: Future<List<_i9.Build>>.value(<_i9.Build>[])) as _i17.Future<List<_i9.Build>>);
+          returnValue: _i17.Future<List<_i9.Build>>.value(<_i9.Build>[])) as _i17.Future<List<_i9.Build>>);
   @override
   _i17.Future<Map<_i39.LuciBuilder, List<_i39.LuciTask>>> getRecentTasks(
           {List<_i39.LuciBuilder>? builders, bool? requireTaskName = false}) =>
       (super.noSuchMethod(
-              Invocation.method(#getRecentTasks, [], {#builders: builders, #requireTaskName: requireTaskName}),
-              returnValue:
-                  Future<Map<_i39.LuciBuilder, List<_i39.LuciTask>>>.value(<_i39.LuciBuilder, List<_i39.LuciTask>>{}))
-          as _i17.Future<Map<_i39.LuciBuilder, List<_i39.LuciTask>>>);
+          Invocation.method(#getRecentTasks, [], {#builders: builders, #requireTaskName: requireTaskName}),
+          returnValue: _i17.Future<Map<_i39.LuciBuilder, List<_i39.LuciTask>>>.value(
+              <_i39.LuciBuilder, List<_i39.LuciTask>>{})) as _i17.Future<Map<_i39.LuciBuilder, List<_i39.LuciTask>>>);
   @override
   _i17.Future<Iterable<_i9.Build>> getBuilds(bool? requireTaskName, List<_i39.LuciBuilder>? builders) =>
       (super.noSuchMethod(Invocation.method(#getBuilds, [requireTaskName, builders]),
-          returnValue: Future<Iterable<_i9.Build>>.value(<_i9.Build>[])) as _i17.Future<Iterable<_i9.Build>>);
+          returnValue: _i17.Future<Iterable<_i9.Build>>.value(<_i9.Build>[])) as _i17.Future<Iterable<_i9.Build>>);
 }
 
 /// A class which mocks [ProcessManager].
@@ -1623,7 +1984,17 @@
             #runInShell: runInShell,
             #mode: mode
           }),
-          returnValue: Future<_i15.Process>.value(_FakeProcess_52())) as _i17.Future<_i15.Process>);
+          returnValue: _i17.Future<_i15.Process>.value(_FakeProcess_52(
+              this,
+              Invocation.method(#start, [
+                command
+              ], {
+                #workingDirectory: workingDirectory,
+                #environment: environment,
+                #includeParentEnvironment: includeParentEnvironment,
+                #runInShell: runInShell,
+                #mode: mode
+              })))) as _i17.Future<_i15.Process>);
   @override
   _i17.Future<_i15.ProcessResult> run(List<Object>? command,
           {String? workingDirectory,
@@ -1643,7 +2014,18 @@
             #stdoutEncoding: stdoutEncoding,
             #stderrEncoding: stderrEncoding
           }),
-          returnValue: Future<_i15.ProcessResult>.value(_FakeProcessResult_53())) as _i17.Future<_i15.ProcessResult>);
+          returnValue: _i17.Future<_i15.ProcessResult>.value(_FakeProcessResult_53(
+              this,
+              Invocation.method(#run, [
+                command
+              ], {
+                #workingDirectory: workingDirectory,
+                #environment: environment,
+                #includeParentEnvironment: includeParentEnvironment,
+                #runInShell: runInShell,
+                #stdoutEncoding: stdoutEncoding,
+                #stderrEncoding: stderrEncoding
+              })))) as _i17.Future<_i15.ProcessResult>);
   @override
   _i15.ProcessResult runSync(List<Object>? command,
           {String? workingDirectory,
@@ -1663,7 +2045,18 @@
             #stdoutEncoding: stdoutEncoding,
             #stderrEncoding: stderrEncoding
           }),
-          returnValue: _FakeProcessResult_53()) as _i15.ProcessResult);
+          returnValue: _FakeProcessResult_53(
+              this,
+              Invocation.method(#runSync, [
+                command
+              ], {
+                #workingDirectory: workingDirectory,
+                #environment: environment,
+                #includeParentEnvironment: includeParentEnvironment,
+                #runInShell: runInShell,
+                #stdoutEncoding: stdoutEncoding,
+                #stderrEncoding: stderrEncoding
+              }))) as _i15.ProcessResult);
   @override
   bool canRun(dynamic executable, {String? workingDirectory}) =>
       (super.noSuchMethod(Invocation.method(#canRun, [executable], {#workingDirectory: workingDirectory}),
@@ -1683,7 +2076,8 @@
 
   @override
   _i10.GitHub get github =>
-      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12()) as _i10.GitHub);
+      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12(this, Invocation.getter(#github)))
+          as _i10.GitHub);
   @override
   _i17.Stream<_i10.PullRequest> list(_i10.RepositorySlug? slug,
           {int? pages,
@@ -1695,60 +2089,69 @@
       (super.noSuchMethod(
           Invocation.method(#list, [slug],
               {#pages: pages, #base: base, #direction: direction, #head: head, #sort: sort, #state: state}),
-          returnValue: Stream<_i10.PullRequest>.empty()) as _i17.Stream<_i10.PullRequest>);
+          returnValue: _i17.Stream<_i10.PullRequest>.empty()) as _i17.Stream<_i10.PullRequest>);
   @override
-  _i17.Future<_i10.PullRequest> get(_i10.RepositorySlug? slug, int? number) =>
-      (super.noSuchMethod(Invocation.method(#get, [slug, number]),
-          returnValue: Future<_i10.PullRequest>.value(_FakePullRequest_22())) as _i17.Future<_i10.PullRequest>);
+  _i17.Future<_i10.PullRequest> get(_i10.RepositorySlug? slug, int? number) => (super.noSuchMethod(
+          Invocation.method(#get, [slug, number]),
+          returnValue:
+              _i17.Future<_i10.PullRequest>.value(_FakePullRequest_22(this, Invocation.method(#get, [slug, number]))))
+      as _i17.Future<_i10.PullRequest>);
   @override
   _i17.Future<_i10.PullRequest> create(_i10.RepositorySlug? slug, _i10.CreatePullRequest? request) =>
       (super.noSuchMethod(Invocation.method(#create, [slug, request]),
-          returnValue: Future<_i10.PullRequest>.value(_FakePullRequest_22())) as _i17.Future<_i10.PullRequest>);
+              returnValue: _i17.Future<_i10.PullRequest>.value(
+                  _FakePullRequest_22(this, Invocation.method(#create, [slug, request]))))
+          as _i17.Future<_i10.PullRequest>);
   @override
   _i17.Future<_i10.PullRequest> edit(_i10.RepositorySlug? slug, int? number,
           {String? title, String? body, String? state, String? base}) =>
       (super.noSuchMethod(
-          Invocation.method(#edit, [slug, number], {#title: title, #body: body, #state: state, #base: base}),
-          returnValue: Future<_i10.PullRequest>.value(_FakePullRequest_22())) as _i17.Future<_i10.PullRequest>);
+              Invocation.method(#edit, [slug, number], {#title: title, #body: body, #state: state, #base: base}),
+              returnValue: _i17.Future<_i10.PullRequest>.value(_FakePullRequest_22(this,
+                  Invocation.method(#edit, [slug, number], {#title: title, #body: body, #state: state, #base: base}))))
+          as _i17.Future<_i10.PullRequest>);
   @override
   _i17.Stream<_i10.RepositoryCommit> listCommits(_i10.RepositorySlug? slug, int? number) =>
       (super.noSuchMethod(Invocation.method(#listCommits, [slug, number]),
-          returnValue: Stream<_i10.RepositoryCommit>.empty()) as _i17.Stream<_i10.RepositoryCommit>);
+          returnValue: _i17.Stream<_i10.RepositoryCommit>.empty()) as _i17.Stream<_i10.RepositoryCommit>);
   @override
   _i17.Stream<_i10.PullRequestFile> listFiles(_i10.RepositorySlug? slug, int? number) =>
       (super.noSuchMethod(Invocation.method(#listFiles, [slug, number]),
-          returnValue: Stream<_i10.PullRequestFile>.empty()) as _i17.Stream<_i10.PullRequestFile>);
+          returnValue: _i17.Stream<_i10.PullRequestFile>.empty()) as _i17.Stream<_i10.PullRequestFile>);
   @override
   _i17.Stream<_i10.PullRequestReview> listReviews(_i10.RepositorySlug? slug, int? number) =>
       (super.noSuchMethod(Invocation.method(#listReviews, [slug, number]),
-          returnValue: Stream<_i10.PullRequestReview>.empty()) as _i17.Stream<_i10.PullRequestReview>);
+          returnValue: _i17.Stream<_i10.PullRequestReview>.empty()) as _i17.Stream<_i10.PullRequestReview>);
   @override
   _i17.Future<bool> isMerged(_i10.RepositorySlug? slug, int? number) =>
-      (super.noSuchMethod(Invocation.method(#isMerged, [slug, number]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#isMerged, [slug, number]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Future<_i10.PullRequestMerge> merge(_i10.RepositorySlug? slug, int? number, {String? message}) =>
       (super.noSuchMethod(Invocation.method(#merge, [slug, number], {#message: message}),
-              returnValue: Future<_i10.PullRequestMerge>.value(_FakePullRequestMerge_54()))
+              returnValue: _i17.Future<_i10.PullRequestMerge>.value(
+                  _FakePullRequestMerge_54(this, Invocation.method(#merge, [slug, number], {#message: message}))))
           as _i17.Future<_i10.PullRequestMerge>);
   @override
   _i17.Stream<_i10.PullRequestComment> listCommentsByPullRequest(_i10.RepositorySlug? slug, int? number) =>
       (super.noSuchMethod(Invocation.method(#listCommentsByPullRequest, [slug, number]),
-          returnValue: Stream<_i10.PullRequestComment>.empty()) as _i17.Stream<_i10.PullRequestComment>);
+          returnValue: _i17.Stream<_i10.PullRequestComment>.empty()) as _i17.Stream<_i10.PullRequestComment>);
   @override
-  _i17.Stream<_i10.PullRequestComment> listComments(_i10.RepositorySlug? slug) => (super
-          .noSuchMethod(Invocation.method(#listComments, [slug]), returnValue: Stream<_i10.PullRequestComment>.empty())
-      as _i17.Stream<_i10.PullRequestComment>);
+  _i17.Stream<_i10.PullRequestComment> listComments(_i10.RepositorySlug? slug) =>
+      (super.noSuchMethod(Invocation.method(#listComments, [slug]),
+          returnValue: _i17.Stream<_i10.PullRequestComment>.empty()) as _i17.Stream<_i10.PullRequestComment>);
   @override
   _i17.Future<_i10.PullRequestComment> createComment(
           _i10.RepositorySlug? slug, int? number, _i10.CreatePullRequestComment? comment) =>
       (super.noSuchMethod(Invocation.method(#createComment, [slug, number, comment]),
-              returnValue: Future<_i10.PullRequestComment>.value(_FakePullRequestComment_55()))
+              returnValue: _i17.Future<_i10.PullRequestComment>.value(
+                  _FakePullRequestComment_55(this, Invocation.method(#createComment, [slug, number, comment]))))
           as _i17.Future<_i10.PullRequestComment>);
   @override
   _i17.Future<_i10.PullRequestReview> createReview(_i10.RepositorySlug? slug, _i10.CreatePullRequestReview? review) =>
       (super.noSuchMethod(Invocation.method(#createReview, [slug, review]),
-              returnValue: Future<_i10.PullRequestReview>.value(_FakePullRequestReview_56()))
+              returnValue: _i17.Future<_i10.PullRequestReview>.value(
+                  _FakePullRequestReview_56(this, Invocation.method(#createReview, [slug, review]))))
           as _i17.Future<_i10.PullRequestReview>);
 }
 
@@ -1762,42 +2165,48 @@
 
   @override
   _i10.GitHub get github =>
-      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12()) as _i10.GitHub);
+      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12(this, Invocation.getter(#github)))
+          as _i10.GitHub);
   @override
   _i17.Stream<_i10.Repository> listRepositories(
           {String? type = r'owner', String? sort = r'full_name', String? direction = r'asc'}) =>
       (super.noSuchMethod(Invocation.method(#listRepositories, [], {#type: type, #sort: sort, #direction: direction}),
-          returnValue: Stream<_i10.Repository>.empty()) as _i17.Stream<_i10.Repository>);
+          returnValue: _i17.Stream<_i10.Repository>.empty()) as _i17.Stream<_i10.Repository>);
   @override
   _i17.Stream<_i10.Repository> listUserRepositories(String? user,
           {String? type = r'owner', String? sort = r'full_name', String? direction = r'asc'}) =>
       (super.noSuchMethod(
           Invocation.method(#listUserRepositories, [user], {#type: type, #sort: sort, #direction: direction}),
-          returnValue: Stream<_i10.Repository>.empty()) as _i17.Stream<_i10.Repository>);
+          returnValue: _i17.Stream<_i10.Repository>.empty()) as _i17.Stream<_i10.Repository>);
   @override
   _i17.Stream<_i10.Repository> listOrganizationRepositories(String? org, {String? type = r'all'}) =>
       (super.noSuchMethod(Invocation.method(#listOrganizationRepositories, [org], {#type: type}),
-          returnValue: Stream<_i10.Repository>.empty()) as _i17.Stream<_i10.Repository>);
+          returnValue: _i17.Stream<_i10.Repository>.empty()) as _i17.Stream<_i10.Repository>);
   @override
   _i17.Stream<_i10.Repository> listPublicRepositories({int? limit = 50, DateTime? since}) =>
       (super.noSuchMethod(Invocation.method(#listPublicRepositories, [], {#limit: limit, #since: since}),
-          returnValue: Stream<_i10.Repository>.empty()) as _i17.Stream<_i10.Repository>);
+          returnValue: _i17.Stream<_i10.Repository>.empty()) as _i17.Stream<_i10.Repository>);
   @override
   _i17.Future<_i10.Repository> createRepository(_i10.CreateRepository? repository, {String? org}) =>
       (super.noSuchMethod(Invocation.method(#createRepository, [repository], {#org: org}),
-          returnValue: Future<_i10.Repository>.value(_FakeRepository_57())) as _i17.Future<_i10.Repository>);
+              returnValue: _i17.Future<_i10.Repository>.value(
+                  _FakeRepository_57(this, Invocation.method(#createRepository, [repository], {#org: org}))))
+          as _i17.Future<_i10.Repository>);
   @override
   _i17.Future<_i10.LicenseDetails> getLicense(_i10.RepositorySlug? slug) => (super.noSuchMethod(
       Invocation.method(#getLicense, [slug]),
-      returnValue: Future<_i10.LicenseDetails>.value(_FakeLicenseDetails_58())) as _i17.Future<_i10.LicenseDetails>);
+      returnValue: _i17.Future<_i10.LicenseDetails>.value(
+          _FakeLicenseDetails_58(this, Invocation.method(#getLicense, [slug])))) as _i17.Future<_i10.LicenseDetails>);
   @override
-  _i17.Future<_i10.Repository> getRepository(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#getRepository, [slug]),
-          returnValue: Future<_i10.Repository>.value(_FakeRepository_57())) as _i17.Future<_i10.Repository>);
+  _i17.Future<_i10.Repository> getRepository(_i10.RepositorySlug? slug) => (super.noSuchMethod(
+          Invocation.method(#getRepository, [slug]),
+          returnValue:
+              _i17.Future<_i10.Repository>.value(_FakeRepository_57(this, Invocation.method(#getRepository, [slug]))))
+      as _i17.Future<_i10.Repository>);
   @override
-  _i17.Stream<_i10.Repository> getRepositories(List<_i10.RepositorySlug>? slugs) =>
-      (super.noSuchMethod(Invocation.method(#getRepositories, [slugs]), returnValue: Stream<_i10.Repository>.empty())
-          as _i17.Stream<_i10.Repository>);
+  _i17.Stream<_i10.Repository> getRepositories(List<_i10.RepositorySlug>? slugs) => (super
+          .noSuchMethod(Invocation.method(#getRepositories, [slugs]), returnValue: _i17.Stream<_i10.Repository>.empty())
+      as _i17.Stream<_i10.Repository>);
   @override
   _i17.Future<_i10.Repository> editRepository(_i10.RepositorySlug? slug,
           {String? name,
@@ -1819,146 +2228,178 @@
             #hasWiki: hasWiki,
             #hasDownloads: hasDownloads
           }),
-          returnValue: Future<_i10.Repository>.value(_FakeRepository_57())) as _i17.Future<_i10.Repository>);
+          returnValue: _i17.Future<_i10.Repository>.value(_FakeRepository_57(
+              this,
+              Invocation.method(#editRepository, [
+                slug
+              ], {
+                #name: name,
+                #description: description,
+                #homepage: homepage,
+                #private: private,
+                #hasIssues: hasIssues,
+                #hasWiki: hasWiki,
+                #hasDownloads: hasDownloads
+              })))) as _i17.Future<_i10.Repository>);
   @override
   _i17.Future<bool> deleteRepository(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#deleteRepository, [slug]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#deleteRepository, [slug]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.Contributor> listContributors(_i10.RepositorySlug? slug, {bool? anon = false}) =>
       (super.noSuchMethod(Invocation.method(#listContributors, [slug], {#anon: anon}),
-          returnValue: Stream<_i10.Contributor>.empty()) as _i17.Stream<_i10.Contributor>);
+          returnValue: _i17.Stream<_i10.Contributor>.empty()) as _i17.Stream<_i10.Contributor>);
   @override
   _i17.Stream<_i10.Team> listTeams(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listTeams, [slug]), returnValue: Stream<_i10.Team>.empty())
+      (super.noSuchMethod(Invocation.method(#listTeams, [slug]), returnValue: _i17.Stream<_i10.Team>.empty())
           as _i17.Stream<_i10.Team>);
   @override
   _i17.Future<_i10.LanguageBreakdown> listLanguages(_i10.RepositorySlug? slug) =>
       (super.noSuchMethod(Invocation.method(#listLanguages, [slug]),
-              returnValue: Future<_i10.LanguageBreakdown>.value(_FakeLanguageBreakdown_59()))
+              returnValue: _i17.Future<_i10.LanguageBreakdown>.value(
+                  _FakeLanguageBreakdown_59(this, Invocation.method(#listLanguages, [slug]))))
           as _i17.Future<_i10.LanguageBreakdown>);
   @override
   _i17.Stream<_i10.Tag> listTags(_i10.RepositorySlug? slug, {int? page = 1, int? pages, int? perPage = 30}) =>
       (super.noSuchMethod(Invocation.method(#listTags, [slug], {#page: page, #pages: pages, #perPage: perPage}),
-          returnValue: Stream<_i10.Tag>.empty()) as _i17.Stream<_i10.Tag>);
+          returnValue: _i17.Stream<_i10.Tag>.empty()) as _i17.Stream<_i10.Tag>);
   @override
   _i17.Stream<_i10.Branch> listBranches(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listBranches, [slug]), returnValue: Stream<_i10.Branch>.empty())
+      (super.noSuchMethod(Invocation.method(#listBranches, [slug]), returnValue: _i17.Stream<_i10.Branch>.empty())
           as _i17.Stream<_i10.Branch>);
   @override
   _i17.Future<_i10.Branch> getBranch(_i10.RepositorySlug? slug, String? branch) =>
       (super.noSuchMethod(Invocation.method(#getBranch, [slug, branch]),
-          returnValue: Future<_i10.Branch>.value(_FakeBranch_60())) as _i17.Future<_i10.Branch>);
+              returnValue:
+                  _i17.Future<_i10.Branch>.value(_FakeBranch_60(this, Invocation.method(#getBranch, [slug, branch]))))
+          as _i17.Future<_i10.Branch>);
   @override
   _i17.Stream<_i10.Collaborator> listCollaborators(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listCollaborators, [slug]), returnValue: Stream<_i10.Collaborator>.empty())
-          as _i17.Stream<_i10.Collaborator>);
+      (super.noSuchMethod(Invocation.method(#listCollaborators, [slug]),
+          returnValue: _i17.Stream<_i10.Collaborator>.empty()) as _i17.Stream<_i10.Collaborator>);
   @override
   _i17.Future<bool> isCollaborator(_i10.RepositorySlug? slug, String? user) =>
-      (super.noSuchMethod(Invocation.method(#isCollaborator, [slug, user]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#isCollaborator, [slug, user]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
-  _i17.Future<bool> addCollaborator(_i10.RepositorySlug? slug, String? user) =>
-      (super.noSuchMethod(Invocation.method(#addCollaborator, [slug, user]), returnValue: Future<bool>.value(false))
-          as _i17.Future<bool>);
+  _i17.Future<bool> addCollaborator(_i10.RepositorySlug? slug, String? user) => (super
+          .noSuchMethod(Invocation.method(#addCollaborator, [slug, user]), returnValue: _i17.Future<bool>.value(false))
+      as _i17.Future<bool>);
   @override
   _i17.Future<bool> removeCollaborator(_i10.RepositorySlug? slug, String? user) =>
-      (super.noSuchMethod(Invocation.method(#removeCollaborator, [slug, user]), returnValue: Future<bool>.value(false))
-          as _i17.Future<bool>);
+      (super.noSuchMethod(Invocation.method(#removeCollaborator, [slug, user]),
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.CommitComment> listSingleCommitComments(_i10.RepositorySlug? slug, _i10.RepositoryCommit? commit) =>
       (super.noSuchMethod(Invocation.method(#listSingleCommitComments, [slug, commit]),
-          returnValue: Stream<_i10.CommitComment>.empty()) as _i17.Stream<_i10.CommitComment>);
+          returnValue: _i17.Stream<_i10.CommitComment>.empty()) as _i17.Stream<_i10.CommitComment>);
   @override
-  _i17.Stream<_i10.CommitComment> listCommitComments(_i10.RepositorySlug? slug) => (super
-          .noSuchMethod(Invocation.method(#listCommitComments, [slug]), returnValue: Stream<_i10.CommitComment>.empty())
-      as _i17.Stream<_i10.CommitComment>);
+  _i17.Stream<_i10.CommitComment> listCommitComments(_i10.RepositorySlug? slug) =>
+      (super.noSuchMethod(Invocation.method(#listCommitComments, [slug]),
+          returnValue: _i17.Stream<_i10.CommitComment>.empty()) as _i17.Stream<_i10.CommitComment>);
   @override
   _i17.Future<_i10.CommitComment> createCommitComment(_i10.RepositorySlug? slug, _i10.RepositoryCommit? commit,
           {String? body, String? path, int? position, int? line}) =>
-      (super.noSuchMethod(
-          Invocation.method(
-              #createCommitComment, [slug, commit], {#body: body, #path: path, #position: position, #line: line}),
-          returnValue: Future<_i10.CommitComment>.value(_FakeCommitComment_61())) as _i17.Future<_i10.CommitComment>);
+      (super.noSuchMethod(Invocation.method(#createCommitComment, [slug, commit], {#body: body, #path: path, #position: position, #line: line}),
+          returnValue: _i17.Future<_i10.CommitComment>.value(_FakeCommitComment_61(
+              this,
+              Invocation.method(#createCommitComment, [slug, commit],
+                  {#body: body, #path: path, #position: position, #line: line})))) as _i17.Future<_i10.CommitComment>);
   @override
   _i17.Future<_i10.CommitComment> getCommitComment(_i10.RepositorySlug? slug, {int? id}) =>
       (super.noSuchMethod(Invocation.method(#getCommitComment, [slug], {#id: id}),
-          returnValue: Future<_i10.CommitComment>.value(_FakeCommitComment_61())) as _i17.Future<_i10.CommitComment>);
+              returnValue: _i17.Future<_i10.CommitComment>.value(
+                  _FakeCommitComment_61(this, Invocation.method(#getCommitComment, [slug], {#id: id}))))
+          as _i17.Future<_i10.CommitComment>);
   @override
   _i17.Future<_i10.CommitComment> updateCommitComment(_i10.RepositorySlug? slug, {int? id, String? body}) =>
       (super.noSuchMethod(Invocation.method(#updateCommitComment, [slug], {#id: id, #body: body}),
-          returnValue: Future<_i10.CommitComment>.value(_FakeCommitComment_61())) as _i17.Future<_i10.CommitComment>);
+              returnValue: _i17.Future<_i10.CommitComment>.value(
+                  _FakeCommitComment_61(this, Invocation.method(#updateCommitComment, [slug], {#id: id, #body: body}))))
+          as _i17.Future<_i10.CommitComment>);
   @override
   _i17.Future<bool> deleteCommitComment(_i10.RepositorySlug? slug, {int? id}) =>
       (super.noSuchMethod(Invocation.method(#deleteCommitComment, [slug], {#id: id}),
-          returnValue: Future<bool>.value(false)) as _i17.Future<bool>);
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.RepositoryCommit> listCommits(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listCommits, [slug]), returnValue: Stream<_i10.RepositoryCommit>.empty())
-          as _i17.Stream<_i10.RepositoryCommit>);
+      (super.noSuchMethod(Invocation.method(#listCommits, [slug]),
+          returnValue: _i17.Stream<_i10.RepositoryCommit>.empty()) as _i17.Stream<_i10.RepositoryCommit>);
   @override
   _i17.Future<_i10.RepositoryCommit> getCommit(_i10.RepositorySlug? slug, String? sha) =>
       (super.noSuchMethod(Invocation.method(#getCommit, [slug, sha]),
-              returnValue: Future<_i10.RepositoryCommit>.value(_FakeRepositoryCommit_62()))
+              returnValue: _i17.Future<_i10.RepositoryCommit>.value(
+                  _FakeRepositoryCommit_62(this, Invocation.method(#getCommit, [slug, sha]))))
           as _i17.Future<_i10.RepositoryCommit>);
   @override
   _i17.Future<String> getCommitDiff(_i10.RepositorySlug? slug, String? sha) =>
-      (super.noSuchMethod(Invocation.method(#getCommitDiff, [slug, sha]), returnValue: Future<String>.value(''))
+      (super.noSuchMethod(Invocation.method(#getCommitDiff, [slug, sha]), returnValue: _i17.Future<String>.value(''))
           as _i17.Future<String>);
   @override
   _i17.Future<_i10.GitHubComparison> compareCommits(_i10.RepositorySlug? slug, String? refBase, String? refHead) =>
       (super.noSuchMethod(Invocation.method(#compareCommits, [slug, refBase, refHead]),
-              returnValue: Future<_i10.GitHubComparison>.value(_FakeGitHubComparison_63()))
+              returnValue: _i17.Future<_i10.GitHubComparison>.value(
+                  _FakeGitHubComparison_63(this, Invocation.method(#compareCommits, [slug, refBase, refHead]))))
           as _i17.Future<_i10.GitHubComparison>);
   @override
   _i17.Future<_i10.GitHubFile> getReadme(_i10.RepositorySlug? slug, {String? ref}) =>
       (super.noSuchMethod(Invocation.method(#getReadme, [slug], {#ref: ref}),
-          returnValue: Future<_i10.GitHubFile>.value(_FakeGitHubFile_64())) as _i17.Future<_i10.GitHubFile>);
+              returnValue: _i17.Future<_i10.GitHubFile>.value(
+                  _FakeGitHubFile_64(this, Invocation.method(#getReadme, [slug], {#ref: ref}))))
+          as _i17.Future<_i10.GitHubFile>);
   @override
   _i17.Future<_i10.RepositoryContents> getContents(_i10.RepositorySlug? slug, String? path, {String? ref}) =>
       (super.noSuchMethod(Invocation.method(#getContents, [slug, path], {#ref: ref}),
-              returnValue: Future<_i10.RepositoryContents>.value(_FakeRepositoryContents_65()))
+              returnValue: _i17.Future<_i10.RepositoryContents>.value(
+                  _FakeRepositoryContents_65(this, Invocation.method(#getContents, [slug, path], {#ref: ref}))))
           as _i17.Future<_i10.RepositoryContents>);
   @override
-  _i17.Future<_i10.ContentCreation> createFile(_i10.RepositorySlug? slug, _i10.CreateFile? file) => (super.noSuchMethod(
-      Invocation.method(#createFile, [slug, file]),
-      returnValue: Future<_i10.ContentCreation>.value(_FakeContentCreation_66())) as _i17.Future<_i10.ContentCreation>);
+  _i17.Future<_i10.ContentCreation> createFile(_i10.RepositorySlug? slug, _i10.CreateFile? file) =>
+      (super.noSuchMethod(Invocation.method(#createFile, [slug, file]),
+              returnValue: _i17.Future<_i10.ContentCreation>.value(
+                  _FakeContentCreation_66(this, Invocation.method(#createFile, [slug, file]))))
+          as _i17.Future<_i10.ContentCreation>);
   @override
   _i17.Future<_i10.ContentCreation> updateFile(
           _i10.RepositorySlug? slug, String? path, String? message, String? content, String? sha, {String? branch}) =>
       (super.noSuchMethod(Invocation.method(#updateFile, [slug, path, message, content, sha], {#branch: branch}),
-              returnValue: Future<_i10.ContentCreation>.value(_FakeContentCreation_66()))
+              returnValue: _i17.Future<_i10.ContentCreation>.value(_FakeContentCreation_66(
+                  this, Invocation.method(#updateFile, [slug, path, message, content, sha], {#branch: branch}))))
           as _i17.Future<_i10.ContentCreation>);
   @override
   _i17.Future<_i10.ContentCreation> deleteFile(
           _i10.RepositorySlug? slug, String? path, String? message, String? sha, String? branch) =>
       (super.noSuchMethod(Invocation.method(#deleteFile, [slug, path, message, sha, branch]),
-              returnValue: Future<_i10.ContentCreation>.value(_FakeContentCreation_66()))
+              returnValue: _i17.Future<_i10.ContentCreation>.value(
+                  _FakeContentCreation_66(this, Invocation.method(#deleteFile, [slug, path, message, sha, branch]))))
           as _i17.Future<_i10.ContentCreation>);
   @override
   _i17.Future<String?> getArchiveLink(_i10.RepositorySlug? slug, String? ref, {String? format = r'tarball'}) =>
       (super.noSuchMethod(Invocation.method(#getArchiveLink, [slug, ref], {#format: format}),
-          returnValue: Future<String?>.value()) as _i17.Future<String?>);
+          returnValue: _i17.Future<String?>.value()) as _i17.Future<String?>);
   @override
   _i17.Stream<_i10.Repository> listForks(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listForks, [slug]), returnValue: Stream<_i10.Repository>.empty())
+      (super.noSuchMethod(Invocation.method(#listForks, [slug]), returnValue: _i17.Stream<_i10.Repository>.empty())
           as _i17.Stream<_i10.Repository>);
   @override
   _i17.Future<_i10.Repository> createFork(_i10.RepositorySlug? slug, [_i10.CreateFork? fork]) =>
       (super.noSuchMethod(Invocation.method(#createFork, [slug, fork]),
-          returnValue: Future<_i10.Repository>.value(_FakeRepository_57())) as _i17.Future<_i10.Repository>);
+          returnValue: _i17.Future<_i10.Repository>.value(
+              _FakeRepository_57(this, Invocation.method(#createFork, [slug, fork])))) as _i17.Future<_i10.Repository>);
   @override
   _i17.Stream<_i10.Hook> listHooks(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listHooks, [slug]), returnValue: Stream<_i10.Hook>.empty())
+      (super.noSuchMethod(Invocation.method(#listHooks, [slug]), returnValue: _i17.Stream<_i10.Hook>.empty())
           as _i17.Stream<_i10.Hook>);
   @override
   _i17.Future<_i10.Hook> getHook(_i10.RepositorySlug? slug, int? id) =>
-      (super.noSuchMethod(Invocation.method(#getHook, [slug, id]), returnValue: Future<_i10.Hook>.value(_FakeHook_67()))
+      (super.noSuchMethod(Invocation.method(#getHook, [slug, id]),
+              returnValue: _i17.Future<_i10.Hook>.value(_FakeHook_67(this, Invocation.method(#getHook, [slug, id]))))
           as _i17.Future<_i10.Hook>);
   @override
-  _i17.Future<_i10.Hook> createHook(_i10.RepositorySlug? slug, _i10.CreateHook? hook) =>
-      (super.noSuchMethod(Invocation.method(#createHook, [slug, hook]),
-          returnValue: Future<_i10.Hook>.value(_FakeHook_67())) as _i17.Future<_i10.Hook>);
+  _i17.Future<_i10.Hook> createHook(_i10.RepositorySlug? slug, _i10.CreateHook? hook) => (super.noSuchMethod(
+          Invocation.method(#createHook, [slug, hook]),
+          returnValue: _i17.Future<_i10.Hook>.value(_FakeHook_67(this, Invocation.method(#createHook, [slug, hook]))))
+      as _i17.Future<_i10.Hook>);
   @override
   _i17.Future<_i10.Hook> editHook(_i10.RepositorySlug? slug, _i10.Hook? hookToEdit,
           {String? configUrl,
@@ -1983,156 +2424,196 @@
             #removeEvents: removeEvents,
             #active: active
           }),
-          returnValue: Future<_i10.Hook>.value(_FakeHook_67())) as _i17.Future<_i10.Hook>);
+          returnValue: _i17.Future<_i10.Hook>.value(_FakeHook_67(
+              this,
+              Invocation.method(#editHook, [
+                slug,
+                hookToEdit
+              ], {
+                #configUrl: configUrl,
+                #configContentType: configContentType,
+                #configSecret: configSecret,
+                #configInsecureSsl: configInsecureSsl,
+                #events: events,
+                #addEvents: addEvents,
+                #removeEvents: removeEvents,
+                #active: active
+              })))) as _i17.Future<_i10.Hook>);
   @override
   _i17.Future<bool> testPushHook(_i10.RepositorySlug? slug, int? id) =>
-      (super.noSuchMethod(Invocation.method(#testPushHook, [slug, id]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#testPushHook, [slug, id]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Future<bool> pingHook(_i10.RepositorySlug? slug, int? id) =>
-      (super.noSuchMethod(Invocation.method(#pingHook, [slug, id]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#pingHook, [slug, id]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Future<bool> deleteHook(_i10.RepositorySlug? slug, int? id) =>
-      (super.noSuchMethod(Invocation.method(#deleteHook, [slug, id]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#deleteHook, [slug, id]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.PublicKey> listDeployKeys(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listDeployKeys, [slug]), returnValue: Stream<_i10.PublicKey>.empty())
+      (super.noSuchMethod(Invocation.method(#listDeployKeys, [slug]), returnValue: _i17.Stream<_i10.PublicKey>.empty())
           as _i17.Stream<_i10.PublicKey>);
   @override
   _i17.Future<_i10.PublicKey> getDeployKey(_i10.RepositorySlug? slug, {int? id}) =>
       (super.noSuchMethod(Invocation.method(#getDeployKey, [slug], {#id: id}),
-          returnValue: Future<_i10.PublicKey>.value(_FakePublicKey_68())) as _i17.Future<_i10.PublicKey>);
+              returnValue: _i17.Future<_i10.PublicKey>.value(
+                  _FakePublicKey_68(this, Invocation.method(#getDeployKey, [slug], {#id: id}))))
+          as _i17.Future<_i10.PublicKey>);
   @override
   _i17.Future<_i10.PublicKey> createDeployKey(_i10.RepositorySlug? slug, _i10.CreatePublicKey? key) =>
       (super.noSuchMethod(Invocation.method(#createDeployKey, [slug, key]),
-          returnValue: Future<_i10.PublicKey>.value(_FakePublicKey_68())) as _i17.Future<_i10.PublicKey>);
+              returnValue: _i17.Future<_i10.PublicKey>.value(
+                  _FakePublicKey_68(this, Invocation.method(#createDeployKey, [slug, key]))))
+          as _i17.Future<_i10.PublicKey>);
   @override
   _i17.Future<bool> deleteDeployKey({_i10.RepositorySlug? slug, _i10.PublicKey? key}) =>
       (super.noSuchMethod(Invocation.method(#deleteDeployKey, [], {#slug: slug, #key: key}),
-          returnValue: Future<bool>.value(false)) as _i17.Future<bool>);
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
   @override
   _i17.Future<_i10.RepositoryCommit> merge(_i10.RepositorySlug? slug, _i10.CreateMerge? merge) =>
       (super.noSuchMethod(Invocation.method(#merge, [slug, merge]),
-              returnValue: Future<_i10.RepositoryCommit>.value(_FakeRepositoryCommit_62()))
+              returnValue: _i17.Future<_i10.RepositoryCommit>.value(
+                  _FakeRepositoryCommit_62(this, Invocation.method(#merge, [slug, merge]))))
           as _i17.Future<_i10.RepositoryCommit>);
   @override
-  _i17.Future<_i10.RepositoryPages> getPagesInfo(_i10.RepositorySlug? slug) => (super.noSuchMethod(
-      Invocation.method(#getPagesInfo, [slug]),
-      returnValue: Future<_i10.RepositoryPages>.value(_FakeRepositoryPages_69())) as _i17.Future<_i10.RepositoryPages>);
+  _i17.Future<_i10.RepositoryPages> getPagesInfo(_i10.RepositorySlug? slug) =>
+      (super.noSuchMethod(Invocation.method(#getPagesInfo, [slug]),
+              returnValue: _i17.Future<_i10.RepositoryPages>.value(
+                  _FakeRepositoryPages_69(this, Invocation.method(#getPagesInfo, [slug]))))
+          as _i17.Future<_i10.RepositoryPages>);
   @override
   _i17.Stream<_i10.PageBuild> listPagesBuilds(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listPagesBuilds, [slug]), returnValue: Stream<_i10.PageBuild>.empty())
+      (super.noSuchMethod(Invocation.method(#listPagesBuilds, [slug]), returnValue: _i17.Stream<_i10.PageBuild>.empty())
           as _i17.Stream<_i10.PageBuild>);
   @override
-  _i17.Future<_i10.PageBuild> getLatestPagesBuild(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#getLatestPagesBuild, [slug]),
-          returnValue: Future<_i10.PageBuild>.value(_FakePageBuild_70())) as _i17.Future<_i10.PageBuild>);
+  _i17.Future<_i10.PageBuild> getLatestPagesBuild(_i10.RepositorySlug? slug) => (super.noSuchMethod(
+      Invocation.method(#getLatestPagesBuild, [slug]),
+      returnValue: _i17.Future<_i10.PageBuild>.value(
+          _FakePageBuild_70(this, Invocation.method(#getLatestPagesBuild, [slug])))) as _i17.Future<_i10.PageBuild>);
   @override
   _i17.Stream<_i10.Release> listReleases(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listReleases, [slug]), returnValue: Stream<_i10.Release>.empty())
+      (super.noSuchMethod(Invocation.method(#listReleases, [slug]), returnValue: _i17.Stream<_i10.Release>.empty())
           as _i17.Stream<_i10.Release>);
   @override
   _i17.Future<_i10.Release> getLatestRelease(_i10.RepositorySlug? slug) =>
       (super.noSuchMethod(Invocation.method(#getLatestRelease, [slug]),
-          returnValue: Future<_i10.Release>.value(_FakeRelease_71())) as _i17.Future<_i10.Release>);
+              returnValue:
+                  _i17.Future<_i10.Release>.value(_FakeRelease_71(this, Invocation.method(#getLatestRelease, [slug]))))
+          as _i17.Future<_i10.Release>);
   @override
-  _i17.Future<_i10.Release> getReleaseById(_i10.RepositorySlug? slug, int? id) =>
-      (super.noSuchMethod(Invocation.method(#getReleaseById, [slug, id]),
-          returnValue: Future<_i10.Release>.value(_FakeRelease_71())) as _i17.Future<_i10.Release>);
+  _i17.Future<_i10.Release> getReleaseById(_i10.RepositorySlug? slug, int? id) => (super.noSuchMethod(
+          Invocation.method(#getReleaseById, [slug, id]),
+          returnValue:
+              _i17.Future<_i10.Release>.value(_FakeRelease_71(this, Invocation.method(#getReleaseById, [slug, id]))))
+      as _i17.Future<_i10.Release>);
   @override
   _i17.Future<_i10.Release> getReleaseByTagName(_i10.RepositorySlug? slug, String? tagName) =>
       (super.noSuchMethod(Invocation.method(#getReleaseByTagName, [slug, tagName]),
-          returnValue: Future<_i10.Release>.value(_FakeRelease_71())) as _i17.Future<_i10.Release>);
+              returnValue: _i17.Future<_i10.Release>.value(
+                  _FakeRelease_71(this, Invocation.method(#getReleaseByTagName, [slug, tagName]))))
+          as _i17.Future<_i10.Release>);
   @override
   _i17.Future<_i10.Release> createRelease(_i10.RepositorySlug? slug, _i10.CreateRelease? createRelease,
           {bool? getIfExists = true}) =>
       (super.noSuchMethod(Invocation.method(#createRelease, [slug, createRelease], {#getIfExists: getIfExists}),
-          returnValue: Future<_i10.Release>.value(_FakeRelease_71())) as _i17.Future<_i10.Release>);
+              returnValue: _i17.Future<_i10.Release>.value(_FakeRelease_71(
+                  this, Invocation.method(#createRelease, [slug, createRelease], {#getIfExists: getIfExists}))))
+          as _i17.Future<_i10.Release>);
   @override
   _i17.Future<_i10.Release> editRelease(_i10.RepositorySlug? slug, _i10.Release? releaseToEdit,
           {String? tagName, String? targetCommitish, String? name, String? body, bool? draft, bool? preRelease}) =>
-      (super.noSuchMethod(
-          Invocation.method(#editRelease, [
-            slug,
-            releaseToEdit
-          ], {
-            #tagName: tagName,
-            #targetCommitish: targetCommitish,
-            #name: name,
-            #body: body,
-            #draft: draft,
-            #preRelease: preRelease
-          }),
-          returnValue: Future<_i10.Release>.value(_FakeRelease_71())) as _i17.Future<_i10.Release>);
+      (super.noSuchMethod(Invocation.method(#editRelease, [slug, releaseToEdit], {#tagName: tagName, #targetCommitish: targetCommitish, #name: name, #body: body, #draft: draft, #preRelease: preRelease}),
+          returnValue: _i17.Future<_i10.Release>.value(_FakeRelease_71(
+              this,
+              Invocation.method(#editRelease, [
+                slug,
+                releaseToEdit
+              ], {
+                #tagName: tagName,
+                #targetCommitish: targetCommitish,
+                #name: name,
+                #body: body,
+                #draft: draft,
+                #preRelease: preRelease
+              })))) as _i17.Future<_i10.Release>);
   @override
-  _i17.Future<bool> deleteRelease(_i10.RepositorySlug? slug, _i10.Release? release) =>
-      (super.noSuchMethod(Invocation.method(#deleteRelease, [slug, release]), returnValue: Future<bool>.value(false))
-          as _i17.Future<bool>);
+  _i17.Future<bool> deleteRelease(_i10.RepositorySlug? slug, _i10.Release? release) => (super
+          .noSuchMethod(Invocation.method(#deleteRelease, [slug, release]), returnValue: _i17.Future<bool>.value(false))
+      as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.ReleaseAsset> listReleaseAssets(_i10.RepositorySlug? slug, _i10.Release? release) =>
       (super.noSuchMethod(Invocation.method(#listReleaseAssets, [slug, release]),
-          returnValue: Stream<_i10.ReleaseAsset>.empty()) as _i17.Stream<_i10.ReleaseAsset>);
+          returnValue: _i17.Stream<_i10.ReleaseAsset>.empty()) as _i17.Stream<_i10.ReleaseAsset>);
   @override
   _i17.Future<_i10.ReleaseAsset> getReleaseAsset(_i10.RepositorySlug? slug, _i10.Release? release, {int? assetId}) =>
       (super.noSuchMethod(Invocation.method(#getReleaseAsset, [slug, release], {#assetId: assetId}),
-          returnValue: Future<_i10.ReleaseAsset>.value(_FakeReleaseAsset_72())) as _i17.Future<_i10.ReleaseAsset>);
+              returnValue: _i17.Future<_i10.ReleaseAsset>.value(_FakeReleaseAsset_72(
+                  this, Invocation.method(#getReleaseAsset, [slug, release], {#assetId: assetId}))))
+          as _i17.Future<_i10.ReleaseAsset>);
   @override
   _i17.Future<_i10.ReleaseAsset> editReleaseAsset(_i10.RepositorySlug? slug, _i10.ReleaseAsset? assetToEdit,
           {String? name, String? label}) =>
       (super.noSuchMethod(Invocation.method(#editReleaseAsset, [slug, assetToEdit], {#name: name, #label: label}),
-          returnValue: Future<_i10.ReleaseAsset>.value(_FakeReleaseAsset_72())) as _i17.Future<_i10.ReleaseAsset>);
+              returnValue: _i17.Future<_i10.ReleaseAsset>.value(_FakeReleaseAsset_72(
+                  this, Invocation.method(#editReleaseAsset, [slug, assetToEdit], {#name: name, #label: label}))))
+          as _i17.Future<_i10.ReleaseAsset>);
   @override
   _i17.Future<bool> deleteReleaseAsset(_i10.RepositorySlug? slug, _i10.ReleaseAsset? asset) =>
-      (super.noSuchMethod(Invocation.method(#deleteReleaseAsset, [slug, asset]), returnValue: Future<bool>.value(false))
-          as _i17.Future<bool>);
+      (super.noSuchMethod(Invocation.method(#deleteReleaseAsset, [slug, asset]),
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
   @override
   _i17.Future<List<_i10.ReleaseAsset>> uploadReleaseAssets(
           _i10.Release? release, Iterable<_i10.CreateReleaseAsset>? createReleaseAssets) =>
       (super.noSuchMethod(Invocation.method(#uploadReleaseAssets, [release, createReleaseAssets]),
-              returnValue: Future<List<_i10.ReleaseAsset>>.value(<_i10.ReleaseAsset>[]))
+              returnValue: _i17.Future<List<_i10.ReleaseAsset>>.value(<_i10.ReleaseAsset>[]))
           as _i17.Future<List<_i10.ReleaseAsset>>);
   @override
   _i17.Future<List<_i10.ContributorStatistics>> listContributorStats(_i10.RepositorySlug? slug) =>
       (super.noSuchMethod(Invocation.method(#listContributorStats, [slug]),
-              returnValue: Future<List<_i10.ContributorStatistics>>.value(<_i10.ContributorStatistics>[]))
+              returnValue: _i17.Future<List<_i10.ContributorStatistics>>.value(<_i10.ContributorStatistics>[]))
           as _i17.Future<List<_i10.ContributorStatistics>>);
   @override
   _i17.Stream<_i10.YearCommitCountWeek> listCommitActivity(_i10.RepositorySlug? slug) =>
       (super.noSuchMethod(Invocation.method(#listCommitActivity, [slug]),
-          returnValue: Stream<_i10.YearCommitCountWeek>.empty()) as _i17.Stream<_i10.YearCommitCountWeek>);
+          returnValue: _i17.Stream<_i10.YearCommitCountWeek>.empty()) as _i17.Stream<_i10.YearCommitCountWeek>);
   @override
   _i17.Stream<_i10.WeeklyChangesCount> listCodeFrequency(_i10.RepositorySlug? slug) =>
       (super.noSuchMethod(Invocation.method(#listCodeFrequency, [slug]),
-          returnValue: Stream<_i10.WeeklyChangesCount>.empty()) as _i17.Stream<_i10.WeeklyChangesCount>);
+          returnValue: _i17.Stream<_i10.WeeklyChangesCount>.empty()) as _i17.Stream<_i10.WeeklyChangesCount>);
   @override
   _i17.Future<_i10.ContributorParticipation> getParticipation(_i10.RepositorySlug? slug) =>
       (super.noSuchMethod(Invocation.method(#getParticipation, [slug]),
-              returnValue: Future<_i10.ContributorParticipation>.value(_FakeContributorParticipation_73()))
+              returnValue: _i17.Future<_i10.ContributorParticipation>.value(
+                  _FakeContributorParticipation_73(this, Invocation.method(#getParticipation, [slug]))))
           as _i17.Future<_i10.ContributorParticipation>);
   @override
   _i17.Stream<_i10.PunchcardEntry> listPunchcard(_i10.RepositorySlug? slug) =>
-      (super.noSuchMethod(Invocation.method(#listPunchcard, [slug]), returnValue: Stream<_i10.PunchcardEntry>.empty())
-          as _i17.Stream<_i10.PunchcardEntry>);
+      (super.noSuchMethod(Invocation.method(#listPunchcard, [slug]),
+          returnValue: _i17.Stream<_i10.PunchcardEntry>.empty()) as _i17.Stream<_i10.PunchcardEntry>);
   @override
   _i17.Stream<_i10.RepositoryStatus> listStatuses(_i10.RepositorySlug? slug, String? ref) =>
       (super.noSuchMethod(Invocation.method(#listStatuses, [slug, ref]),
-          returnValue: Stream<_i10.RepositoryStatus>.empty()) as _i17.Stream<_i10.RepositoryStatus>);
+          returnValue: _i17.Stream<_i10.RepositoryStatus>.empty()) as _i17.Stream<_i10.RepositoryStatus>);
   @override
   _i17.Future<_i10.RepositoryStatus> createStatus(_i10.RepositorySlug? slug, String? ref, _i10.CreateStatus? request) =>
       (super.noSuchMethod(Invocation.method(#createStatus, [slug, ref, request]),
-              returnValue: Future<_i10.RepositoryStatus>.value(_FakeRepositoryStatus_74()))
+              returnValue: _i17.Future<_i10.RepositoryStatus>.value(
+                  _FakeRepositoryStatus_74(this, Invocation.method(#createStatus, [slug, ref, request]))))
           as _i17.Future<_i10.RepositoryStatus>);
   @override
   _i17.Future<_i10.CombinedRepositoryStatus> getCombinedStatus(_i10.RepositorySlug? slug, String? ref) =>
       (super.noSuchMethod(Invocation.method(#getCombinedStatus, [slug, ref]),
-              returnValue: Future<_i10.CombinedRepositoryStatus>.value(_FakeCombinedRepositoryStatus_75()))
+              returnValue: _i17.Future<_i10.CombinedRepositoryStatus>.value(
+                  _FakeCombinedRepositoryStatus_75(this, Invocation.method(#getCombinedStatus, [slug, ref]))))
           as _i17.Future<_i10.CombinedRepositoryStatus>);
   @override
   _i17.Future<_i10.ReleaseNotes> generateReleaseNotes(_i10.CreateReleaseNotes? crn) =>
       (super.noSuchMethod(Invocation.method(#generateReleaseNotes, [crn]),
-          returnValue: Future<_i10.ReleaseNotes>.value(_FakeReleaseNotes_76())) as _i17.Future<_i10.ReleaseNotes>);
+              returnValue: _i17.Future<_i10.ReleaseNotes>.value(
+                  _FakeReleaseNotes_76(this, Invocation.method(#generateReleaseNotes, [crn]))))
+          as _i17.Future<_i10.ReleaseNotes>);
 }
 
 /// A class which mocks [TabledataResource].
@@ -2148,7 +2629,8 @@
           _i6.TableDataInsertAllRequest? request, String? projectId, String? datasetId, String? tableId,
           {String? $fields}) =>
       (super.noSuchMethod(Invocation.method(#insertAll, [request, projectId, datasetId, tableId], {#$fields: $fields}),
-              returnValue: Future<_i6.TableDataInsertAllResponse>.value(_FakeTableDataInsertAllResponse_77()))
+              returnValue: _i17.Future<_i6.TableDataInsertAllResponse>.value(_FakeTableDataInsertAllResponse_77(
+                  this, Invocation.method(#insertAll, [request, projectId, datasetId, tableId], {#$fields: $fields}))))
           as _i17.Future<_i6.TableDataInsertAllResponse>);
   @override
   _i17.Future<_i6.TableDataList> list(String? projectId, String? datasetId, String? tableId,
@@ -2165,7 +2647,19 @@
             #startIndex: startIndex,
             #$fields: $fields
           }),
-          returnValue: Future<_i6.TableDataList>.value(_FakeTableDataList_78())) as _i17.Future<_i6.TableDataList>);
+          returnValue: _i17.Future<_i6.TableDataList>.value(_FakeTableDataList_78(
+              this,
+              Invocation.method(#list, [
+                projectId,
+                datasetId,
+                tableId
+              ], {
+                #maxResults: maxResults,
+                #pageToken: pageToken,
+                #selectedFields: selectedFields,
+                #startIndex: startIndex,
+                #$fields: $fields
+              })))) as _i17.Future<_i6.TableDataList>);
 }
 
 /// A class which mocks [UsersService].
@@ -2178,11 +2672,12 @@
 
   @override
   _i10.GitHub get github =>
-      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12()) as _i10.GitHub);
+      (super.noSuchMethod(Invocation.getter(#github), returnValue: _FakeGitHub_12(this, Invocation.getter(#github)))
+          as _i10.GitHub);
   @override
-  _i17.Future<_i10.User> getUser(String? name) =>
-      (super.noSuchMethod(Invocation.method(#getUser, [name]), returnValue: Future<_i10.User>.value(_FakeUser_79()))
-          as _i17.Future<_i10.User>);
+  _i17.Future<_i10.User> getUser(String? name) => (super.noSuchMethod(Invocation.method(#getUser, [name]),
+          returnValue: _i17.Future<_i10.User>.value(_FakeUser_79(this, Invocation.method(#getUser, [name]))))
+      as _i17.Future<_i10.User>);
   @override
   _i17.Future<_i10.CurrentUser> editCurrentUser(
           {String? name,
@@ -2192,80 +2687,86 @@
           String? location,
           bool? hireable,
           String? bio}) =>
-      (super.noSuchMethod(
-          Invocation.method(#editCurrentUser, [], {
-            #name: name,
-            #email: email,
-            #blog: blog,
-            #company: company,
-            #location: location,
-            #hireable: hireable,
-            #bio: bio
-          }),
-          returnValue: Future<_i10.CurrentUser>.value(_FakeCurrentUser_80())) as _i17.Future<_i10.CurrentUser>);
+      (super
+          .noSuchMethod(Invocation.method(#editCurrentUser, [], {#name: name, #email: email, #blog: blog, #company: company, #location: location, #hireable: hireable, #bio: bio}),
+              returnValue: _i17.Future<_i10.CurrentUser>.value(_FakeCurrentUser_80(
+                  this,
+                  Invocation.method(#editCurrentUser, [], {
+                    #name: name,
+                    #email: email,
+                    #blog: blog,
+                    #company: company,
+                    #location: location,
+                    #hireable: hireable,
+                    #bio: bio
+                  })))) as _i17.Future<_i10.CurrentUser>);
   @override
-  _i17.Stream<_i10.User> getUsers(List<String>? names, {int? pages}) => (super
-          .noSuchMethod(Invocation.method(#getUsers, [names], {#pages: pages}), returnValue: Stream<_i10.User>.empty())
-      as _i17.Stream<_i10.User>);
+  _i17.Stream<_i10.User> getUsers(List<String>? names, {int? pages}) =>
+      (super.noSuchMethod(Invocation.method(#getUsers, [names], {#pages: pages}),
+          returnValue: _i17.Stream<_i10.User>.empty()) as _i17.Stream<_i10.User>);
   @override
   _i17.Future<_i10.CurrentUser> getCurrentUser() => (super.noSuchMethod(Invocation.method(#getCurrentUser, []),
-      returnValue: Future<_i10.CurrentUser>.value(_FakeCurrentUser_80())) as _i17.Future<_i10.CurrentUser>);
+          returnValue:
+              _i17.Future<_i10.CurrentUser>.value(_FakeCurrentUser_80(this, Invocation.method(#getCurrentUser, []))))
+      as _i17.Future<_i10.CurrentUser>);
   @override
   _i17.Future<bool> isUser(String? name) =>
-      (super.noSuchMethod(Invocation.method(#isUser, [name]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#isUser, [name]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.User> listUsers({int? pages, int? since}) =>
       (super.noSuchMethod(Invocation.method(#listUsers, [], {#pages: pages, #since: since}),
-          returnValue: Stream<_i10.User>.empty()) as _i17.Stream<_i10.User>);
+          returnValue: _i17.Stream<_i10.User>.empty()) as _i17.Stream<_i10.User>);
   @override
   _i17.Stream<_i10.UserEmail> listEmails() =>
-      (super.noSuchMethod(Invocation.method(#listEmails, []), returnValue: Stream<_i10.UserEmail>.empty())
+      (super.noSuchMethod(Invocation.method(#listEmails, []), returnValue: _i17.Stream<_i10.UserEmail>.empty())
           as _i17.Stream<_i10.UserEmail>);
   @override
   _i17.Stream<_i10.UserEmail> addEmails(List<String>? emails) =>
-      (super.noSuchMethod(Invocation.method(#addEmails, [emails]), returnValue: Stream<_i10.UserEmail>.empty())
+      (super.noSuchMethod(Invocation.method(#addEmails, [emails]), returnValue: _i17.Stream<_i10.UserEmail>.empty())
           as _i17.Stream<_i10.UserEmail>);
   @override
   _i17.Future<bool> deleteEmails(List<String>? emails) =>
-      (super.noSuchMethod(Invocation.method(#deleteEmails, [emails]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#deleteEmails, [emails]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.User> listUserFollowers(String? user) =>
-      (super.noSuchMethod(Invocation.method(#listUserFollowers, [user]), returnValue: Stream<_i10.User>.empty())
+      (super.noSuchMethod(Invocation.method(#listUserFollowers, [user]), returnValue: _i17.Stream<_i10.User>.empty())
           as _i17.Stream<_i10.User>);
   @override
   _i17.Future<bool> isFollowingUser(String? user) =>
-      (super.noSuchMethod(Invocation.method(#isFollowingUser, [user]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#isFollowingUser, [user]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Future<bool> isUserFollowing(String? user, String? target) =>
-      (super.noSuchMethod(Invocation.method(#isUserFollowing, [user, target]), returnValue: Future<bool>.value(false))
-          as _i17.Future<bool>);
+      (super.noSuchMethod(Invocation.method(#isUserFollowing, [user, target]),
+          returnValue: _i17.Future<bool>.value(false)) as _i17.Future<bool>);
   @override
   _i17.Future<bool> followUser(String? user) =>
-      (super.noSuchMethod(Invocation.method(#followUser, [user]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#followUser, [user]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Future<bool> unfollowUser(String? user) =>
-      (super.noSuchMethod(Invocation.method(#unfollowUser, [user]), returnValue: Future<bool>.value(false))
+      (super.noSuchMethod(Invocation.method(#unfollowUser, [user]), returnValue: _i17.Future<bool>.value(false))
           as _i17.Future<bool>);
   @override
   _i17.Stream<_i10.User> listCurrentUserFollowers() =>
-      (super.noSuchMethod(Invocation.method(#listCurrentUserFollowers, []), returnValue: Stream<_i10.User>.empty())
+      (super.noSuchMethod(Invocation.method(#listCurrentUserFollowers, []), returnValue: _i17.Stream<_i10.User>.empty())
           as _i17.Stream<_i10.User>);
   @override
   _i17.Stream<_i10.User> listCurrentUserFollowing() =>
-      (super.noSuchMethod(Invocation.method(#listCurrentUserFollowing, []), returnValue: Stream<_i10.User>.empty())
+      (super.noSuchMethod(Invocation.method(#listCurrentUserFollowing, []), returnValue: _i17.Stream<_i10.User>.empty())
           as _i17.Stream<_i10.User>);
   @override
   _i17.Stream<_i10.PublicKey> listPublicKeys([String? userLogin]) =>
-      (super.noSuchMethod(Invocation.method(#listPublicKeys, [userLogin]), returnValue: Stream<_i10.PublicKey>.empty())
-          as _i17.Stream<_i10.PublicKey>);
+      (super.noSuchMethod(Invocation.method(#listPublicKeys, [userLogin]),
+          returnValue: _i17.Stream<_i10.PublicKey>.empty()) as _i17.Stream<_i10.PublicKey>);
   @override
-  _i17.Future<_i10.PublicKey> createPublicKey(_i10.CreatePublicKey? key) =>
-      (super.noSuchMethod(Invocation.method(#createPublicKey, [key]),
-          returnValue: Future<_i10.PublicKey>.value(_FakePublicKey_68())) as _i17.Future<_i10.PublicKey>);
+  _i17.Future<_i10.PublicKey> createPublicKey(_i10.CreatePublicKey? key) => (super.noSuchMethod(
+          Invocation.method(#createPublicKey, [key]),
+          returnValue:
+              _i17.Future<_i10.PublicKey>.value(_FakePublicKey_68(this, Invocation.method(#createPublicKey, [key]))))
+      as _i17.Future<_i10.PublicKey>);
 }
 
 /// A class which mocks [Cache].
@@ -2277,20 +2778,20 @@
   }
 
   @override
-  _i21.Entry<_i27.Uint8List> operator [](String? key) =>
-      (super.noSuchMethod(Invocation.method(#[], [key]), returnValue: _FakeEntry_81<_i27.Uint8List>())
-          as _i21.Entry<_i27.Uint8List>);
+  _i21.Entry<_i27.Uint8List> operator [](String? key) => (super.noSuchMethod(Invocation.method(#[], [key]),
+      returnValue: _FakeEntry_81<_i27.Uint8List>(this, Invocation.method(#[], [key]))) as _i21.Entry<_i27.Uint8List>);
   @override
-  _i21.Cache<_i27.Uint8List> withPrefix(String? prefix) =>
-      (super.noSuchMethod(Invocation.method(#withPrefix, [prefix]), returnValue: _FakeCache_82<_i27.Uint8List>())
-          as _i21.Cache<_i27.Uint8List>);
+  _i21.Cache<_i27.Uint8List> withPrefix(String? prefix) => (super.noSuchMethod(Invocation.method(#withPrefix, [prefix]),
+          returnValue: _FakeCache_82<_i27.Uint8List>(this, Invocation.method(#withPrefix, [prefix])))
+      as _i21.Cache<_i27.Uint8List>);
   @override
   _i21.Cache<S> withCodec<S>(_i16.Codec<S, _i27.Uint8List>? codec) =>
-      (super.noSuchMethod(Invocation.method(#withCodec, [codec]), returnValue: _FakeCache_82<S>()) as _i21.Cache<S>);
+      (super.noSuchMethod(Invocation.method(#withCodec, [codec]),
+          returnValue: _FakeCache_82<S>(this, Invocation.method(#withCodec, [codec]))) as _i21.Cache<S>);
   @override
-  _i21.Cache<_i27.Uint8List> withTTL(Duration? ttl) =>
-      (super.noSuchMethod(Invocation.method(#withTTL, [ttl]), returnValue: _FakeCache_82<_i27.Uint8List>())
-          as _i21.Cache<_i27.Uint8List>);
+  _i21.Cache<_i27.Uint8List> withTTL(Duration? ttl) => (super.noSuchMethod(Invocation.method(#withTTL, [ttl]),
+          returnValue: _FakeCache_82<_i27.Uint8List>(this, Invocation.method(#withTTL, [ttl])))
+      as _i21.Cache<_i27.Uint8List>);
 }
 
 /// A class which mocks [GitHub].
@@ -2307,52 +2808,53 @@
   @override
   String get endpoint => (super.noSuchMethod(Invocation.getter(#endpoint), returnValue: '') as String);
   @override
-  _i2.Client get client => (super.noSuchMethod(Invocation.getter(#client), returnValue: _FakeClient_0()) as _i2.Client);
+  _i2.Client get client =>
+      (super.noSuchMethod(Invocation.getter(#client), returnValue: _FakeClient_0(this, Invocation.getter(#client)))
+          as _i2.Client);
   @override
-  _i10.ActivityService get activity =>
-      (super.noSuchMethod(Invocation.getter(#activity), returnValue: _FakeActivityService_83())
-          as _i10.ActivityService);
+  _i10.ActivityService get activity => (super.noSuchMethod(Invocation.getter(#activity),
+      returnValue: _FakeActivityService_83(this, Invocation.getter(#activity))) as _i10.ActivityService);
   @override
-  _i10.AuthorizationsService get authorizations =>
-      (super.noSuchMethod(Invocation.getter(#authorizations), returnValue: _FakeAuthorizationsService_84())
-          as _i10.AuthorizationsService);
+  _i10.AuthorizationsService get authorizations => (super.noSuchMethod(Invocation.getter(#authorizations),
+          returnValue: _FakeAuthorizationsService_84(this, Invocation.getter(#authorizations)))
+      as _i10.AuthorizationsService);
   @override
   _i10.GistsService get gists =>
-      (super.noSuchMethod(Invocation.getter(#gists), returnValue: _FakeGistsService_85()) as _i10.GistsService);
+      (super.noSuchMethod(Invocation.getter(#gists), returnValue: _FakeGistsService_85(this, Invocation.getter(#gists)))
+          as _i10.GistsService);
   @override
   _i10.GitService get git =>
-      (super.noSuchMethod(Invocation.getter(#git), returnValue: _FakeGitService_86()) as _i10.GitService);
+      (super.noSuchMethod(Invocation.getter(#git), returnValue: _FakeGitService_86(this, Invocation.getter(#git)))
+          as _i10.GitService);
   @override
-  _i10.IssuesService get issues =>
-      (super.noSuchMethod(Invocation.getter(#issues), returnValue: _FakeIssuesService_87()) as _i10.IssuesService);
+  _i10.IssuesService get issues => (super.noSuchMethod(Invocation.getter(#issues),
+      returnValue: _FakeIssuesService_87(this, Invocation.getter(#issues))) as _i10.IssuesService);
   @override
   _i10.MiscService get misc =>
-      (super.noSuchMethod(Invocation.getter(#misc), returnValue: _FakeMiscService_88()) as _i10.MiscService);
+      (super.noSuchMethod(Invocation.getter(#misc), returnValue: _FakeMiscService_88(this, Invocation.getter(#misc)))
+          as _i10.MiscService);
   @override
-  _i10.OrganizationsService get organizations =>
-      (super.noSuchMethod(Invocation.getter(#organizations), returnValue: _FakeOrganizationsService_89())
-          as _i10.OrganizationsService);
+  _i10.OrganizationsService get organizations => (super.noSuchMethod(Invocation.getter(#organizations),
+      returnValue: _FakeOrganizationsService_89(this, Invocation.getter(#organizations))) as _i10.OrganizationsService);
   @override
-  _i10.PullRequestsService get pullRequests =>
-      (super.noSuchMethod(Invocation.getter(#pullRequests), returnValue: _FakePullRequestsService_90())
-          as _i10.PullRequestsService);
+  _i10.PullRequestsService get pullRequests => (super.noSuchMethod(Invocation.getter(#pullRequests),
+      returnValue: _FakePullRequestsService_90(this, Invocation.getter(#pullRequests))) as _i10.PullRequestsService);
   @override
-  _i10.RepositoriesService get repositories =>
-      (super.noSuchMethod(Invocation.getter(#repositories), returnValue: _FakeRepositoriesService_91())
-          as _i10.RepositoriesService);
+  _i10.RepositoriesService get repositories => (super.noSuchMethod(Invocation.getter(#repositories),
+      returnValue: _FakeRepositoriesService_91(this, Invocation.getter(#repositories))) as _i10.RepositoriesService);
   @override
-  _i10.SearchService get search =>
-      (super.noSuchMethod(Invocation.getter(#search), returnValue: _FakeSearchService_92()) as _i10.SearchService);
+  _i10.SearchService get search => (super.noSuchMethod(Invocation.getter(#search),
+      returnValue: _FakeSearchService_92(this, Invocation.getter(#search))) as _i10.SearchService);
   @override
-  _i10.UrlShortenerService get urlShortener =>
-      (super.noSuchMethod(Invocation.getter(#urlShortener), returnValue: _FakeUrlShortenerService_93())
-          as _i10.UrlShortenerService);
+  _i10.UrlShortenerService get urlShortener => (super.noSuchMethod(Invocation.getter(#urlShortener),
+      returnValue: _FakeUrlShortenerService_93(this, Invocation.getter(#urlShortener))) as _i10.UrlShortenerService);
   @override
   _i10.UsersService get users =>
-      (super.noSuchMethod(Invocation.getter(#users), returnValue: _FakeUsersService_94()) as _i10.UsersService);
+      (super.noSuchMethod(Invocation.getter(#users), returnValue: _FakeUsersService_94(this, Invocation.getter(#users)))
+          as _i10.UsersService);
   @override
-  _i10.ChecksService get checks =>
-      (super.noSuchMethod(Invocation.getter(#checks), returnValue: _FakeChecksService_95()) as _i10.ChecksService);
+  _i10.ChecksService get checks => (super.noSuchMethod(Invocation.getter(#checks),
+      returnValue: _FakeChecksService_95(this, Invocation.getter(#checks))) as _i10.ChecksService);
   @override
   _i17.Future<T> getJSON<S, T>(String? path,
           {int? statusCode,
@@ -2372,7 +2874,7 @@
             #convert: convert,
             #preview: preview
           }),
-          returnValue: Future<T>.value(null)) as _i17.Future<T>);
+          returnValue: _i17.Future<T>.value(null)) as _i17.Future<T>);
   @override
   _i17.Future<T> postJSON<S, T>(String? path,
           {int? statusCode,
@@ -2423,7 +2925,7 @@
             #body: body,
             #preview: preview
           }),
-          returnValue: Future<T>.value(null)) as _i17.Future<T>);
+          returnValue: _i17.Future<T>.value(null)) as _i17.Future<T>);
   @override
   _i17.Future<T> patchJSON<S, T>(String? path,
           {int? statusCode,
@@ -2445,7 +2947,7 @@
             #body: body,
             #preview: preview
           }),
-          returnValue: Future<T>.value(null)) as _i17.Future<T>);
+          returnValue: _i17.Future<T>.value(null)) as _i17.Future<T>);
   @override
   _i17.Future<T> requestJson<S, T>(String? method, String? path,
           {int? statusCode,
@@ -2468,7 +2970,7 @@
             #body: body,
             #preview: preview
           }),
-          returnValue: Future<T>.value(null)) as _i17.Future<T>);
+          returnValue: _i17.Future<T>.value(null)) as _i17.Future<T>);
   @override
   _i17.Future<_i2.Response> request(String? method, String? path,
           {Map<String, String>? headers,
@@ -2477,19 +2979,20 @@
           int? statusCode,
           void Function(_i2.Response)? fail,
           String? preview}) =>
-      (super.noSuchMethod(
-          Invocation.method(#request, [
-            method,
-            path
-          ], {
-            #headers: headers,
-            #params: params,
-            #body: body,
-            #statusCode: statusCode,
-            #fail: fail,
-            #preview: preview
-          }),
-          returnValue: Future<_i2.Response>.value(_FakeResponse_96())) as _i17.Future<_i2.Response>);
+      (super.noSuchMethod(Invocation.method(#request, [method, path], {#headers: headers, #params: params, #body: body, #statusCode: statusCode, #fail: fail, #preview: preview}),
+          returnValue: _i17.Future<_i2.Response>.value(_FakeResponse_96(
+              this,
+              Invocation.method(#request, [
+                method,
+                path
+              ], {
+                #headers: headers,
+                #params: params,
+                #body: body,
+                #statusCode: statusCode,
+                #fail: fail,
+                #preview: preview
+              })))) as _i17.Future<_i2.Response>);
   @override
   void handleStatusCode(_i2.Response? response) =>
       super.noSuchMethod(Invocation.method(#handleStatusCode, [response]), returnValueForMissingStub: null);
diff --git a/app_dart/test/src/utilities/webhook_generators.dart b/app_dart/test/src/utilities/webhook_generators.dart
index def75e7..67f6429 100644
--- a/app_dart/test/src/utilities/webhook_generators.dart
+++ b/app_dart/test/src/utilities/webhook_generators.dart
@@ -4,33 +4,65 @@
 
 import 'dart:convert';
 
+import 'package:cocoon_service/protos.dart' as pb;
+import 'package:cocoon_service/src/model/luci/push_message.dart';
 import 'package:cocoon_service/src/service/config.dart';
+import 'package:github/github.dart';
 import 'package:github/hooks.dart';
 
-String generatePullRequestEvent(
+PushMessage generateGithubWebhookMessage({
+  String event = 'pull_request',
+  String action = 'merged',
+  int number = 123,
+  String baseRef = kDefaultBranchName,
+  String login = 'dash',
+  String headRef = 'abc',
+  bool isDraft = false,
+  bool merged = false,
+  bool mergeable = true,
+  RepositorySlug? slug,
+}) {
+  final String data = (pb.GithubWebhookMessage.create()
+        ..event = event
+        ..payload = _generatePullRequestEvent(
+          action,
+          number,
+          baseRef,
+          login: login,
+          headRef: headRef,
+          isDraft: isDraft,
+          merged: merged,
+          isMergeable: mergeable,
+          slug: slug,
+        ))
+      .writeToJson();
+  return PushMessage(data: data, messageId: 'abc123');
+}
+
+String _generatePullRequestEvent(
   String action,
   int number,
   String baseRef, {
+  RepositorySlug? slug,
   String login = 'flutter',
   String headRef = 'wait_for_reassemble',
   bool includeCqLabel = false,
   bool isDraft = false,
   bool merged = false,
-  String repoFullName = 'flutter/flutter',
-  String repoName = 'flutter',
   bool isMergeable = true,
-}) =>
-    '''{
+}) {
+  slug ??= Config.flutterSlug;
+  return '''{
   "action": "$action",
   "number": $number,
   "pull_request": {
-    "url": "https://api.github.com/repos/$repoFullName/pulls/$number",
+    "url": "https://api.github.com/repos/${slug.fullName}/pulls/$number",
     "id": 294034,
     "node_id": "MDExOlB1bGxSZXF1ZXN0Mjk0MDMzODQx",
-    "html_url": "https://github.com/$repoFullName/pull/$number",
-    "diff_url": "https://github.com/$repoFullName/pull/$number.diff",
-    "patch_url": "https://github.com/$repoFullName/pull/$number.patch",
-    "issue_url": "https://api.github.com/repos/$repoFullName/issues/$number",
+    "html_url": "https://github.com/${slug.fullName}/pull/$number",
+    "diff_url": "https://github.com/${slug.fullName}/pull/$number.diff",
+    "patch_url": "https://github.com/${slug.fullName}/pull/$number.patch",
+    "issue_url": "https://api.github.com/repos/${slug.fullName}/issues/$number",
     "number": $number,
     "state": "open",
     "locked": false,
@@ -70,7 +102,7 @@
       {
         "id": 487496476,
         "node_id": "MDU6TGFiZWw0ODc0OTY0NzY=",
-        "url": "https://api.github.com/repos/$repoFullName/labels/cla:%20yes",
+        "url": "https://api.github.com/repos/${slug.fullName}/labels/cla:%20yes",
         "name": "cla: yes",
         "color": "ffffff",
         "default": false
@@ -78,7 +110,7 @@
       {
         "id": 284437560,
         "node_id": "MDU6TGFiZWwyODQ0Mzc1NjA=",
-        "url": "https://api.github.com/repos/$repoFullName/labels/framework",
+        "url": "https://api.github.com/repos/${slug.fullName}/labels/framework",
         "name": "framework",
         "color": "207de5",
         "default": false
@@ -87,25 +119,25 @@
       {
         "id": 283480100,
         "node_id": "MDU6TGFiZWwyODM0ODAxMDA=",
-        "url": "https://api.github.com/repos/$repoFullName/labels/tool",
+        "url": "https://api.github.com/repos/${slug.fullName}/labels/tool",
         "color": "5319e7",
         "default": false
       },''' : ''}
       {
         "id": 283480100,
         "node_id": "MDU6TGFiZWwyODM0ODAxMDA=",
-        "url": "https://api.github.com/repos/$repoFullName/labels/tool",
+        "url": "https://api.github.com/repos/${slug.fullName}/labels/tool",
         "name": "tool",
         "color": "5319e7",
         "default": false
       }
     ],
     "milestone": null,
-    "commits_url": "https://api.github.com/repos/$repoFullName/pulls/$number/commits",
-    "review_comments_url": "https://api.github.com/repos/$repoFullName/pulls/$number/comments",
-    "review_comment_url": "https://api.github.com/repos/$repoFullName/pulls/comments{/number}",
-    "comments_url": "https://api.github.com/repos/$repoFullName/issues/$number/comments",
-    "statuses_url": "https://api.github.com/repos/$repoFullName/statuses/be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
+    "commits_url": "https://api.github.com/repos/${slug.fullName}/pulls/$number/commits",
+    "review_comments_url": "https://api.github.com/repos/${slug.fullName}/pulls/$number/comments",
+    "review_comment_url": "https://api.github.com/repos/${slug.fullName}/pulls/comments{/number}",
+    "comments_url": "https://api.github.com/repos/${slug.fullName}/issues/$number/comments",
+    "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
     "head": {
       "label": "$login:$headRef",
       "ref": "$headRef",
@@ -133,8 +165,8 @@
       "repo": {
         "id": 131232406,
         "node_id": "MDEwOlJlcG9zaXRvcnkxMzEyMzI0MDY=",
-        "name": "$repoName",
-        "full_name": "$repoFullName",
+        "name": "${slug.name}",
+        "full_name": "${slug.fullName}",
         "private": false,
         "owner": {
           "login": "flutter",
@@ -156,53 +188,53 @@
           "type": "User",
           "site_admin": false
         },
-        "html_url": "https://github.com/$repoFullName",
+        "html_url": "https://github.com/${slug.fullName}",
         "description": "Flutter makes it easy and fast to build beautiful mobile apps.",
         "fork": true,
-        "url": "https://api.github.com/repos/$repoFullName",
-        "forks_url": "https://api.github.com/repos/$repoFullName/forks",
-        "keys_url": "https://api.github.com/repos/$repoFullName/keys{/key_id}",
-        "collaborators_url": "https://api.github.com/repos/$repoFullName/collaborators{/collaborator}",
-        "teams_url": "https://api.github.com/repos/$repoFullName/teams",
-        "hooks_url": "https://api.github.com/repos/$repoFullName/hooks",
-        "issue_events_url": "https://api.github.com/repos/$repoFullName/issues/events{/number}",
-        "events_url": "https://api.github.com/repos/$repoFullName/events",
-        "assignees_url": "https://api.github.com/repos/$repoFullName/assignees{/user}",
-        "branches_url": "https://api.github.com/repos/$repoFullName/branches{/branch}",
-        "tags_url": "https://api.github.com/repos/$repoFullName/tags",
-        "blobs_url": "https://api.github.com/repos/$repoFullName/git/blobs{/sha}",
-        "git_tags_url": "https://api.github.com/repos/$repoFullName/git/tags{/sha}",
-        "git_refs_url": "https://api.github.com/repos/$repoFullName/git/refs{/sha}",
-        "trees_url": "https://api.github.com/repos/$repoFullName/git/trees{/sha}",
-        "statuses_url": "https://api.github.com/repos/$repoFullName/statuses/{sha}",
-        "languages_url": "https://api.github.com/repos/$repoFullName/languages",
-        "stargazers_url": "https://api.github.com/repos/$repoFullName/stargazers",
-        "contributors_url": "https://api.github.com/repos/$repoFullName/contributors",
-        "subscribers_url": "https://api.github.com/repos/$repoFullName/subscribers",
-        "subscription_url": "https://api.github.com/repos/$repoFullName/subscription",
-        "commits_url": "https://api.github.com/repos/$repoFullName/commits{/sha}",
-        "git_commits_url": "https://api.github.com/repos/$repoFullName/git/commits{/sha}",
-        "comments_url": "https://api.github.com/repos/$repoFullName/comments{/number}",
-        "issue_comment_url": "https://api.github.com/repos/$repoFullName/issues/comments{/number}",
-        "contents_url": "https://api.github.com/repos/$repoFullName/contents/{+path}",
-        "compare_url": "https://api.github.com/repos/$repoFullName/compare/{base}...{head}",
-        "merges_url": "https://api.github.com/repos/$repoFullName/merges",
-        "archive_url": "https://api.github.com/repos/$repoFullName/{archive_format}{/ref}",
-        "downloads_url": "https://api.github.com/repos/$repoFullName/downloads",
-        "issues_url": "https://api.github.com/repos/$repoFullName/issues{/number}",
-        "pulls_url": "https://api.github.com/repos/$repoFullName/pulls{/number}",
-        "milestones_url": "https://api.github.com/repos/$repoFullName/milestones{/number}",
-        "notifications_url": "https://api.github.com/repos/$repoFullName/notifications{?since,all,participating}",
-        "labels_url": "https://api.github.com/repos/$repoFullName/labels{/name}",
-        "releases_url": "https://api.github.com/repos/$repoFullName/releases{/id}",
-        "deployments_url": "https://api.github.com/repos/$repoFullName/deployments",
+        "url": "https://api.github.com/repos/${slug.fullName}",
+        "forks_url": "https://api.github.com/repos/${slug.fullName}/forks",
+        "keys_url": "https://api.github.com/repos/${slug.fullName}/keys{/key_id}",
+        "collaborators_url": "https://api.github.com/repos/${slug.fullName}/collaborators{/collaborator}",
+        "teams_url": "https://api.github.com/repos/${slug.fullName}/teams",
+        "hooks_url": "https://api.github.com/repos/${slug.fullName}/hooks",
+        "issue_events_url": "https://api.github.com/repos/${slug.fullName}/issues/events{/number}",
+        "events_url": "https://api.github.com/repos/${slug.fullName}/events",
+        "assignees_url": "https://api.github.com/repos/${slug.fullName}/assignees{/user}",
+        "branches_url": "https://api.github.com/repos/${slug.fullName}/branches{/branch}",
+        "tags_url": "https://api.github.com/repos/${slug.fullName}/tags",
+        "blobs_url": "https://api.github.com/repos/${slug.fullName}/git/blobs{/sha}",
+        "git_tags_url": "https://api.github.com/repos/${slug.fullName}/git/tags{/sha}",
+        "git_refs_url": "https://api.github.com/repos/${slug.fullName}/git/refs{/sha}",
+        "trees_url": "https://api.github.com/repos/${slug.fullName}/git/trees{/sha}",
+        "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/{sha}",
+        "languages_url": "https://api.github.com/repos/${slug.fullName}/languages",
+        "stargazers_url": "https://api.github.com/repos/${slug.fullName}/stargazers",
+        "contributors_url": "https://api.github.com/repos/${slug.fullName}/contributors",
+        "subscribers_url": "https://api.github.com/repos/${slug.fullName}/subscribers",
+        "subscription_url": "https://api.github.com/repos/${slug.fullName}/subscription",
+        "commits_url": "https://api.github.com/repos/${slug.fullName}/commits{/sha}",
+        "git_commits_url": "https://api.github.com/repos/${slug.fullName}/git/commits{/sha}",
+        "comments_url": "https://api.github.com/repos/${slug.fullName}/comments{/number}",
+        "issue_comment_url": "https://api.github.com/repos/${slug.fullName}/issues/comments{/number}",
+        "contents_url": "https://api.github.com/repos/${slug.fullName}/contents/{+path}",
+        "compare_url": "https://api.github.com/repos/${slug.fullName}/compare/{base}...{head}",
+        "merges_url": "https://api.github.com/repos/${slug.fullName}/merges",
+        "archive_url": "https://api.github.com/repos/${slug.fullName}/{archive_format}{/ref}",
+        "downloads_url": "https://api.github.com/repos/${slug.fullName}/downloads",
+        "issues_url": "https://api.github.com/repos/${slug.fullName}/issues{/number}",
+        "pulls_url": "https://api.github.com/repos/${slug.fullName}/pulls{/number}",
+        "milestones_url": "https://api.github.com/repos/${slug.fullName}/milestones{/number}",
+        "notifications_url": "https://api.github.com/repos/${slug.fullName}/notifications{?since,all,participating}",
+        "labels_url": "https://api.github.com/repos/${slug.fullName}/labels{/name}",
+        "releases_url": "https://api.github.com/repos/${slug.fullName}/releases{/id}",
+        "deployments_url": "https://api.github.com/repos/${slug.fullName}/deployments",
         "created_at": "2018-04-27T02:03:08Z",
         "updated_at": "2019-06-27T06:56:59Z",
         "pushed_at": "2019-07-03T19:40:11Z",
-        "git_url": "git://github.com/$repoFullName.git",
-        "ssh_url": "git@github.com:$repoFullName.git",
-        "clone_url": "https://github.com/$repoFullName.git",
-        "svn_url": "https://github.com/$repoFullName",
+        "git_url": "git://github.com/${slug.fullName}.git",
+        "ssh_url": "git@github.com:${slug.fullName}.git",
+        "clone_url": "https://github.com/${slug.fullName}.git",
+        "svn_url": "https://github.com/${slug.fullName}",
         "homepage": "https://flutter.io",
         "size": 94508,
         "stargazers_count": 1,
@@ -258,8 +290,8 @@
       "repo": {
         "id": 31792824,
         "node_id": "MDEwOlJlcG9zaXRvcnkzMTc5MjgyNA==",
-        "name": "$repoName",
-        "full_name": "$repoFullName",
+        "name": "${slug.name}",
+        "full_name": "${slug.fullName}",
         "private": false,
         "owner": {
           "login": "flutter",
@@ -281,53 +313,53 @@
           "type": "Organization",
           "site_admin": false
         },
-        "html_url": "https://github.com/$repoFullName",
+        "html_url": "https://github.com/${slug.fullName}",
         "description": "Flutter makes it easy and fast to build beautiful mobile apps.",
         "fork": false,
-        "url": "https://api.github.com/repos/$repoFullName",
-        "forks_url": "https://api.github.com/repos/$repoFullName/forks",
-        "keys_url": "https://api.github.com/repos/$repoFullName/keys{/key_id}",
-        "collaborators_url": "https://api.github.com/repos/$repoFullName/collaborators{/collaborator}",
-        "teams_url": "https://api.github.com/repos/$repoFullName/teams",
-        "hooks_url": "https://api.github.com/repos/$repoFullName/hooks",
-        "issue_events_url": "https://api.github.com/repos/$repoFullName/issues/events{/number}",
-        "events_url": "https://api.github.com/repos/$repoFullName/events",
-        "assignees_url": "https://api.github.com/repos/$repoFullName/assignees{/user}",
-        "branches_url": "https://api.github.com/repos/$repoFullName/branches{/branch}",
-        "tags_url": "https://api.github.com/repos/$repoFullName/tags",
-        "blobs_url": "https://api.github.com/repos/$repoFullName/git/blobs{/sha}",
-        "git_tags_url": "https://api.github.com/repos/$repoFullName/git/tags{/sha}",
-        "git_refs_url": "https://api.github.com/repos/$repoFullName/git/refs{/sha}",
-        "trees_url": "https://api.github.com/repos/$repoFullName/git/trees{/sha}",
-        "statuses_url": "https://api.github.com/repos/$repoFullName/statuses/{sha}",
-        "languages_url": "https://api.github.com/repos/$repoFullName/languages",
-        "stargazers_url": "https://api.github.com/repos/$repoFullName/stargazers",
-        "contributors_url": "https://api.github.com/repos/$repoFullName/contributors",
-        "subscribers_url": "https://api.github.com/repos/$repoFullName/subscribers",
-        "subscription_url": "https://api.github.com/repos/$repoFullName/subscription",
-        "commits_url": "https://api.github.com/repos/$repoFullName/commits{/sha}",
-        "git_commits_url": "https://api.github.com/repos/$repoFullName/git/commits{/sha}",
-        "comments_url": "https://api.github.com/repos/$repoFullName/comments{/number}",
-        "issue_comment_url": "https://api.github.com/repos/$repoFullName/issues/comments{/number}",
-        "contents_url": "https://api.github.com/repos/$repoFullName/contents/{+path}",
-        "compare_url": "https://api.github.com/repos/$repoFullName/compare/{base}...{head}",
-        "merges_url": "https://api.github.com/repos/$repoFullName/merges",
-        "archive_url": "https://api.github.com/repos/$repoFullName/{archive_format}{/ref}",
-        "downloads_url": "https://api.github.com/repos/$repoFullName/downloads",
-        "issues_url": "https://api.github.com/repos/$repoFullName/issues{/number}",
-        "pulls_url": "https://api.github.com/repos/$repoFullName/pulls{/number}",
-        "milestones_url": "https://api.github.com/repos/$repoFullName/milestones{/number}",
-        "notifications_url": "https://api.github.com/repos/$repoFullName/notifications{?since,all,participating}",
-        "labels_url": "https://api.github.com/repos/$repoFullName/labels{/name}",
-        "releases_url": "https://api.github.com/repos/$repoFullName/releases{/id}",
-        "deployments_url": "https://api.github.com/repos/$repoFullName/deployments",
+        "url": "https://api.github.com/repos/${slug.fullName}",
+        "forks_url": "https://api.github.com/repos/${slug.fullName}/forks",
+        "keys_url": "https://api.github.com/repos/${slug.fullName}/keys{/key_id}",
+        "collaborators_url": "https://api.github.com/repos/${slug.fullName}/collaborators{/collaborator}",
+        "teams_url": "https://api.github.com/repos/${slug.fullName}/teams",
+        "hooks_url": "https://api.github.com/repos/${slug.fullName}/hooks",
+        "issue_events_url": "https://api.github.com/repos/${slug.fullName}/issues/events{/number}",
+        "events_url": "https://api.github.com/repos/${slug.fullName}/events",
+        "assignees_url": "https://api.github.com/repos/${slug.fullName}/assignees{/user}",
+        "branches_url": "https://api.github.com/repos/${slug.fullName}/branches{/branch}",
+        "tags_url": "https://api.github.com/repos/${slug.fullName}/tags",
+        "blobs_url": "https://api.github.com/repos/${slug.fullName}/git/blobs{/sha}",
+        "git_tags_url": "https://api.github.com/repos/${slug.fullName}/git/tags{/sha}",
+        "git_refs_url": "https://api.github.com/repos/${slug.fullName}/git/refs{/sha}",
+        "trees_url": "https://api.github.com/repos/${slug.fullName}/git/trees{/sha}",
+        "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/{sha}",
+        "languages_url": "https://api.github.com/repos/${slug.fullName}/languages",
+        "stargazers_url": "https://api.github.com/repos/${slug.fullName}/stargazers",
+        "contributors_url": "https://api.github.com/repos/${slug.fullName}/contributors",
+        "subscribers_url": "https://api.github.com/repos/${slug.fullName}/subscribers",
+        "subscription_url": "https://api.github.com/repos/${slug.fullName}/subscription",
+        "commits_url": "https://api.github.com/repos/${slug.fullName}/commits{/sha}",
+        "git_commits_url": "https://api.github.com/repos/${slug.fullName}/git/commits{/sha}",
+        "comments_url": "https://api.github.com/repos/${slug.fullName}/comments{/number}",
+        "issue_comment_url": "https://api.github.com/repos/${slug.fullName}/issues/comments{/number}",
+        "contents_url": "https://api.github.com/repos/${slug.fullName}/contents/{+path}",
+        "compare_url": "https://api.github.com/repos/${slug.fullName}/compare/{base}...{head}",
+        "merges_url": "https://api.github.com/repos/${slug.fullName}/merges",
+        "archive_url": "https://api.github.com/repos/${slug.fullName}/{archive_format}{/ref}",
+        "downloads_url": "https://api.github.com/repos/${slug.fullName}/downloads",
+        "issues_url": "https://api.github.com/repos/${slug.fullName}/issues{/number}",
+        "pulls_url": "https://api.github.com/repos/${slug.fullName}/pulls{/number}",
+        "milestones_url": "https://api.github.com/repos/${slug.fullName}/milestones{/number}",
+        "notifications_url": "https://api.github.com/repos/${slug.fullName}/notifications{?since,all,participating}",
+        "labels_url": "https://api.github.com/repos/${slug.fullName}/labels{/name}",
+        "releases_url": "https://api.github.com/repos/${slug.fullName}/releases{/id}",
+        "deployments_url": "https://api.github.com/repos/${slug.fullName}/deployments",
         "created_at": "2015-03-06T22:54:58Z",
         "updated_at": "2019-07-04T02:08:44Z",
         "pushed_at": "2019-07-04T02:03:04Z",
-        "git_url": "git://github.com/$repoFullName.git",
-        "ssh_url": "git@github.com:$repoFullName.git",
-        "clone_url": "https://github.com/$repoFullName.git",
-        "svn_url": "https://github.com/$repoFullName",
+        "git_url": "git://github.com/${slug.fullName}.git",
+        "ssh_url": "git@github.com:${slug.fullName}.git",
+        "clone_url": "https://github.com/${slug.fullName}.git",
+        "svn_url": "https://github.com/${slug.fullName}",
         "homepage": "https://flutter.dev",
         "size": 65507,
         "stargazers_count": 68944,
@@ -358,28 +390,28 @@
     },
     "_links": {
       "self": {
-        "href": "https://api.github.com/repos/$repoFullName/pulls/$number"
+        "href": "https://api.github.com/repos/${slug.fullName}/pulls/$number"
       },
       "html": {
-        "href": "https://github.com/$repoFullName/pull/$number"
+        "href": "https://github.com/${slug.fullName}/pull/$number"
       },
       "issue": {
-        "href": "https://api.github.com/repos/$repoFullName/issues/$number"
+        "href": "https://api.github.com/repos/${slug.fullName}/issues/$number"
       },
       "comments": {
-        "href": "https://api.github.com/repos/$repoFullName/issues/$number/comments"
+        "href": "https://api.github.com/repos/${slug.fullName}/issues/$number/comments"
       },
       "review_comments": {
-        "href": "https://api.github.com/repos/$repoFullName/pulls/$number/comments"
+        "href": "https://api.github.com/repos/${slug.fullName}/pulls/$number/comments"
       },
       "review_comment": {
-        "href": "https://api.github.com/repos/$repoFullName/pulls/comments{/number}"
+        "href": "https://api.github.com/repos/${slug.fullName}/pulls/comments{/number}"
       },
       "commits": {
-        "href": "https://api.github.com/repos/$repoFullName/pulls/$number/commits"
+        "href": "https://api.github.com/repos/${slug.fullName}/pulls/$number/commits"
       },
       "statuses": {
-        "href": "https://api.github.com/repos/$repoFullName/statuses/deadbeef"
+        "href": "https://api.github.com/repos/${slug.fullName}/statuses/deadbeef"
       }
     },
     "author_association": "MEMBER",
@@ -400,8 +432,8 @@
   "repository": {
     "id": 1868532,
     "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=",
-    "name": "$repoName",
-    "full_name": "$repoFullName",
+    "name": "${slug.name}",
+    "full_name": "${slug.fullName}",
     "private": false,
     "owner": {
       "login": "flutter",
@@ -423,53 +455,53 @@
       "type": "User",
       "site_admin": false
     },
-    "html_url": "https://github.com/$repoFullName",
+    "html_url": "https://github.com/${slug.fullName}",
     "description": null,
     "fork": false,
-    "url": "https://api.github.com/repos/$repoFullName",
-    "forks_url": "https://api.github.com/repos/$repoFullName/forks",
-    "keys_url": "https://api.github.com/repos/$repoFullName/keys{/key_id}",
-    "collaborators_url": "https://api.github.com/repos/$repoFullName/collaborators{/collaborator}",
-    "teams_url": "https://api.github.com/repos/$repoFullName/teams",
-    "hooks_url": "https://api.github.com/repos/$repoFullName/hooks",
-    "issue_events_url": "https://api.github.com/repos/$repoFullName/issues/events{/number}",
-    "events_url": "https://api.github.com/repos/$repoFullName/events",
-    "assignees_url": "https://api.github.com/repos/$repoFullName/assignees{/user}",
-    "branches_url": "https://api.github.com/repos/$repoFullName/branches{/branch}",
-    "tags_url": "https://api.github.com/repos/$repoFullName/tags",
-    "blobs_url": "https://api.github.com/repos/$repoFullName/git/blobs{/sha}",
-    "git_tags_url": "https://api.github.com/repos/$repoFullName/git/tags{/sha}",
-    "git_refs_url": "https://api.github.com/repos/$repoFullName/git/refs{/sha}",
-    "trees_url": "https://api.github.com/repos/$repoFullName/git/trees{/sha}",
-    "statuses_url": "https://api.github.com/repos/$repoFullName/statuses/{sha}",
-    "languages_url": "https://api.github.com/repos/$repoFullName/languages",
-    "stargazers_url": "https://api.github.com/repos/$repoFullName/stargazers",
-    "contributors_url": "https://api.github.com/repos/$repoFullName/contributors",
-    "subscribers_url": "https://api.github.com/repos/$repoFullName/subscribers",
-    "subscription_url": "https://api.github.com/repos/$repoFullName/subscription",
-    "commits_url": "https://api.github.com/repos/$repoFullName/commits{/sha}",
-    "git_commits_url": "https://api.github.com/repos/$repoFullName/git/commits{/sha}",
-    "comments_url": "https://api.github.com/repos/$repoFullName/comments{/number}",
-    "issue_comment_url": "https://api.github.com/repos/$repoFullName/issues/comments{/number}",
-    "contents_url": "https://api.github.com/repos/$repoFullName/contents/{+path}",
-    "compare_url": "https://api.github.com/repos/$repoFullName/compare/{base}...{head}",
-    "merges_url": "https://api.github.com/repos/$repoFullName/merges",
-    "archive_url": "https://api.github.com/repos/$repoFullName/{archive_format}{/ref}",
-    "downloads_url": "https://api.github.com/repos/$repoFullName/downloads",
-    "issues_url": "https://api.github.com/repos/$repoFullName/issues{/number}",
-    "pulls_url": "https://api.github.com/repos/$repoFullName/pulls{/number}",
-    "milestones_url": "https://api.github.com/repos/$repoFullName/milestones{/number}",
-    "notifications_url": "https://api.github.com/repos/$repoFullName/notifications{?since,all,participating}",
-    "labels_url": "https://api.github.com/repos/$repoFullName/labels{/name}",
-    "releases_url": "https://api.github.com/repos/$repoFullName/releases{/id}",
-    "deployments_url": "https://api.github.com/repos/$repoFullName/deployments",
+    "url": "https://api.github.com/repos/${slug.fullName}",
+    "forks_url": "https://api.github.com/repos/${slug.fullName}/forks",
+    "keys_url": "https://api.github.com/repos/${slug.fullName}/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/${slug.fullName}/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/${slug.fullName}/teams",
+    "hooks_url": "https://api.github.com/repos/${slug.fullName}/hooks",
+    "issue_events_url": "https://api.github.com/repos/${slug.fullName}/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/${slug.fullName}/events",
+    "assignees_url": "https://api.github.com/repos/${slug.fullName}/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/${slug.fullName}/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/${slug.fullName}/tags",
+    "blobs_url": "https://api.github.com/repos/${slug.fullName}/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/${slug.fullName}/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/${slug.fullName}/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/${slug.fullName}/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/${slug.fullName}/languages",
+    "stargazers_url": "https://api.github.com/repos/${slug.fullName}/stargazers",
+    "contributors_url": "https://api.github.com/repos/${slug.fullName}/contributors",
+    "subscribers_url": "https://api.github.com/repos/${slug.fullName}/subscribers",
+    "subscription_url": "https://api.github.com/repos/${slug.fullName}/subscription",
+    "commits_url": "https://api.github.com/repos/${slug.fullName}/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/${slug.fullName}/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/${slug.fullName}/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/${slug.fullName}/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/${slug.fullName}/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/${slug.fullName}/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/${slug.fullName}/merges",
+    "archive_url": "https://api.github.com/repos/${slug.fullName}/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/${slug.fullName}/downloads",
+    "issues_url": "https://api.github.com/repos/${slug.fullName}/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/${slug.fullName}/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/${slug.fullName}/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/${slug.fullName}/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/${slug.fullName}/labels{/name}",
+    "releases_url": "https://api.github.com/repos/${slug.fullName}/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/${slug.fullName}/deployments",
     "created_at": "2019-05-15T15:19:25Z",
     "updated_at": "2019-05-15T15:19:27Z",
     "pushed_at": "2019-05-15T15:20:32Z",
-    "git_url": "git://github.com/$repoFullName.git",
-    "ssh_url": "git@github.com:$repoFullName.git",
-    "clone_url": "https://github.com/$repoFullName.git",
-    "svn_url": "https://github.com/$repoFullName",
+    "git_url": "git://github.com/${slug.fullName}.git",
+    "ssh_url": "git@github.com:${slug.fullName}.git",
+    "clone_url": "https://github.com/${slug.fullName}.git",
+    "svn_url": "https://github.com/${slug.fullName}",
     "homepage": null,
     "size": 0,
     "stargazers_count": 0,
@@ -512,12 +544,13 @@
     "site_admin": false
   }
 }''';
+}
 
-String generateCheckRunEvent({
+PushMessage generateCheckRunEvent({
   String action = 'created',
   int numberOfPullRequests = 1,
 }) {
-  String body = '''{
+  String data = '''{
   "action": "$action",
   "check_run": {
     "id": 128620228,
@@ -684,7 +717,7 @@
     "pull_requests": [''';
 
   for (int i = 0; i < numberOfPullRequests; i++) {
-    body += '''{
+    data += '''{
         "url": "https://api.github.com/repos/flutter/flutter/pulls/2",
         "id": 279147437,
         "number": ${i + 2},
@@ -708,10 +741,10 @@
         }
       }''';
     if (i < numberOfPullRequests - 1) {
-      body += ',';
+      data += ',';
     }
   }
-  body += '''],
+  data += '''],
     "deployment": {
       "url": "https://api.github.com/repos/flutter/flutter/deployments/326191728",
       "id": 326191728,
@@ -841,7 +874,23 @@
     "site_admin": false
   }
 }''';
-  return body;
+  final pb.GithubWebhookMessage message = pb.GithubWebhookMessage(
+    event: 'check_run',
+    payload: data,
+  );
+  return PushMessage(
+    data: message.writeToJson(),
+    messageId: 'abc123',
+  );
+}
+
+PushMessage generateCreateBranchMessage(String branchName, String repository, {bool forked = false}) {
+  final CreateEvent createEvent = generateCreateBranchEvent(branchName, repository, forked: forked);
+  final pb.GithubWebhookMessage message = pb.GithubWebhookMessage(
+    event: 'create',
+    payload: jsonEncode(createEvent),
+  );
+  return PushMessage(data: message.writeToJson(), messageId: 'abc123');
 }
 
 CreateEvent generateCreateBranchEvent(String branchName, String repository, {bool forked = false}) =>