[functions] Dependency-inject HttpClient to enable unit testing with mocks
Change-Id: I5e509f9ae27b7b2d31f10d9f21ba2fdaed6562c7
Reviewed-on: https://dart-review.googlesource.com/c/dart_ci/+/127020
Reviewed-by: Jonas Termansen <sortie@google.com>
diff --git a/functions/node/builder.dart b/functions/node/builder.dart
index ccb97be..03ca510 100644
--- a/functions/node/builder.dart
+++ b/functions/node/builder.dart
@@ -3,9 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:convert';
-import 'package:node_http/node_http.dart' as http;
-// For testing, use instead: import 'package:http/http.dart' as http;
-// TODO(whesse): Inject http Client() through dependency injection.
+
+import 'package:http/http.dart' as http;
import 'firestore.dart';
@@ -51,6 +50,7 @@
/// Tryjob builds are represented by a subclass Tryjob of this class.
class Build {
final FirestoreService firestore;
+ final http.BaseClient httpClient;
final String commitHash;
final Map<String, dynamic> firstResult;
String builderName;
@@ -61,7 +61,7 @@
Statistics stats = Statistics();
- Build(this.commitHash, this.firstResult, this.firestore)
+ Build(this.commitHash, this.firstResult, this.firestore, this.httpClient)
: builderName = firstResult['builder_name'],
buildNumber = int.parse(firstResult['build_number']);
@@ -121,9 +121,7 @@
final range = '$lastHash..master';
final parameters = ['format=JSON', 'topo-order', 'n=1000'];
final url = '$logUrl$range?${parameters.join('&')}';
- final client = http.NodeClient();
- // For testing, use http.Client(); TODO(whesse): Inject correct http.
- final response = await client.get(url);
+ final response = await httpClient.get(url);
final protectedJson = response.body;
if (!protectedJson.startsWith(prefix))
throw Exception('Gerrit response missing prefix $prefix: $protectedJson');
diff --git a/functions/node/gerrit_change.dart b/functions/node/gerrit_change.dart
index 5bb6de0..e9ab4ba 100644
--- a/functions/node/gerrit_change.dart
+++ b/functions/node/gerrit_change.dart
@@ -4,7 +4,7 @@
import 'dart:convert';
-import 'package:node_http/node_http.dart' as http;
+import 'package:http/http.dart' as http;
import 'firestore.dart';
@@ -17,11 +17,12 @@
};
static const prefix = ")]}'\n";
+ http.BaseClient httpClient;
FirestoreService firestore;
String review;
String patchset;
- GerritInfo(int review, int patchset, this.firestore) {
+ GerritInfo(int review, int patchset, this.firestore, this.httpClient) {
this.review = review.toString();
this.patchset = patchset.toString();
}
@@ -29,10 +30,9 @@
// Fetch the owner, changeId, message, and date of a Gerrit change.
Future<void> update() async {
if (await firestore.hasPatchset(review, patchset)) return;
- final client = http.NodeClient();
// Get the Gerrit change's commit from the Gerrit API.
final url = '$gerritUrl/$review?o=ALL_REVISIONS&o=DETAILED_ACCOUNTS';
- final response = await client.get(url);
+ final response = await httpClient.get(url);
final protectedJson = response.body;
if (!protectedJson.startsWith(prefix))
throw Exception('Gerrit response missing prefix $prefix: $protectedJson');
diff --git a/functions/node/index.dart b/functions/node/index.dart
index 4b7735d..1242414 100644
--- a/functions/node/index.dart
+++ b/functions/node/index.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:firebase_functions_interop/firebase_functions_interop.dart';
+import 'package:node_http/node_http.dart' as http;
import 'builder.dart';
import 'firestore_impl.dart';
@@ -19,8 +20,10 @@
final String commit = first['commit_hash'];
if (commit.startsWith('refs/changes')) {
- return Tryjob(commit, FirestoreServiceImpl()).process(results);
+ return Tryjob(commit, FirestoreServiceImpl(), http.NodeClient())
+ .process(results);
} else {
- return Build(commit, first, FirestoreServiceImpl()).process(results);
+ return Build(commit, first, FirestoreServiceImpl(), http.NodeClient())
+ .process(results);
}
}
diff --git a/functions/node/test/http_mock.dart b/functions/node/test/http_mock.dart
new file mode 100644
index 0000000..0ce904e
--- /dev/null
+++ b/functions/node/test/http_mock.dart
@@ -0,0 +1,10 @@
+// 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:http/http.dart';
+import 'package:mockito/mockito.dart';
+
+import 'test_data.dart';
+
+class HttpClientMock extends Mock implements BaseClient {}
diff --git a/functions/node/test/test.dart b/functions/node/test/test.dart
index dcbe90a..912007f 100644
--- a/functions/node/test/test.dart
+++ b/functions/node/test/test.dart
@@ -7,14 +7,16 @@
import '../builder.dart';
import 'firestore_mock.dart';
+import 'http_mock.dart';
import 'test_data.dart';
void main() async {
test("Store already existing commit", () async {
+ final client = HttpClientMock();
final firestore = FirestoreServiceMock();
final change = alreadyExistingCommitChange;
- final builder = Build(existingCommitHash, change, firestore);
+ final builder = Build(existingCommitHash, change, firestore, client);
await builder.storeBuildCommitsInfo();
expect(builder.endIndex, existingCommit['index']);
expect(builder.startIndex, previousCommit['index'] + 1);
diff --git a/functions/node/tryjob.dart b/functions/node/tryjob.dart
index 7158d7f..f72504e 100644
--- a/functions/node/tryjob.dart
+++ b/functions/node/tryjob.dart
@@ -2,6 +2,8 @@
// 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:http/http.dart' as http show BaseClient;
+
import 'firestore.dart';
import 'gerrit_change.dart';
@@ -10,18 +12,19 @@
class Tryjob {
static final changeRefRegExp = RegExp(r'refs/changes/(\d*)/(\d*)');
+ http.BaseClient httpClient;
FirestoreService firestore;
int review;
int patchset;
- Tryjob(String changeRef, this.firestore) {
+ Tryjob(String changeRef, this.firestore, this.httpClient) {
final match = changeRefRegExp.matchAsPrefix(changeRef);
review = int.parse(match[1]);
patchset = int.parse(match[2]);
}
Future<void> update() {
- return GerritInfo(review, patchset, firestore).update();
+ return GerritInfo(review, patchset, firestore, httpClient).update();
}
Future<void> process(List<Map<String, dynamic>> results) async {