Skip to content

Commit fc2ddcc

Browse files
authored
Migrate tests in frontend/backend.dart (#2638)
* Migrate tests in frontend/backend.dart * PackageRejectedException * Migrate GCloudRepository.upload successful test case * Merge download test with upload test.
1 parent 0d0035f commit fc2ddcc

File tree

4 files changed

+90
-220
lines changed

4 files changed

+90
-220
lines changed

app/lib/frontend/backend.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -837,9 +837,8 @@ Future _saveTarballToFS(Stream<List<int>> data, String filename) async {
837837
if (receivedBytes <= UploadSignerService.maxUploadSize) {
838838
sink.add(chunk);
839839
} else {
840-
final error = 'Invalid upload: Exceeded '
841-
'${UploadSignerService.maxUploadSize} upload size.';
842-
sink.addError(error);
840+
sink.addError(PackageRejectedException.archiveTooLarge(
841+
UploadSignerService.maxUploadSize));
843842
}
844843
},
845844
),

app/lib/shared/exceptions.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,15 @@ class InvalidInputException extends ResponseException {
141141
}
142142
}
143143

144+
/// Throws when a package upload is rejected for a reason.
145+
class PackageRejectedException extends ResponseException
146+
implements GenericProcessingException {
147+
/// The package archive tar.gz file is above [limit] bytes.
148+
PackageRejectedException.archiveTooLarge(int limit)
149+
: super._(
150+
400, 'PackageRejected', 'Package archive exceeded $limit bytes.');
151+
}
152+
144153
/// Thrown when authentication failed, credentials is missing or invalid.
145154
class AuthenticationException extends ResponseException
146155
implements UnauthorizedAccessException {

app/test/frontend/backend_test.dart

Lines changed: 74 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -151,25 +151,11 @@ void main() {
151151
});
152152

153153
group('Backend.lookupLatestVersions', () {
154-
(<String, List<PackageVersion>>{
155-
'one version': [foobarStablePV],
156-
'empty': [null],
157-
})
158-
.forEach((String testName, List<PackageVersion> expectedVersions) {
159-
test(testName, () async {
160-
List<PackageVersion> lookupFun(List<Key> keys) {
161-
expect(keys, hasLength(1));
162-
expect(keys.first, foobarStablePV.key);
163-
return expectedVersions;
164-
}
165-
166-
final db = DatastoreDBMock(lookupFun: expectAsync1(lookupFun));
167-
final backend = Backend(db, null);
168-
169-
final versions = await backend.lookupLatestVersions([foobarPackage]);
170-
expect(versions, hasLength(1));
171-
expect(versions.first, equals(expectedVersions.first));
172-
});
154+
testWithServices('two packages', () async {
155+
final list = await backend
156+
.lookupLatestVersions([hydrogen.package, helium.package]);
157+
expect(list.map((pv) => pv.qualifiedVersionKey.toString()),
158+
['hydrogen-2.0.8', 'helium-2.0.5']);
173159
});
174160
});
175161

@@ -349,22 +335,6 @@ void main() {
349335
});
350336
});
351337

