blob: 920ecf3d57992a1a1ec7d0425733338ed9f2b58e [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:convert';
import 'package:cocoon_service/src/model/appengine/service_account_info.dart';
import 'package:cocoon_service/src/model/luci/buildbucket.dart';
import 'package:cocoon_service/src/model/luci/push_message.dart'
as push_message;
import 'package:cocoon_service/src/service/github_status_service.dart';
import 'package:cocoon_service/src/service/luci_build_service.dart';
import 'package:github/github.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
import '../src/datastore/fake_cocoon_config.dart';
import '../src/utilities/mocks.dart';
void main() {
ServiceAccountInfo serviceAccountInfo;
FakeConfig config;
MockBuildBucketClient mockBuildBucketClient;
LuciBuildService service;
GithubStatusService githubStatusService;
MockGitHub mockGitHub;
MockRepositoriesService mockRepositoriesService;
RepositorySlug slug;
const Build macBuild = Build(
id: 999,
builderId: BuilderId(
project: 'flutter',
bucket: 'prod',
builder: 'MacDoesNotExist',
),
status: Status.scheduled,
);
const Build linuxBuild = Build(
id: 998,
builderId: BuilderId(
project: 'flutter',
bucket: 'prod',
builder: 'Linux',
),
status: Status.scheduled,
);
setUp(() {
serviceAccountInfo = const ServiceAccountInfo(email: 'abc@abcd.com');
config = FakeConfig(deviceLabServiceAccountValue: serviceAccountInfo);
mockBuildBucketClient = MockBuildBucketClient();
service =
LuciBuildService(config, mockBuildBucketClient, serviceAccountInfo);
githubStatusService = GithubStatusService(config, service);
mockGitHub = MockGitHub();
mockRepositoriesService = MockRepositoriesService();
when(mockGitHub.repositories).thenAnswer((_) {
return mockRepositoriesService;
});
config.githubClient = mockGitHub;
config.luciTryBuildersValue = <Map<String, dynamic>>[];
slug = RepositorySlug('flutter', 'flutter');
});
group('setBuildsPendingStatus', () {
test('Empty builds do nothing', () async {
when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
return const BatchResponse(
responses: <Response>[
Response(
searchBuilds: SearchBuildsResponse(
builds: <Build>[],
),
),
],
);
});
await githubStatusService.setBuildsPendingStatus(123, 'abc', slug);
verifyNever(mockGitHub.repositories);
});
test('A build list creates status', () async {
when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
return const BatchResponse(
responses: <Response>[
Response(
searchBuilds: SearchBuildsResponse(
builds: <Build>[linuxBuild],
),
),
],
);
});
config.luciTryBuildersValue =
(json.decode('[{"name": "Linux"}]') as List<dynamic>)
.cast<Map<String, dynamic>>();
final List<RepositoryStatus> repositoryStatuses = <RepositoryStatus>[];
when(mockRepositoriesService.listStatuses(any, any)).thenAnswer((_) {
return Stream<RepositoryStatus>.fromIterable(repositoryStatuses);
});
await githubStatusService.setBuildsPendingStatus(123, 'abc', slug);
expect(
verify(mockRepositoriesService.createStatus(any, any, captureAny))
.captured
.first
.toJson(),
jsonDecode(
'{"state":"pending","target_url":"","description":"Flutter LUCI Build: Linux","context":"Linux"}'));
});
test('Only builds in luciTryBuilders create statuses', () async {
when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
return const BatchResponse(
responses: <Response>[
Response(
searchBuilds: SearchBuildsResponse(
builds: <Build>[linuxBuild, macBuild],
),
),
],
);
});
config.luciTryBuildersValue =
(json.decode('[{"name": "Linux"}]') as List<dynamic>)
.cast<Map<String, dynamic>>();
final List<RepositoryStatus> repositoryStatuses = <RepositoryStatus>[];
when(mockRepositoriesService.listStatuses(any, any)).thenAnswer((_) {
return Stream<RepositoryStatus>.fromIterable(repositoryStatuses);
});
await githubStatusService.setBuildsPendingStatus(123, 'abc', slug);
expect(
verify(mockRepositoriesService.createStatus(any, any, captureAny))
.captured
.first
.toJson(),
jsonDecode(
'{"state":"pending","target_url":"","description":"Flutter LUCI Build: Linux","context":"Linux"}'));
});
});
group('setPendingStatus', () {
setUp(() {});
test('Status not updated if builder does not exist', () async {
final bool success = await githubStatusService.setPendingStatus(
ref: '123hash',
builderName: 'MacNoExists',
buildUrl: 'myurl',
slug: slug,
);
expect(success, isFalse);
verifyNever(mockGitHub.repositories);
});
test('Status not updated if it is already pending', () async {
when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
return const BatchResponse(
responses: <Response>[
Response(
searchBuilds: SearchBuildsResponse(
builds: <Build>[macBuild],
),
),
],
);
});
final List<RepositoryStatus> repositoryStatuses = <RepositoryStatus>[
RepositoryStatus()
..context = 'Mac'
..state = 'pending'
..targetUrl = 'url'
];
when(mockRepositoriesService.listStatuses(any, any)).thenAnswer((_) {
return Stream<RepositoryStatus>.fromIterable(repositoryStatuses);
});
final bool success = await githubStatusService.setPendingStatus(
ref: '123hash',
builderName: 'Mac',
buildUrl: 'url',
slug: slug,
);
expect(success, isFalse);
verifyNever(mockRepositoriesService.createStatus(any, any, captureAny));
});
test('Status updated when not pending or url is different', () async {
List<RepositoryStatus> repositoryStatuses = <RepositoryStatus>[
RepositoryStatus()
..context = 'Mac'
..state = 'failed'
..targetUrl = 'url'
];
when(mockRepositoriesService.listStatuses(any, any)).thenAnswer((_) {
return Stream<RepositoryStatus>.fromIterable(repositoryStatuses);
});
final bool success = await githubStatusService.setPendingStatus(
ref: '123hash',
builderName: 'Mac',
buildUrl: 'url',
slug: slug,
);
expect(success, isTrue);
expect(
verify(mockRepositoriesService.createStatus(any, any, captureAny))
.captured
.first
.toJson(),
jsonDecode(
'{"state":"pending","target_url":"url?reload=30","description":"Flutter LUCI Build: Mac","context":"Mac"}'));
repositoryStatuses = <RepositoryStatus>[
RepositoryStatus()
..context = 'Mac'
..state = 'pending'
..targetUrl = 'url'
];
await githubStatusService.setPendingStatus(
ref: '123hash',
builderName: 'Mac',
buildUrl: 'different_url',
slug: slug,
);
expect(
verify(mockRepositoriesService.createStatus(any, any, captureAny))
.captured
.first
.toJson(),
jsonDecode(
'{"state":"pending","target_url":"different_url?reload=30","description":"Flutter LUCI Build: Mac","context":"Mac"}'));
});
});
group('setCompletedStatus', () {
setUp(() {});
test('Builder does not exist', () async {
await githubStatusService.setCompletedStatus(
ref: '123hash',
builderName: 'MacNoExists',
buildUrl: 'myurl',
slug: slug,
result: push_message.Result.canceled);
verifyNever(mockGitHub.repositories);
});
test('Status updated to cancelled', () async {
final List<RepositoryStatus> repositoryStatuses = <RepositoryStatus>[
RepositoryStatus()
..context = 'Mac'
..state = 'pending'
..targetUrl = 'url'
];
when(mockRepositoriesService.listStatuses(any, any)).thenAnswer((_) {
return Stream<RepositoryStatus>.fromIterable(repositoryStatuses);
});
await githubStatusService.setCompletedStatus(
ref: '123hash',
builderName: 'Mac',
buildUrl: 'url',
slug: slug,
result: push_message.Result.canceled);
expect(
verify(mockRepositoriesService.createStatus(any, any, captureAny))
.captured
.first
.toJson(),
jsonDecode(
'{"state":"failure","target_url":"url","description":"Flutter LUCI Build: Mac","context":"Mac"}'));
});
test('Status updated to success', () async {
final List<RepositoryStatus> repositoryStatuses = <RepositoryStatus>[
RepositoryStatus()
..context = 'Mac'
..state = 'pending'
..targetUrl = 'url'
];
when(mockRepositoriesService.listStatuses(any, any)).thenAnswer((_) {
return Stream<RepositoryStatus>.fromIterable(repositoryStatuses);
});
await githubStatusService.setCompletedStatus(
ref: '123hash',
builderName: 'Mac',
buildUrl: 'url',
slug: slug,
result: push_message.Result.success);
expect(
verify(mockRepositoriesService.createStatus(any, any, captureAny))
.captured
.first
.toJson(),
jsonDecode(
'{"state":"success","target_url":"url","description":"Flutter LUCI Build: Mac","context":"Mac"}'));
});
});
}