blob: 29c4e19a1600e2ef08f78fc19da8fa3f94c48e97 [file] [log] [blame]
// Copyright 2020 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:io';
import 'package:cocoon_service/src/model/gerrit/commit.dart';
import 'package:cocoon_service/src/request_handling/exceptions.dart';
import 'package:cocoon_service/src/service/branch_service.dart';
import 'package:cocoon_service/src/service/config.dart';
import 'package:cocoon_service/src/service/gerrit_service.dart';
import 'package:github/github.dart';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
import 'package:http/testing.dart';
import 'package:test/test.dart';
import '../src/datastore/fake_config.dart';
import '../src/service/fake_auth_client.dart';
import '../src/utilities/matchers.dart';
void main() {
late MockClient mockHttpClient;
late GerritService gerritService;
group('getBranches', () {
test('Too many retries raise an exception', () async {
mockHttpClient = MockClient((_) async => http.Response(')]}\'\n[]', HttpStatus.forbidden));
gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
try {
await gerritService.branches(
'myhost',
'a/b/c',
filterRegex: 'flutter-*',
);
} catch (e) {
expect(e, isA<RetryException>());
}
});
test('Returns a list of branches', () async {
Request? requestAux;
const String body =
')]}\'\n[{"web_links":[{"name":"browse","url":"https://a.com/branch_a","target":"_blank"}],"ref":"refs/heads/branch_a","revision":"0bc"}]';
mockHttpClient = MockClient((Request request) async {
requestAux = request;
return http.Response(body, HttpStatus.ok);
});
gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
final List<String> branches = await gerritService.branches(
'myhost',
'a/b/c',
filterRegex: 'flutter-*|fuchsia*',
);
expect(branches, equals(<String>['refs/heads/branch_a']));
expect(requestAux!.url.queryParameters, equals(<dynamic, dynamic>{'r': 'flutter-*|fuchsia*'}));
});
test('No results return an empty list', () async {
mockHttpClient = MockClient((_) async => http.Response(')]}\'\n[]', HttpStatus.ok));
gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
final List<String> branches = await gerritService.branches(
'myhost',
'a/b/c',
filterRegex: 'flutter-',
);
expect(branches, equals(<String>[]));
});
});
group('commits', () {
test('Returns a list of commits', () async {
mockHttpClient = MockClient((_) async => http.Response(commitsListJson, HttpStatus.ok));
gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
final Iterable<GerritCommit> commits = await gerritService.commits(Config.recipesSlug, 'main');
expect(commits.length, 1);
final GerritCommit commit = commits.single;
expect(commit.author?.email, 'dash@flutter.dev');
expect(commit.author?.name, 'Dash');
expect(commit.author?.time, isNotNull);
expect(commit.committer?.email, 'flutter-scoped@luci-project-accounts.iam.gserviceaccount.com');
expect(commit.committer?.name, 'CQ Bot Account');
expect(commit.committer?.time, isNotNull);
final DateTime time = commit.author!.time!;
final DateTime expectedTime = DateTime(2023, 4, 20, 18, 00, 14);
expect(time, expectedTime);
});
});
group('getCommit', () {
test('Returns a commit', () async {
mockHttpClient = MockClient((_) async => http.Response(getCommitJson, HttpStatus.ok));
gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
final GerritCommit? commit = await gerritService.getCommit(
RepositorySlug('flutter', 'flutter'),
'7a702db7c1c8dc057d95e0d23849c885b3463ff3',
);
expect(commit, isNotNull);
expect(commit!.author?.email, 'dash@flutter.dev');
expect(commit.author?.name, 'Dash');
expect(commit.author?.time, isNotNull);
final DateTime time = commit.author!.time!;
final DateTime expectedTime = DateTime(2023, 4, 20, 18, 0, 14);
expect(time, expectedTime);
});
test('Uses percent-encoding for project name', () async {
mockHttpClient = MockClient((request) async {
expect(request.url.path, startsWith('/projects/mirrors%2Fflutter'));
return http.Response(getCommitJson, HttpStatus.ok);
});
gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
final commit = await gerritService.getCommit(
RepositorySlug('flutter', 'mirrors/flutter'),
'7a702db7c1c8dc057d95e0d23849c885b3463ff3',
);
expect(commit, isNotNull);
});
});
group('findMirroredCommit', () {
test('Uses matching slug for GoB mirror', () async {
mockHttpClient = MockClient((request) async {
expect(request.url.path, startsWith('/projects/mirrors%2Fpackages'));
return http.Response(getCommitJson, HttpStatus.ok);
});
gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
final commit = await gerritService.findMirroredCommit(
RepositorySlug('flutter', 'packages'),
'7a702db7c1c8dc057d95e0d23849c885b3463ff3',
);
expect(commit, isNotNull);
});
});
group('createBranch', () {
test('ok response', () async {
mockHttpClient = MockClient((_) async => http.Response(createBranchJson, HttpStatus.ok));
gerritService = GerritService(
config: FakeConfig(),
httpClient: mockHttpClient,
authClientProvider: ({
Client? baseClient,
required List<String> scopes,
}) async =>
FakeAuthClient(baseClient!),
retryDelay: Duration.zero,
);
await gerritService.createBranch(
Config.recipesSlug,
'flutter-2.13-candidate.0',
'00439ab49a991db42595f14078adb9811a6f60c6',
);
});
test('unexpected response', () async {
mockHttpClient = MockClient((_) async => http.Response(createBranchJson, HttpStatus.ok));
gerritService = GerritService(
config: FakeConfig(),
httpClient: mockHttpClient,
authClientProvider: ({
Client? baseClient,
required List<String> scopes,
}) async =>
FakeAuthClient(baseClient!),
retryDelay: Duration.zero,
);
expect(
() async => gerritService.createBranch(Config.recipesSlug, 'flutter-2.13-candidate.0', 'abc'),
throwsExceptionWith<InternalServerError>('Failed to create branch'),
);
});
test('retries non-200 responses', () async {
int attempts = 0;
mockHttpClient = MockClient((_) async {
attempts = attempts + 1;
// Only send a failed response on the first attempt
if (attempts == 1) {
return http.Response('error', HttpStatus.internalServerError);
}
return http.Response(createBranchJson, HttpStatus.accepted);
});
gerritService = GerritService(
config: FakeConfig(),
httpClient: mockHttpClient,
authClientProvider: ({
Client? baseClient,
required List<String> scopes,
}) async =>
FakeAuthClient(baseClient!),
retryDelay: Duration.zero,
);
await gerritService.createBranch(
Config.recipesSlug,
'flutter-2.13-candidate.0',
'00439ab49a991db42595f14078adb9811a6f60c6',
);
expect(attempts, 2);
});
});
}
const String commitsListJson = ''')]}'
{
"log": [
{
"commit": "7a702db7c1c8dc057d95e0d23849c885b3463ff3",
"tree": "9c1529c1e92b3431534dcbf01d2b63547b73b005",
"parents": [
"289edf3f90678e7ce1319aa1e8a57d7506c461d1"
],
"author": {
"name": "Dash",
"email": "dash@flutter.dev",
"time": "Tue Apr 20 18:00:14 2023 +0000"
},
"committer": {
"name": "CQ Bot Account",
"email": "flutter-scoped@luci-project-accounts.iam.gserviceaccount.com",
"time": "Tue Apr 20 18:00:14 2023 +0000"
},
"message": "My first recipe change\\n\\ntested through `led get-builder"
}
],
"next": "00439ab49a991db42595f14078adb9811a6f60c6"
}
''';
const String createBranchJson = ''')]}'
{
"revision": "00439ab49a991db42595f14078adb9811a6f60c6"
}
''';
const String getCommitJson = ''')]}'
{
"commit": "7a702db7c1c8dc057d95e0d23849c885b3463ff3",
"parents": [
{
"commit": "1eee2c9d8f352483781e772f35dc586a69ff5646",
"subject": "Migrate contributor agreements to All-Projects."
}
],
"author": {
"name": "Dash",
"email": "dash@flutter.dev",
"time": "Wed Apr 20 18:00:14 2023 +0000"
},
"committer": {
"name": "CQ Bot Account",
"email": "flutter-scoped@luci-project-accounts.iam.gserviceaccount.com",
"time": "Wed Apr 20 18:00:14 2023 +0000"
},
"subject": "Use an EventBus to manage star icons",
"message": "Use an EventBus to manage star icons\\n\\nImage widgets that need to ..."
}
''';