352-
group('GCloudRepository.download', () {
353-
test('successful', () async {
354-
final tarballStorage =
355-
TarballStorageMock(downloadFun: expectAsync2((package, version) {
356-
return Stream.fromIterable([
357-
[1, 2, 3]
358-
]);
359-
}));
360-
final repo = GCloudPackageRepository(null, tarballStorage);
361-
362-
final stream = await repo.download('foo', '0.1.0');
363-
final data = await stream.fold([], (b, d) => b..addAll(d));
364-
expect(data, [1, 2, 3]);
365-
});
366-
});
367-
368338
group('GCloudRepository.lookupVersion', () {
369339
testWithServices('package not found', () async {
370340
final version =
@@ -482,15 +452,10 @@ void main() {
482452
group('GCloudRepository.startAsyncUpload', () {
483453
final Uri redirectUri = Uri.parse('http://blobstore.com/upload');
484454

485-
testWithCache('no active user', () async {
486-
final db = DatastoreDBMock();
487-
final repo = GCloudPackageRepository(db, null);
488-
registerUploadSigner(UploadSignerServiceMock(null));
489-
await repo
490-
.startAsyncUpload(redirectUri)
491-
.catchError(expectAsync2((e, _) {
492-
expect(e is pub_server.UnauthorizedAccessException, isTrue);
493-
}));
455+
testWithServices('no active user', () async {
456+
final rs = backend.repository
457+
.startAsyncUpload(Uri.parse('http://example.com/'));
458+
expectLater(rs, throwsA(isA<AuthenticationException>()));
494459
});
495460

496461
testWithCache('successful', () async {
@@ -543,13 +508,8 @@ void main() {
543508
registerAuthenticatedUser(hansAuthenticated);
544509
final historyBackendMock = HistoryBackendMock();
545510
registerHistoryBackend(historyBackendMock);
546-
final Future result = repo.finishAsyncUpload(redirectUri);
547-
await result.catchError(expectAsync2((error, _) {
548-
expect(
549-
error,
550-
contains(
551-
'Exceeded ${UploadSignerService.maxUploadSize} upload size'));
552-
}));
511+
final result = repo.finishAsyncUpload(redirectUri);
512+
expectLater(result, throwsA(isA<PackageRejectedException>()));
553513
expect(historyBackendMock.storedHistories, hasLength(0));
554514
}, timeout: Timeout.factor(2));
555515

@@ -613,87 +573,54 @@ void main() {
613573
});
614574

615575
group('GCloudRepository.upload', () {
616-
testWithCache('not logged in', () async {
576+
testWithServices('not logged in', () async {
617577
return withTestPackage((List<int> tarball) async {
618-
final tarballStorage = TarballStorageMock();
619-
final transactionMock = TransactionMock();
620-
final db = DatastoreDBMock(transactionMock: transactionMock);
621-
final repo = GCloudPackageRepository(db, tarballStorage);
622-
repo
623-
.upload(Stream.fromIterable([tarball]))
624-
.catchError(expectAsync2((error, _) {
625-
expect(error is pub_server.UnauthorizedAccessException, isTrue);
626-
}));
578+
final rs =
579+
backend.repository.upload(Stream.fromIterable([tarball]));
580+
await expectLater(rs, throwsA(isA<AuthenticationException>()));
627581
});
628582
});
629583

630-
testWithCache('not authorized', () async {
631-
return withTestPackage((List<int> tarball) async {
632-
final tarballStorage = TarballStorageMock();
633-
final transactionMock = TransactionMock(
634-
lookupFun: expectAsync1((keys) {
635-
expect(keys, hasLength(2));
636-
expect(keys.first, foobarStablePV.key);
637-
expect(keys.last, foobarPackage.key);
638-
return [null, foobarPackage];
639-
}),
640-
rollbackFun: expectAsync0(() {}));
641-
final db = DatastoreDBMock(transactionMock: transactionMock);
642-
final repo = GCloudPackageRepository(db, tarballStorage);
643-
registerAuthenticatedUser(AuthenticatedUser(
644-
'uuid-no-at-authorized-dot-com', '[email protected]'));
645-
registerNameTracker(NameTracker(null));
646-
await repo
647-
.upload(Stream.fromIterable([tarball]))
648-
.catchError(expectAsync2((error, _) {
649-
expect(error is pub_server.UnauthorizedAccessException, isTrue);
650-
}));
651-
});
584+
testWithServices('not authorized', () async {
585+
return withTestPackage(
586+
(List<int> tarball) async {
587+
registerAuthenticatedUser(
588+
AuthenticatedUser(joeUser.userId, joeUser.email));
589+
final rs =
590+
backend.repository.upload(Stream.fromIterable([tarball]));
591+
await expectLater(rs, throwsA(isA<AuthorizationException>()));
592+
},
593+
pubspecContent: generatePubspecYaml(foobarPackage.name, '0.2.0'),
594+
);
652595
});
653596

654-
testWithCache('versions already exist', () async {
597+
testWithServices('versions already exist', () async {
655598
return withTestPackage((List<int> tarball) async {
656-
final tarballStorage = TarballStorageMock();
657-
final transactionMock = TransactionMock(
658-
lookupFun: expectAsync1((keys) {
659-
expect(keys, hasLength(2));
660-
expect(keys.first, foobarStablePV.key);
661-
expect(keys.last, foobarPackage.key);
662-
return [foobarStablePV, foobarPackage];
663-
}),
664-
rollbackFun: expectAsync0(() {}));
665-
final db = DatastoreDBMock(transactionMock: transactionMock);
666-
final repo = GCloudPackageRepository(db, tarballStorage);
667-
registerAuthenticatedUser(AuthenticatedUser(
668-
'uuid-no-at-authorized-dot-com', '[email protected]'));
669-
registerNameTracker(NameTracker(null));
670-
await repo
671-
.upload(Stream.fromIterable([tarball]))
672-
.catchError(expectAsync2((error, _) {
673-
expect(
674-
'$error'.contains(
599+
registerAuthenticatedUser(
600+
AuthenticatedUser(joeUser.userId, joeUser.email));
601+
final rs =
602+
backend.repository.upload(Stream.fromIterable([tarball]));
603+
await expectLater(
604+
rs,
605+
throwsA(isA<Exception>().having(
606+
(e) => '$e',
607+
'text',
608+
contains(
675609
'Version 0.1.1+5 of package foobar_pkg already exists'),
676-
isTrue);
677-
}));
610+
)));
678611
});
679612
});
680613

681-
testWithCache('bad package names are rejected', () async {
682-
final tarballStorage = TarballStorageMock();
683-
final transactionMock = TransactionMock();
684-
final db = DatastoreDBMock(transactionMock: transactionMock);
685-
final repo = GCloudPackageRepository(db, tarballStorage);
614+
testWithServices('bad package names are rejected', () async {
615+
await nameTracker.scanDatastore();
686616
registerAuthenticatedUser(hansAuthenticated);
687-
registerNameTracker(NameTracker(null));
688-
nameTracker.add('foobar_pkg');
689617

690618
// Returns the error message as String or null if it succeeded.
691619
Future<String> fn(String name) async {
692-
final String pubspecContent =
693-
foobarStablePubspec.replaceAll('foobar_pkg', name);
620+
final pubspecContent = generatePubspecYaml(name, '0.2.0');
694621
try {
695622
await withTestPackage((List<int> tarball) async {
696-
await repo.upload(Stream.fromIterable([tarball]));
623+
await backend.repository.upload(Stream.fromIterable([tarball]));
697624
}, pubspecContent: pubspecContent);
698625
} catch (e) {
699626
return e.toString();
@@ -709,10 +636,12 @@ void main() {
709636
expect(await fn('With Space'),
710637
'Package name may only contain letters, numbers, and underscores.');
711638

712-
expect(await fn('ok_name'), 'Exception: no lookupFun');
639+
expect(await fn('ok_name'), isNull);
713640
});
714641

715-
testWithCache('upload-too-big', () async {
642+
testWithServices('upload-too-big', () async {
643+
registerAuthenticatedUser(hansAuthenticated);
644+
716645
final oneKB = List.filled(1024, 42);
717646
final List<List<int>> bigTarball = [];
718647
for (int i = 0; i < UploadSignerService.maxUploadSize ~/ 1024; i++) {
@@ -721,105 +650,36 @@ void main() {
721650
// Add one more byte than allowed.
722651
bigTarball.add([1]);
723652

724-
final tarballStorage = TarballStorageMock();
725-
final transactionMock = TransactionMock();
726-
final db = DatastoreDBMock(transactionMock: transactionMock);
727-
final repo = GCloudPackageRepository(db, tarballStorage);
728-
registerAuthenticatedUser(hansAuthenticated);
729-
registerAnalyzerClient(AnalyzerClientMock());
730-
registerDartdocClient(DartdocClientMock());
731-
final historyBackendMock = HistoryBackendMock();
732-
registerHistoryBackend(historyBackendMock);
733-
registerNameTracker(NameTracker(null));
734-
final Future result = repo.upload(Stream.fromIterable(bigTarball));
735-
await result.catchError(expectAsync2((error, _) {
736-
expect(
737-
error,
738-
contains(
739-
'Exceeded ${UploadSignerService.maxUploadSize} upload size'));
740-
}));
741-
expect(historyBackendMock.storedHistories, hasLength(0));
742-
}, timeout: Timeout.factor(2));
743-
744-
testWithCache('successful', () async {
745-
return withTestPackage((List<int> tarball) async {
746-
final completion = TestDelayCompletion(count: 2);
747-
final tarballStorage = TarballStorageMock(uploadFun:
748-
(String package, String version,
749-
Stream<List<int>> uploadTarball) async {
750-
expect(package, foobarPackage.name);
751-
expect(version, foobarStablePV.version);
752-
753-
final bytes =
754-
await uploadTarball.fold([], (b, d) => b..addAll(d));
755-
756-
expect(bytes, tarball);
757-
});
758-
759-
// NOTE: There will be two transactions:
760-
// a) for inserting a new Package + PackageVersion
761-
// b) for inserting a new PackageVersions sorted by `sort_order`.
762-
int queueMutationCallNr = 0;
763-
final queryMock = QueryMock(sortOrderUpdateQueryMock);
764-
final transactionMock = TransactionMock(
765-
lookupFun: expectAsync1((keys) {
766-
expect(queueMutationCallNr, 0);
767-
768-
expect(keys, hasLength(2));
769-
expect(keys.first, foobarStablePV.key);
770-
expect(keys.last, foobarPackage.key);
771-
return [null, null];
772-
}),
773-
queueMutationFun: ({List<Model> inserts, deletes}) {
774-
if (queueMutationCallNr == 0) {
775-
validateSuccessfullUpdate(inserts);
776-
} else {
777-
expect(queueMutationCallNr, 1);
778-
expect(inserts, [foobarStablePV]);
779-
validateSuccessfullSortOrderUpdate(
780-
inserts.first as PackageVersion);
781-
}
782-
queueMutationCallNr++;
783-
completion.complete();
784-
},
785-
commitFun: expectAsync0(() {}, count: 2),
786-
queryMock: queryMock);
653+
final rs = backend.repository.upload(Stream.fromIterable(bigTarball));
654+
await expectLater(rs, throwsA(isA<PackageRejectedException>()));
655+
});
787656

788-
final db = DatastoreDBMock(transactionMock: transactionMock);
789-
final repo = GCloudPackageRepository(db, tarballStorage);
790-
registerAuthenticatedUser(hansAuthenticated);
791-
registerAnalyzerClient(AnalyzerClientMock());
792-
registerDartdocClient(DartdocClientMock());
793-
registerAccountBackend(
794-
AccountBackendMock(authenticatedUsers: [hansAuthenticated]));
795-
registerHistoryBackend(HistoryBackendMock());
796-
final emailSenderMock = EmailSenderMock();
797-
registerEmailSender(emailSenderMock);
798-
registerNameTracker(NameTracker(null));
799-
final version = await repo.upload(Stream.fromIterable([tarball]));
800-
expect(version.packageName, foobarPackage.name);
801-
expect(version.versionString, foobarStablePV.version);
802-
expect(emailSenderMock.sentMessages, hasLength(1));
803-
final email = emailSenderMock.sentMessages.single;
804-
expect(email.subject, contains('foobar_pkg'));
805-
expect(email.subject, contains('0.1.1+5'));
806-
expect(email.recipients.join(', '), '[email protected]');
807-
});
657+
testWithServices('successful upload + download', () async {
658+
registerAuthenticatedUser(hansAuthenticated);
659+
List<int> uploaded;
660+
await withTestPackage(
661+
(List<int> tarball) async {
662+
uploaded = tarball;
663+
final version = await backend.repository
664+
.upload(Stream.fromIterable([tarball]));
665+
expect(version.packageName, foobarPackage.name);
666+
expect(version.versionString, '1.2.3');
667+
// TODO: check sent e-mail
668+
},
669+
pubspecContent: generatePubspecYaml(foobarPackage.name, '1.2.3'),
670+
);
671+
final packages = await backend.latestPackages();
672+
expect(packages.first.name, foobarPackage.name);
673+
expect(packages.first.latestVersion, '1.2.3');
674+
675+
final stream =
676+
await backend.repository.download(foobarPackage.name, '1.2.3');
677+
final chunks = await stream.toList();
678+
final bytes = chunks.fold<List<int>>(
679+
<int>[], (buffer, chunk) => buffer..addAll(chunk));
680+
expect(bytes, uploaded);
808681
});
809682
});
810-
}, timeout: Timeout.factor(2));
683+
});
811684
});
812685
}
813-
814-
class TestDelayCompletion {
815-
final int count;
816-
final Function _complete = expectAsync0(() {});
817-
int _got = 0;
818-
819-
TestDelayCompletion({this.count = 1});
820-
821-
void complete() {
822-
_got++;
823-
if (_got == count) _complete();
824-
}
825-
}

0 commit comments

Comments
 (0)