diff --git a/app/lib/package/backend.dart b/app/lib/package/backend.dart index 0555121d64..0e7249428c 100644 --- a/app/lib/package/backend.dart +++ b/app/lib/package/backend.dart @@ -1054,9 +1054,18 @@ class PackageBackend { final pv = await withRetryTransaction(db, (tx) async { _logger.info('Starting datastore transaction.'); - final tuple = (await tx.lookup([newVersion.key, newVersion.packageKey!])); + final tuple = (await tx.lookup([ + newVersion.key, + newVersion.packageKey!, + db.emptyKey.append(ModeratedPackage, id: newVersion.package), + ])); final version = tuple[0] as PackageVersion?; package = tuple[1] as Package?; + final moderatedPackage = tuple[2] as ModeratedPackage?; + + if (moderatedPackage != null) { + throw PackageRejectedException.nameReserved(newVersion.package); + } // If the version already exists, we fail. if (version != null) { diff --git a/app/test/package/upload_test.dart b/app/test/package/upload_test.dart index c2e9e7f136..daec434442 100644 --- a/app/test/package/upload_test.dart +++ b/app/test/package/upload_test.dart @@ -1103,9 +1103,9 @@ void main() { }); }); - group('other limits', () { + group('other rejections', () { testWithProfile( - 'max versions', + 'max version count', testProfile: TestProfile( defaultUser: 'admin@pub.dev', packages: [ @@ -1131,5 +1131,38 @@ void main() { }, timeout: Timeout.factor(1.5), ); + + testWithProfile('moderated package immediately re-published', fn: () async { + final pubspecContent = generatePubspecYaml('abcd_package', '1.0.0'); + final bytes = await packageArchiveBytes(pubspecContent: pubspecContent); + final message = await createPubApiClient(authToken: adminClientToken) + .uploadPackageBytes(bytes); + expect(message.success.message, contains('Successfully uploaded')); + await nameTracker.reloadFromDatastore(); + + await accountBackend.withBearerToken( + siteAdminToken, () => adminBackend.removePackage('abcd_package')); + + // NOTE: do not refresh name tracker and publish again + final rs1 = createPubApiClient(authToken: adminClientToken) + .uploadPackageBytes(bytes); + await expectApiException( + rs1, + status: 400, + code: 'PackageRejected', + message: 'Package name abcd_package is reserved', + ); + + // NOTE: refresh name tracker and publish again + await nameTracker.reloadFromDatastore(); + final rs2 = createPubApiClient(authToken: adminClientToken) + .uploadPackageBytes(bytes); + await expectApiException( + rs2, + status: 400, + code: 'PackageRejected', + message: 'is too similar to a moderated package', + ); + }); }); }