| // 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 'package:cocoon_service/src/model/appengine/commit.dart'; |
| import 'package:cocoon_service/src/model/appengine/task.dart'; |
| import 'package:cocoon_service/src/model/luci/buildbucket.dart'; |
| import 'package:cocoon_service/src/request_handlers/reset_prod_task.dart'; |
| import 'package:cocoon_service/src/request_handling/exceptions.dart'; |
| import 'package:mockito/mockito.dart'; |
| import 'package:test/test.dart'; |
| |
| import '../src/datastore/fake_config.dart'; |
| import '../src/datastore/fake_datastore.dart'; |
| import '../src/request_handling/api_request_handler_tester.dart'; |
| import '../src/request_handling/fake_authentication.dart'; |
| import '../src/utilities/mocks.dart'; |
| |
| const Build startedBuild = Build( |
| id: 999, |
| builderId: BuilderId( |
| project: 'flutter', |
| bucket: 'prod', |
| builder: 'Mac', |
| ), |
| status: Status.started, |
| ); |
| const Build scheduledBuild = Build( |
| id: 999, |
| builderId: BuilderId( |
| project: 'flutter', |
| bucket: 'prod', |
| builder: 'Mac', |
| ), |
| status: Status.scheduled, |
| ); |
| const Build succeededBuild = Build( |
| id: 999, |
| builderId: BuilderId( |
| project: 'flutter', |
| bucket: 'prod', |
| builder: 'Mac', |
| ), |
| status: Status.success, |
| ); |
| |
| void main() { |
| group('ResetProdTask', () { |
| FakeClientContext clientContext; |
| ResetProdTask handler; |
| FakeConfig config; |
| MockLuciBuildService mockLuciBuildService; |
| FakeAuthenticatedContext authContext; |
| ApiRequestHandlerTester tester; |
| Commit commit; |
| |
| setUp(() { |
| final FakeDatastoreDB datastoreDB = FakeDatastoreDB(); |
| config = FakeConfig(dbValue: datastoreDB); |
| clientContext = FakeClientContext(); |
| clientContext.isDevelopmentEnvironment = false; |
| authContext = FakeAuthenticatedContext(clientContext: clientContext); |
| tester = ApiRequestHandlerTester(context: authContext); |
| mockLuciBuildService = MockLuciBuildService(); |
| handler = ResetProdTask( |
| config, |
| FakeAuthenticationProvider(clientContext: clientContext), |
| mockLuciBuildService, |
| ); |
| tester.requestData = <String, dynamic>{ |
| 'Key': |
| 'ag9zfnR2b2xrZXJ0LXRlc3RyWAsSCUNoZWNrbGlzdCI4Zmx1dHRlci9mbHV0dGVyLzdkMDMzNzE2MTBjMDc5NTNhNWRlZjUwZDUwMDA0NTk0MWRlNTE2YjgMCxIEVGFzaxiAgIDg5eGTCAw' |
| }; |
| commit = Commit( |
| key: config.db.emptyKey.append( |
| Commit, |
| id: 'flutter/flutter/7d03371610c07953a5def50d500045941de516b8', |
| ), |
| repository: 'flutter/flutter', |
| sha: '7d03371610c07953a5def50d500045941de516b8', |
| ); |
| }); |
| test('Schedule new task', () async { |
| final Task task = Task( |
| key: commit.key.append(Task, id: 4590522719010816), |
| commitKey: commit.key, |
| attempts: 0, |
| status: 'Failed', |
| name: 'windows_bot', |
| ); |
| config.db.values[task.key] = task; |
| config.db.values[commit.key] = commit; |
| when(mockLuciBuildService.getProdBuilds(any, any, any, any)).thenAnswer((_) async { |
| return <Build>[]; |
| }); |
| await tester.post(handler); |
| expect( |
| verify(mockLuciBuildService.rescheduleProdBuild( |
| commitSha: captureAnyNamed('commitSha'), |
| builderName: captureAnyNamed('builderName'), |
| )).captured, |
| <dynamic>['7d03371610c07953a5def50d500045941de516b8', 'Windows'], |
| ); |
| expect(task.attempts, equals(1)); |
| }); |
| |
| test('Re-schedule existing task', () async { |
| Task task = Task( |
| key: commit.key.append(Task, id: 4590522719010816), |
| commitKey: commit.key, |
| attempts: 0, |
| status: 'Failed', |
| builderName: 'Windows'); |
| config.db.values[task.key] = task; |
| config.db.values[commit.key] = commit; |
| when(mockLuciBuildService.getProdBuilds(any, any, any, any)).thenAnswer((_) async { |
| return <Build>[]; |
| }); |
| await tester.post(handler); |
| expect( |
| verify(mockLuciBuildService.rescheduleProdBuild( |
| commitSha: captureAnyNamed('commitSha'), |
| builderName: captureAnyNamed('builderName'), |
| )).captured, |
| <dynamic>['7d03371610c07953a5def50d500045941de516b8', 'Windows'], |
| ); |
| task = config.db.values[task.key] as Task; |
| expect(task.attempts, equals(1)); |
| }); |
| |
| test('Re-schedule passing all the parameters', () async { |
| tester.requestData = <String, dynamic>{ |
| 'Commit': 'commitSha', |
| 'Builder': 'Windows', |
| 'Repo': 'engine', |
| }; |
| when(mockLuciBuildService.getProdBuilds(any, any, any, any)).thenAnswer((_) async { |
| return <Build>[]; |
| }); |
| await tester.post(handler); |
| expect( |
| verify(mockLuciBuildService.rescheduleProdBuild( |
| commitSha: captureAnyNamed('commitSha'), |
| builderName: captureAnyNamed('builderName'), |
| branch: captureAnyNamed('branch'), |
| repo: captureAnyNamed('repo'), |
| )).captured, |
| <dynamic>['commitSha', 'Windows', 'master', 'engine'], |
| ); |
| }); |
| |
| test('Using curl with flutter repo raises exception', () async { |
| tester.requestData = <String, dynamic>{ |
| 'Commit': 'commitSha', |
| 'Builder': 'Windows', |
| 'Repo': 'flutter', |
| }; |
| expect(() => tester.post(handler), throwsA(isA<BadRequestException>())); |
| }); |
| |
| test('Re-schedule without all the parameters raises exception', () async { |
| tester.requestData = <String, dynamic>{}; |
| expect(() => tester.post(handler), throwsA(isA<BadRequestException>())); |
| }); |
| |
| test('Re-schedule existing task even though builderName is missing in the task', () async { |
| Task task = Task( |
| key: commit.key.append(Task, id: 4590522719010816), |
| commitKey: commit.key, |
| attempts: 0, |
| name: 'windows_bot', |
| status: 'Failed'); |
| config.db.values[task.key] = task; |
| config.db.values[commit.key] = commit; |
| when(mockLuciBuildService.getProdBuilds(any, any, any, any)).thenAnswer((_) async { |
| return <Build>[]; |
| }); |
| await tester.post(handler); |
| expect( |
| verify(mockLuciBuildService.rescheduleProdBuild( |
| commitSha: captureAnyNamed('commitSha'), |
| builderName: captureAnyNamed('builderName'), |
| )).captured, |
| <dynamic>['7d03371610c07953a5def50d500045941de516b8', 'Windows'], |
| ); |
| task = config.db.values[task.key] as Task; |
| expect(task.attempts, equals(1)); |
| }); |
| |
| test('Does nothing if task already passed', () async { |
| final Task task = Task( |
| key: commit.key.append(Task, id: 4590522719010816), |
| commitKey: commit.key, |
| attempts: 0, |
| status: 'Succeeded', |
| builderName: 'Windows'); |
| config.db.values[task.key] = task; |
| config.db.values[commit.key] = commit; |
| await tester.post(handler); |
| verifyNever(mockLuciBuildService.rescheduleProdBuild( |
| commitSha: captureAnyNamed('commitSha'), |
| builderName: captureAnyNamed('builderName'), |
| )); |
| }); |
| |
| test('Fails if task already scheduled', () async { |
| final Task task = Task( |
| key: commit.key.append(Task, id: 4590522719010816), |
| commitKey: commit.key, |
| attempts: 0, |
| status: 'Failed', |
| builderName: 'Windows'); |
| config.db.values[task.key] = task; |
| config.db.values[commit.key] = commit; |
| when(mockLuciBuildService.getProdBuilds(any, any, any, any)).thenAnswer((_) async { |
| return <Build>[scheduledBuild]; |
| }); |
| expect(() => tester.post(handler), throwsA(isA<ConflictException>())); |
| }); |
| |
| test('Fails if task already running', () async { |
| final Task task = Task( |
| key: commit.key.append(Task, id: 4590522719010816), |
| commitKey: commit.key, |
| attempts: 0, |
| status: 'Failed', |
| builderName: 'Windows'); |
| config.db.values[task.key] = task; |
| config.db.values[commit.key] = commit; |
| when(mockLuciBuildService.getProdBuilds(any, any, any, any)).thenAnswer((_) async { |
| return <Build>[startedBuild]; |
| }); |
| expect(() => tester.post(handler), throwsA(isA<ConflictException>())); |
| }); |
| |
| test('Fails if task already succeeded', () async { |
| final Task task = Task( |
| key: commit.key.append(Task, id: 4590522719010816), |
| commitKey: commit.key, |
| attempts: 0, |
| status: 'Failed', |
| builderName: 'Windows'); |
| config.db.values[task.key] = task; |
| config.db.values[commit.key] = commit; |
| when(mockLuciBuildService.getProdBuilds(any, any, any, any)).thenAnswer((_) async { |
| return <Build>[succeededBuild]; |
| }); |
| expect(() => tester.post(handler), throwsA(isA<ConflictException>())); |
| }); |
| |
| test('Reschedules if build is empty', () async { |
| Task task = Task( |
| key: commit.key.append(Task, id: 4590522719010816), |
| commitKey: commit.key, |
| attempts: 0, |
| status: 'Failed', |
| builderName: 'Windows'); |
| config.db.values[task.key] = task; |
| config.db.values[commit.key] = commit; |
| when(mockLuciBuildService.getProdBuilds(any, any, any, any)).thenAnswer((_) async { |
| return <Build>[]; |
| }); |
| await tester.post(handler); |
| expect( |
| verify(mockLuciBuildService.rescheduleProdBuild( |
| commitSha: captureAnyNamed('commitSha'), |
| builderName: captureAnyNamed('builderName'), |
| )).captured, |
| <dynamic>['7d03371610c07953a5def50d500045941de516b8', 'Windows'], |
| ); |
| task = config.db.values[task.key] as Task; |
| expect(task.attempts, equals(1)); |
| }); |
| |
| test('Fails if commit does not exist', () async { |
| final Task task = Task( |
| key: commit.key.append(Task, id: 4590522719010816), |
| commitKey: commit.key, |
| attempts: 0, |
| status: 'Failed', |
| builderName: 'Windows'); |
| config.db.values[task.key] = task; |
| expect(() => tester.post(handler), throwsA(isA<BadRequestException>())); |
| }); |
| }); |
| } |