blob: cd37ea30293bcb08743e3f2939117ee7b2462910 [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. 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:firebase_admin_interop/firebase_admin_interop.dart';
import 'package:firebase_functions_interop/firebase_functions_interop.dart';
import 'package:node_interop/node.dart';
import 'package:test/test.dart';
import '../firestore_impl.dart' as fs;
import '../tryjob.dart';
import 'test_data.dart';
// These tests read and write data from the Firestore database, and
// should only be run locally against the dart-ci-staging project.
// Requires the environment variable GOOGLE_APPLICATION_CREDENTIALS
// to point to a json key to a service account
// with write access to dart_ci_staging datastore.
// Set the database with 'firebase use --add dart-ci-staging'
// The test must be compiled with nodejs, and run using the 'node' command.
void main() async {
final firestore = fs.FirestoreServiceImpl();
if (!await firestore.isStaging()) {
console
.error('Error: firestore_test_nodejs.dart is being run on production');
throw (TestFailure(
'Error: firestore_test_nodejs.dart is being run on production'));
}
test('Test chunk storing', () async {
final builder = testBuilder;
final configuration = testConfiguration;
final index = 123;
await firestore.updateConfiguration(configuration, builder);
final failingResultReference = await fs.firestore
.collection('results')
.add(DocumentData.fromMap(activeFailureResult));
final document = fs.firestore.document('builds/$builder:$index');
await document.delete();
await firestore.updateBuildInfo(builder, 3456, index);
await firestore.storeChunkStatus(builder, index, true);
await firestore.storeBuildChunkCount(builder, index, 4);
await firestore.storeChunkStatus(builder, index, true);
DocumentSnapshot snapshot = await document.get();
var data = snapshot.data.toMap();
expect(data['success'], isTrue);
expect(data['num_chunks'], 4);
expect(data['processed_chunks'], 2);
expect(data['completed'], isNull);
await firestore.storeChunkStatus(builder, index, false);
await firestore.storeChunkStatus(builder, index, true);
snapshot = await document.get();
data = snapshot.data.toMap();
expect(data['success'], isFalse);
expect(data['num_chunks'], 4);
expect(data['processed_chunks'], 4);
expect(data['completed'], isTrue);
expect(data['active_failures'], isTrue);
await document.delete();
await failingResultReference.delete();
await fs.firestore.document('configurations/$testConfiguration').delete();
});
group('Try results', () {
tearDown(() async {
// Delete database records created by the tests.
var snapshot = await fs.firestore
.collection('try_builds')
.where('review', isEqualTo: testReview)
.get();
for (final doc in snapshot.documents) {
await doc.reference.delete();
}
snapshot = await fs.firestore
.collection('try_results')
.where('review', isEqualTo: testReview)
.get();
for (final doc in snapshot.documents) {
await doc.reference.delete();
}
snapshot =
await fs.firestore.collection('reviews/$testReview/patchsets').get();
for (final doc in snapshot.documents) {
await doc.reference.delete();
}
await fs.firestore.document('reviews/$testReview').delete();
});
test('approved try result fetching', () async {
await firestore.storeReview(testReview.toString(), {
'subject': 'test review: approved try result fetching',
});
await firestore.storePatchset(testReview.toString(), 1, {
'kind': 'REWORK',
'description': 'Initial upload',
'patchset_group': 1,
'number': 1,
});
await firestore.storePatchset(testReview.toString(), 2, {
'kind': 'REWORK',
'description': 'change',
'patchset_group': 2,
'number': 2,
});
await firestore.storePatchset(testReview.toString(), 3, {
'kind': 'NO_CODE_CHANGE',
'description': 'Edit commit message',
'patchset_group': 2,
'number': 3,
});
final tryResult = {
'review': testReview,
'configuration': 'test_configuration',
'name': 'test_suite/test_name',
'patchset': 1,
'result': 'RuntimeError',
'expected': 'Pass',
'previous_result': 'Pass',
};
await firestore.storeTryChange(tryResult, testReview, 1);
final tryResult2 = Map<String, dynamic>.from(tryResult);
tryResult2['patchset'] = 2;
tryResult2['name'] = 'test_suite/test_name_2';
await firestore.storeTryChange(tryResult2, testReview, 2);
tryResult['patchset'] = 3;
tryResult['name'] = 'test_suite/test_name';
tryResult['expected'] = 'CompileTimeError';
await firestore.storeTryChange(tryResult, testReview, 3);
// Set the results on patchsets 1 and 2 to approved.
final snapshot = await fs.firestore
.collection('try_results')
.where('approved', isEqualTo: false)
.where('review', isEqualTo: testReview)
.where('patchset', isLessThanOrEqualTo: 2)
.get();
for (final document in snapshot.documents) {
await document.reference
.updateData(UpdateData.fromMap({'approved': true}));
}
// Should return only the approved change on patchset 2,
// not the one on patchset 1 or the unapproved change on patchset 3.
final approvals = await firestore.tryApprovals(testReview);
tryResult2['configurations'] = [tryResult2['configuration']];
tryResult2['approved'] = true;
tryResult2.remove('configuration');
expect(approvals, [tryResult2]);
});
test('Test tryjob result processing', () async {
// Set up database with approved results on previous patchset.
await firestore.storePatchset(testReview.toString(), testPreviousPatchset,
{'number': testPreviousPatchset});
final previousFailingChange = Map<String, dynamic>.from(
tryjobFailingChange)
..addAll(
{'commit_hash': testPreviousPatchsetPath, 'build_number': '307'});
final buildID0 = 'test buildbucket id 0';
await Tryjob(testPreviousPatchsetPath, 1, buildID0, null, null, firestore,
null)
.process([previousFailingChange]);
var snapshot = await fs.firestore
.collection('try_results')
.where('name', isEqualTo: previousFailingChange['name'])
.where('review', isEqualTo: testReview)
.where('patchset', isEqualTo: testPreviousPatchset)
.get();
expect(snapshot.isNotEmpty, isTrue);
expect(snapshot.documents.length, 1);
await snapshot.documents.first.reference
.updateData(UpdateData.fromMap({'approved': true}));
await firestore.storePatchset(testReview.toString(), testPatchset,
{'number': testPreviousPatchset});
// Send first chunk with a previously approved result and a passing result
final buildID1 = 'test buildbucket id 1';
await Tryjob(testReviewPath, null, buildID1, null, null, firestore, null)
.process([tryjobPassingChange, tryjobFailingChange]);
// Send second & final chunk with an unchanged failure.
await Tryjob(testReviewPath, 2, buildID1, null, null, firestore, null)
.process([tryjobExistingFailure, tryjobFailingChange]);
// Verify state
snapshot = await fs.firestore
.collection('try_builds')
.where('builder', isEqualTo: testBuilder)
.where('review', isEqualTo: testReview)
.where('patchset', isEqualTo: testPatchset)
.get();
expect(snapshot.documents.length, 1);
DocumentSnapshot document = snapshot.documents.first;
expect(document.documentID, '$testBuilder:$testReview:$testPatchset');
expect(document.data.getInt('build_number'), int.parse(testBuildNumber));
expect(document.data.getString('buildbucket_id'), buildID1);
expect(document.data.getBool('success'), isTrue);
expect(document.data.getBool('completed'), isTrue);
// Verify that sending a result twice only adds its configuration once
// to the try result.
snapshot = await fs.firestore
.collection('try_results')
.where('name', isEqualTo: 'test_suite/failing_test')
.where('review', isEqualTo: testReview)
.where('patchset', isEqualTo: testPatchset)
.get();
expect(snapshot.documents.length, 1);
document = snapshot.documents.first;
expect(document.data.getList('configurations'), ['test_configuration']);
expect(document.data.getBool('approved'), isTrue);
// Send first chunk of second run on the same patchset, with an approved
// failure and an unapproved failure.
final buildID2 = 'test buildbucket id 2';
await Tryjob(testReviewPath, null, buildID2, null, null, firestore, null)
.process([tryjob2OtherFailingChange, tryjob2FailingChange]);
final reference = fs.firestore
.document('try_builds/$testBuilder:$testReview:$testPatchset');
document = await reference.get();
expect(document.exists, isTrue);
expect(document.data.getBool('success'), isFalse);
expect(document.data.getBool('completed'), isNull);
expect(document.data.getString('buildbucket_id'), buildID2);
expect(document.data.getInt('num_chunks'), isNull);
expect(document.data.getInt('processed_chunks'), 1);
// Send second chunk.
await Tryjob(testReviewPath, 3, buildID2, null, null, firestore, null)
.process([tryjob2ExistingFailure]);
document = await reference.get();
expect(document.data.getBool('success'), isFalse);
expect(document.data.getBool('completed'), isNull);
expect(document.data.getString('buildbucket_id'), buildID2);
expect(document.data.getInt('num_chunks'), 3);
expect(document.data.getInt('processed_chunks'), 2);
// Send third and final chunk.
await Tryjob(testReviewPath, null, buildID2, null, null, firestore, null)
.process([tryjob2PassingChange]);
document = await reference.get();
expect(document.data.getBool('success'), isFalse);
expect(document.data.getBool('completed'), isTrue);
expect(document.data.getString('buildbucket_id'), buildID2);
expect(document.data.getInt('num_chunks'), 3);
expect(document.data.getInt('processed_chunks'), 3);
// Send first chunk of a third run, with only one chunk.
final buildID3 = 'test buildbucket id 3';
await Tryjob(testReviewPath, 1, buildID3, null, null, firestore, null)
.process([tryjob3PassingChange]);
document = await reference.get();
expect(document.data.getBool('success'), isTrue);
expect(document.data.getBool('completed'), isTrue);
expect(document.data.getString('buildbucket_id'), buildID3);
expect(document.data.getInt('num_chunks'), 1);
expect(document.data.getInt('processed_chunks'), 1);
});
});
}