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 |