blob: 175757c210ff21bfc95f6a7d8649a9395b8bb0fa [file] [log] [blame]
// Copyright 2023 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:auto_submit/configuration/repository_configuration.dart';
import 'package:auto_submit/model/auto_submit_query_result.dart';
import 'package:auto_submit/validations/approval.dart';
import 'package:auto_submit/validations/validation.dart';
import 'package:test/test.dart';
import 'package:github/github.dart' as gh;
import '../configuration/repository_configuration_data.dart';
import '../requests/github_webhook_test_data.dart';
import '../src/service/fake_config.dart';
import '../src/service/fake_github_service.dart';
import '../src/service/fake_graphql_client.dart';
import '../utilities/mocks.mocks.dart';
import 'approval_test_data.dart';
void main() {
late Approval approval;
late FakeConfig config;
final FakeGithubService githubService = FakeGithubService();
late FakeGraphQLClient githubGraphQLClient;
final MockGitHub gitHub = MockGitHub();
setUp(() {
githubGraphQLClient = FakeGraphQLClient();
config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient, githubClient: gitHub);
config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride);
approval = Approval(config: config);
});
group('Approval group tests', () {
Future<ValidationResult> computeValidationResult(String review) async {
final Map<String, dynamic> queryResultJsonDecode = jsonDecode(review) as Map<String, dynamic>;
final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode);
final gh.PullRequest pullRequest = generatePullRequest();
return approval.validate(queryResult, pullRequest);
}
test('Author and reviewer in flutter-hackers, pr approved', () async {
final String review = constructSingleReviewerReview(
reviewState: 'APPROVED',
);
// githubService.isTeamMemberMockList = [true, true];
githubService.isTeamMemberMockMap['author1'] = true;
githubService.isTeamMemberMockMap['keyonghan'] = true;
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isTrue);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue);
});
test('Author is a NON member and reviewer is a member, needs 1 more review', () async {
// githubService.isTeamMemberMockList = [true, true];
githubService.isTeamMemberMockMap['author1'] = false;
githubService.isTeamMemberMockMap['keyonghan'] = true;
final String review = constructSingleReviewerReview(
reviewState: 'APPROVED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
expect(result.message.contains('needs 1 more review'), isTrue);
});
test('Author is a NON member and reviewer is a NON member, needs 2 more reviews', () async {
// githubService.isTeamMemberMockList = [true, true];
githubService.isTeamMemberMockMap['author1'] = false;
githubService.isTeamMemberMockMap['keyonghan'] = false;
final String review = constructSingleReviewerReview(
reviewState: 'APPROVED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
expect(result.message.contains('needs 2 more review'), isTrue);
});
test('Author is a member and reviewer is NON member, needs 1 more review', () async {
// githubService.isTeamMemberMockList = [true, true];
githubService.isTeamMemberMockMap['author1'] = true;
githubService.isTeamMemberMockMap['keyonghan'] = false;
final String review = constructSingleReviewerReview(
reviewState: 'APPROVED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.IGNORE_TEMPORARILY);
expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
expect(result.message.contains('needs 1 more review'), isTrue);
});
test('Author is NON member and reviewers are members, pr approved', () async {
githubService.isTeamMemberMockMap['author1'] = false;
githubService.isTeamMemberMockMap['author2'] = true;
githubService.isTeamMemberMockMap['author3'] = true;
final String review = constructTwoReviewerReview(
reviewState: 'APPROVED',
secondReviewState: 'APPROVED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isTrue);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue);
});
test('Author is NON member and one reviewer is a NON member, needs 1 more review', () async {
githubService.isTeamMemberMockMap['author1'] = false;
githubService.isTeamMemberMockMap['author2'] = true;
githubService.isTeamMemberMockMap['author3'] = false;
final String review = constructTwoReviewerReview(
reviewState: 'APPROVED',
secondReviewState: 'APPROVED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
expect(result.message.contains('needs 1 more review'), isTrue);
});
test('Author is member and reviewers are NON members, needs 1 more review', () async {
githubService.isTeamMemberMockMap['author1'] = true;
githubService.isTeamMemberMockMap['author2'] = false;
githubService.isTeamMemberMockMap['author3'] = false;
final String review = constructTwoReviewerReview(
reviewState: 'APPROVED',
secondReviewState: 'APPROVED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.IGNORE_TEMPORARILY);
expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
expect(result.message.contains('needs 1 more review'), isTrue);
});
test('Author is NON member and reviewers are NON members, needs 2 reviews', () async {
githubService.isTeamMemberMockMap['author1'] = false;
githubService.isTeamMemberMockMap['author2'] = false;
githubService.isTeamMemberMockMap['author3'] = false;
final String review = constructTwoReviewerReview(
reviewState: 'APPROVED',
secondReviewState: 'APPROVED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
expect(result.message.contains('needs 2 more review'), isTrue);
});
test('Verify author review count does not go negative', () async {
githubService.isTeamMemberMockMap['author1'] = false;
githubService.isTeamMemberMockMap['ricardoamador'] = false;
githubService.isTeamMemberMockMap['keyonghan'] = false;
githubService.isTeamMemberMockMap['nehalvpatel'] = false;
final String review = constructMultipleReviewerReview(
reviewState: 'APPROVED',
secondReviewState: 'APPROVED',
thirdReviewState: 'APPROVED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
expect(result.message.contains('needs 2 more review'), isTrue);
});
test('Verify author review count does not go negative', () async {
githubService.isTeamMemberMockMap['author1'] = true;
githubService.isTeamMemberMockMap['ricardoamador'] = true;
githubService.isTeamMemberMockMap['keyonghan'] = true;
githubService.isTeamMemberMockMap['nehalvpatel'] = true;
final String review = constructMultipleReviewerReview(
reviewState: 'APPROVED',
secondReviewState: 'APPROVED',
thirdReviewState: 'APPROVED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isTrue);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue);
});
test('Author is member and member requests changes, 1 review is needed', () async {
githubService.isTeamMemberMockMap['author1'] = true;
githubService.isTeamMemberMockMap['keyonghan'] = true;
final String review = constructSingleReviewerReview(
reviewState: 'CHANGES_REQUESTED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
expect(result.message.contains('Changes were requested by'), isTrue);
});
test('Author is member and two member reviews, 1 change request, review is not approved', () async {
githubService.isTeamMemberMockMap['author1'] = true;
githubService.isTeamMemberMockMap['author2'] = true;
githubService.isTeamMemberMockMap['author3'] = true;
final String review = constructTwoReviewerReview(
reviewState: 'CHANGES_REQUESTED',
secondReviewState: 'APPROVED',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
expect(result.message.contains('Changes were requested by'), isTrue);
});
test('Multiple approving reviews from the same author are counted only 1 time.', () async {
githubService.isTeamMemberMockMap['author1'] = false;
githubService.isTeamMemberMockMap['ricardoamador'] = true;
final String review = constructTwoReviewerReview(
reviewState: 'APPROVED',
secondReviewState: 'APPROVED',
author: 'ricardoamador',
secondAuthor: 'ricardoamador',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.REMOVE_LABEL);
expect(
result.message.contains(
'This PR has not met approval requirements for merging. The PR author is not a member of flutter-hackers and needs 1 more review(s) in order to merge this PR.',
),
isTrue,
);
});
test('Successful review overwrites previous changes requested.', () async {
githubService.isTeamMemberMockMap['author1'] = true;
githubService.isTeamMemberMockMap['keyonghan'] = true;
githubService.isTeamMemberMockMap['jmagman'] = true;
final ValidationResult result = await computeValidationResult(multipleReviewsSameAuthor);
expect(result.result, isTrue);
expect(result.action, Action.REMOVE_LABEL);
expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue);
});
test('Author cannot review own pr', () async {
githubService.isTeamMemberMockMap['author1'] = false;
githubService.isTeamMemberMockMap['author3'] = true;
final String review = constructTwoReviewerReview(
reviewState: 'APPROVED',
secondReviewState: 'APPROVED',
author: 'author1',
);
final ValidationResult result = await computeValidationResult(review);
expect(result.result, isFalse);
expect(result.action, Action.REMOVE_LABEL);
expect(
result.message.contains(
'This PR has not met approval requirements for merging. The PR author is not a member of flutter-hackers',
),
isTrue,
);
});
});
}