OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, 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 gcloud.storage; |
| 6 |
| 7 import 'dart:async'; |
| 8 import 'dart:convert'; |
| 9 |
| 10 import 'package:http/http.dart' as http; |
| 11 import 'package:unittest/unittest.dart'; |
| 12 |
| 13 import 'package:gcloud/storage.dart'; |
| 14 |
| 15 import 'package:googleapis/storage/v1.dart' as storage; |
| 16 import 'package:googleapis/common/common.dart' as common; |
| 17 |
| 18 import '../common.dart'; |
| 19 import '../common_e2e.dart'; |
| 20 |
| 21 |
| 22 const String ROOT_PATH = '/storage/v1/'; |
| 23 |
| 24 |
| 25 http.Client mockClient() => new MockClient(ROOT_PATH); |
| 26 |
| 27 withMockClient(function) { |
| 28 var mock = mockClient(); |
| 29 function(mock, new Storage(mock, PROJECT)); |
| 30 } |
| 31 |
| 32 main() { |
| 33 group('bucket', () { |
| 34 var bucketName = 'test-bucket'; |
| 35 var absoluteName = 'gs://test-bucket'; |
| 36 |
| 37 test('create', () { |
| 38 withMockClient((mock, api) { |
| 39 mock.register('POST', 'b', expectAsync((request) { |
| 40 var requestBucket = |
| 41 new storage.Bucket.fromJson(JSON.decode(request.body)); |
| 42 expect(requestBucket.name, bucketName); |
| 43 return mock.respond(new storage.Bucket()..name = bucketName); |
| 44 })); |
| 45 |
| 46 expect(api.createBucket(bucketName), completion(isNull)); |
| 47 }); |
| 48 }); |
| 49 |
| 50 test('create-with-predefined-acl', () { |
| 51 var predefined = |
| 52 [[PredefinedAcl.authenticatedRead, 'authenticatedRead'], |
| 53 [PredefinedAcl.private, 'private'], |
| 54 [PredefinedAcl.projectPrivate, 'projectPrivate'], |
| 55 [PredefinedAcl.publicRead, 'publicRead'], |
| 56 [PredefinedAcl.publicReadWrite, 'publicReadWrite']]; |
| 57 |
| 58 withMockClient((mock, api) { |
| 59 int count = 0; |
| 60 |
| 61 mock.register('POST', 'b', expectAsync((request) { |
| 62 var requestBucket = |
| 63 new storage.Bucket.fromJson(JSON.decode(request.body)); |
| 64 expect(requestBucket.name, bucketName); |
| 65 expect(requestBucket.acl, isNull); |
| 66 expect(request.url.queryParameters['predefinedAcl'], |
| 67 predefined[count++][1]); |
| 68 return mock.respond(new storage.Bucket()..name = bucketName); |
| 69 }, count: predefined.length)); |
| 70 |
| 71 var futures = []; |
| 72 for (int i = 0; i < predefined.length; i++) { |
| 73 futures.add(api.createBucket(bucketName, |
| 74 predefinedAcl: predefined[i][0])); |
| 75 } |
| 76 return Future.wait(futures); |
| 77 }); |
| 78 }); |
| 79 |
| 80 test('create-with-acl', () { |
| 81 var acl1 = new Acl([ |
| 82 new AclEntry(new AccountScope('user@example.com'), |
| 83 AclPermission.FULL_CONTROL), |
| 84 ]); |
| 85 var acl2 = new Acl([ |
| 86 new AclEntry(new AccountScope('user@example.com'), |
| 87 AclPermission.FULL_CONTROL), |
| 88 new AclEntry(new GroupScope('group@example.com'), |
| 89 AclPermission.WRITE), |
| 90 ]); |
| 91 var acl3 = new Acl([ |
| 92 new AclEntry(new AccountScope('user@example.com'), |
| 93 AclPermission.FULL_CONTROL), |
| 94 new AclEntry(new GroupScope('group@example.com'), |
| 95 AclPermission.WRITE), |
| 96 new AclEntry(new DomainScope('example.com'), |
| 97 AclPermission.READ), |
| 98 ]); |
| 99 |
| 100 var acls = [acl1, acl2, acl3]; |
| 101 |
| 102 withMockClient((mock, api) { |
| 103 int count = 0; |
| 104 |
| 105 mock.register('POST', 'b', expectAsync((request) { |
| 106 var requestBucket = |
| 107 new storage.Bucket.fromJson(JSON.decode(request.body)); |
| 108 expect(requestBucket.name, bucketName); |
| 109 expect(request.url.queryParameters['predefinedAcl'], isNull); |
| 110 expect(requestBucket.acl, isNotNull); |
| 111 expect(requestBucket.acl.length, count + 1); |
| 112 expect(requestBucket.acl[0].entity, 'user-user@example.com'); |
| 113 expect(requestBucket.acl[0].role, 'OWNER'); |
| 114 if (count > 0) { |
| 115 expect(requestBucket.acl[1].entity, 'group-group@example.com'); |
| 116 expect(requestBucket.acl[1].role, 'WRITER'); |
| 117 } |
| 118 if (count > 2) { |
| 119 expect(requestBucket.acl[2].entity, 'domain-example.com'); |
| 120 expect(requestBucket.acl[2].role, 'READER'); |
| 121 } |
| 122 count++; |
| 123 return mock.respond(new storage.Bucket()..name = bucketName); |
| 124 }, count: acls.length)); |
| 125 |
| 126 var futures = []; |
| 127 for (int i = 0; i < acls.length; i++) { |
| 128 futures.add(api.createBucket(bucketName, acl: acls[i])); |
| 129 } |
| 130 return Future.wait(futures); |
| 131 }); |
| 132 }); |
| 133 |
| 134 test('create-with-acl-and-predefined-acl', () { |
| 135 var predefined = |
| 136 [[PredefinedAcl.authenticatedRead, 'authenticatedRead'], |
| 137 [PredefinedAcl.private, 'private'], |
| 138 [PredefinedAcl.projectPrivate, 'projectPrivate'], |
| 139 [PredefinedAcl.publicRead, 'publicRead'], |
| 140 [PredefinedAcl.publicReadWrite, 'publicReadWrite']]; |
| 141 |
| 142 var acl1 = new Acl([ |
| 143 new AclEntry(new AccountScope('user@example.com'), |
| 144 AclPermission.FULL_CONTROL), |
| 145 ]); |
| 146 var acl2 = new Acl([ |
| 147 new AclEntry(new AccountScope('user@example.com'), |
| 148 AclPermission.FULL_CONTROL), |
| 149 new AclEntry(new GroupScope('group@example.com'), |
| 150 AclPermission.WRITE), |
| 151 ]); |
| 152 var acl3 = new Acl([ |
| 153 new AclEntry(new AccountScope('user@example.com'), |
| 154 AclPermission.FULL_CONTROL), |
| 155 new AclEntry(new GroupScope('group@example.com'), |
| 156 AclPermission.WRITE), |
| 157 new AclEntry(new DomainScope('example.com'), |
| 158 AclPermission.READ), |
| 159 ]); |
| 160 |
| 161 var acls = [acl1, acl2, acl3]; |
| 162 |
| 163 withMockClient((mock, api) { |
| 164 int count = 0; |
| 165 |
| 166 mock.register('POST', 'b', expectAsync((request) { |
| 167 var requestBucket = |
| 168 new storage.Bucket.fromJson(JSON.decode(request.body)); |
| 169 int predefinedIndex = count ~/ acls.length; |
| 170 int aclIndex = count % acls.length; |
| 171 expect(requestBucket.name, bucketName); |
| 172 expect(request.url.queryParameters['predefinedAcl'], |
| 173 predefined[predefinedIndex][1]); |
| 174 expect(requestBucket.acl, isNotNull); |
| 175 expect(requestBucket.acl.length, aclIndex + 1); |
| 176 expect(requestBucket.acl[0].entity, 'user-user@example.com'); |
| 177 expect(requestBucket.acl[0].role, 'OWNER'); |
| 178 if (aclIndex > 0) { |
| 179 expect(requestBucket.acl[1].entity, 'group-group@example.com'); |
| 180 expect(requestBucket.acl[1].role, 'WRITER'); |
| 181 } |
| 182 if (aclIndex > 2) { |
| 183 expect(requestBucket.acl[2].entity, 'domain-example.com'); |
| 184 expect(requestBucket.acl[2].role, 'READER'); |
| 185 } |
| 186 count++; |
| 187 return mock.respond(new storage.Bucket()..name = bucketName); |
| 188 }, count: predefined.length * acls.length)); |
| 189 |
| 190 var futures = []; |
| 191 for (int i = 0; i < predefined.length; i++) { |
| 192 for (int j = 0; j < acls.length; j++) { |
| 193 futures.add(api.createBucket( |
| 194 bucketName, predefinedAcl: predefined[i][0], acl: acls[j])); |
| 195 } |
| 196 } |
| 197 return Future.wait(futures); |
| 198 }); |
| 199 }); |
| 200 |
| 201 test('delete', () { |
| 202 withMockClient((mock, api) { |
| 203 mock.register( |
| 204 'DELETE', new RegExp(r'b/[a-z/-]*$'), expectAsync((request) { |
| 205 expect(request.url.path, '${ROOT_PATH}b/$bucketName'); |
| 206 expect(request.body.length, 0); |
| 207 return mock.respond(new storage.Bucket()..name = bucketName); |
| 208 })); |
| 209 |
| 210 expect(api.deleteBucket(bucketName), completion(isNull)); |
| 211 }); |
| 212 }); |
| 213 |
| 214 test('exists', () { |
| 215 var exists = true; |
| 216 |
| 217 withMockClient((mock, api) { |
| 218 mock.register( |
| 219 'GET', new RegExp(r'b/[a-z/-]*$'), expectAsync((request) { |
| 220 expect(request.url.path, '${ROOT_PATH}b/$bucketName'); |
| 221 expect(request.body.length, 0); |
| 222 if (exists) { |
| 223 return mock.respond(new storage.Bucket()..name = bucketName); |
| 224 } else { |
| 225 return mock.respondError(404); |
| 226 } |
| 227 }, count: 2)); |
| 228 |
| 229 return api.bucketExists(bucketName).then(expectAsync((result) { |
| 230 expect(result, isTrue); |
| 231 exists = false; |
| 232 expect(api.bucketExists(bucketName), completion(isFalse)); |
| 233 })); |
| 234 }); |
| 235 }); |
| 236 |
| 237 test('stat', () { |
| 238 withMockClient((mock, api) { |
| 239 mock.register( |
| 240 'GET', new RegExp(r'b/[a-z/-]*$'), expectAsync((request) { |
| 241 expect(request.url.path, '${ROOT_PATH}b/$bucketName'); |
| 242 expect(request.body.length, 0); |
| 243 return mock.respond(new storage.Bucket() |
| 244 ..name = bucketName |
| 245 ..timeCreated = new DateTime(2014)); |
| 246 })); |
| 247 |
| 248 return api.bucketInfo(bucketName).then(expectAsync((result) { |
| 249 expect(result.bucketName, bucketName); |
| 250 expect(result.created, new DateTime(2014)); |
| 251 })); |
| 252 }); |
| 253 }); |
| 254 |
| 255 group('list', () { |
| 256 test('empty', () { |
| 257 withMockClient((mock, api) { |
| 258 mock.register('GET', 'b', expectAsync((request) { |
| 259 expect(request.body.length, 0); |
| 260 return mock.respond(new storage.Buckets()); |
| 261 })); |
| 262 |
| 263 api.listBucketNames().listen( |
| 264 (_) => throw 'Unexpected', |
| 265 onDone: expectAsync(() => null)); |
| 266 }); |
| 267 }); |
| 268 |
| 269 test('immediate-cancel', () { |
| 270 withMockClient((mock, api) { |
| 271 api.listBucketNames().listen( |
| 272 (_) => throw 'Unexpected', |
| 273 onDone: () => throw 'Unexpected') |
| 274 ..cancel(); |
| 275 }); |
| 276 }); |
| 277 |
| 278 test('list', () { |
| 279 // TODO: Test list. |
| 280 }); |
| 281 |
| 282 test('page', () { |
| 283 // TODO: Test page. |
| 284 }); |
| 285 }); |
| 286 |
| 287 test('copy', () { |
| 288 withMockClient((mock, api) { |
| 289 mock.register( |
| 290 'POST', |
| 291 'b/srcBucket/o/srcObject/copyTo/b/destBucket/o/destObject', |
| 292 expectAsync((request) { |
| 293 return mock.respond(new storage.Object()..name = 'destObject'); |
| 294 })); |
| 295 expect(api.copyObject('gs://srcBucket/srcObject', |
| 296 'gs://destBucket/destObject'), |
| 297 completion(isNull)); |
| 298 }); |
| 299 }); |
| 300 |
| 301 test('copy-invalid-args', () { |
| 302 withMockClient((mock, api) { |
| 303 expect(() => api.copyObject('a', 'b'), throwsA(isFormatException)); |
| 304 expect(() => api.copyObject('a/b', 'c/d'), throwsA(isFormatException)); |
| 305 expect(() => api.copyObject('gs://a/b', 'gs://c/'), |
| 306 throwsA(isFormatException)); |
| 307 expect(() => api.copyObject('gs://a/b', 'gs:///c'), |
| 308 throwsA(isFormatException)); |
| 309 }); |
| 310 }); |
| 311 }); |
| 312 |
| 313 group('object', () { |
| 314 var bucketName = 'test-bucket'; |
| 315 var objectName = 'test-object'; |
| 316 |
| 317 var bytesNormalUpload = [1, 2, 3]; |
| 318 |
| 319 // Generate a list just above the limit when changing to resumable upload. |
| 320 const int MB = 1024 * 1024; |
| 321 const int maxNormalUpload = 1 * MB; |
| 322 const int minResumableUpload = maxNormalUpload + 1; |
| 323 var bytesResumableUpload = |
| 324 new List.generate(minResumableUpload, (e) => e & 255); |
| 325 |
| 326 bool testArgumentError(e) => e is ArgumentError; |
| 327 bool testApiError(e) => e is common.ApiRequestError; |
| 328 bool testDetailedApiError(e) => e is common.DetailedApiRequestError; |
| 329 Function expectStatus(status) => (e) => expect(e.status, status); |
| 330 Function expectNotNull(status) => (o) => expect(o, isNotNull); |
| 331 |
| 332 expectNormalUpload(mock, data, objectName) { |
| 333 var bytes = data.fold([], (p, e) => p..addAll(e)); |
| 334 mock.registerUpload( |
| 335 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 336 return mock.processNormalMediaUpload(request) |
| 337 .then(expectAsync((mediaUpload) { |
| 338 var object = |
| 339 new storage.Object.fromJson(JSON.decode(mediaUpload.json)); |
| 340 expect(object.name, objectName); |
| 341 expect(mediaUpload.bytes, bytes); |
| 342 expect(mediaUpload.contentType, 'application/octet-stream'); |
| 343 return mock.respond(new storage.Object()..name = objectName); |
| 344 })); |
| 345 })); |
| 346 } |
| 347 |
| 348 expectResumableUpload(mock, data, objectName) { |
| 349 var bytes = data.fold([], (p, e) => p..addAll(e)); |
| 350 expect(bytes.length, bytesResumableUpload.length); |
| 351 int count = 0; |
| 352 mock.registerResumableUpload( |
| 353 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 354 var requestObject = |
| 355 new storage.Object.fromJson(JSON.decode(request.body)); |
| 356 expect(requestObject.name, objectName); |
| 357 return mock.respondInitiateResumableUpload(PROJECT); |
| 358 })); |
| 359 mock.registerResumableUpload( |
| 360 'PUT', 'b/$PROJECT/o', expectAsync((request) { |
| 361 count++; |
| 362 if (count == 1) { |
| 363 expect(request.bodyBytes.length, MB); |
| 364 return mock.respondContinueResumableUpload(); |
| 365 } else { |
| 366 expect(request.bodyBytes.length, 1); |
| 367 return mock.respond(new storage.Object()..name = objectName); |
| 368 } |
| 369 }, count: 2)); |
| 370 } |
| 371 |
| 372 checkResult(result) { |
| 373 expect(result.name, objectName); |
| 374 } |
| 375 |
| 376 Future pipeToSink(sink, List<List<int>> data) { |
| 377 sink.done.then(expectAsync(checkResult)); |
| 378 sink.done.catchError((e) => throw 'Unexpected $e'); |
| 379 return new Stream.fromIterable(data) |
| 380 .pipe(sink) |
| 381 .then(expectAsync(checkResult)) |
| 382 .catchError((e) => throw 'Unexpected $e'); |
| 383 } |
| 384 |
| 385 Future addStreamToSink(sink, List<List<int>> data) { |
| 386 sink.done.then(expectAsync(checkResult)); |
| 387 sink.done.catchError((e) => throw 'Unexpected $e'); |
| 388 return sink.addStream(new Stream.fromIterable(data)) |
| 389 .then((_) => sink.close()) |
| 390 .then(expectAsync(checkResult)) |
| 391 .catchError((e) => throw 'Unexpected $e'); |
| 392 } |
| 393 |
| 394 Future addToSink(sink, List<List<int>> data) { |
| 395 sink.done.then(expectAsync(checkResult)); |
| 396 sink.done.catchError((e) => throw 'Unexpected $e'); |
| 397 data.forEach((bytes) => sink.add(bytes)); |
| 398 return sink.close() |
| 399 .then(expectAsync(checkResult)) |
| 400 .catchError((e) => throw 'Unexpected $e'); |
| 401 } |
| 402 |
| 403 Future runTest(mock, api, data, length) { |
| 404 var bucket = api.bucket(bucketName); |
| 405 |
| 406 Future upload(fn, sendLength) { |
| 407 mock.clear(); |
| 408 if (length <= maxNormalUpload) { |
| 409 expectNormalUpload(mock, data, objectName); |
| 410 } else { |
| 411 expectResumableUpload(mock, data, objectName); |
| 412 } |
| 413 var sink; |
| 414 if (sendLength) { |
| 415 sink = bucket.write(objectName, length: length); |
| 416 } else { |
| 417 sink = bucket.write(objectName); |
| 418 } |
| 419 return fn(sink, data); |
| 420 } |
| 421 |
| 422 return upload(pipeToSink, true) |
| 423 .then(expectAsync((_) => upload(pipeToSink, false))) |
| 424 .then(expectAsync((_) => upload(addStreamToSink, true))) |
| 425 .then(expectAsync((_) => upload(addStreamToSink, false))) |
| 426 .then(expectAsync((_) => upload(addToSink, true))) |
| 427 .then(expectAsync((_) => upload(addToSink, false))); |
| 428 }; |
| 429 |
| 430 test('write-short-1', () { |
| 431 withMockClient((mock, api) { |
| 432 runTest(mock, api, [bytesNormalUpload], bytesNormalUpload.length); |
| 433 }); |
| 434 }); |
| 435 |
| 436 test('write-short-2', () { |
| 437 withMockClient((mock, api) { |
| 438 runTest(mock, |
| 439 api, |
| 440 [bytesNormalUpload, bytesNormalUpload], |
| 441 bytesNormalUpload.length * 2); |
| 442 }); |
| 443 }); |
| 444 |
| 445 test('write-long', () { |
| 446 withMockClient((mock, api) { |
| 447 runTest(mock, api, [bytesResumableUpload], bytesResumableUpload.length); |
| 448 }); |
| 449 }); |
| 450 |
| 451 test('write-short-error', () { |
| 452 withMockClient((mock, api) { |
| 453 |
| 454 Future test(length) { |
| 455 mock.clear(); |
| 456 mock.registerUpload( |
| 457 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 458 return mock.respondError(500); |
| 459 })); |
| 460 |
| 461 var bucket = api.bucket(bucketName); |
| 462 var sink = bucket.write(bucketName, length: length); |
| 463 sink.done |
| 464 .then((_) => throw 'Unexpected') |
| 465 .catchError(expectAsync(expectNotNull), |
| 466 test: testDetailedApiError); |
| 467 sink.done |
| 468 .catchError(expectAsync(expectNotNull), |
| 469 test: testDetailedApiError); |
| 470 return new Stream.fromIterable([bytesNormalUpload]) |
| 471 .pipe(sink) |
| 472 .then((_) => throw 'Unexpected') |
| 473 .catchError(expectAsync(expectNotNull), |
| 474 test: testDetailedApiError); |
| 475 } |
| 476 |
| 477 test(null) // Unknown length. |
| 478 .then(expectAsync((_) => test(1))) |
| 479 .then(expectAsync((_) => test(10))) |
| 480 .then(expectAsync((_) => test(maxNormalUpload))); |
| 481 }); |
| 482 }); |
| 483 |
| 484 // TODO: Mock the resumable upload timeout. |
| 485 test('write-long-error', () { |
| 486 withMockClient((mock, api) { |
| 487 |
| 488 Future test(length) { |
| 489 mock.clear(); |
| 490 mock.registerResumableUpload( |
| 491 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 492 return mock.respondInitiateResumableUpload(PROJECT); |
| 493 })); |
| 494 mock.registerResumableUpload( |
| 495 'PUT', 'b/$PROJECT/o', expectAsync((request) { |
| 496 return mock.respondError(502); |
| 497 }, count: 3)); // Default 3 retries in googleapis library. |
| 498 |
| 499 |
| 500 var bucket = api.bucket(bucketName); |
| 501 var sink = bucket.write(bucketName); |
| 502 sink.done |
| 503 .then((_) => throw 'Unexpected') |
| 504 .catchError(expectAsync(expectNotNull), |
| 505 test: testDetailedApiError); |
| 506 return new Stream.fromIterable([bytesResumableUpload]) |
| 507 .pipe(sink) |
| 508 .then((_) => throw 'Unexpected') |
| 509 .catchError(expectAsync(expectNotNull), |
| 510 test: testDetailedApiError); |
| 511 } |
| 512 |
| 513 test(null) // Unknown length. |
| 514 .then(expectAsync((_) => test(minResumableUpload))); |
| 515 }); |
| 516 }); |
| 517 |
| 518 test('write-long-wrong-length', () { |
| 519 withMockClient((mock, api) { |
| 520 |
| 521 Future test(data, length) { |
| 522 mock.clear(); |
| 523 mock.registerResumableUpload( |
| 524 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 525 return mock.respondInitiateResumableUpload(PROJECT); |
| 526 })); |
| 527 mock.registerResumableUpload( |
| 528 'PUT', 'b/$PROJECT/o', expectAsync((request) { |
| 529 return mock.respondContinueResumableUpload(); |
| 530 })); // Default 3 retries in googleapis library. |
| 531 |
| 532 var bucket = api.bucket(bucketName); |
| 533 var sink = bucket.write(bucketName, length: length); |
| 534 sink.done |
| 535 .then((_) => throw 'Unexpected') |
| 536 .catchError( |
| 537 expectAsync(expectNotNull), |
| 538 test: (e) => e is String || e is common.ApiRequestError); |
| 539 return new Stream.fromIterable(data) |
| 540 .pipe(sink) |
| 541 .then((_) => throw 'Unexpected') |
| 542 .catchError( |
| 543 expectAsync(expectNotNull), |
| 544 test: (e) => e is String || e is common.ApiRequestError); |
| 545 } |
| 546 |
| 547 test([bytesResumableUpload], bytesResumableUpload.length + 1) |
| 548 .then(expectAsync((_) => test([bytesResumableUpload, [1, 2]], |
| 549 bytesResumableUpload.length + 1))); |
| 550 }); |
| 551 }); |
| 552 |
| 553 test('write-add-error', () { |
| 554 withMockClient((mock, api) { |
| 555 var bucket = api.bucket(bucketName); |
| 556 var controller = new StreamController(sync: true); |
| 557 var sink = bucket.write(bucketName); |
| 558 sink.done |
| 559 .then((_) => throw 'Unexpected') |
| 560 .catchError(expectAsync(expectNotNull), test: testArgumentError); |
| 561 var stream = new Stream.fromIterable([[1, 2, 3]]); |
| 562 sink.addStream(stream).then((_) { |
| 563 sink.addError(new ArgumentError()); |
| 564 sink.close() |
| 565 .catchError(expectAsync(expectNotNull), test: testArgumentError); |
| 566 }); |
| 567 }); |
| 568 }); |
| 569 |
| 570 test('write-long-add-error', () { |
| 571 int count = 0; |
| 572 withMockClient((mock, api) { |
| 573 mock.registerResumableUpload( |
| 574 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 575 return mock.respondInitiateResumableUpload(PROJECT); |
| 576 })); |
| 577 // The resumable upload will buffer until either close or a full chunk, |
| 578 // so when we add an error the last byte is never sent. Therefore this |
| 579 // PUT is only called once. |
| 580 mock.registerResumableUpload( |
| 581 'PUT', 'b/$PROJECT/o', expectAsync((request) { |
| 582 expect(request.bodyBytes.length, 1024 * 1024); |
| 583 return mock.respondContinueResumableUpload(); |
| 584 })); |
| 585 |
| 586 var bucket = api.bucket(bucketName); |
| 587 var sink = bucket.write(bucketName); |
| 588 sink.done |
| 589 .then((_) => throw 'Unexpected') |
| 590 .catchError(expectAsync(expectNotNull), test: testArgumentError); |
| 591 var stream = new Stream.fromIterable([bytesResumableUpload]); |
| 592 sink.addStream(stream).then((_) { |
| 593 sink.addError(new ArgumentError()); |
| 594 sink.close() |
| 595 .catchError(expectAsync(expectNotNull), test: testArgumentError); |
| 596 }); |
| 597 }); |
| 598 }); |
| 599 |
| 600 test('write-with-metadata-short', () { |
| 601 var metadata = |
| 602 [new ObjectMetadata(contentType: 'mime/type'), |
| 603 new ObjectMetadata(contentType: 'type/mime', |
| 604 cacheControl: 'control-cache'), |
| 605 new ObjectMetadata(cacheControl: 'control-cache'), |
| 606 new ObjectMetadata(cacheControl: 'control-cache', |
| 607 contentDisposition: 'disp-content'), |
| 608 new ObjectMetadata(contentDisposition: 'disp-content', |
| 609 contentEncoding: 'encoding', |
| 610 contentLanguage: 'language'), |
| 611 new ObjectMetadata(custom: {'x': 'y'}), |
| 612 new ObjectMetadata(custom: {'a': 'b', 'x': 'y'}) |
| 613 ]; |
| 614 |
| 615 withMockClient((mock, api) { |
| 616 int count = 0; |
| 617 var bytes = [1, 2, 3]; |
| 618 |
| 619 mock.registerUpload( |
| 620 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 621 return mock.processNormalMediaUpload(request) |
| 622 .then(expectAsync((mediaUpload) { |
| 623 var object = |
| 624 new storage.Object.fromJson(JSON.decode(mediaUpload.json)); |
| 625 ObjectMetadata m = metadata[count]; |
| 626 expect(object.name, objectName); |
| 627 expect(mediaUpload.bytes, bytes); |
| 628 var contentType = m.contentType != null |
| 629 ? m.contentType : 'application/octet-stream'; |
| 630 expect(mediaUpload.contentType, contentType); |
| 631 expect(object.cacheControl, m.cacheControl); |
| 632 expect(object.contentDisposition, m.contentDisposition); |
| 633 expect(object.contentEncoding, m.contentEncoding); |
| 634 expect(object.contentLanguage, m.contentLanguage); |
| 635 expect(object.metadata, m.custom); |
| 636 count++; |
| 637 return mock.respond(new storage.Object()..name = objectName); |
| 638 })); |
| 639 }, count: metadata.length)); |
| 640 |
| 641 var bucket = api.bucket(bucketName); |
| 642 var futures = []; |
| 643 for (int i = 0; i < metadata.length; i++) { |
| 644 futures.add(bucket.writeBytes(objectName, bytes, |
| 645 metadata: metadata[i])); |
| 646 } |
| 647 return Future.wait(futures); |
| 648 }); |
| 649 }); |
| 650 |
| 651 test('write-with-metadata-long', () { |
| 652 var metadata = |
| 653 [new ObjectMetadata(contentType: 'mime/type'), |
| 654 new ObjectMetadata(contentType: 'type/mime', |
| 655 cacheControl: 'control-cache'), |
| 656 new ObjectMetadata(cacheControl: 'control-cache'), |
| 657 new ObjectMetadata(cacheControl: 'control-cache', |
| 658 contentDisposition: 'disp-content'), |
| 659 new ObjectMetadata(contentDisposition: 'disp-content', |
| 660 contentEncoding: 'encoding', |
| 661 contentLanguage: 'language'), |
| 662 new ObjectMetadata(custom: {'x': 'y'}), |
| 663 new ObjectMetadata(custom: {'a': 'b', 'x': 'y'}) |
| 664 ]; |
| 665 |
| 666 withMockClient((mock, api) { |
| 667 int countInitial = 0; |
| 668 int countData = 0; |
| 669 |
| 670 mock.registerResumableUpload( |
| 671 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 672 var object = new storage.Object.fromJson(JSON.decode(request.body)); |
| 673 ObjectMetadata m = metadata[countInitial]; |
| 674 expect(object.name, objectName); |
| 675 var contentType = m.contentType != null |
| 676 ? m.contentType : 'application/octet-stream'; |
| 677 expect(object.cacheControl, m.cacheControl); |
| 678 expect(object.contentDisposition, m.contentDisposition); |
| 679 expect(object.contentEncoding, m.contentEncoding); |
| 680 expect(object.contentLanguage, m.contentLanguage); |
| 681 expect(object.metadata, m.custom); |
| 682 countInitial++; |
| 683 return mock.respondInitiateResumableUpload(PROJECT); |
| 684 }, count: metadata.length)); |
| 685 mock.registerResumableUpload( |
| 686 'PUT', 'b/$PROJECT/o', expectAsync((request) { |
| 687 ObjectMetadata m = metadata[countData % metadata.length]; |
| 688 var contentType = m.contentType != null |
| 689 ? m.contentType : 'application/octet-stream'; |
| 690 expect(request.headers['content-type'], contentType); |
| 691 bool firstPart = countData < metadata.length; |
| 692 countData++; |
| 693 if (firstPart) { |
| 694 expect(request.bodyBytes.length, MB); |
| 695 return mock.respondContinueResumableUpload(); |
| 696 } else { |
| 697 expect(request.bodyBytes.length, 1); |
| 698 return mock.respond(new storage.Object()..name = objectName); |
| 699 } |
| 700 }, count: metadata.length * 2)); |
| 701 |
| 702 var bucket = api.bucket(bucketName); |
| 703 var futures = []; |
| 704 for (int i = 0; i < metadata.length; i++) { |
| 705 futures.add(bucket.writeBytes(objectName, bytesResumableUpload, |
| 706 metadata: metadata[i])); |
| 707 } |
| 708 return Future.wait(futures); |
| 709 }); |
| 710 }); |
| 711 |
| 712 test('write-with-predefined-acl', () { |
| 713 var predefined = |
| 714 [[PredefinedAcl.authenticatedRead, 'authenticatedRead'], |
| 715 [PredefinedAcl.private, 'private'], |
| 716 [PredefinedAcl.projectPrivate, 'projectPrivate'], |
| 717 [PredefinedAcl.publicRead, 'publicRead'], |
| 718 [PredefinedAcl.bucketOwnerFullControl, 'bucketOwnerFullControl'], |
| 719 [PredefinedAcl.bucketOwnerRead, 'bucketOwnerRead']]; |
| 720 |
| 721 withMockClient((mock, api) { |
| 722 int count = 0; |
| 723 var bytes = [1,2,3]; |
| 724 |
| 725 mock.registerUpload( |
| 726 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 727 return mock.processNormalMediaUpload(request) |
| 728 .then(expectAsync((mediaUpload) { |
| 729 var object = |
| 730 new storage.Object.fromJson(JSON.decode(mediaUpload.json)); |
| 731 expect(object.name, objectName); |
| 732 expect(mediaUpload.bytes, bytes); |
| 733 expect(mediaUpload.contentType, 'application/octet-stream'); |
| 734 expect(request.url.queryParameters['predefinedAcl'], |
| 735 predefined[count++][1]); |
| 736 expect(object.acl, isNull); |
| 737 return mock.respond(new storage.Object()..name = objectName); |
| 738 })); |
| 739 }, count: predefined.length)); |
| 740 |
| 741 var bucket = api.bucket(bucketName); |
| 742 var futures = []; |
| 743 for (int i = 0; i < predefined.length; i++) { |
| 744 futures.add(bucket.writeBytes(objectName, bytes, |
| 745 predefinedAcl: predefined[i][0])); |
| 746 } |
| 747 return Future.wait(futures); |
| 748 }); |
| 749 }); |
| 750 |
| 751 test('write-with-acl', () { |
| 752 var acl1 = new Acl([ |
| 753 new AclEntry(new AccountScope('user@example.com'), |
| 754 AclPermission.FULL_CONTROL), |
| 755 ]); |
| 756 var acl2 = new Acl([ |
| 757 new AclEntry(new AccountScope('user@example.com'), |
| 758 AclPermission.FULL_CONTROL), |
| 759 new AclEntry(new GroupScope('group@example.com'), |
| 760 AclPermission.WRITE), |
| 761 ]); |
| 762 var acl3 = new Acl([ |
| 763 new AclEntry(new AccountScope('user@example.com'), |
| 764 AclPermission.FULL_CONTROL), |
| 765 new AclEntry(new GroupScope('group@example.com'), |
| 766 AclPermission.WRITE), |
| 767 new AclEntry(new DomainScope('example.com'), |
| 768 AclPermission.READ), |
| 769 ]); |
| 770 |
| 771 var acls = [acl1, acl2, acl3]; |
| 772 |
| 773 withMockClient((mock, api) { |
| 774 int count = 0; |
| 775 var bytes = [1,2,3]; |
| 776 |
| 777 mock.registerUpload( |
| 778 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 779 return mock.processNormalMediaUpload(request) |
| 780 .then(expectAsync((mediaUpload) { |
| 781 var object = |
| 782 new storage.Object.fromJson(JSON.decode(mediaUpload.json)); |
| 783 expect(object.name, objectName); |
| 784 expect(mediaUpload.bytes, bytes); |
| 785 expect(mediaUpload.contentType, 'application/octet-stream'); |
| 786 expect(request.url.queryParameters['predefinedAcl'], isNull); |
| 787 expect(object.acl, isNotNull); |
| 788 expect(object.acl.length, count + 1); |
| 789 expect(object.acl[0].entity, 'user-user@example.com'); |
| 790 expect(object.acl[0].role, 'OWNER'); |
| 791 if (count > 0) { |
| 792 expect(object.acl[1].entity, 'group-group@example.com'); |
| 793 expect(object.acl[1].role, 'OWNER'); |
| 794 } |
| 795 if (count > 2) { |
| 796 expect(object.acl[2].entity, 'domain-example.com'); |
| 797 expect(object.acl[2].role, 'READER'); |
| 798 } |
| 799 count++; |
| 800 return mock.respond(new storage.Object()..name = objectName); |
| 801 })); |
| 802 }, count: acls.length)); |
| 803 |
| 804 var bucket = api.bucket(bucketName); |
| 805 var futures = []; |
| 806 for (int i = 0; i < acls.length; i++) { |
| 807 futures.add(bucket.writeBytes(objectName, bytes, acl: acls[i])); |
| 808 } |
| 809 return Future.wait(futures); |
| 810 }); |
| 811 }); |
| 812 |
| 813 test('write-with-acl-and-predefined-acl', () { |
| 814 var predefined = |
| 815 [[PredefinedAcl.authenticatedRead, 'authenticatedRead'], |
| 816 [PredefinedAcl.private, 'private'], |
| 817 [PredefinedAcl.projectPrivate, 'projectPrivate'], |
| 818 [PredefinedAcl.publicRead, 'publicRead'], |
| 819 [PredefinedAcl.bucketOwnerFullControl, 'bucketOwnerFullControl'], |
| 820 [PredefinedAcl.bucketOwnerRead, 'bucketOwnerRead']]; |
| 821 |
| 822 var acl1 = new Acl([ |
| 823 new AclEntry(new AccountScope('user@example.com'), |
| 824 AclPermission.FULL_CONTROL), |
| 825 ]); |
| 826 var acl2 = new Acl([ |
| 827 new AclEntry(new AccountScope('user@example.com'), |
| 828 AclPermission.FULL_CONTROL), |
| 829 new AclEntry(new GroupScope('group@example.com'), |
| 830 AclPermission.WRITE), |
| 831 ]); |
| 832 var acl3 = new Acl([ |
| 833 new AclEntry(new AccountScope('user@example.com'), |
| 834 AclPermission.FULL_CONTROL), |
| 835 new AclEntry(new GroupScope('group@example.com'), |
| 836 AclPermission.WRITE), |
| 837 new AclEntry(new DomainScope('example.com'), |
| 838 AclPermission.READ), |
| 839 ]); |
| 840 |
| 841 var acls = [acl1, acl2, acl3]; |
| 842 |
| 843 withMockClient((mock, api) { |
| 844 int count = 0; |
| 845 var bytes = [1,2,3]; |
| 846 |
| 847 mock.registerUpload( |
| 848 'POST', 'b/$bucketName/o', expectAsync((request) { |
| 849 return mock.processNormalMediaUpload(request) |
| 850 .then(expectAsync((mediaUpload) { |
| 851 int predefinedIndex = count ~/ acls.length; |
| 852 int aclIndex = count % acls.length; |
| 853 var object = |
| 854 new storage.Object.fromJson(JSON.decode(mediaUpload.json)); |
| 855 expect(object.name, objectName); |
| 856 expect(mediaUpload.bytes, bytes); |
| 857 expect(mediaUpload.contentType, 'application/octet-stream'); |
| 858 expect(request.url.queryParameters['predefinedAcl'], |
| 859 predefined[predefinedIndex][1]); |
| 860 expect(object.acl, isNotNull); |
| 861 expect(object.acl.length, aclIndex + 1); |
| 862 expect(object.acl[0].entity, 'user-user@example.com'); |
| 863 expect(object.acl[0].role, 'OWNER'); |
| 864 if (aclIndex > 0) { |
| 865 expect(object.acl[1].entity, 'group-group@example.com'); |
| 866 expect(object.acl[1].role, 'OWNER'); |
| 867 } |
| 868 if (aclIndex > 2) { |
| 869 expect(object.acl[2].entity, 'domain-example.com'); |
| 870 expect(object.acl[2].role, 'READER'); |
| 871 } |
| 872 count++; |
| 873 return mock.respond(new storage.Object()..name = objectName); |
| 874 })); |
| 875 }, count: predefined.length * acls.length)); |
| 876 |
| 877 var bucket = api.bucket(bucketName); |
| 878 var futures = []; |
| 879 for (int i = 0; i < predefined.length; i++) { |
| 880 for (int j = 0; j < acls.length; j++) { |
| 881 futures.add(bucket.writeBytes( |
| 882 objectName, bytes, |
| 883 acl: acls[j], predefinedAcl: predefined[i][0])); |
| 884 } |
| 885 } |
| 886 return Future.wait(futures); |
| 887 }); |
| 888 }); |
| 889 |
| 890 |
| 891 |
| 892 test('read', () { |
| 893 var bytes = [1, 2, 3]; |
| 894 withMockClient((mock, api) { |
| 895 mock.register( |
| 896 'GET', 'b/$bucketName/o/$objectName', expectAsync((request) { |
| 897 expect(request.url.queryParameters['alt'], 'media'); |
| 898 return mock.respondBytes(bytes); |
| 899 })); |
| 900 |
| 901 var bucket = api.bucket(bucketName); |
| 902 var data = []; |
| 903 bucket.read(objectName).listen(data.addAll).asFuture() |
| 904 .then(expectAsync((_) => expect(data, bytes))); |
| 905 }); |
| 906 }); |
| 907 |
| 908 test('stat', () { |
| 909 withMockClient((mock, api) { |
| 910 mock.register( |
| 911 'GET', 'b/$bucketName/o/$objectName', expectAsync((request) { |
| 912 expect(request.url.queryParameters['alt'], 'json'); |
| 913 return mock.respond(new storage.Object() |
| 914 ..name = objectName |
| 915 ..updated = new DateTime(2014) |
| 916 ..contentType = 'mime/type'); |
| 917 })); |
| 918 |
| 919 var api = new Storage(mock, PROJECT); |
| 920 var bucket = api.bucket(bucketName); |
| 921 bucket.info(objectName).then(expectAsync((stat) { |
| 922 expect(stat.name, objectName); |
| 923 expect(stat.updated, new DateTime(2014)); |
| 924 expect(stat.metadata.contentType, 'mime/type'); |
| 925 })); |
| 926 }); |
| 927 }); |
| 928 |
| 929 test('stat-acl', () { |
| 930 withMockClient((mock, api) { |
| 931 mock.register( |
| 932 'GET', 'b/$bucketName/o/$objectName', expectAsync((request) { |
| 933 expect(request.url.queryParameters['alt'], 'json'); |
| 934 var acl1 = new storage.ObjectAccessControl(); |
| 935 acl1.entity = 'user-1234567890'; |
| 936 acl1.role = 'OWNER'; |
| 937 var acl2 = new storage.ObjectAccessControl(); |
| 938 acl2.entity = 'user-xxx@yyy.zzz'; |
| 939 acl2.role = 'OWNER'; |
| 940 var acl3 = new storage.ObjectAccessControl(); |
| 941 acl3.entity = 'xxx-1234567890'; |
| 942 acl3.role = 'OWNER'; |
| 943 return mock.respond(new storage.Object() |
| 944 ..name = objectName |
| 945 ..acl = [acl1, acl2, acl3]); |
| 946 })); |
| 947 |
| 948 var api = new Storage(mock, PROJECT); |
| 949 var bucket = api.bucket(bucketName); |
| 950 bucket.info(objectName).then(expectAsync((ObjectInfo info) { |
| 951 expect(info.name, objectName); |
| 952 expect(info.metadata.acl.entries.length, 3); |
| 953 expect(info.metadata.acl.entries[0] is AclEntry, isTrue); |
| 954 expect(info.metadata.acl.entries[0].scope is StorageIdScope, isTrue); |
| 955 expect(info.metadata.acl.entries[1] is AclEntry, isTrue); |
| 956 expect(info.metadata.acl.entries[1].scope is AccountScope, isTrue); |
| 957 expect(info.metadata.acl.entries[2] is AclEntry, isTrue); |
| 958 expect(info.metadata.acl.entries[2].scope is OpaqueScope, isTrue); |
| 959 })); |
| 960 }); |
| 961 }); |
| 962 |
| 963 group('list', () { |
| 964 test('empty', () { |
| 965 withMockClient((mock, api) { |
| 966 mock.register('GET', 'b/$bucketName/o', expectAsync((request) { |
| 967 expect(request.body.length, 0); |
| 968 return mock.respond(new storage.Objects()); |
| 969 })); |
| 970 |
| 971 var bucket = api.bucket(bucketName); |
| 972 bucket.list().listen( |
| 973 (_) => throw 'Unexpected', |
| 974 onDone: expectAsync(() => null)); |
| 975 }); |
| 976 }); |
| 977 |
| 978 test('immediate-cancel', () { |
| 979 withMockClient((mock, api) { |
| 980 var bucket = api.bucket(bucketName); |
| 981 bucket.list().listen( |
| 982 (_) => throw 'Unexpected', |
| 983 onDone: () => throw 'Unexpected') |
| 984 ..cancel(); |
| 985 }); |
| 986 }); |
| 987 |
| 988 test('list', () { |
| 989 // TODO: Test list. |
| 990 }); |
| 991 |
| 992 test('page', () { |
| 993 // TODO: Test page. |
| 994 }); |
| 995 }); |
| 996 }); |
| 997 |
| 998 group('acl', () { |
| 999 var id = new StorageIdScope('1234567890'); |
| 1000 var user = new AccountScope('sgjesse@google.com'); |
| 1001 var group = new GroupScope('dart'); |
| 1002 var domain = new DomainScope('dartlang.org'); |
| 1003 |
| 1004 var userRead = new AclEntry(user, AclPermission.READ); |
| 1005 var groupWrite = new AclEntry(group, AclPermission.WRITE); |
| 1006 var domainFullControl = |
| 1007 new AclEntry(domain, AclPermission.FULL_CONTROL); |
| 1008 |
| 1009 test('compare-scope', () { |
| 1010 expect(id, new StorageIdScope('1234567890')); |
| 1011 expect(user, new AccountScope('sgjesse@google.com')); |
| 1012 expect(group, new GroupScope('dart')); |
| 1013 expect(domain, new DomainScope('dartlang.org')); |
| 1014 expect(AclScope.allAuthenticated, new AllAuthenticatedScope()); |
| 1015 expect(AclScope.allUsers, new AllUsersScope()); |
| 1016 }); |
| 1017 |
| 1018 test('compare-entries', () { |
| 1019 expect(userRead, new AclEntry(user, AclPermission.READ)); |
| 1020 expect(groupWrite, new AclEntry(group, AclPermission.WRITE)); |
| 1021 expect(domainFullControl, |
| 1022 new AclEntry(domain, AclPermission.FULL_CONTROL)); |
| 1023 }); |
| 1024 |
| 1025 test('compare-acls', () { |
| 1026 var acl = new Acl([userRead, groupWrite, domainFullControl]); |
| 1027 expect(acl, new Acl([new AclEntry(user, AclPermission.READ), |
| 1028 new AclEntry(group, AclPermission.WRITE), |
| 1029 new AclEntry(domain, AclPermission.FULL_CONTROL)])); |
| 1030 expect(acl, |
| 1031 isNot(equals(new Acl([new AclEntry(group, AclPermission.WRITE), |
| 1032 new AclEntry(user, AclPermission.READ), |
| 1033 new AclEntry(domain, AclPermission.FULL_CONTROL)])))); |
| 1034 }); |
| 1035 |
| 1036 |
| 1037 test('compare-predefined-acls', () { |
| 1038 expect(PredefinedAcl.private, PredefinedAcl.private); |
| 1039 expect(PredefinedAcl.private, isNot(equals(PredefinedAcl.publicRead))); |
| 1040 }); |
| 1041 }); |
| 1042 } |
OLD | NEW |