blob: 77a4f838ba474375e6128a5100688a45d7319c2a [file] [log] [blame]
// Copyright (c) 2014, 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.
library gcloud.storage;
import 'dart:async';
import 'package:googleapis/storage/v1.dart' as storage_api;
import 'package:gcloud/storage.dart';
import 'package:unittest/unittest.dart';
import '../common_e2e.dart';
String generateBucketName() {
var id = new DateTime.now().millisecondsSinceEpoch;
return 'dart-e2e-test-$id';
}
bool testDetailedApiError(e) => e is storage_api.DetailedApiRequestError;
// Generate a list just above the limit when changing to resumable upload.
const int MB = 1024 * 1024;
const int maxNormalUpload = 1 * MB;
const int minResumableUpload = maxNormalUpload + 1;
var bytesResumableUpload =
new List.generate(minResumableUpload, (e) => e & 255);
runTests(Storage storage, Bucket testBucket) {
group('bucket', () {
test('create-info-delete', () {
var bucketName = generateBucketName();
return storage.createBucket(bucketName).then(expectAsync((result) {
expect(result, isNull);
return storage.bucketInfo(bucketName).then(expectAsync((info) {
expect(info.bucketName, bucketName);
expect(info.etag, isNotNull);
expect(info.created is DateTime, isTrue);
expect(info.id, isNotNull);
return storage.deleteBucket(bucketName)
.then(expectAsync((result) {
expect(result, isNull);
}));
}));
}));
});
test('create-with-predefined-acl-delete', () {
Future<Acl> test(predefinedAcl, expectedLength) {
var bucketName = generateBucketName();
return storage.createBucket(bucketName, predefinedAcl: predefinedAcl)
.then(expectAsync((result) {
expect(result, isNull);
return storage.bucketInfo(bucketName).then(expectAsync((info) {
var acl = info.acl;
expect(info.bucketName, bucketName);
expect(acl.entries.length, expectedLength);
return storage.deleteBucket(bucketName)
.then(expectAsync((result) {
expect(result, isNull);
}));
}));
}));
}
return Future.forEach([
// TODO: Figure out why some returned ACLs are empty.
() => test(PredefinedAcl.authenticatedRead, 0),
// [test, [PredefinedAcl.private, 0]], // TODO: Cannot delete.
() => test(PredefinedAcl.projectPrivate, 3),
() => test(PredefinedAcl.publicRead, 0),
() => test(PredefinedAcl.publicReadWrite, 0)
], (f) => f().then(expectAsync((_) {})));
});
test('create-error', () {
storage.createBucket('goog-reserved').catchError(expectAsync((e) {
expect(e, isNotNull);
}), test: testDetailedApiError);
});
});
group('object', () {
// Run all object tests in the same bucket to try to avoid the rate-limit
// for creating and deleting buckets while testing.
Future withTestBucket(function) {
return function(testBucket).whenComplete(() {
// TODO: Clean the bucket.
});
}
test('create-read-delete', () {
Future test(name, bytes) {
return withTestBucket((Bucket bucket) {
return bucket.writeBytes('test', bytes).then(expectAsync((info) {
expect(info, isNotNull);
return bucket.read('test')
.fold([], (p, e) => p..addAll(e))
.then(expectAsync((result) {
expect(result, bytes);
return bucket.delete('test').then(expectAsync((result) {
expect(result, isNull);
}));
}));
}));
});
}
return Future.forEach([
() => test('test-1', [1, 2, 3]),
() => test('test-2', bytesResumableUpload)
], (f) => f().then(expectAsync((_) {})));
});
test('create-with-predefined-acl-delete', () {
return withTestBucket((Bucket bucket) {
Future test(objectName, predefinedAcl, expectedLength) {
return bucket.writeBytes(
objectName, [1, 2, 3], predefinedAcl: predefinedAcl)
.then(expectAsync((result) {
expect(result, isNotNull);
return bucket.info(objectName).then(expectAsync((info) {
var acl = info.metadata.acl;
expect(info.name, objectName);
expect(info.etag, isNotNull);
expect(acl.entries.length, expectedLength);
return bucket.delete(objectName).then(expectAsync((result) {
expect(result, isNull);
}));
}));
}));
}
return Future.forEach([
() => test('test-1', PredefinedAcl.authenticatedRead, 2),
() => test('test-2', PredefinedAcl.private, 1),
() => test('test-3', PredefinedAcl.projectPrivate, 4),
() => test('test-4', PredefinedAcl.publicRead, 2),
() => test('test-5', PredefinedAcl.bucketOwnerFullControl, 2),
() => test('test-6', PredefinedAcl.bucketOwnerRead, 2)
], (f) => f().then(expectAsync((_) {})));
});
});
test('create-with-acl-delete', () {
return withTestBucket((Bucket bucket) {
Future test(objectName, acl, expectedLength) {
return bucket.writeBytes(objectName, [1, 2, 3], acl: acl)
.then(expectAsync((result) {
expect(result, isNotNull);
return bucket.info(objectName).then(expectAsync((info) {
var acl = info.metadata.acl;
expect(info.name, objectName);
expect(info.etag, isNotNull);
expect(acl.entries.length, expectedLength);
return bucket.delete(objectName).then(expectAsync((result) {
expect(result, isNull);
}));
}));
}));
}
Acl acl1 = new Acl(
[new AclEntry(AclScope.allAuthenticated, AclPermission.WRITE)]);
Acl acl2 = new Acl(
[new AclEntry(AclScope.allUsers, AclPermission.WRITE),
new AclEntry(new AccountScope('sgjesse@google.com'),
AclPermission.WRITE)]);
Acl acl3 = new Acl(
[new AclEntry(AclScope.allUsers, AclPermission.WRITE),
new AclEntry(new AccountScope('sgjesse@google.com'),
AclPermission.WRITE),
new AclEntry(new GroupScope('misc@dartlang.org'),
AclPermission.READ)]);
Acl acl4 = new Acl(
[new AclEntry(AclScope.allUsers, AclPermission.WRITE),
new AclEntry(new AccountScope('sgjesse@google.com'),
AclPermission.WRITE),
new AclEntry(new GroupScope('misc@dartlang.org'),
AclPermission.READ),
new AclEntry(new DomainScope('dartlang.org'),
AclPermission.FULL_CONTROL)]);
// The expected length of the returned ACL is one longer than the one
// use during creation as an additional 'used-ID' ACL entry is added
// by cloud storage during creation.
return Future.forEach([
() => test('test-1', acl1, acl1.entries.length + 1),
() => test('test-2', acl2, acl2.entries.length + 1),
() => test('test-3', acl3, acl3.entries.length + 1),
() => test('test-4', acl4, acl4.entries.length + 1)
], (f) => f().then(expectAsync((_) {})));
});
});
test('create-with-metadata-delete', () {
return withTestBucket((Bucket bucket) {
Future test(objectName, metadata, bytes) {
return bucket.writeBytes(objectName, bytes, metadata: metadata)
.then(expectAsync((result) {
expect(result, isNotNull);
return bucket.info(objectName).then(expectAsync((info) {
expect(info.name, objectName);
expect(info.length, bytes.length);
expect(info.updated is DateTime, isTrue);
expect(info.md5Hash, isNotNull);
expect(info.crc32CChecksum, isNotNull);
expect(info.downloadLink is Uri, isTrue);
expect(info.generation.objectGeneration, isNotNull);
expect(info.generation.metaGeneration, 1);
expect(info.metadata.contentType, metadata.contentType);
expect(info.metadata.cacheControl, metadata.cacheControl);
expect(info.metadata.contentDisposition,
metadata.contentDisposition);
expect(info.metadata.contentEncoding,
metadata.contentEncoding);
expect(info.metadata.contentLanguage,
metadata.contentLanguage);
expect(info.metadata.custom, metadata.custom);
return bucket.delete(objectName).then(expectAsync((result) {
expect(result, isNull);
}));
}));
}));
}
var metadata1 = new ObjectMetadata(contentType: 'text/plain');
var metadata2 = new ObjectMetadata(
contentType: 'text/plain',
cacheControl: 'no-cache',
contentDisposition: 'attachment; filename="test.txt"',
contentEncoding: 'gzip',
contentLanguage: 'da',
custom: {'a': 'b', 'c': 'd'});
return Future.forEach([
() => test('test-1', metadata1, [65, 66, 67]),
() => test('test-2', metadata2, [65, 66, 67]),
() => test('test-3', metadata1, bytesResumableUpload),
() => test('test-4', metadata2, bytesResumableUpload)
], (f) => f().then(expectAsync((_) {})));
});
});
});
}
main() {
withAuthClient(Storage.SCOPES, (String project, httpClient) {
var testBucket = generateBucketName();
// Share the same storage connection for all tests.
var storage = new Storage(httpClient, project);
// Create a shared bucket for all object tests.
return storage.createBucket(testBucket).then((_) {
return runE2EUnittest(() {
runTests(storage, storage.bucket(testBucket));
}).whenComplete(() {
// Deleting a bucket relies on eventually consistent behaviour, hence
// the delay in attempt to prevent test flakiness.
return new Future.delayed(STORAGE_LIST_DELAY, () {
return storage.deleteBucket(testBucket);
});
});
});
});
}