blob: cc94df0f6939929cab20ffe97cfedf87289eeb64 [file] [log] [blame] [edit]
// Copyright 2022 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/service/github_service.dart';
import 'package:cocoon_server_test/mocks.dart';
import 'package:github/github.dart';
import 'package:shelf/src/response.dart';
import 'package:test/test.dart';
/// A fake GithubService implementation.
class FakeGithubService implements GithubService {
FakeGithubService({
MockGitHub? client,
String? checkRunsMock,
String? commitMock,
String? pullRequest,
String? compareTwoCommitsMock,
String? successMergeMock,
String? createCommentMock,
String? pullRequestMergeMock,
}) : github = client ?? MockGitHub();
@override
final MockGitHub github;
String? checkRunsMock;
String? commitMock;
PullRequest? pullRequestMock;
String? compareTwoCommitsMock;
String? successMergeMock;
String? createCommentMock;
String? pullRequestMergeMock;
String? pullRequestFilesJsonMock;
Issue? githubIssueMock;
String? githubFileContents;
bool useMergeRequestMockList = false;
bool trackMergeRequestCalls = false;
PullRequestMerge? mergeRequestMock;
List<PullRequestMerge> pullRequestMergeMockList = [];
/// map to track pull request calls using pull number and repository slug.
Map<int, RepositorySlug> verifyPullRequestMergeCallMap = {};
bool throwOnCreateIssue = false;
/// Setting either of these flags to true will pop the front element from the
/// list. Setting either to false will just return the non list version from
/// the appropriate method.
bool usePullRequestList = false;
bool usePullRequestFilesList = false;
List<String?> pullRequestFilesMockList = [];
List<PullRequest?> pullRequestMockList = [];
IssueComment? issueComment;
bool useRealComment = false;
bool labelRemoved = false;
bool compareReturnValue = false;
bool skipRealCompare = false;
set checkRunsData(String? checkRunsMock) {
this.checkRunsMock = checkRunsMock;
}
set commitData(String? commitMock) {
this.commitMock = commitMock;
}
set pullRequestData(PullRequest? pullRequestMock) {
this.pullRequestMock = pullRequestMock;
}
set compareTwoCommitsData(String? compareTwoCommitsMock) {
this.compareTwoCommitsMock = compareTwoCommitsMock;
}
set successMergeData(String? successMergeMock) {
this.successMergeMock = successMergeMock;
}
set createCommentData(String? createCommentMock) {
this.createCommentMock = createCommentMock;
}
set pullRequestMergeData(String? pullRequestMergeMock) {
this.pullRequestMergeMock = pullRequestMergeMock;
}
set pullrequestFilesData(String? pullRequestFilesMock) {
pullRequestFilesJsonMock = pullRequestFilesMock;
}
set githubIssue(Issue? issue) {
githubIssueMock = issue;
}
@override
Future<List<CheckRun>> getCheckRuns(RepositorySlug slug, String ref) async {
final rawBody = json.decode(checkRunsMock!) as Map<String, dynamic>;
final checkRunsBody = rawBody['check_runs']! as List<dynamic>;
final checkRuns = <CheckRun>[];
if ((checkRunsBody[0] as Map<String, dynamic>).isNotEmpty) {
checkRuns.addAll(
checkRunsBody
.map(
(dynamic checkRun) =>
CheckRun.fromJson(checkRun as Map<String, dynamic>),
)
.toList(),
);
}
return checkRuns;
}
@override
Future<List<CheckRun>> getCheckRunsFiltered({
required RepositorySlug slug,
required String ref,
String? checkName,
CheckRunStatus? status,
CheckRunFilter? filter,
}) async {
final checkRuns = await getCheckRuns(slug, ref);
if (checkName != null) {
final checkRunsFilteredByName = <CheckRun>[];
for (var checkRun in checkRuns) {
if (checkRun.name == checkName && checkRun.headSha == ref) {
checkRunsFilteredByName.add(checkRun);
}
}
return checkRunsFilteredByName;
}
return checkRuns;
}
final List<
({
RepositorySlug slug,
CheckRun checkRun,
String? name,
String? detailsUrl,
String? externalId,
DateTime? startedAt,
CheckRunStatus status,
CheckRunConclusion? conclusion,
DateTime? completedAt,
CheckRunOutput? output,
List<CheckRunAction>? actions,
})
>
checkRunUpdates = [];
@override
Future<CheckRun> updateCheckRun({
required RepositorySlug slug,
required CheckRun checkRun,
String? name,
String? detailsUrl,
String? externalId,
DateTime? startedAt,
CheckRunStatus status = CheckRunStatus.queued,
CheckRunConclusion? conclusion,
DateTime? completedAt,
CheckRunOutput? output,
List<CheckRunAction>? actions,
}) async {
final Map<String, Object?> json = checkRun.toJson();
checkRunUpdates.add((
slug: slug,
checkRun: checkRun,
name: name,
detailsUrl: detailsUrl,
externalId: externalId,
startedAt: startedAt,
status: status,
conclusion: conclusion,
completedAt: completedAt,
output: output,
actions: actions,
));
if (conclusion != null) {
json['conclusion'] = conclusion.value;
}
if (status != checkRun.status) {
json['status'] = status.value;
}
return CheckRun.fromJson(json);
}
@override
Future<RepositoryCommit> getCommit(RepositorySlug slug, String sha) async {
final commit = RepositoryCommit.fromJson(
jsonDecode(commitMock!) as Map<String, dynamic>,
);
return commit;
}
@override
Future<PullRequest> getPullRequest(
RepositorySlug slug,
int pullRequestNumber,
) async {
PullRequest pullRequest;
if (usePullRequestList && pullRequestMockList.isNotEmpty) {
pullRequest = pullRequestMockList.removeAt(0)!;
} else if (usePullRequestList && pullRequestMockList.isEmpty) {
throw Exception('List is empty.');
} else if (pullRequestMock != null) {
pullRequest = pullRequestMock!;
} else {
throw StateError('Unexpected invocation of getPullRequest method');
}
return pullRequest;
}
@override
Future<GitHubComparison> compareTwoCommits(
RepositorySlug slug,
String refBase,
String refHead,
) async {
final githubComparison = GitHubComparison.fromJson(
jsonDecode(compareTwoCommitsMock!) as Map<String, dynamic>,
);
return githubComparison;
}
@override
Future<bool> removeLabel(
RepositorySlug slug,
int issueNumber,
String label,
) async {
labelRemoved = true;
return labelRemoved;
}
@override
Future<List<IssueLabel>> addLabels(
RepositorySlug slug,
int issueNumber,
List<String> labels,
) async {
final labelsAdded = <IssueLabel>[];
for (var labelName in labels) {
labelsAdded.add(IssueLabel(name: labelName));
}
return labelsAdded;
}
@override
Future<IssueComment> createComment(
RepositorySlug slug,
int number,
String commentBody,
) async {
if (useRealComment) {
issueComment = IssueComment(id: number, body: commentBody);
} else {
issueComment = IssueComment.fromJson(
jsonDecode(createCommentMock!) as Map<String, dynamic>,
);
}
return issueComment!;
}
@override
Future<bool> updateBranch(
RepositorySlug slug,
int number,
String headSha,
) async {
return true;
}
@override
Future<Response> autoMergeBranch(PullRequest pullRequest) {
// TODO: implement autoMergeBranch
throw UnimplementedError();
}
@override
Future<List<PullRequestFile>> getPullRequestFiles(
RepositorySlug slug,
PullRequest pullRequest,
) async {
String pullRequestData;
if (usePullRequestFilesList && pullRequestFilesMockList.isNotEmpty) {
pullRequestData = pullRequestFilesMockList.removeAt(0)!;
} else if (usePullRequestFilesList && pullRequestFilesMockList.isEmpty) {
throw Exception('File list is empty.');
} else {
pullRequestData = pullRequestFilesJsonMock as String;
}
final pullRequestFileList = <PullRequestFile>[];
final dynamic parsedList = jsonDecode(pullRequestData);
for (dynamic d in parsedList as Iterable) {
final file = PullRequestFile.fromJson(d as Map<String, dynamic>);
pullRequestFileList.add(file);
}
return pullRequestFileList;
}
@override
Future<Issue> createIssue({
required RepositorySlug slug,
required String title,
required String body,
List<String>? labels,
String? assignee,
List<String>? assignees,
String? state,
}) async {
if (throwOnCreateIssue) {
throw GitHubError(github, 'Exception on github create issue.');
}
return githubIssueMock!;
}
@override
Future<Issue> getIssue({
required RepositorySlug slug,
required int issueNumber,
}) async {
return githubIssueMock!;
}
bool throwExceptionOnMerge = false;
/// If useMergeRequestMockList is true then we will return elements from that
/// list until it is empty.
///
/// The developer should track the number of times this method is called as
/// managing an empty list is not done here.
@override
Future<PullRequestMerge> mergePullRequest(
RepositorySlug slug,
int number, {
String? commitMessage,
MergeMethod? mergeMethod,
String? requestSha,
}) async {
if (throwExceptionOnMerge) {
throw Exception('Exception occurred during merging of pull request.');
}
verifyPullRequestMergeCallMap[number] = slug;
if (useMergeRequestMockList) {
return pullRequestMergeMockList.removeAt(0);
} else {
return mergeRequestMock!;
}
}
void verifyMergePullRequests(Map<int, RepositorySlug> expected) {
expect(
reason:
'Pull request numbers in mergePullRequest invocations do not match',
verifyPullRequestMergeCallMap.keys.toList(),
expected.keys.toList(),
);
verifyPullRequestMergeCallMap.forEach((key, value) {
expect(value, expected[key]);
});
}
bool throwExceptionFileContents = false;
List<String> fileContentsMockList = [];
@override
Future<String> getFileContents(
RepositorySlug slug,
String path, {
String? ref,
}) async {
if (throwExceptionFileContents) {
throw 'Contents do not point to a file.';
}
// Assume that the list is not empty.
return fileContentsMockList.removeAt(0);
}
TeamMembershipState? teamMembershipStateMock = TeamMembershipState('active');
String defaultBranch = 'main';
bool throwOnDefaultBranch = false;
Exception exception = Exception('Generic exception.');
@override
Future<String> getDefaultBranch(RepositorySlug slug) async {
if (throwOnDefaultBranch) {
throw exception;
} else {
return defaultBranch;
}
}
Repository repositoryMock = Repository();
@override
Future<Repository> getRepository(RepositorySlug slug) async {
return repositoryMock;
}
Map<String, bool> isTeamMemberMockMap = <String, bool>{};
@override
Future<bool> isTeamMember(String team, String user, String org) async {
if (!isTeamMemberMockMap.containsKey(user)) {
return false;
}
return isTeamMemberMockMap[user]!;
}
@override
Future<PullRequest> createPullRequest({
required RepositorySlug slug,
String? title,
String? head,
required String base,
bool draft = false,
String? body,
}) {
// TODO: implement createPullRequest
throw UnimplementedError();
}
@override
Future<List<PullRequest>> listPullRequests(
RepositorySlug slug, {
int? pages,
String? base,
String direction = 'desc',
String? head,
String sort = 'created',
String state = 'open',
}) {
// TODO: implement listPullRequests
throw UnimplementedError();
}
String? branchMockData;
set branchMock(String data) => branchMock = data;
@override
Future<Branch> getBranch(RepositorySlug slug, String branchName) async {
return Branch.fromJson(
json.decode(branchMockData!) as Map<String, Object?>,
);
}
bool addReviewersToPullRequestMock = true;
@override
Future<bool> addReviewersToPullRequest(
RepositorySlug slug,
int pullRequestNumber,
List<String> reviewerLogins,
) async {
return addReviewersToPullRequestMock;
}
bool addAssigneeMock = true;
@override
Future<bool> addAssignee(
RepositorySlug slug,
int number,
List<String> assignees,
) async {
return addAssigneeMock;
}
final List<(RepositorySlug, String)> deletedBranches = [];
bool deleteBranchMock = true;
@override
Future<bool> deleteBranch(RepositorySlug slug, String branchName) async {
deletedBranches.add((slug, branchName));
return deleteBranchMock;
}
List<IssueComment> issueCommentsMock = [];
@override
Future<List<IssueComment>> getIssueComments(
RepositorySlug slug,
int issueNumber,
) async {
return issueCommentsMock;
}
List<PullRequestReview> pullRequestReviewsMock = [];
@override
Future<List<PullRequestReview>> getPullRequestReviews(
RepositorySlug slug,
int pullRequestNumber,
) async {
return pullRequestReviewsMock;
}
}