blob: c6efe29b2307b18d379652fea0c95472ce3f0b48 [file] [log] [blame]
// 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:cocoon_service/ci_yaml.dart';
import 'package:cocoon_service/cocoon_service.dart';
import 'package:cocoon_service/src/request_handlers/flaky_handler_utils.dart';
import 'package:cocoon_service/src/service/bigquery.dart';
import 'package:cocoon_service/src/service/github_service.dart';
import 'package:collection/src/equality.dart';
import 'package:github/github.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
import 'package:yaml/yaml.dart';
import 'package:cocoon_service/src/model/proto/internal/scheduler.pb.dart' as pb;
import '../src/datastore/fake_config.dart';
import '../src/request_handling/api_request_handler_tester.dart';
import '../src/request_handling/fake_authentication.dart';
import '../src/request_handling/fake_http.dart';
import '../src/utilities/mocks.dart';
import 'check_flaky_builders_test_data.dart';
const String kThreshold = '0.02';
const String kCurrentMasterSHA = 'b6156fc8d1c6e992fe4ea0b9128f9aef10443bdb';
const String kCurrentUserName = 'Name';
const String kCurrentUserLogin = 'login';
const String kCurrentUserEmail = 'login@email.com';
class MockYaml extends Mock implements CiYaml {}
void main() {
group('Deflake', () {
late CheckFlakyBuilders handler;
late ApiRequestHandlerTester tester;
FakeHttpRequest request;
late FakeConfig config;
FakeClientContext clientContext;
FakeAuthenticationProvider auth;
late MockBigqueryService mockBigqueryService;
MockGitHub mockGitHubClient;
late MockRepositoriesService mockRepositoriesService;
late MockPullRequestsService mockPullRequestsService;
late MockIssuesService mockIssuesService;
late MockGitService mockGitService;
MockUsersService mockUsersService;
setUp(() {
request = FakeHttpRequest(
queryParametersValue: <String, dynamic>{
FileFlakyIssueAndPR.kThresholdKey: kThreshold,
},
);
clientContext = FakeClientContext();
auth = FakeAuthenticationProvider(clientContext: clientContext);
mockBigqueryService = MockBigqueryService();
mockGitHubClient = MockGitHub();
mockRepositoriesService = MockRepositoriesService();
mockIssuesService = MockIssuesService();
mockPullRequestsService = MockPullRequestsService();
mockGitService = MockGitService();
mockUsersService = MockUsersService();
// when gets the content of .ci.yaml
when(mockRepositoriesService.getContents(
captureAny,
kCiYamlPath,
)).thenAnswer((Invocation invocation) {
return Future<RepositoryContents>.value(
RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContent))));
});
// when gets the content of TESTOWNERS
when(mockRepositoriesService.getContents(
captureAny,
kTestOwnerPath,
)).thenAnswer((Invocation invocation) {
return Future<RepositoryContents>.value(
RepositoryContents(file: GitHubFile(content: gitHubEncode(testOwnersContent))));
});
// when gets existing marks flaky prs.
when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) {
return const Stream<PullRequest>.empty();
});
// when gets the current head of master branch
when(mockGitService.getReference(captureAny, kMasterRefs)).thenAnswer((Invocation invocation) {
return Future<GitReference>.value(
GitReference(ref: 'refs/$kMasterRefs', object: GitObject('', kCurrentMasterSHA, '')),
);
});
// when gets the current user.
when(mockUsersService.getCurrentUser()).thenAnswer((Invocation invocation) {
final CurrentUser result = CurrentUser();
result.email = kCurrentUserEmail;
result.name = kCurrentUserName;
result.login = kCurrentUserLogin;
return Future<CurrentUser>.value(result);
});
// when assigns pull request reviewer.
when(mockGitHubClient.postJSON<Map<String, dynamic>, PullRequest>(
captureAny,
statusCode: captureAnyNamed('statusCode'),
fail: captureAnyNamed('fail'),
headers: captureAnyNamed('headers'),
params: captureAnyNamed('params'),
convert: captureAnyNamed('convert'),
body: captureAnyNamed('body'),
preview: captureAnyNamed('preview'),
)).thenAnswer((Invocation invocation) {
return Future<PullRequest>.value(PullRequest());
});
when(mockGitHubClient.repositories).thenReturn(mockRepositoriesService);
when(mockGitHubClient.issues).thenReturn(mockIssuesService);
when(mockGitHubClient.pullRequests).thenReturn(mockPullRequestsService);
when(mockGitHubClient.git).thenReturn(mockGitService);
when(mockGitHubClient.users).thenReturn(mockUsersService);
config = FakeConfig(
githubService: GithubService(mockGitHubClient),
bigqueryService: mockBigqueryService,
);
tester = ApiRequestHandlerTester(request: request);
handler = CheckFlakyBuilders(
config: config,
authenticationProvider: auth,
);
});
test('Can create pr if the flaky test is no longer flaky with a closed issue', () async {
// When queries flaky data from BigQuery.
when(mockBigqueryService.listRecentBuildRecordsForBuilder(kBigQueryProjectId,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsAllPassed);
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
});
// When get issue
when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
return Future<Issue>.value(Issue(state: 'closed', htmlUrl: existingIssueURL));
});
// When creates git tree
when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
});
// When creates git commit
when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
});
// When creates git reference
when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
});
// When creates pr to deflake test
when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
});
CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length;
final Map<String, dynamic> result = await utf8.decoder
.bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
.transform(json.decoder)
.single as Map<String, dynamic>;
// Verify BigQuery is called correctly.
List<dynamic> captured = verify(mockBigqueryService.listRecentBuildRecordsForBuilder(captureAny,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.captured;
expect(captured.length, 3);
expect(captured[0].toString(), kBigQueryProjectId);
expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName);
expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber);
// Verify it gets the correct issue.
captured = verify(mockIssuesService.get(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0], Config.flutterSlug);
expect(captured[1] as int?, existingIssueNumber);
// Verify tree is created correctly.
captured = verify(mockGitService.createTree(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
expect(captured[1], isA<CreateGitTree>());
final CreateGitTree tree = captured[1] as CreateGitTree;
expect(tree.baseTree, kCurrentMasterSHA);
expect(tree.entries!.length, 1);
expect(tree.entries![0].content, expectedSemanticsIntegrationTestCiYamlContent);
expect(tree.entries![0].path, kCiYamlPath);
expect(tree.entries![0].mode, kModifyMode);
expect(tree.entries![0].type, kModifyType);
// Verify commit is created correctly.
captured = verify(mockGitService.createCommit(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
expect(captured[1], isA<CreateGitCommit>());
final CreateGitCommit commit = captured[1] as CreateGitCommit;
expect(commit.message, expectedSemanticsIntegrationTestPullRequestTitle);
expect(commit.author!.name, kCurrentUserName);
expect(commit.author!.email, kCurrentUserEmail);
expect(commit.committer!.name, kCurrentUserName);
expect(commit.committer!.email, kCurrentUserEmail);
expect(commit.tree, expectedSemanticsIntegrationTestTreeSha);
expect(commit.parents!.length, 1);
expect(commit.parents![0], kCurrentMasterSHA);
// Verify reference is created correctly.
captured = verify(mockGitService.createReference(captureAny, captureAny, captureAny)).captured;
expect(captured.length, 3);
expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
expect(captured[2], expectedSemanticsIntegrationTestTreeSha);
final String? ref = captured[1] as String?;
// Verify pr is created correctly.
captured = verify(mockPullRequestsService.create(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0].toString(), Config.flutterSlug.toString());
expect(captured[1], isA<CreatePullRequest>());
final CreatePullRequest pr = captured[1] as CreatePullRequest;
expect(pr.title, expectedSemanticsIntegrationTestPullRequestTitle);
expect(pr.body, expectedSemanticsIntegrationTestPullRequestBody);
expect(pr.head, '$kCurrentUserLogin:$ref');
expect(pr.base, 'refs/$kMasterRefs');
expect(result['Status'], 'success');
});
test('Can create pr if the flaky test is no longer flaky without an issue', () async {
// when gets the content of .ci.yaml
when(mockRepositoriesService.getContents(
captureAny,
kCiYamlPath,
)).thenAnswer((Invocation invocation) {
return Future<RepositoryContents>.value(
RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentNoIssue))));
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listRecentBuildRecordsForBuilder(kBigQueryProjectId,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsAllPassed);
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
});
// When creates git tree
when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
});
// When creates git commit
when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
});
// When creates git reference
when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
});
// When creates pr to deflake test
when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
});
CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length;
final Map<String, dynamic> result = await utf8.decoder
.bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
.transform(json.decoder)
.single as Map<String, dynamic>;
// Verify BigQuery is called correctly.
List<dynamic> captured = verify(mockBigqueryService.listRecentBuildRecordsForBuilder(captureAny,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.captured;
expect(captured.length, 3);
expect(captured[0].toString(), kBigQueryProjectId);
expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName);
expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber);
// Verify it does not get issue.
verifyNever(mockIssuesService.get(captureAny, captureAny));
// Verify tree is created correctly.
captured = verify(mockGitService.createTree(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
expect(captured[1], isA<CreateGitTree>());
final CreateGitTree tree = captured[1] as CreateGitTree;
expect(tree.baseTree, kCurrentMasterSHA);
expect(tree.entries!.length, 1);
expect(tree.entries![0].content, expectedSemanticsIntegrationTestCiYamlContent);
expect(tree.entries![0].path, kCiYamlPath);
expect(tree.entries![0].mode, kModifyMode);
expect(tree.entries![0].type, kModifyType);
// Verify commit is created correctly.
captured = verify(mockGitService.createCommit(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
expect(captured[1], isA<CreateGitCommit>());
final CreateGitCommit commit = captured[1] as CreateGitCommit;
expect(commit.message, expectedSemanticsIntegrationTestPullRequestTitle);
expect(commit.author!.name, kCurrentUserName);
expect(commit.author!.email, kCurrentUserEmail);
expect(commit.committer!.name, kCurrentUserName);
expect(commit.committer!.email, kCurrentUserEmail);
expect(commit.tree, expectedSemanticsIntegrationTestTreeSha);
expect(commit.parents!.length, 1);
expect(commit.parents![0], kCurrentMasterSHA);
// Verify reference is created correctly.
captured = verify(mockGitService.createReference(captureAny, captureAny, captureAny)).captured;
expect(captured.length, 3);
expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
expect(captured[2], expectedSemanticsIntegrationTestTreeSha);
final String? ref = captured[1] as String?;
// Verify pr is created correctly.
captured = verify(mockPullRequestsService.create(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0].toString(), Config.flutterSlug.toString());
expect(captured[1], isA<CreatePullRequest>());
final CreatePullRequest pr = captured[1] as CreatePullRequest;
expect(pr.title, expectedSemanticsIntegrationTestPullRequestTitle);
expect(pr.body, expectedSemanticsIntegrationTestPullRequestBodyNoIssue);
expect(pr.head, '$kCurrentUserLogin:$ref');
expect(pr.base, 'refs/$kMasterRefs');
expect(result['Status'], 'success');
});
test('Do not create PR if the builder is in the ignored list', () async {
// when gets the content of .ci.yaml
when(mockRepositoriesService.getContents(
captureAny,
kCiYamlPath,
)).thenAnswer((Invocation invocation) {
return Future<RepositoryContents>.value(
RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentFlakyInIgnoreList))));
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
});
CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length;
final Map<String, dynamic> result = await utf8.decoder
.bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
.transform(json.decoder)
.single as Map<String, dynamic>;
// Verify pr is not called correctly.
verifyNever(mockPullRequestsService.create(captureAny, captureAny)).captured;
expect(result['Status'], 'success');
});
test('Do not create pr if the issue is still open', () async {
// When queries flaky data from BigQuery.
when(mockBigqueryService.listRecentBuildRecordsForBuilder(kBigQueryProjectId,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsAllPassed);
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
});
// When get issue
when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
return Future<Issue>.value(Issue(state: 'open', htmlUrl: existingIssueURL));
});
CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length;
final Map<String, dynamic> result = await utf8.decoder
.bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
.transform(json.decoder)
.single as Map<String, dynamic>;
// Verify it gets the correct issue.
final List<dynamic> captured = verify(mockIssuesService.get(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0], Config.flutterSlug);
expect(captured[1] as int?, existingIssueNumber);
// Verify pr is not created.
verifyNever(mockPullRequestsService.create(captureAny, captureAny));
expect(result['Status'], 'success');
});
test('Do not create pr but do create issue if the records have flaky runs and there is no open issue', () async {
// When queries flaky data from BigQuery.
when(mockBigqueryService.listRecentBuildRecordsForBuilder(kBigQueryProjectId,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsFlaky);
});
// When get issue
when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
return Future<Issue>.value(Issue(
state: 'closed',
htmlUrl: existingIssueURL,
closedAt: DateTime.now().subtract(const Duration(days: kGracePeriodForClosedFlake + 1)),
));
});
when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) {
return Future<Issue>.value(Issue());
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
});
CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length + 1;
final Map<String, dynamic> result = await utf8.decoder
.bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
.transform(json.decoder)
.single as Map<String, dynamic>;
// Verify BigQuery is called correctly.
List<dynamic> captured = verify(mockBigqueryService.listRecentBuildRecordsForBuilder(captureAny,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.captured;
expect(captured.length, 3);
expect(captured[0].toString(), kBigQueryProjectId);
expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName);
expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber);
// Verify it gets the correct issue.
captured = verify(mockIssuesService.get(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0], Config.flutterSlug);
expect(captured[1] as int?, existingIssueNumber);
// Verify pr is not created.
verifyNever(mockPullRequestsService.create(captureAny, captureAny));
// Verify issue is created correctly.
captured = verify(mockIssuesService.create(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0].toString(), Config.flutterSlug.toString());
expect(captured[1], isA<IssueRequest>());
final IssueRequest issueRequest = captured[1] as IssueRequest;
expect(issueRequest.assignee, expectedSemanticsIntegrationTestOwner);
expect(const ListEquality<String>().equals(issueRequest.labels, expectedSemanticsIntegrationTestLabels), isTrue);
expect(issueRequest.body, expectedSemanticsIntegrationTestResponseBody);
expect(result['Status'], 'success');
});
test('Do not create pr and do not create issue if the records have flaky runs and there is an open issue',
() async {
// When queries flaky data from BigQuery.
when(mockBigqueryService.listRecentBuildRecordsForBuilder(kBigQueryProjectId,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsFlaky);
});
// When get issue
when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
return Future<Issue>.value(Issue(
state: 'closed',
htmlUrl: existingIssueURL,
closedAt: DateTime.now().subtract(const Duration(days: kGracePeriodForClosedFlake - 1)),
));
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
});
CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length + 1;
final Map<String, dynamic> result = await utf8.decoder
.bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
.transform(json.decoder)
.single as Map<String, dynamic>;
// Verify pr is not created.
verifyNever(mockPullRequestsService.create(captureAny, captureAny));
// Verify issue is created correctly.
verifyNever(mockPullRequestsService.create(captureAny, captureAny));
expect(result['Status'], 'success');
});
test('Do not create pr and do not create issue if the records have flaky runs and there is a recently closed issue',
() async {
// When get issue
when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
return Future<Issue>.value(Issue(
state: 'open',
htmlUrl: existingIssueURL,
));
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
});
CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length + 1;
final Map<String, dynamic> result = await utf8.decoder
.bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
.transform(json.decoder)
.single as Map<String, dynamic>;
// Verify pr is not created.
verifyNever(mockPullRequestsService.create(captureAny, captureAny));
// Verify issue is created correctly.
verifyNever(mockPullRequestsService.create(captureAny, captureAny));
expect(result['Status'], 'success');
});
test('Do not create pr if the records have failed runs', () async {
// When queries flaky data from BigQuery.
when(mockBigqueryService.listRecentBuildRecordsForBuilder(kBigQueryProjectId,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsFailed);
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
});
// When get issue
when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
return Future<Issue>.value(Issue(
state: 'closed', htmlUrl: existingIssueURL, closedAt: DateTime.now().subtract(const Duration(days: 50))));
});
CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsFailed.length;
final Map<String, dynamic> result = await utf8.decoder
.bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
.transform(json.decoder)
.single as Map<String, dynamic>;
// Verify BigQuery is called correctly.
List<dynamic> captured = verify(mockBigqueryService.listRecentBuildRecordsForBuilder(captureAny,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.captured;
expect(captured.length, 3);
expect(captured[0].toString(), kBigQueryProjectId);
expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName);
expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber);
// Verify it gets the correct issue.
captured = verify(mockIssuesService.get(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0], Config.flutterSlug);
expect(captured[1] as int?, existingIssueNumber);
// Verify pr is not created.
verifyNever(mockPullRequestsService.create(captureAny, captureAny));
expect(result['Status'], 'success');
});
test('Do not create pr if there is an open one', () async {
// When queries flaky data from BigQuery.
when(mockBigqueryService.listRecentBuildRecordsForBuilder(kBigQueryProjectId,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsAllPassed);
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
});
// when gets existing marks flaky prs.
when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) {
return Stream<PullRequest>.value(PullRequest(body: expectedSemanticsIntegrationTestPullRequestBody));
});
// When get issue
when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
return Future<Issue>.value(Issue(state: 'closed', htmlUrl: existingIssueURL));
});
CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length;
final Map<String, dynamic> result = await utf8.decoder
.bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
.transform(json.decoder)
.single as Map<String, dynamic>;
// Verify pr is not created.
verifyNever(mockPullRequestsService.create(captureAny, captureAny));
expect(result['Status'], 'success');
});
test('Do not create pr if not enough records', () async {
// When queries flaky data from BigQuery.
when(mockBigqueryService.listRecentBuildRecordsForBuilder(kBigQueryProjectId,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsAllPassed);
});
// When queries flaky data from BigQuery.
when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
.thenAnswer((Invocation invocation) {
return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
});
// When get issue
when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
return Future<Issue>.value(Issue(
state: 'closed', htmlUrl: existingIssueURL, closedAt: DateTime.now().subtract(const Duration(days: 50))));
});
CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length + 1;
final Map<String, dynamic> result = await utf8.decoder
.bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
.transform(json.decoder)
.single as Map<String, dynamic>;
// Verify BigQuery is called correctly.
List<dynamic> captured = verify(mockBigqueryService.listRecentBuildRecordsForBuilder(captureAny,
builder: captureAnyNamed('builder'), limit: captureAnyNamed('limit')))
.captured;
expect(captured.length, 3);
expect(captured[0].toString(), kBigQueryProjectId);
expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName);
expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber);
// Verify it gets the correct issue.
captured = verify(mockIssuesService.get(captureAny, captureAny)).captured;
expect(captured.length, 2);
expect(captured[0], Config.flutterSlug);
expect(captured[1] as int?, existingIssueNumber);
// Verify pr is not created.
verifyNever(mockPullRequestsService.create(captureAny, captureAny));
expect(result['Status'], 'success');
});
test('getIgnoreFlakiness handles non-existing builderame', () async {
final YamlMap? ci = loadYaml(ciYamlContent) as YamlMap?;
final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci);
final CiYaml ciYaml = CiYaml(
slug: Config.flutterSlug,
branch: Config.defaultBranch(Config.flutterSlug),
config: unCheckedSchedulerConfig,
);
CheckFlakyBuilders.getIgnoreFlakiness('Non_existing', ciYaml);
});
});
}