| // 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: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 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); |
| }); |
| }); |
| } |