22// for details. All rights reserved. Use of this source code is governed by a
33// BSD-style license that can be found in the LICENSE file.
44
5+ import 'dart:convert' ;
6+ import 'dart:io' ;
57import 'dart:typed_data' ;
68
79import 'package:_pub_shared/data/account_api.dart' ;
@@ -19,6 +21,7 @@ import 'package:pub_dev/search/backend.dart';
1921import 'package:pub_dev/shared/configuration.dart' ;
2022import 'package:pub_dev/shared/datastore.dart' ;
2123import 'package:pub_dev/shared/exceptions.dart' ;
24+ import 'package:pub_dev/shared/versions.dart' ;
2225import 'package:pub_dev/task/backend.dart' ;
2326import 'package:test/test.dart' ;
2427
@@ -141,7 +144,7 @@ void main() {
141144 expect (optionsUpdates.isRetracted, true );
142145 });
143146
144- testWithProfile ('cannot moderated last visible version' , fn: () async {
147+ testWithProfile ('cannot moderate last visible version' , fn: () async {
145148 await _moderate ('oxygen' , '1.2.0' , state: true );
146149 final p1 = await packageBackend.lookupPackage ('oxygen' );
147150 expect (p1! .latestVersion, '1.0.0' );
@@ -186,15 +189,29 @@ void main() {
186189 );
187190 });
188191
189- testWithProfile ('archive file is removed from public bucket' , fn: () async {
192+ testWithProfile ('archive file is removed from public buckets' ,
193+ fn: () async {
190194 Future <Uint8List ?> expectStatusCode (int statusCode,
191195 {String version = '1.0.0' }) async {
192- final publicUri = Uri .parse ('${activeConfiguration .storageBaseUrl }'
193- '/${activeConfiguration .publicPackagesBucketName }'
194- '/packages/oxygen-$version .tar.gz' );
195- final rs1 = await http.get (publicUri);
196- expect (rs1.statusCode, statusCode);
197- return rs1.bodyBytes;
196+ final publicUrls = [
197+ '${activeConfiguration .storageBaseUrl }'
198+ '/${activeConfiguration .publicPackagesBucketName }'
199+ '/packages/oxygen-$version .tar.gz' ,
200+ '${activeConfiguration .storageBaseUrl }'
201+ '/${activeConfiguration .exportedApiBucketName }'
202+ '/latest/api/archives/oxygen-$version .tar.gz' ,
203+ '${activeConfiguration .storageBaseUrl }'
204+ '/${activeConfiguration .exportedApiBucketName }'
205+ '/$runtimeVersion /api/archives/oxygen-$version .tar.gz' ,
206+ ];
207+
208+ final rs = await Future .wait (
209+ publicUrls.map ((url) => http.get (Uri .parse (url))));
210+ for (final r in rs) {
211+ expect (r.statusCode, statusCode);
212+ expect (r.bodyBytes, rs.first.bodyBytes);
213+ }
214+ return rs.first.bodyBytes;
198215 }
199216
200217 final bytes = await expectStatusCode (200 );
@@ -215,6 +232,32 @@ void main() {
215232 expect (restoredBytes, bytes);
216233 });
217234
235+ testWithProfile ('versions file is updated in exported bucket' ,
236+ fn: () async {
237+ Future <void > expectIncluded (String version, bool isIncluded) async {
238+ final prefixes = ['latest' , runtimeVersion];
239+ for (final prefix in prefixes) {
240+ final url = '${activeConfiguration .storageBaseUrl }'
241+ '/${activeConfiguration .exportedApiBucketName }'
242+ '/$prefix /api/packages/oxygen' ;
243+ final rs = await http.get (Uri .parse (url));
244+ expect (rs.statusCode, 200 );
245+ final data = json.decode (utf8.decode (gzip.decode (rs.bodyBytes)))
246+ as Map <String , dynamic >;
247+ final versions = (data['versions' ] as List )
248+ .map ((i) => (i as Map )['version' ])
249+ .toSet ();
250+ expect (versions.contains (version), isIncluded);
251+ }
252+ }
253+
254+ await expectIncluded ('1.0.0' , true );
255+ await _moderate ('oxygen' , '1.0.0' , state: true );
256+ await expectIncluded ('1.0.0' , false );
257+ await _moderate ('oxygen' , '1.0.0' , state: false );
258+ await expectIncluded ('1.0.0' , true );
259+ });
260+
218261 testWithProfile ('search is updated with new version' , fn: () async {
219262 await searchBackend.doCreateAndUpdateSnapshot (
220263 FakeGlobalLockClaim (clock.now ().add (Duration (seconds: 3 ))),
0 commit comments