Merge lp:~cjwatson/launchpad/copy-auto-approve into lp:launchpad

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: no longer in the source branch.
Merged at revision: 15722
Proposed branch: lp:~cjwatson/launchpad/copy-auto-approve
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/copy-allows-queue-admin
Diff against target: 379 lines (+148/-19)
5 files modified
lib/lp/soyuz/interfaces/archive.py (+18/-2)
lib/lp/soyuz/interfaces/packagecopyjob.py (+13/-2)
lib/lp/soyuz/model/archive.py (+6/-4)
lib/lp/soyuz/model/packagecopyjob.py (+26/-11)
lib/lp/soyuz/tests/test_packagecopyjob.py (+85/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/copy-auto-approve
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+117299@code.launchpad.net

Commit message

Add auto_approve parameter to Archive.copyPackage and Archive.copyPackages, allowing users with queue admin to bypass the unapproved status.

Description of the change

== Summary ==

Bug 1006871: in some cases it is cumbersome to have to approve package copies as a separate step, particularly given the delay on processing PackageCopyJobs. There should be a way for queue admins to copy a package and approve the copy in a single step. The bug has some concrete use cases for this.

== Proposed fix ==

Add an auto_approve parameter to Archive.copyPackage and Archive.copyPackages, defaulting to False. When True, copies are automatically accepted even if they would normally have required approval, although only if the requester has appropriate queue admin permissions.

== Pre-implementation notes ==

Iain Lane suggested that this should be explicit rather than (my initial proposal) implicit. On reflection, this is obviously correct. If I run syncpackage to copy a package from Debian, and Ubuntu happens to be frozen at the time, then it should go through UNAPPROVED even though I'm an Ubuntu archive administrator and thus have queue admin permissions. On the other hand, if (acting as an archive admin) I run sru-release to copy a verified stable release update from -proposed to -updates, I'm always going to accept it immediately afterwards so it makes sense to let me skip that extra step.

== Implementation details ==

There's a little duplication in PlainPackageCopyJob._checkPolicies (the calls to canAdministerQueue in both if branches), but if you look closely that's necessary because it has to come after the source override handling.

I arranged to silently ignore auto_approve=True from non-queue-admins because it's harmless to do so and it makes it easier to write tools that work for people both with and without queue admin permissions. (For instance, the kernel team sometimes copy packages from their PPA to the -proposed pocket in the primary archive and ask us to accept them; but since the copy-proposed-kernel tool is often run by archive admins, it would make sense for it to use auto_approve=True.)

== LOC Rationale ==

+129. I have 1833 lines of credit at the moment, and, as mentioned in https://code.launchpad.net/~cjwatson/launchpad/copy-allows-queue-admin/+merge/116731, this is the last known piece needed to remove the copy-package.py script.

== Tests ==

bin/test -vvct test_packagecopyjob

== Demo and Q/A ==

Add auto_approve=True to the Archive.copyPackage calls in sru-release in lp:ubuntu-archive-tools, and use this to copy a package from -proposed to -updates on dogfood (with queue admin permissions). It should be automatically accepted. Without that parameter, the copy should land in the unapproved queue.

== Lint ==

Just a pre-existing false positive due to pocketlint not understanding property setters:

./lib/lp/soyuz/model/archive.py
     346: redefinition of function 'private' from line 342

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

Looks good.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2012-07-30 17:05:29 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2012-07-30 17:05:29 +0000
@@ -1292,12 +1292,17 @@
1292 title=_("Sponsored Person"),1292 title=_("Sponsored Person"),
1293 description=_("The person who is being sponsored for this copy.")),1293 description=_("The person who is being sponsored for this copy.")),
1294 unembargo=Bool(title=_("Unembargo restricted files")),1294 unembargo=Bool(title=_("Unembargo restricted files")),
1295 auto_approve=Bool(
1296 title=_("Automatic approval"),
1297 description=_("Automatically approve this copy (queue admins "
1298 "only)."),
1299 required=False),
1295 )1300 )
1296 @export_write_operation()1301 @export_write_operation()
1297 @operation_for_version('devel')1302 @operation_for_version('devel')
1298 def copyPackage(source_name, version, from_archive, to_pocket,1303 def copyPackage(source_name, version, from_archive, to_pocket,
1299 person, to_series=None, include_binaries=False,1304 person, to_series=None, include_binaries=False,
1300 sponsored=None, unembargo=False):1305 sponsored=None, unembargo=False, auto_approve=False):
1301 """Copy a single named source into this archive.1306 """Copy a single named source into this archive.
13021307
1303 Asynchronously copy a specific version of a named source to the1308 Asynchronously copy a specific version of a named source to the
@@ -1321,6 +1326,9 @@
1321 :param unembargo: if True, allow copying restricted files from a1326 :param unembargo: if True, allow copying restricted files from a
1322 private archive to a public archive, and re-upload them to the1327 private archive to a public archive, and re-upload them to the
1323 public librarian when doing so.1328 public librarian when doing so.
1329 :param auto_approve: if True and the `IPerson` requesting the sync
1330 has queue admin permissions on the target, accept the copy
1331 immediately rather than setting it to unapproved.
13241332
1325 :raises NoSuchSourcePackageName: if the source name is invalid1333 :raises NoSuchSourcePackageName: if the source name is invalid
1326 :raises PocketNotFound: if the pocket name is invalid1334 :raises PocketNotFound: if the pocket name is invalid
@@ -1354,12 +1362,17 @@
1354 title=_("Sponsored Person"),1362 title=_("Sponsored Person"),
1355 description=_("The person who is being sponsored for this copy.")),1363 description=_("The person who is being sponsored for this copy.")),
1356 unembargo=Bool(title=_("Unembargo restricted files")),1364 unembargo=Bool(title=_("Unembargo restricted files")),
1365 auto_approve=Bool(
1366 title=_("Automatic approval"),
1367 description=_("Automatically approve this copy (queue admins "
1368 "only)."),
1369 required=False),
1357 )1370 )
1358 @export_write_operation()1371 @export_write_operation()
1359 @operation_for_version('devel')1372 @operation_for_version('devel')
1360 def copyPackages(source_names, from_archive, to_pocket, person,1373 def copyPackages(source_names, from_archive, to_pocket, person,
1361 to_series=None, from_series=None, include_binaries=False,1374 to_series=None, from_series=None, include_binaries=False,
1362 sponsored=None, unembargo=False):1375 sponsored=None, unembargo=False, auto_approve=False):
1363 """Copy multiple named sources into this archive from another.1376 """Copy multiple named sources into this archive from another.
13641377
1365 Asynchronously copy the most recent PUBLISHED versions of the named1378 Asynchronously copy the most recent PUBLISHED versions of the named
@@ -1386,6 +1399,9 @@
1386 :param unembargo: if True, allow copying restricted files from a1399 :param unembargo: if True, allow copying restricted files from a
1387 private archive to a public archive, and re-upload them to the1400 private archive to a public archive, and re-upload them to the
1388 public librarian when doing so.1401 public librarian when doing so.
1402 :param auto_approve: if True and the `IPerson` requesting the sync
1403 has queue admin permissions on the target, accept the copies
1404 immediately rather than setting it to unapproved.
13891405
1390 :raises NoSuchSourcePackageName: if the source name is invalid1406 :raises NoSuchSourcePackageName: if the source name is invalid
1391 :raises PocketNotFound: if the pocket name is invalid1407 :raises PocketNotFound: if the pocket name is invalid
13921408
=== modified file 'lib/lp/soyuz/interfaces/packagecopyjob.py'
--- lib/lp/soyuz/interfaces/packagecopyjob.py 2012-07-12 08:32:15 +0000
+++ lib/lp/soyuz/interfaces/packagecopyjob.py 2012-07-30 17:05:29 +0000
@@ -128,7 +128,7 @@
128 target_archive, target_distroseries, target_pocket,128 target_archive, target_distroseries, target_pocket,
129 include_binaries=False, package_version=None,129 include_binaries=False, package_version=None,
130 copy_policy=PackageCopyPolicy.INSECURE, requester=None,130 copy_policy=PackageCopyPolicy.INSECURE, requester=None,
131 sponsored=None, unembargo=False):131 sponsored=None, unembargo=False, auto_approve=False):
132 """Create a new `IPlainPackageCopyJob`.132 """Create a new `IPlainPackageCopyJob`.
133133
134 :param package_name: The name of the source package to copy.134 :param package_name: The name of the source package to copy.
@@ -147,11 +147,15 @@
147 :param sponsored: The user who is being sponsored to make the copy.147 :param sponsored: The user who is being sponsored to make the copy.
148 The person who is making this request then becomes the sponsor.148 The person who is making this request then becomes the sponsor.
149 :param unembargo: See `do_copy`.149 :param unembargo: See `do_copy`.
150 :param auto_approve: if True and the user requesting the sync has
151 queue admin permissions on the target, accept the copy
152 immediately rather than setting it to unapproved.
150 """153 """
151154
152 def createMultiple(target_distroseries, copy_tasks, requester,155 def createMultiple(target_distroseries, copy_tasks, requester,
153 copy_policy=PackageCopyPolicy.INSECURE,156 copy_policy=PackageCopyPolicy.INSECURE,
154 include_binaries=False, unembargo=False):157 include_binaries=False, unembargo=False,
158 auto_approve=False):
155 """Create multiple new `IPlainPackageCopyJob`s at once.159 """Create multiple new `IPlainPackageCopyJob`s at once.
156160
157 :param target_distroseries: The `IDistroSeries` to which to copy the161 :param target_distroseries: The `IDistroSeries` to which to copy the
@@ -164,6 +168,9 @@
164 :param include_binaries: As in `do_copy`.168 :param include_binaries: As in `do_copy`.
165 :param unembargo: As in `do_copy`.169 :param unembargo: As in `do_copy`.
166 :return: An iterable of `PackageCopyJob` ids.170 :return: An iterable of `PackageCopyJob` ids.
171 :param auto_approve: if True and the user requesting the sync has
172 queue admin permissions on the target, accept the copy
173 immediately rather than setting it to unapproved.
167 """174 """
168175
169 def getActiveJobs(target_archive):176 def getActiveJobs(target_archive):
@@ -216,6 +223,10 @@
216 title=_("Unembargo restricted files"),223 title=_("Unembargo restricted files"),
217 required=False, readonly=True)224 required=False, readonly=True)
218225
226 auto_approve = Bool(
227 title=_("Automatic approval"),
228 required=False, readonly=True)
229
219 def addSourceOverride(override):230 def addSourceOverride(override):
220 """Add an `ISourceOverride` to the metadata."""231 """Add an `ISourceOverride` to the metadata."""
221232
222233
=== modified file 'lib/lp/soyuz/model/archive.py'
--- lib/lp/soyuz/model/archive.py 2012-07-30 17:05:29 +0000
+++ lib/lp/soyuz/model/archive.py 2012-07-30 17:05:29 +0000
@@ -1638,7 +1638,7 @@
16381638
1639 def copyPackage(self, source_name, version, from_archive, to_pocket,1639 def copyPackage(self, source_name, version, from_archive, to_pocket,
1640 person, to_series=None, include_binaries=False,1640 person, to_series=None, include_binaries=False,
1641 sponsored=None, unembargo=False):1641 sponsored=None, unembargo=False, auto_approve=False):
1642 """See `IArchive`."""1642 """See `IArchive`."""
1643 self._checkCopyPackageFeatureFlags()1643 self._checkCopyPackageFeatureFlags()
16441644
@@ -1660,11 +1660,13 @@
1660 target_pocket=pocket,1660 target_pocket=pocket,
1661 package_version=version, include_binaries=include_binaries,1661 package_version=version, include_binaries=include_binaries,
1662 copy_policy=PackageCopyPolicy.INSECURE, requester=person,1662 copy_policy=PackageCopyPolicy.INSECURE, requester=person,
1663 sponsored=sponsored, unembargo=unembargo)1663 sponsored=sponsored, unembargo=unembargo,
1664 auto_approve=auto_approve)
16641665
1665 def copyPackages(self, source_names, from_archive, to_pocket,1666 def copyPackages(self, source_names, from_archive, to_pocket,
1666 person, to_series=None, from_series=None,1667 person, to_series=None, from_series=None,
1667 include_binaries=None, sponsored=None, unembargo=False):1668 include_binaries=None, sponsored=None, unembargo=False,
1669 auto_approve=False):
1668 """See `IArchive`."""1670 """See `IArchive`."""
1669 self._checkCopyPackageFeatureFlags()1671 self._checkCopyPackageFeatureFlags()
16701672
@@ -1696,7 +1698,7 @@
1696 job_source.createMultiple(1698 job_source.createMultiple(
1697 copy_tasks, person, copy_policy=PackageCopyPolicy.MASS_SYNC,1699 copy_tasks, person, copy_policy=PackageCopyPolicy.MASS_SYNC,
1698 include_binaries=include_binaries, sponsored=sponsored,1700 include_binaries=include_binaries, sponsored=sponsored,
1699 unembargo=unembargo)1701 unembargo=unembargo, auto_approve=auto_approve)
17001702
1701 def _collectLatestPublishedSources(self, from_archive, from_series,1703 def _collectLatestPublishedSources(self, from_archive, from_series,
1702 source_names):1704 source_names):
17031705
=== modified file 'lib/lp/soyuz/model/packagecopyjob.py'
--- lib/lp/soyuz/model/packagecopyjob.py 2012-07-12 08:32:15 +0000
+++ lib/lp/soyuz/model/packagecopyjob.py 2012-07-30 17:05:29 +0000
@@ -256,7 +256,8 @@
256256
257 @classmethod257 @classmethod
258 def _makeMetadata(cls, target_pocket, package_version,258 def _makeMetadata(cls, target_pocket, package_version,
259 include_binaries, sponsored=None, unembargo=False):259 include_binaries, sponsored=None, unembargo=False,
260 auto_approve=False):
260 """Produce a metadata dict for this job."""261 """Produce a metadata dict for this job."""
261 if sponsored:262 if sponsored:
262 sponsored_name = sponsored.name263 sponsored_name = sponsored.name
@@ -268,6 +269,7 @@
268 'include_binaries': bool(include_binaries),269 'include_binaries': bool(include_binaries),
269 'sponsored': sponsored_name,270 'sponsored': sponsored_name,
270 'unembargo': unembargo,271 'unembargo': unembargo,
272 'auto_approve': auto_approve,
271 }273 }
272274
273 @classmethod275 @classmethod
@@ -275,13 +277,13 @@
275 target_archive, target_distroseries, target_pocket,277 target_archive, target_distroseries, target_pocket,
276 include_binaries=False, package_version=None,278 include_binaries=False, package_version=None,
277 copy_policy=PackageCopyPolicy.INSECURE, requester=None,279 copy_policy=PackageCopyPolicy.INSECURE, requester=None,
278 sponsored=None, unembargo=False):280 sponsored=None, unembargo=False, auto_approve=False):
279 """See `IPlainPackageCopyJobSource`."""281 """See `IPlainPackageCopyJobSource`."""
280 assert package_version is not None, "No package version specified."282 assert package_version is not None, "No package version specified."
281 assert requester is not None, "No requester specified."283 assert requester is not None, "No requester specified."
282 metadata = cls._makeMetadata(284 metadata = cls._makeMetadata(
283 target_pocket, package_version, include_binaries, sponsored,285 target_pocket, package_version, include_binaries, sponsored,
284 unembargo)286 unembargo, auto_approve)
285 job = PackageCopyJob(287 job = PackageCopyJob(
286 job_type=cls.class_job_type,288 job_type=cls.class_job_type,
287 source_archive=source_archive,289 source_archive=source_archive,
@@ -298,7 +300,8 @@
298300
299 @classmethod301 @classmethod
300 def _composeJobInsertionTuple(cls, copy_policy, include_binaries, job_id,302 def _composeJobInsertionTuple(cls, copy_policy, include_binaries, job_id,
301 copy_task, sponsored, unembargo):303 copy_task, sponsored, unembargo,
304 auto_approve):
302 """Create an SQL fragment for inserting a job into the database.305 """Create an SQL fragment for inserting a job into the database.
303306
304 :return: A string representing an SQL tuple containing initializers307 :return: A string representing an SQL tuple containing initializers
@@ -315,7 +318,7 @@
315 ) = copy_task318 ) = copy_task
316 metadata = cls._makeMetadata(319 metadata = cls._makeMetadata(
317 target_pocket, package_version, include_binaries, sponsored,320 target_pocket, package_version, include_binaries, sponsored,
318 unembargo)321 unembargo, auto_approve)
319 data = (322 data = (
320 cls.class_job_type, target_distroseries, copy_policy,323 cls.class_job_type, target_distroseries, copy_policy,
321 source_archive, target_archive, package_name, job_id,324 source_archive, target_archive, package_name, job_id,
@@ -326,14 +329,14 @@
326 def createMultiple(cls, copy_tasks, requester,329 def createMultiple(cls, copy_tasks, requester,
327 copy_policy=PackageCopyPolicy.INSECURE,330 copy_policy=PackageCopyPolicy.INSECURE,
328 include_binaries=False, sponsored=None,331 include_binaries=False, sponsored=None,
329 unembargo=False):332 unembargo=False, auto_approve=False):
330 """See `IPlainPackageCopyJobSource`."""333 """See `IPlainPackageCopyJobSource`."""
331 store = IMasterStore(Job)334 store = IMasterStore(Job)
332 job_ids = Job.createMultiple(store, len(copy_tasks), requester)335 job_ids = Job.createMultiple(store, len(copy_tasks), requester)
333 job_contents = [336 job_contents = [
334 cls._composeJobInsertionTuple(337 cls._composeJobInsertionTuple(
335 copy_policy, include_binaries, job_id, task, sponsored,338 copy_policy, include_binaries, job_id, task, sponsored,
336 unembargo)339 unembargo, auto_approve)
337 for job_id, task in zip(job_ids, copy_tasks)]340 for job_id, task in zip(job_ids, copy_tasks)]
338 return bulk.create(341 return bulk.create(
339 (PackageCopyJob.job_type, PackageCopyJob.target_distroseries,342 (PackageCopyJob.job_type, PackageCopyJob.target_distroseries,
@@ -415,6 +418,10 @@
415 def unembargo(self):418 def unembargo(self):
416 return self.metadata.get('unembargo', False)419 return self.metadata.get('unembargo', False)
417420
421 @property
422 def auto_approve(self):
423 return self.metadata.get('auto_approve', False)
424
418 def _createPackageUpload(self, unapproved=False):425 def _createPackageUpload(self, unapproved=False):
419 pu = self.target_distroseries.createQueueEntry(426 pu = self.target_distroseries.createQueueEntry(
420 pocket=self.target_pocket, archive=self.target_archive,427 pocket=self.target_pocket, archive=self.target_archive,
@@ -452,7 +459,8 @@
452459
453 return SourceOverride(source_package_name, component, section)460 return SourceOverride(source_package_name, component, section)
454461
455 def _checkPolicies(self, source_name, source_component=None):462 def _checkPolicies(self, source_name, source_component=None,
463 auto_approve=False):
456 # This helper will only return if it's safe to carry on with the464 # This helper will only return if it's safe to carry on with the
457 # copy, otherwise it raises SuspendJobException to tell the job465 # copy, otherwise it raises SuspendJobException to tell the job
458 # runner to suspend the job.466 # runner to suspend the job.
@@ -470,8 +478,11 @@
470 self.target_archive, self.target_distroseries,478 self.target_archive, self.target_distroseries,
471 self.target_pocket, [source_name], source_component)479 self.target_pocket, [source_name], source_component)
472 self.addSourceOverride(defaults[0])480 self.addSourceOverride(defaults[0])
481 if auto_approve:
482 auto_approve = self.target_archive.canAdministerQueue(
483 self.requester, self.getSourceOverride().component)
473484
474 approve_new = copy_policy.autoApproveNew(485 approve_new = auto_approve or copy_policy.autoApproveNew(
475 self.target_archive, self.target_distroseries,486 self.target_archive, self.target_distroseries,
476 self.target_pocket)487 self.target_pocket)
477488
@@ -483,10 +494,13 @@
483 else:494 else:
484 # Put the existing override in the metadata.495 # Put the existing override in the metadata.
485 self.addSourceOverride(ancestry[0])496 self.addSourceOverride(ancestry[0])
497 if auto_approve:
498 auto_approve = self.target_archive.canAdministerQueue(
499 self.requester, self.getSourceOverride().component)
486500
487 # The package is not new (it has ancestry) so check the copy501 # The package is not new (it has ancestry) so check the copy
488 # policy for existing packages.502 # policy for existing packages.
489 approve_existing = copy_policy.autoApprove(503 approve_existing = auto_approve or copy_policy.autoApprove(
490 self.target_archive, self.target_distroseries, self.target_pocket)504 self.target_archive, self.target_distroseries, self.target_pocket)
491 if not approve_existing:505 if not approve_existing:
492 self._createPackageUpload(unapproved=True)506 self._createPackageUpload(unapproved=True)
@@ -565,7 +579,8 @@
565 [self.context.id]).any()579 [self.context.id]).any()
566 if pu is None:580 if pu is None:
567 self._checkPolicies(581 self._checkPolicies(
568 source_name, source_package.sourcepackagerelease.component)582 source_name, source_package.sourcepackagerelease.component,
583 self.auto_approve)
569584
570 # The package is free to go right in, so just copy it now.585 # The package is free to go right in, so just copy it now.
571 ancestry = self.target_archive.getPublishedSources(586 ancestry = self.target_archive.getPublishedSources(
572587
=== modified file 'lib/lp/soyuz/tests/test_packagecopyjob.py'
--- lib/lp/soyuz/tests/test_packagecopyjob.py 2012-07-25 18:56:53 +0000
+++ lib/lp/soyuz/tests/test_packagecopyjob.py 2012-07-30 17:05:29 +0000
@@ -925,6 +925,91 @@
925 # the target archive.925 # the target archive.
926 self.assertEqual('main', pcj.metadata['component_override'])926 self.assertEqual('main', pcj.metadata['component_override'])
927927
928 def createAutoApproveEnvironment(self, create_ancestry, component_names):
929 """Create an environment for testing the auto_approve flag."""
930 if create_ancestry:
931 self.distroseries.status = SeriesStatus.FROZEN
932 target_archive = self.factory.makeArchive(
933 self.distroseries.distribution, purpose=ArchivePurpose.PRIMARY)
934 source_archive = self.factory.makeArchive()
935 requester = self.factory.makePerson()
936 with person_logged_in(target_archive.owner):
937 for component_name in component_names:
938 target_archive.newQueueAdmin(requester, component_name)
939 spph = self.publisher.getPubSource(
940 distroseries=self.distroseries,
941 status=PackagePublishingStatus.PUBLISHED, archive=source_archive)
942 spr = spph.sourcepackagerelease
943 if create_ancestry:
944 self.publisher.getPubSource(
945 distroseries=self.distroseries, sourcename=spr.name,
946 version="%s~" % spr.version,
947 status=PackagePublishingStatus.PUBLISHED,
948 archive=target_archive)
949 return target_archive, source_archive, requester, spph, spr
950
951 def test_auto_approve(self):
952 # The auto_approve flag causes the job to be processed immediately,
953 # even if it would normally have required manual approval.
954 (target_archive, source_archive, requester,
955 spph, spr) = self.createAutoApproveEnvironment(True, ["main"])
956 job = self.createCopyJobForSPPH(
957 spph, source_archive, target_archive, requester=requester,
958 auto_approve=True)
959 self.runJob(job)
960 self.assertEqual(JobStatus.COMPLETED, job.status)
961 new_publication = target_archive.getPublishedSources(
962 name=spr.name, version=spr.version, exact_match=True).one()
963 self.assertEqual(target_archive, new_publication.archive)
964
965 def test_auto_approve_non_queue_admin(self):
966 # The auto_approve flag is ignored for people without queue admin
967 # permissions.
968 (target_archive, source_archive, requester,
969 spph, spr) = self.createAutoApproveEnvironment(True, [])
970 job = self.createCopyJobForSPPH(
971 spph, source_archive, target_archive, requester=requester,
972 auto_approve=True)
973 self.runJob(job)
974 self.assertEqual(JobStatus.SUSPENDED, job.status)
975
976 def test_auto_approve_new(self):
977 # The auto_approve flag causes copies to bypass the NEW queue.
978 (target_archive, source_archive, requester,
979 spph, spr) = self.createAutoApproveEnvironment(False, ["universe"])
980
981 # Without auto_approve, this job would be suspended and the upload
982 # moved to the NEW queue.
983 job = self.createCopyJobForSPPH(
984 spph, source_archive, target_archive, requester=requester)
985 self.runJob(job)
986 self.assertEqual(JobStatus.SUSPENDED, job.status)
987 switch_dbuser("launchpad_main")
988 pu = getUtility(IPackageUploadSet).getByPackageCopyJobIDs(
989 [removeSecurityProxy(job).context.id]).one()
990 self.assertEqual(PackageUploadStatus.NEW, pu.status)
991
992 # With auto_approve, the job completes immediately.
993 job = self.createCopyJobForSPPH(
994 spph, source_archive, target_archive, requester=requester,
995 auto_approve=True)
996 self.runJob(job)
997 self.assertEqual(JobStatus.COMPLETED, job.status)
998 new_publication = target_archive.getPublishedSources(
999 name=spr.name, version=spr.version, exact_match=True).one()
1000 self.assertEqual(target_archive, new_publication.archive)
1001
1002 def test_auto_approve_new_non_queue_admin(self):
1003 # For NEW packages, the auto_approve flag is ignored for people
1004 # without queue admin permissions.
1005 (target_archive, source_archive, requester,
1006 spph, spr) = self.createAutoApproveEnvironment(False, [])
1007 job = self.createCopyJobForSPPH(
1008 spph, source_archive, target_archive, requester=requester,
1009 auto_approve=True)
1010 self.runJob(job)
1011 self.assertEqual(JobStatus.SUSPENDED, job.status)
1012
928 def test_copying_after_job_released(self):1013 def test_copying_after_job_released(self):
929 # The first pass of the job may have created a PackageUpload and1014 # The first pass of the job may have created a PackageUpload and
930 # suspended the job. Here we test the second run to make sure1015 # suspended the job. Here we test the second run to make sure