| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library pubserver.shelf_pubserver_test; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:convert'; | |
| 9 | |
| 10 import 'package:shelf/shelf.dart' as shelf; | |
| 11 import 'package:pubserver/repository.dart'; | |
| 12 import 'package:pubserver/shelf_pubserver.dart'; | |
| 13 import 'package:unittest/unittest.dart'; | |
| 14 | |
| 15 class RepositoryMock implements PackageRepository { | |
| 16 final Function downloadFun; | |
| 17 final Function downloadUrlFun; | |
| 18 final Function finishAsyncUploadFun; | |
| 19 final Function lookupVersionFun; | |
| 20 final Function startAsyncUploadFun; | |
| 21 final Function uploadFun; | |
| 22 final Function versionsFun; | |
| 23 | |
| 24 RepositoryMock( | |
| 25 {this.downloadFun, this.downloadUrlFun, this.finishAsyncUploadFun, | |
| 26 this.lookupVersionFun, this.startAsyncUploadFun, this.uploadFun, | |
| 27 this.versionsFun, this.supportsAsyncUpload: false, | |
| 28 this.supportsDownloadUrl: false, this.supportsUpload: false}); | |
| 29 | |
| 30 Future<Stream> download(String package, String version) async { | |
| 31 if (downloadFun != null) return downloadFun(package, version); | |
| 32 throw 'download'; | |
| 33 } | |
| 34 | |
| 35 Future<Uri> downloadUrl(String package, String version) async { | |
| 36 if (downloadUrlFun != null) return downloadUrlFun(package, version); | |
| 37 throw 'downloadUrl'; | |
| 38 } | |
| 39 | |
| 40 Future finishAsyncUpload(Uri uri) async { | |
| 41 if (finishAsyncUploadFun != null) return finishAsyncUploadFun(uri); | |
| 42 throw 'finishAsyncUpload'; | |
| 43 } | |
| 44 | |
| 45 Future<PackageVersion> lookupVersion(String package, String version) async { | |
| 46 if (lookupVersionFun != null) return lookupVersionFun(package, version); | |
| 47 throw 'lookupVersion'; | |
| 48 } | |
| 49 | |
| 50 Future<AsyncUploadInfo> startAsyncUpload(Uri redirectUrl) async { | |
| 51 if (startAsyncUploadFun != null) { | |
| 52 return startAsyncUploadFun(redirectUrl); | |
| 53 } | |
| 54 throw 'startAsyncUpload'; | |
| 55 } | |
| 56 | |
| 57 final bool supportsAsyncUpload; | |
| 58 | |
| 59 final bool supportsDownloadUrl; | |
| 60 | |
| 61 final bool supportsUpload; | |
| 62 | |
| 63 Future upload(Stream<List<int>> data) { | |
| 64 if (uploadFun != null) return uploadFun(data); | |
| 65 throw 'upload'; | |
| 66 } | |
| 67 | |
| 68 Stream<PackageVersion> versions(String package) { | |
| 69 if (versionsFun != null) return versionsFun(package); | |
| 70 return new Stream.fromFuture(new Future.error('versions')); | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 Uri getUri(String path) => Uri.parse('http://www.example.com$path'); | |
| 75 | |
| 76 shelf.Request getRequest(String path) { | |
| 77 var url = getUri(path); | |
| 78 return new shelf.Request('GET', url); | |
| 79 } | |
| 80 | |
| 81 shelf.Request multipartRequest(Uri uri, List<int> bytes) { | |
| 82 var requestBytes = []; | |
| 83 String boundary = 'testboundary'; | |
| 84 | |
| 85 requestBytes.addAll(ASCII.encode('--$boundary\r\n')); | |
| 86 requestBytes.addAll( | |
| 87 ASCII.encode('Content-Type: application/octet-stream\r\n')); | |
| 88 requestBytes.addAll(ASCII.encode('Content-Length: ${bytes.length}\r\n')); | |
| 89 requestBytes.addAll(ASCII.encode('Content-Disposition: ' | |
| 90 'form-data; name="file"; ' | |
| 91 'filename="package.tar.gz"\r\n\r\n')); | |
| 92 requestBytes.addAll(bytes); | |
| 93 requestBytes.addAll(ASCII.encode('\r\n--$boundary--\r\n')); | |
| 94 | |
| 95 var headers = { | |
| 96 'Content-Type' : 'multipart/form-data; boundary="$boundary"', | |
| 97 'Content-Length' : '${requestBytes.length}', | |
| 98 }; | |
| 99 | |
| 100 var body = new Stream.fromIterable([requestBytes]); | |
| 101 return new shelf.Request('POST', uri, headers: headers, body: body); | |
| 102 } | |
| 103 | |
| 104 main() { | |
| 105 group('shelf_pubserver', () { | |
| 106 test('invalid endpoint', () async { | |
| 107 var mock = new RepositoryMock(); | |
| 108 var server = new ShelfPubServer(mock); | |
| 109 | |
| 110 testInvalidUrl(String path) async { | |
| 111 var request = getRequest(path); | |
| 112 var response = await server.requestHandler(request); | |
| 113 await response.read().drain(); | |
| 114 expect(response.statusCode, equals(404)); | |
| 115 } | |
| 116 | |
| 117 await testInvalidUrl('/foobar'); | |
| 118 await testInvalidUrl('/api'); | |
| 119 await testInvalidUrl('/api/'); | |
| 120 await testInvalidUrl('/api/packages/analyzer/0.1.0'); | |
| 121 }); | |
| 122 | |
| 123 group('/api/packages/<package>', () { | |
| 124 test('does not exist', () async { | |
| 125 var mock = | |
| 126 new RepositoryMock(versionsFun: (_) => new Stream.fromIterable([])); | |
| 127 var server = new ShelfPubServer(mock); | |
| 128 var request = getRequest('/api/packages/analyzer'); | |
| 129 | |
| 130 var response = await server.requestHandler(request); | |
| 131 await response.read().drain(); | |
| 132 expect(response.statusCode, equals(404)); | |
| 133 }); | |
| 134 | |
| 135 test('success full retrieval of version', () async { | |
| 136 var mock = new RepositoryMock(versionsFun: (String package) { | |
| 137 // The pubspec is invalid, but that is irrelevant for this test. | |
| 138 var pubspec = JSON.encode({'foo': 1}); | |
| 139 var analyzer = new PackageVersion('analyzer', '0.1.0', pubspec); | |
| 140 return new Stream.fromIterable([analyzer]); | |
| 141 }); | |
| 142 var server = new ShelfPubServer(mock); | |
| 143 var request = getRequest('/api/packages/analyzer'); | |
| 144 var response = await server.requestHandler(request); | |
| 145 var body = await response.readAsString(); | |
| 146 | |
| 147 var expectedVersionJson = { | |
| 148 'pubspec': {'foo': 1}, | |
| 149 'version': '0.1.0', | |
| 150 'archive_url': | |
| 151 '${getUri('/packages/analyzer/versions/0.1.0.tar.gz')}', | |
| 152 }; | |
| 153 var expectedJson = { | |
| 154 'name': 'analyzer', | |
| 155 'latest': expectedVersionJson, | |
| 156 'versions': [expectedVersionJson], | |
| 157 }; | |
| 158 | |
| 159 expect(response.mimeType, equals('application/json')); | |
| 160 expect(response.statusCode, equals(200)); | |
| 161 expect(JSON.decode(body), equals(expectedJson)); | |
| 162 }); | |
| 163 }); | |
| 164 | |
| 165 group('/api/packages/<package>/versions/<version>', () { | |
| 166 test('does not exist', () async { | |
| 167 var mock = new RepositoryMock(lookupVersionFun: (_, __) => null); | |
| 168 var server = new ShelfPubServer(mock); | |
| 169 var request = getRequest('/api/packages/analyzer/versions/0.1.0'); | |
| 170 | |
| 171 var response = await server.requestHandler(request); | |
| 172 await response.read().drain(); | |
| 173 expect(response.statusCode, equals(404)); | |
| 174 }); | |
| 175 | |
| 176 test('success full retrieval of version', () async { | |
| 177 var mock = new RepositoryMock( | |
| 178 lookupVersionFun: (String package, String version) { | |
| 179 // The pubspec is invalid, but that is irrelevant for this test. | |
| 180 var pubspec = JSON.encode({'foo': 1}); | |
| 181 return new PackageVersion(package, version, pubspec); | |
| 182 }); | |
| 183 var server = new ShelfPubServer(mock); | |
| 184 var request = getRequest('/api/packages/analyzer/versions/0.1.0'); | |
| 185 var response = await server.requestHandler(request); | |
| 186 var body = await response.readAsString(); | |
| 187 | |
| 188 var expectedJson = { | |
| 189 'pubspec': {'foo': 1}, | |
| 190 'version': '0.1.0', | |
| 191 'archive_url': | |
| 192 '${getUri('/packages/analyzer/versions/0.1.0.tar.gz')}', | |
| 193 }; | |
| 194 | |
| 195 expect(response.mimeType, equals('application/json')); | |
| 196 expect(response.statusCode, equals(200)); | |
| 197 expect(JSON.decode(body), equals(expectedJson)); | |
| 198 }); | |
| 199 }); | |
| 200 | |
| 201 group('/packages/<package>/versions/<version>.tar.gz', () { | |
| 202 group('download', () { | |
| 203 test('successfull redirect', () async { | |
| 204 var mock = new RepositoryMock( | |
| 205 downloadFun: (String package, String version) { | |
| 206 return new Stream.fromIterable([[1, 2, 3]]); | |
| 207 }); | |
| 208 var server = new ShelfPubServer(mock); | |
| 209 var request = getRequest('/packages/analyzer/versions/0.1.0.tar.gz'); | |
| 210 var response = await server.requestHandler(request); | |
| 211 var body = await response.read().fold([], (b, d) => b..addAll(d)); | |
| 212 | |
| 213 expect(response.statusCode, equals(200)); | |
| 214 expect(body, equals([1, 2, 3])); | |
| 215 }); | |
| 216 }); | |
| 217 | |
| 218 group('download url', () { | |
| 219 test('successfull redirect', () async { | |
| 220 var expectedUrl = | |
| 221 Uri.parse('https://blobs.com/analyzer-0.1.0.tar.gz'); | |
| 222 var mock = new RepositoryMock(supportsDownloadUrl: true, | |
| 223 downloadUrlFun: (String package, String version) { | |
| 224 return expectedUrl; | |
| 225 }); | |
| 226 var server = new ShelfPubServer(mock); | |
| 227 var request = getRequest('/packages/analyzer/versions/0.1.0.tar.gz'); | |
| 228 var response = await server.requestHandler(request); | |
| 229 var body = await response.readAsString(); | |
| 230 | |
| 231 expect(response.statusCode, equals(303)); | |
| 232 expect(response.headers['location'], equals('$expectedUrl')); | |
| 233 }); | |
| 234 }); | |
| 235 }); | |
| 236 | |
| 237 group('/api/packages/versions/new', () { | |
| 238 test('async successfull', () async { | |
| 239 var expectedUrl = Uri.parse('https://storage.googleapis.com'); | |
| 240 var foobarUrl = Uri.parse('https://foobar.com/package/done'); | |
| 241 var newUrl = getUri('/api/packages/versions/new'); | |
| 242 var finishUrl = getUri('/api/packages/versions/newUploadFinish'); | |
| 243 var mock = new RepositoryMock( | |
| 244 supportsUpload: true, | |
| 245 supportsAsyncUpload: true, | |
| 246 startAsyncUploadFun: (Uri redirectUri) { | |
| 247 expect(redirectUri, equals(finishUrl)); | |
| 248 return new Future.value( | |
| 249 new AsyncUploadInfo(expectedUrl, {'a' : '$foobarUrl'})); | |
| 250 }, finishAsyncUploadFun: (Uri uri) { | |
| 251 expect('$uri', equals('$finishUrl')); | |
| 252 }); | |
| 253 var server = new ShelfPubServer(mock); | |
| 254 | |
| 255 // Start upload | |
| 256 var request = new shelf.Request('GET', newUrl); | |
| 257 var response = await server.requestHandler(request); | |
| 258 | |
| 259 expect(response.statusCode, equals(200)); | |
| 260 expect(response.headers['content-type'], equals('application/json')); | |
| 261 | |
| 262 var jsonBody = JSON.decode(await response.readAsString()); | |
| 263 expect(jsonBody, equals({ | |
| 264 'url' : '$expectedUrl', | |
| 265 'fields' : { | |
| 266 'a' : '$foobarUrl', | |
| 267 }, | |
| 268 })); | |
| 269 | |
| 270 // We would do now a multipart POST to `expectedUrl` which would | |
| 271 // redirect us back to the pub.dartlang.org app via `finishUrl`. | |
| 272 | |
| 273 // Call the `finishUrl`. | |
| 274 request = new shelf.Request('GET', finishUrl); | |
| 275 response = await server.requestHandler(request); | |
| 276 jsonBody = JSON.decode(await response.readAsString()); | |
| 277 expect(jsonBody, equals({ | |
| 278 'success' : { | |
| 279 'message' : 'Successfully uploaded package.' | |
| 280 }, | |
| 281 })); | |
| 282 }); | |
| 283 | |
| 284 test('sync successfull', () async { | |
| 285 var tarballBytes = const [1, 2, 3]; | |
| 286 var newUrl = getUri('/api/packages/versions/new'); | |
| 287 var uploadUrl = getUri('/api/packages/versions/newUpload'); | |
| 288 var finishUrl = getUri('/api/packages/versions/newUploadFinish'); | |
| 289 var mock = new RepositoryMock( | |
| 290 supportsUpload: true, | |
| 291 uploadFun: (Stream<List<int>> stream) { | |
| 292 return stream.fold([], (b, d) => b..addAll(d)).then((List<int> data) { | |
| 293 expect(data, equals(tarballBytes)); | |
| 294 }); | |
| 295 }); | |
| 296 var server = new ShelfPubServer(mock); | |
| 297 | |
| 298 // Start upload | |
| 299 var request = new shelf.Request('GET', newUrl); | |
| 300 var response = await server.requestHandler(request); | |
| 301 expect(response.statusCode, equals(200)); | |
| 302 expect(response.headers['content-type'], equals('application/json')); | |
| 303 var jsonBody = JSON.decode(await response.readAsString()); | |
| 304 expect(jsonBody, equals({ | |
| 305 'url' : '$uploadUrl', | |
| 306 'fields' : {}, | |
| 307 })); | |
| 308 | |
| 309 // Post data via a multipart request. | |
| 310 request = multipartRequest(uploadUrl, tarballBytes); | |
| 311 response = await server.requestHandler(request); | |
| 312 await response.read(); | |
| 313 expect(response.statusCode, equals(302)); | |
| 314 expect(response.headers['location'], equals('$finishUrl')); | |
| 315 | |
| 316 // Call the `finishUrl`. | |
| 317 request = new shelf.Request('GET', finishUrl); | |
| 318 response = await server.requestHandler(request); | |
| 319 jsonBody = JSON.decode(await response.readAsString()); | |
| 320 expect(jsonBody, equals({ | |
| 321 'success' : { | |
| 322 'message' : 'Successfully uploaded package.' | |
| 323 }, | |
| 324 })); | |
| 325 }); | |
| 326 | |
| 327 test('sync failure', () async { | |
| 328 var tarballBytes = const [1, 2, 3]; | |
| 329 var newUrl = getUri('/api/packages/versions/new'); | |
| 330 var uploadUrl = getUri('/api/packages/versions/newUpload'); | |
| 331 var finishUrl = | |
| 332 getUri('/api/packages/versions/newUploadFinish?error=abc'); | |
| 333 var mock = new RepositoryMock( | |
| 334 supportsUpload: true, | |
| 335 uploadFun: (Stream<List<int>> stream) { | |
| 336 return new Future.error('abc'); | |
| 337 }); | |
| 338 var server = new ShelfPubServer(mock); | |
| 339 | |
| 340 // Start upload - would happen here. | |
| 341 | |
| 342 // Post data via a multipart request. | |
| 343 var request = multipartRequest(uploadUrl, tarballBytes); | |
| 344 var response = await server.requestHandler(request); | |
| 345 await response.read(); | |
| 346 expect(response.statusCode, equals(302)); | |
| 347 expect(response.headers['location'], equals('$finishUrl')); | |
| 348 | |
| 349 // Call the `finishUrl`. | |
| 350 request = new shelf.Request('GET', finishUrl); | |
| 351 response = await server.requestHandler(request); | |
| 352 var jsonBody = JSON.decode(await response.readAsString()); | |
| 353 expect(jsonBody, equals({ | |
| 354 'error' : { | |
| 355 'message' : 'abc' | |
| 356 }, | |
| 357 })); | |
| 358 }); | |
| 359 | |
| 360 test('unsupported', () async { | |
| 361 var newUrl = getUri('/api/packages/versions/new'); | |
| 362 var mock = new RepositoryMock(); | |
| 363 var server = new ShelfPubServer(mock); | |
| 364 var request = new shelf.Request('GET', newUrl); | |
| 365 var response = await server.requestHandler(request); | |
| 366 | |
| 367 expect(response.statusCode, equals(404)); | |
| 368 }); | |
| 369 }); | |
| 370 }); | |
| 371 } | |
| OLD | NEW |