| // 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 'package:cocoon_service/cocoon_service.dart'; |
| 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/model/luci/push_message.dart' as push; |
| import 'package:cocoon_service/src/service/datastore.dart'; |
| import 'package:gcloud/db.dart'; |
| import 'package:mockito/mockito.dart'; |
| import 'package:test/test.dart'; |
| |
| import '../src/datastore/fake_config.dart'; |
| import '../src/request_handling/fake_authentication.dart'; |
| import '../src/request_handling/fake_http.dart'; |
| import '../src/request_handling/subscription_tester.dart'; |
| import '../src/utilities/entity_generators.dart'; |
| import '../src/utilities/mocks.dart'; |
| |
| void main() { |
| late DartInternalSubscription handler; |
| late FakeConfig config; |
| late FakeHttpRequest request; |
| late MockBuildBucketClient buildBucketClient; |
| late SubscriptionTester tester; |
| late Commit commit; |
| final DateTime startTime = DateTime(2023, 1, 1, 0, 0, 0); |
| final DateTime endTime = DateTime(2023, 1, 1, 0, 14, 23); |
| const String project = 'dart-internal'; |
| const String bucket = 'flutter'; |
| const String builder = 'Linux packaging_release_builder'; |
| const int buildId = 123456; |
| const String fakeHash = 'HASH12345'; |
| const String fakeBranch = 'test-branch'; |
| const String fakePubsubMessage = ''' |
| { |
| "build": { |
| "id": "$buildId", |
| "builder": { |
| "project": "$project", |
| "bucket": "$bucket", |
| "builder": "$builder" |
| } |
| } |
| } |
| '''; |
| |
| setUp(() async { |
| config = FakeConfig(); |
| buildBucketClient = MockBuildBucketClient(); |
| handler = DartInternalSubscription( |
| cache: CacheService(inMemory: true), |
| config: config, |
| authProvider: FakeAuthenticationProvider(), |
| buildBucketClient: buildBucketClient, |
| datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), |
| ); |
| request = FakeHttpRequest(); |
| |
| tester = SubscriptionTester( |
| request: request, |
| ); |
| |
| commit = generateCommit( |
| 1, |
| sha: fakeHash, |
| branch: fakeBranch, |
| owner: 'flutter', |
| repo: 'flutter', |
| timestamp: 0, |
| ); |
| |
| final Build fakeBuild = Build( |
| builderId: const BuilderId(project: project, bucket: bucket, builder: builder), |
| number: buildId, |
| id: 'fake-build-id', |
| status: Status.success, |
| startTime: startTime, |
| endTime: endTime, |
| input: const Input( |
| gitilesCommit: GitilesCommit( |
| project: 'flutter/flutter', |
| hash: fakeHash, |
| ref: 'refs/heads/$fakeBranch', |
| ), |
| ), |
| ); |
| when( |
| buildBucketClient.getBuild( |
| any, |
| buildBucketUri: 'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds', |
| ), |
| ).thenAnswer((_) => Future<Build>.value(fakeBuild)); |
| |
| final List<Commit> datastoreCommit = <Commit>[commit]; |
| await config.db.commit(inserts: datastoreCommit); |
| }); |
| |
| test('creates a new task successfully', () async { |
| tester.message = const push.PushMessage(data: fakePubsubMessage); |
| |
| await tester.post(handler); |
| |
| verify( |
| buildBucketClient.getBuild(any), |
| ).called(1); |
| |
| // This is used for testing to pull the data out of the "datastore" so that |
| // we can verify what was saved. |
| late Task taskInDb; |
| late Commit commitInDb; |
| config.db.values.forEach((k, v) { |
| if (v is Task && v.buildNumberList == buildId.toString()) { |
| taskInDb = v; |
| } |
| if (v is Commit) { |
| commitInDb = v; |
| } |
| }); |
| |
| // Ensure the task has the correct parent and commit key |
| expect( |
| commitInDb.id, |
| equals(taskInDb.commitKey?.id), |
| ); |
| |
| expect( |
| commitInDb.id, |
| equals(taskInDb.parentKey?.id), |
| ); |
| |
| // Ensure the task in the db is exactly what we expect |
| final Task expectedTask = Task( |
| attempts: 1, |
| buildNumber: buildId, |
| buildNumberList: buildId.toString(), |
| builderName: builder, |
| commitKey: commitInDb.key, |
| createTimestamp: startTime.millisecondsSinceEpoch, |
| endTimestamp: endTime.millisecondsSinceEpoch, |
| luciBucket: bucket, |
| name: builder, |
| stageName: 'dart-internal', |
| startTimestamp: startTime.millisecondsSinceEpoch, |
| status: 'Succeeded', |
| key: commit.key.append(Task), |
| timeoutInMinutes: 0, |
| reason: '', |
| requiredCapabilities: [], |
| reservedForAgentId: '', |
| ); |
| |
| expect( |
| taskInDb.toString(), |
| equals(expectedTask.toString()), |
| ); |
| }); |
| |
| test('updates an existing task successfully', () async { |
| const int existingTaskId = 123; |
| final Task fakeTask = Task( |
| attempts: 1, |
| buildNumber: existingTaskId, |
| buildNumberList: existingTaskId.toString(), |
| builderName: builder, |
| commitKey: commit.key, |
| createTimestamp: startTime.millisecondsSinceEpoch, |
| endTimestamp: endTime.millisecondsSinceEpoch, |
| luciBucket: bucket, |
| name: builder, |
| stageName: 'dart-internal', |
| startTimestamp: startTime.millisecondsSinceEpoch, |
| status: 'Succeeded', |
| key: commit.key.append(Task), |
| timeoutInMinutes: 0, |
| reason: '', |
| requiredCapabilities: [], |
| reservedForAgentId: '', |
| ); |
| final List<Task> datastoreCommit = <Task>[fakeTask]; |
| await config.db.commit(inserts: datastoreCommit); |
| |
| tester.message = const push.PushMessage(data: fakePubsubMessage); |
| |
| await tester.post(handler); |
| |
| verify( |
| buildBucketClient.getBuild(any), |
| ).called(1); |
| |
| // This is used for testing to pull the data out of the "datastore" so that |
| // we can verify what was saved. |
| final String expectedBuilderList = '${existingTaskId.toString()},${buildId.toString()}'; |
| late Task taskInDb; |
| late Commit commitInDb; |
| config.db.values.forEach((k, v) { |
| if (v is Task && v.buildNumberList == expectedBuilderList) { |
| taskInDb = v; |
| } |
| if (v is Commit) { |
| commitInDb = v; |
| } |
| }); |
| |
| // Ensure the task has the correct parent and commit key |
| expect( |
| commitInDb.id, |
| equals(taskInDb.commitKey?.id), |
| ); |
| |
| expect( |
| commitInDb.id, |
| equals(taskInDb.parentKey?.id), |
| ); |
| |
| // Ensure the task in the db is exactly what we expect |
| final Task expectedTask = Task( |
| attempts: 2, |
| buildNumber: buildId, |
| buildNumberList: expectedBuilderList, |
| builderName: builder, |
| commitKey: commitInDb.key, |
| createTimestamp: startTime.millisecondsSinceEpoch, |
| endTimestamp: endTime.millisecondsSinceEpoch, |
| luciBucket: bucket, |
| name: builder, |
| stageName: 'dart-internal', |
| startTimestamp: startTime.millisecondsSinceEpoch, |
| status: 'Succeeded', |
| key: commit.key.append(Task), |
| timeoutInMinutes: 0, |
| reason: '', |
| requiredCapabilities: [], |
| reservedForAgentId: '', |
| ); |
| |
| expect( |
| taskInDb.toString(), |
| equals(expectedTask.toString()), |
| ); |
| }); |
| |
| test('ignores null message', () async { |
| tester.message = const push.PushMessage(data: null); |
| expect(await tester.post(handler), equals(Body.empty)); |
| }); |
| |
| test('ignores message with empty build data', () async { |
| tester.message = const push.PushMessage(data: '{}'); |
| expect(await tester.post(handler), equals(Body.empty)); |
| }); |
| |
| test('ignores message not from flutter bucket', () async { |
| tester.message = const push.PushMessage( |
| data: ''' |
| { |
| "build": { |
| "id": "$buildId", |
| "builder": { |
| "project": "$project", |
| "bucket": "dart", |
| "builder": "$builder" |
| } |
| } |
| } |
| ''', |
| ); |
| expect(await tester.post(handler), equals(Body.empty)); |
| }); |
| |
| test('ignores message not from dart-internal project', () async { |
| tester.message = const push.PushMessage( |
| data: ''' |
| { |
| "build": { |
| "id": "$buildId", |
| "builder": { |
| "project": "different-project", |
| "bucket": "$bucket", |
| "builder": "$builder" |
| } |
| } |
| } |
| ''', |
| ); |
| expect(await tester.post(handler), equals(Body.empty)); |
| }); |
| |
| test('ignores message not from an accepted builder', () async { |
| tester.message = const push.PushMessage( |
| data: ''' |
| { |
| "build": { |
| "id": "$buildId", |
| "builder": { |
| "project": "different-project", |
| "bucket": "$bucket", |
| "builder": "different-builder" |
| } |
| } |
| } |
| ''', |
| ); |
| expect(await tester.post(handler), equals(Body.empty)); |
| }); |
| } |