def test_retry_delay(self):
     # The job is retried every minute, unless it just made one of its
     # first four attempts to poll the status endpoint, in which case the
     # delays are 15/15/30/30 seconds.
     self.useFixture(FakeLogger())
     snapbuild = self.makeSnapBuild()
     job = SnapStoreUploadJob.create(snapbuild)
     client = FakeSnapStoreClient()
     client.upload.failure = UploadFailedResponse("Proxy error",
                                                  can_retry=True)
     self.useFixture(ZopeUtilityFixture(client, ISnapStoreClient))
     with dbuser(config.ISnapStoreUploadJobSource.dbuser):
         JobRunner([job]).runAll()
     self.assertNotIn("status_url", job.metadata)
     self.assertEqual(timedelta(seconds=60), job.retry_delay)
     job.scheduled_start = None
     client.upload.failure = None
     client.upload.result = self.status_url
     client.checkStatus.failure = UploadNotScannedYetResponse()
     for expected_delay in (15, 15, 30, 30, 60):
         with dbuser(config.ISnapStoreUploadJobSource.dbuser):
             JobRunner([job]).runAll()
         self.assertIn("status_url", job.snapbuild.store_upload_metadata)
         self.assertIsNone(job.store_url)
         self.assertEqual(timedelta(seconds=expected_delay),
                          job.retry_delay)
         job.scheduled_start = None
     client.checkStatus.failure = None
     client.checkStatus.result = (self.store_url, 1)
     with dbuser(config.ISnapStoreUploadJobSource.dbuser):
         JobRunner([job]).runAll()
     self.assertEqual(self.store_url, job.store_url)
     self.assertIsNone(job.error_message)
     self.assertEqual([], pop_notifications())
     self.assertEqual(JobStatus.COMPLETED, job.job.status)
Example #2
0
    def test_runAll_mails_user_errors(self):
        """User errors should be mailed out without oopsing.

        User errors are identified by the RunnableJob.user_error_types
        attribute.  They do not cause an oops to be recorded, and their
        error messages are mailed to interested parties verbatim.
        """
        job_1, job_2 = self.makeTwoJobs()

        class ExampleError(Exception):
            pass

        def raiseError():
            raise ExampleError('Fake exception.  Foobar, I say!')

        job_1.run = raiseError
        job_1.user_error_types = (ExampleError, )
        job_1.error_recipients = ['*****@*****.**']
        runner = JobRunner([job_1, job_2])
        runner.runAll()
        self.assertEqual([], self.oopses)
        notifications = pop_notifications()
        self.assertEqual(1, len(notifications))
        body = notifications[0].get_payload(decode=True)
        self.assertEqual(
            'Launchpad encountered an error during the following operation:'
            ' appending a string to a list.  Fake exception.  Foobar, I say!',
            body)
        self.assertEqual('Launchpad error while appending a string to a list',
                         notifications[0]['subject'])
    def test_runAll_mails_user_errors(self):
        """User errors should be mailed out without oopsing.

        User errors are identified by the RunnableJob.user_error_types
        attribute.  They do not cause an oops to be recorded, and their
        error messages are mailed to interested parties verbatim.
        """
        job_1, job_2 = self.makeTwoJobs()

        class ExampleError(Exception):
            pass

        def raiseError():
            raise ExampleError('Fake exception.  Foobar, I say!')
        job_1.run = raiseError
        job_1.user_error_types = (ExampleError,)
        job_1.error_recipients = ['*****@*****.**']
        runner = JobRunner([job_1, job_2])
        runner.runAll()
        self.assertEqual([], self.oopses)
        notifications = pop_notifications()
        self.assertEqual(1, len(notifications))
        body = notifications[0].get_payload(decode=True)
        self.assertEqual(
            'Launchpad encountered an error during the following operation:'
            ' appending a string to a list.  Fake exception.  Foobar, I say!',
            body)
        self.assertEqual(
            'Launchpad error while appending a string to a list',
            notifications[0]['subject'])
Example #4
0
 def test_runJob(self):
     """Ensure status is set to completed when a job runs to completion."""
     job_1, job_2 = self.makeTwoJobs()
     runner = JobRunner(job_1)
     runner.runJob(job_1, None)
     self.assertEqual(JobStatus.COMPLETED, job_1.job.status)
     self.assertEqual([job_1], runner.completed_jobs)
 def test_runJob(self):
     """Ensure status is set to completed when a job runs to completion."""
     job_1, job_2 = self.makeTwoJobs()
     runner = JobRunner(job_1)
     runner.runJob(job_1, None)
     self.assertEqual(JobStatus.COMPLETED, job_1.job.status)
     self.assertEqual([job_1], runner.completed_jobs)
Example #6
0
 def test_run_all(self):
     """The job can be run under the JobRunner successfully."""
     job = make_runnable_incremental_diff_job(self)
     with dbuser("merge-proposal-jobs"):
         runner = JobRunner([job])
         runner.runAll()
     self.assertEqual([job], runner.completed_jobs)
Example #7
0
 def test_runJob_raising_retry_error(self):
     """If a job raises a retry_error, it should be re-queued."""
     job = RaisingRetryJob('completion')
     runner = JobRunner([job])
     with self.expectedLog('Scheduling retry due to RetryError'):
         runner.runJob(job, None)
     self.assertEqual(JobStatus.WAITING, job.status)
     self.assertNotIn(job, runner.completed_jobs)
     self.assertIn(job, runner.incomplete_jobs)
 def test_runJob_raising_retry_error(self):
     """If a job raises a retry_error, it should be re-queued."""
     job = RaisingRetryJob('completion')
     runner = JobRunner([job])
     with self.expectedLog('Scheduling retry due to RetryError'):
         runner.runJob(job, None)
     self.assertEqual(JobStatus.WAITING, job.status)
     self.assertNotIn(job, runner.completed_jobs)
     self.assertIn(job, runner.incomplete_jobs)
    def test_runJobHandleErrors_oops_generated_user_notify_fails(self):
        """A second oops is logged if the notification of the oops fails.

        In this test case the error is a user expected error, so the
        notifyUserError is called, and in this case the notify raises too.
        """
        job = RaisingJobRaisingNotifyUserError('boom')
        runner = JobRunner([job])
        runner.runJobHandleError(job)
        self.assertEqual(1, len(self.oopses))
Example #10
0
 def test_runJobHandleErrors_oops_timeline_detail_filter(self):
     """A job can choose to filter oops timeline details."""
     job = RaisingJobTimelineMessage('boom')
     job.timeline_detail_filter = lambda _, detail: '<redacted>'
     flush_database_updates()
     runner = JobRunner([job])
     runner.runJobHandleError(job)
     self.assertEqual(1, len(self.oopses))
     actions = [action[2:4] for action in self.oopses[0]['timeline']]
     self.assertIn(('job', '<redacted>'), actions)
Example #11
0
    def test_runJob_with_SuspendJobException(self):
        # A job that raises SuspendJobError should end up suspended.
        job = NullJob('suspended')
        job.run = FakeMethod(failure=SuspendJobException())
        runner = JobRunner([job])
        runner.runJob(job, None)

        self.assertEqual(JobStatus.SUSPENDED, job.status)
        self.assertNotIn(job, runner.completed_jobs)
        self.assertIn(job, runner.incomplete_jobs)
Example #12
0
    def test_runJobHandleErrors_oops_generated_user_notify_fails(self):
        """A second oops is logged if the notification of the oops fails.

        In this test case the error is a user expected error, so the
        notifyUserError is called, and in this case the notify raises too.
        """
        job = RaisingJobRaisingNotifyUserError('boom')
        runner = JobRunner([job])
        runner.runJobHandleError(job)
        self.assertEqual(1, len(self.oopses))
Example #13
0
    def test_runJob_with_SuspendJobException(self):
        # A job that raises SuspendJobError should end up suspended.
        job = NullJob('suspended')
        job.run = FakeMethod(failure=SuspendJobException())
        runner = JobRunner([job])
        runner.runJob(job, None)

        self.assertEqual(JobStatus.SUSPENDED, job.status)
        self.assertNotIn(job, runner.completed_jobs)
        self.assertIn(job, runner.incomplete_jobs)
Example #14
0
 def test_runAll_skips_lease_failures(self):
     """Ensure runAll skips jobs whose leases can't be acquired."""
     job_1, job_2 = self.makeTwoJobs()
     job_2.job.acquireLease()
     runner = JobRunner([job_1, job_2])
     runner.runAll()
     self.assertEqual(JobStatus.COMPLETED, job_1.job.status)
     self.assertEqual(JobStatus.WAITING, job_2.job.status)
     self.assertEqual([job_1], runner.completed_jobs)
     self.assertEqual([job_2], runner.incomplete_jobs)
     self.assertEqual([], self.oopses)
Example #15
0
 def test_runJob_exceeding_max_retries(self):
     """If a job exceeds maximum retries, it should raise normally."""
     job = RaisingRetryJob('completion')
     JobRunner([job]).runJob(job, None)
     self.assertEqual(JobStatus.WAITING, job.status)
     runner = JobRunner([job])
     with ExpectedException(RetryError, ''):
         runner.runJob(job, None)
     self.assertEqual(JobStatus.FAILED, job.status)
     self.assertNotIn(job, runner.completed_jobs)
     self.assertIn(job, runner.incomplete_jobs)
Example #16
0
 def test_runJob_exceeding_max_retries(self):
     """If a job exceeds maximum retries, it should raise normally."""
     job = RaisingRetryJob('completion')
     JobRunner([job]).runJob(job, None)
     self.assertEqual(JobStatus.WAITING, job.status)
     runner = JobRunner([job])
     with ExpectedException(RetryError, ''):
         runner.runJob(job, None)
     self.assertEqual(JobStatus.FAILED, job.status)
     self.assertNotIn(job, runner.completed_jobs)
     self.assertIn(job, runner.incomplete_jobs)
Example #17
0
 def test_runAll_skips_lease_failures(self):
     """Ensure runAll skips jobs whose leases can't be acquired."""
     job_1, job_2 = self.makeTwoJobs()
     job_2.job.acquireLease()
     runner = JobRunner([job_1, job_2])
     runner.runAll()
     self.assertEqual(JobStatus.COMPLETED, job_1.job.status)
     self.assertEqual(JobStatus.WAITING, job_2.job.status)
     self.assertEqual([job_1], runner.completed_jobs)
     self.assertEqual([job_2], runner.incomplete_jobs)
     self.assertEqual([], self.oopses)
Example #18
0
 def test_empty_branch(self):
     self.makeBzrSync(self.db_branch).syncBranchAndClose()
     JobRunner.fromReady(getUtility(IRevisionMailJobSource)).runAll()
     self.assertEqual(len(stub.test_emails), 1)
     [initial_email] = stub.test_emails
     expected = 'First scan of the branch detected 0 revisions'
     message = email.message_from_string(initial_email[2])
     email_body = message.get_payload()
     self.assertTextIn(expected, email_body)
     self.assertEmailHeadersEqual(
         '[Branch %s] 0 revisions' % self.db_branch.unique_name,
         message['Subject'])
Example #19
0
 def test_runAll(self):
     """Ensure runAll works in the normal case."""
     job_1, job_2 = self.makeTwoJobs()
     runner = JobRunner([job_1, job_2])
     runner.runAll()
     self.assertEqual(JobStatus.COMPLETED, job_1.job.status)
     self.assertEqual(JobStatus.COMPLETED, job_2.job.status)
     msg1 = NullJob.JOB_COMPLETIONS.pop()
     msg2 = NullJob.JOB_COMPLETIONS.pop()
     self.assertEqual(msg1, "job 2")
     self.assertEqual(msg2, "job 1")
     self.assertEqual([job_1, job_2], runner.completed_jobs)
Example #20
0
 def test_runAll(self):
     """Ensure runAll works in the normal case."""
     job_1, job_2 = self.makeTwoJobs()
     runner = JobRunner([job_1, job_2])
     runner.runAll()
     self.assertEqual(JobStatus.COMPLETED, job_1.job.status)
     self.assertEqual(JobStatus.COMPLETED, job_2.job.status)
     msg1 = NullJob.JOB_COMPLETIONS.pop()
     msg2 = NullJob.JOB_COMPLETIONS.pop()
     self.assertEqual(msg1, "job 2")
     self.assertEqual(msg2, "job 1")
     self.assertEqual([job_1, job_2], runner.completed_jobs)
Example #21
0
 def test_runJobHandleErrors_oops_timeline(self):
     """The oops timeline only covers the job itself."""
     timeline = get_request_timeline(get_current_browser_request())
     timeline.start('test', 'sentinel').finish()
     job = RaisingJobTimelineMessage('boom')
     flush_database_updates()
     runner = JobRunner([job])
     runner.runJobHandleError(job)
     self.assertEqual(1, len(self.oopses))
     actions = [action[2:4] for action in self.oopses[0]['timeline']]
     self.assertIn(('job', 'boom'), actions)
     self.assertNotIn(('test', 'sentinel'), actions)
Example #22
0
 def test_empty_branch(self):
     self.makeBzrSync(self.db_branch).syncBranchAndClose()
     JobRunner.fromReady(getUtility(IRevisionMailJobSource)).runAll()
     self.assertEqual(len(stub.test_emails), 1)
     [initial_email] = stub.test_emails
     expected = 'First scan of the branch detected 0 revisions'
     message = email.message_from_string(initial_email[2])
     email_body = message.get_payload()
     self.assertIn(expected, email_body)
     self.assertEmailHeadersEqual(
         '[Branch %s] 0 revisions' % self.db_branch.unique_name,
         message['Subject'])
Example #23
0
 def runJob(self, job):
     with dbuser("webhookrunner"):
         runner = JobRunner([job])
         runner.runAll()
     job.lease_expires = None
     if len(runner.completed_jobs) == 1 and not runner.incomplete_jobs:
         return True
     if len(runner.incomplete_jobs) == 1 and not runner.completed_jobs:
         return False
     if not runner.incomplete_jobs and not runner.completed_jobs:
         return None
     raise Exception("Unexpected jobs.")
Example #24
0
    def test_runAll_requires_IRunnable(self):
        """Supplied classes must implement IRunnableJob.

        If they don't, we get a TypeError.  If they do, then we get an
        AttributeError, because we don't actually implement the interface.
        """
        runner = JobRunner([object()])
        self.assertRaises(TypeError, runner.runAll)

        class Runnable:
            implements(IRunnableJob)

        runner = JobRunner([Runnable()])
        self.assertRaises(AttributeError, runner.runAll)
Example #25
0
 def test_import_revision(self):
     self.commitRevision()
     self.makeBzrSync(self.db_branch).syncBranchAndClose()
     JobRunner.fromReady(getUtility(IRevisionMailJobSource)).runAll()
     self.assertEqual(len(stub.test_emails), 1)
     [initial_email] = stub.test_emails
     expected = ('First scan of the branch detected 1 revision'
                 ' in the revision history of the=\n branch.')
     message = email.message_from_string(initial_email[2])
     email_body = message.get_payload()
     self.assertTextIn(expected, email_body)
     self.assertEmailHeadersEqual(
         '[Branch %s] 1 revision' % self.db_branch.unique_name,
         message['Subject'])
Example #26
0
    def test_oops_messages_used_when_handling(self):
        """Oops messages should appear even when exceptions are handled."""
        job_1, job_2 = self.makeTwoJobs()

        def handleError():
            reporter = errorlog.globalErrorUtility
            try:
                raise ValueError('Fake exception.  Foobar, I say!')
            except ValueError:
                reporter.raising(sys.exc_info())
        job_1.run = handleError
        runner = JobRunner([job_1, job_2])
        runner.runAll()
        oops = self.oopses[-1]
        self.assertEqual(["{'foo': 'bar'}"], oops['req_vars'].values())
Example #27
0
    def test_runAll_aborts_transaction_on_error(self):
        """runAll should abort the transaction on oops."""
        class DBAlterJob(NullJob):
            def __init__(self):
                super(DBAlterJob, self).__init__('')

            def run(self):
                self.job.log = 'hello'
                raise ValueError

        job = DBAlterJob()
        runner = JobRunner([job])
        runner.runAll()
        # If the transaction was committed, job.log == 'hello'.  If it was
        # aborted, it is None.
        self.assertIs(None, job.job.log)
Example #28
0
    def test_oops_messages_used_when_handling(self):
        """Oops messages should appear even when exceptions are handled."""
        job_1, job_2 = self.makeTwoJobs()

        def handleError():
            reporter = errorlog.globalErrorUtility
            try:
                raise ValueError('Fake exception.  Foobar, I say!')
            except ValueError:
                reporter.raising(sys.exc_info())

        job_1.run = handleError
        runner = JobRunner([job_1, job_2])
        runner.runAll()
        oops = self.oopses[-1]
        self.assertEqual(["{'foo': 'bar'}"], oops['req_vars'].values())
Example #29
0
 def test_runJob_raising_retry_error(self):
     """If a job raises a retry_error, it should be re-queued."""
     job = RaisingRetryJob('completion')
     runner = JobRunner([job])
     self.assertIs(None, job.scheduled_start)
     with self.expectedLog('Scheduling retry due to RetryError'):
         runner.runJob(job, None)
     self.assertEqual(JobStatus.WAITING, job.status)
     expected_delay = datetime.now(UTC) + timedelta(minutes=10)
     self.assertThat(
         job.scheduled_start,
         MatchesAll(GreaterThan(expected_delay - timedelta(minutes=1)),
                    LessThan(expected_delay + timedelta(minutes=1))))
     self.assertIsNone(job.lease_expires)
     self.assertNotIn(job, runner.completed_jobs)
     self.assertIn(job, runner.incomplete_jobs)
Example #30
0
 def test_triggers_webhooks(self):
     # Jobs trigger any relevant webhooks when they're enabled.
     self.useFixture(FeatureFixture({'code.git.webhooks.enabled': 'on'}))
     repository = self.factory.makeGitRepository()
     self.factory.makeGitRefs(
         repository, paths=['refs/heads/master', 'refs/tags/1.0'])
     hook = self.factory.makeWebhook(
         target=repository, event_types=['git:push:0.1'])
     job = GitRefScanJob.create(repository)
     paths = ('refs/heads/master', 'refs/tags/2.0')
     self.useFixture(GitHostingFixture(refs=self.makeFakeRefs(paths)))
     with dbuser('branchscanner'):
         JobRunner([job]).runAll()
     delivery = hook.deliveries.one()
     sha1 = lambda s: hashlib.sha1(s).hexdigest()
     self.assertThat(
         delivery,
         MatchesStructure(
             event_type=Equals('git:push:0.1'),
             payload=MatchesDict({
                 'git_repository': Equals('/' + repository.unique_name),
                 'git_repository_path': Equals(repository.unique_name),
                 'ref_changes': Equals({
                     'refs/tags/1.0': {
                         'old': {'commit_sha1': sha1('refs/tags/1.0')},
                         'new': None},
                     'refs/tags/2.0': {
                         'old': None,
                         'new': {'commit_sha1': sha1('refs/tags/2.0')}},
                 })})))
     with dbuser(config.IWebhookDeliveryJobSource.dbuser):
         self.assertEqual(
             "<WebhookDeliveryJob for webhook %d on %r>" % (
                 hook.id, hook.target),
             repr(delivery))
Example #31
0
def run_mail_jobs():
    """Process job queues that send out emails.

    If a new job type is added that sends emails, this function can be
    extended to run those jobs, so that testing emails doesn't require a
    bunch of different function calls to process different queues.
    """
    # Circular import.
    from lp.testing.pages import permissive_security_policy

    # Commit the transaction to make sure that the JobRunner can find
    # the queued jobs.
    transaction.commit()
    for interface in (
            IExpiringMembershipNotificationJobSource,
            IMembershipNotificationJobSource,
            ISelfRenewalNotificationJobSource,
            ITeamInvitationNotificationJobSource,
            ITeamJoinNotificationJobSource,
            ):
        job_source = getUtility(interface)
        logger = DevNullLogger()
        dbuser_name = getattr(config, interface.__name__).dbuser
        with permissive_security_policy(dbuser_name):
            runner = JobRunner.fromReady(job_source, logger)
            runner.runAll()
    def test_works_in_job(self):
        # `BranchHostingClient` is usable from a running job.
        blob = b"".join(chr(i) for i in range(256))

        @implementer(IRunnableJob)
        class GetBlobJob(BaseRunnableJob):
            def __init__(self, testcase):
                super(GetBlobJob, self).__init__()
                self.job = Job()
                self.testcase = testcase

            def run(self):
                with self.testcase.mockRequests("GET",
                                                body=blob,
                                                set_default_timeout=False):
                    self.blob = self.testcase.client.getBlob(123, "file-id")
                # We must make this assertion inside the job, since the job
                # runner creates a separate timeline.
                self.testcase.assertRequest(
                    "+branch-id/123/download/head%3A/file-id")

        job = GetBlobJob(self)
        JobRunner([job]).runAll()
        self.assertEqual(JobStatus.COMPLETED, job.job.status)
        self.assertEqual(blob, job.blob)
Example #33
0
 def test_run_branches_empty(self):
     """If the branches are empty, we tell the user."""
     # If the job has been waiting for a significant period of time (15
     # minutes for now), we run the job anyway.  The checkReady method
     # then raises and this is caught as a user error by the job system,
     # and as such sends an email to the error recipients, which for this
     # job is the merge proposal registrant.
     eric = self.factory.makePerson(name='eric', email='*****@*****.**')
     bmp = self.factory.makeBranchMergeProposal(registrant=eric)
     job = UpdatePreviewDiffJob.create(bmp)
     pop_notifications()
     JobRunner([job]).runAll()
     [email] = pop_notifications()
     self.assertEqual('Eric <*****@*****.**>', email['to'])
     self.assertEqual(
         'Launchpad error while generating the diff for a merge proposal',
         email['subject'])
     branch = bmp.source_branch
     self.assertEqual(
         'Launchpad encountered an error during the following operation: '
         'generating the diff for a merge proposal.  '
         'The source branch of http://code.launchpad.dev/~%s/%s/%s/'
         '+merge/%d has no revisions.' %
         (branch.owner.name, branch.target.name, branch.name, bmp.id),
         email.get_payload(decode=True))
Example #34
0
 def test_import_uncommit(self):
     self.commitRevision()
     self.makeBzrSync(self.db_branch).syncBranchAndClose()
     JobRunner.fromReady(getUtility(IRevisionMailJobSource)).runAll()
     stub.test_emails = []
     self.uncommitRevision()
     self.makeBzrSync(self.db_branch).syncBranchAndClose()
     JobRunner.fromReady(getUtility(IRevisionMailJobSource)).runAll()
     self.assertEqual(len(stub.test_emails), 1)
     [uncommit_email] = stub.test_emails
     expected = '1 revision was removed from the branch.'
     message = email.message_from_string(uncommit_email[2])
     email_body = message.get_payload()
     self.assertTextIn(expected, email_body)
     self.assertEmailHeadersEqual(
         '[Branch %s] 1 revision removed' % self.db_branch.unique_name,
         message['Subject'])
Example #35
0
 def test_import_uncommit(self):
     self.commitRevision()
     self.makeBzrSync(self.db_branch).syncBranchAndClose()
     JobRunner.fromReady(getUtility(IRevisionMailJobSource)).runAll()
     stub.test_emails = []
     self.uncommitRevision()
     self.makeBzrSync(self.db_branch).syncBranchAndClose()
     JobRunner.fromReady(getUtility(IRevisionMailJobSource)).runAll()
     self.assertEqual(len(stub.test_emails), 1)
     [uncommit_email] = stub.test_emails
     expected = '1 revision was removed from the branch.'
     message = email.message_from_string(uncommit_email[2])
     email_body = message.get_payload()
     self.assertIn(expected, email_body)
     self.assertEmailHeadersEqual(
         '[Branch %s] 1 revision removed' % self.db_branch.unique_name,
         message['Subject'])
Example #36
0
 def test_run(self):
     bmp = self.createExampleBzrMerge()[0]
     job = UpdatePreviewDiffJob.create(bmp)
     self.factory.makeRevisionsForBranch(bmp.source_branch, count=1)
     bmp.source_branch.next_mirror_time = None
     with dbuser("merge-proposal-jobs"):
         JobRunner([job]).runAll()
     self.checkExampleBzrMerge(bmp.preview_diff.text)
Example #37
0
 def test_run_sends_email(self):
     """MergeProposalCreationJob.run sends an email."""
     bmp = self.createProposalWithEmptyBranches()
     job = MergeProposalNeedsReviewEmailJob.create(bmp)
     self.assertEqual([], pop_notifications())
     with dbuser("merge-proposal-jobs"):
         JobRunner([job]).runAll()
     self.assertEqual(2, len(pop_notifications()))
Example #38
0
    def test_runAll_aborts_transaction_on_error(self):
        """runAll should abort the transaction on oops."""

        class DBAlterJob(NullJob):

            def __init__(self):
                super(DBAlterJob, self).__init__('')

            def run(self):
                self.job.log = 'hello'
                raise ValueError

        job = DBAlterJob()
        runner = JobRunner([job])
        runner.runAll()
        # If the transaction was committed, job.log == 'hello'.  If it was
        # aborted, it is None.
        self.assertIs(None, job.job.log)
Example #39
0
 def test_runJob_records_failure(self):
     """When a job fails, the failure needs to be recorded."""
     job = RaisingJob('boom')
     runner = JobRunner([job])
     self.assertRaises(RaisingJobException, runner.runJob, job, None)
     # Abort the transaction to confirm that the update of the job status
     # has been committed.
     transaction.abort()
     self.assertEqual(JobStatus.FAILED, job.job.status)
Example #40
0
    def test_runAll_reports_oopses(self):
        """When an error is encountered, report an oops and continue."""
        job_1, job_2 = self.makeTwoJobs()

        def raiseError():
            # Ensure that jobs which call transaction.abort work, too.
            transaction.abort()
            raise Exception('Fake exception.  Foobar, I say!')
        job_1.run = raiseError
        runner = JobRunner([job_1, job_2])
        runner.runAll()
        self.assertEqual([], pop_notifications())
        self.assertEqual([job_2], runner.completed_jobs)
        self.assertEqual([job_1], runner.incomplete_jobs)
        self.assertEqual(JobStatus.FAILED, job_1.job.status)
        self.assertEqual(JobStatus.COMPLETED, job_2.job.status)
        oops = self.oopses[-1]
        self.assertIn('Fake exception.  Foobar, I say!', oops['tb_text'])
        self.assertEqual(["{'foo': 'bar'}"], oops['req_vars'].values())
Example #41
0
 def test_run(self):
     # A proper test run closes bugs.
     spr = self.factory.makeSourcePackageRelease(
         distroseries=self.distroseries, changelog_entry="changelog")
     bug = self.factory.makeBug()
     bugtask = self.factory.makeBugTask(target=spr.sourcepackage, bug=bug)
     self.assertEqual(BugTaskStatus.NEW, bugtask.status)
     job = self.makeJob(spr=spr, bug_ids=[bug.id])
     JobRunner([job]).runAll()
     self.assertEqual(BugTaskStatus.FIXRELEASED, bugtask.status)
 def test_run_scan_pending_retries(self):
     # A run that finds that the store has not yet finished scanning the
     # package schedules itself to be retried.
     self.useFixture(FakeLogger())
     snapbuild = self.makeSnapBuild()
     self.assertContentEqual([], snapbuild.store_upload_jobs)
     job = SnapStoreUploadJob.create(snapbuild)
     client = FakeSnapStoreClient()
     client.upload.result = self.status_url
     client.checkStatus.failure = UploadNotScannedYetResponse()
     self.useFixture(ZopeUtilityFixture(client, ISnapStoreClient))
     with dbuser(config.ISnapStoreUploadJobSource.dbuser):
         JobRunner([job]).runAll()
     self.assertEqual([((snapbuild, ), {})], client.upload.calls)
     self.assertEqual([((self.status_url, ), {})], client.checkStatus.calls)
     self.assertEqual([], client.release.calls)
     self.assertContentEqual([job], snapbuild.store_upload_jobs)
     self.assertIsNone(job.store_url)
     self.assertIsNone(job.store_revision)
     self.assertIsNone(job.error_message)
     self.assertEqual([], pop_notifications())
     self.assertEqual(JobStatus.WAITING, job.job.status)
     self.assertWebhookDeliveries(snapbuild, ["Pending"])
     # Try again.  The upload part of the job is not retried, and this
     # time the scan completes.
     job.scheduled_start = None
     client.upload.calls = []
     client.checkStatus.calls = []
     client.checkStatus.failure = None
     client.checkStatus.result = (self.store_url, 1)
     with dbuser(config.ISnapStoreUploadJobSource.dbuser):
         JobRunner([job]).runAll()
     self.assertEqual([], client.upload.calls)
     self.assertEqual([((self.status_url, ), {})], client.checkStatus.calls)
     self.assertEqual([], client.release.calls)
     self.assertContentEqual([job], snapbuild.store_upload_jobs)
     self.assertEqual(self.store_url, job.store_url)
     self.assertEqual(1, job.store_revision)
     self.assertIsNone(job.error_message)
     self.assertEqual([], pop_notifications())
     self.assertEqual(JobStatus.COMPLETED, job.job.status)
     self.assertWebhookDeliveries(snapbuild, ["Pending", "Uploaded"])
 def test_run_502_retries(self):
     # A run that gets a 502 error from the store schedules itself to be
     # retried.
     self.useFixture(FakeLogger())
     snapbuild = self.makeSnapBuild()
     self.assertContentEqual([], snapbuild.store_upload_jobs)
     job = SnapStoreUploadJob.create(snapbuild)
     client = FakeSnapStoreClient()
     client.upload.failure = UploadFailedResponse("Proxy error",
                                                  can_retry=True)
     self.useFixture(ZopeUtilityFixture(client, ISnapStoreClient))
     with dbuser(config.ISnapStoreUploadJobSource.dbuser):
         JobRunner([job]).runAll()
     self.assertEqual([((snapbuild, ), {})], client.upload.calls)
     self.assertEqual([], client.checkStatus.calls)
     self.assertEqual([], client.release.calls)
     self.assertContentEqual([job], snapbuild.store_upload_jobs)
     self.assertIsNone(job.store_url)
     self.assertIsNone(job.store_revision)
     self.assertIsNone(job.error_message)
     self.assertEqual([], pop_notifications())
     self.assertEqual(JobStatus.WAITING, job.job.status)
     self.assertWebhookDeliveries(snapbuild, ["Pending"])
     # Try again.  The upload part of the job is retried, and this time
     # it succeeds.
     job.scheduled_start = None
     client.upload.calls = []
     client.upload.failure = None
     client.upload.result = self.status_url
     client.checkStatus.result = (self.store_url, 1)
     with dbuser(config.ISnapStoreUploadJobSource.dbuser):
         JobRunner([job]).runAll()
     self.assertEqual([((snapbuild, ), {})], client.upload.calls)
     self.assertEqual([((self.status_url, ), {})], client.checkStatus.calls)
     self.assertEqual([], client.release.calls)
     self.assertContentEqual([job], snapbuild.store_upload_jobs)
     self.assertEqual(self.store_url, job.store_url)
     self.assertEqual(1, job.store_revision)
     self.assertIsNone(job.error_message)
     self.assertEqual([], pop_notifications())
     self.assertEqual(JobStatus.COMPLETED, job.job.status)
     self.assertWebhookDeliveries(snapbuild, ["Pending", "Uploaded"])
Example #44
0
    def test_runAll_mails_oopses(self):
        """Email interested parties about OOPses."""
        job_1, job_2 = self.makeTwoJobs()

        def raiseError():
            # Ensure that jobs which call transaction.abort work, too.
            transaction.abort()
            raise Exception('Fake exception.  Foobar, I say!')
        job_1.run = raiseError
        job_1.oops_recipients = ['*****@*****.**']
        runner = JobRunner([job_1, job_2])
        runner.runAll()
        (notification,) = pop_notifications()
        oops = self.oopses[-1]
        self.assertIn(
            'Launchpad encountered an internal error during the following'
            ' operation: appending a string to a list.  It was logged with id'
            ' %s.  Sorry for the inconvenience.' % oops['id'],
            notification.get_payload(decode=True))
        self.assertNotIn('Fake exception.  Foobar, I say!',
                         notification.get_payload(decode=True))
        self.assertEqual('Launchpad internal error', notification['subject'])
Example #45
0
    def test_import_recommit(self):
        # When scanning the uncommit and new commit there should be an email
        # generated saying that 1 (in this case) revision has been removed,
        # and another email with the diff and log message.
        self.commitRevision('first')
        self.makeBzrSync(self.db_branch).syncBranchAndClose()
        JobRunner.fromReady(getUtility(IRevisionMailJobSource)).runAll()
        stub.test_emails = []
        self.uncommitRevision()
        self.writeToFile(filename="hello.txt",
                         contents="Hello World\n")
        author = self.factory.getUniqueString()
        self.commitRevision('second', committer=author)
        self.makeBzrSync(self.db_branch).syncBranchAndClose()
        JobRunner.fromReady(getUtility(IRevisionsAddedJobSource)).runAll()
        JobRunner.fromReady(getUtility(IRevisionMailJobSource)).runAll()
        self.assertEqual(len(stub.test_emails), 2)
        [recommit_email, uncommit_email] = stub.test_emails
        uncommit_email_body = uncommit_email[2]
        expected = '1 revision was removed from the branch.'
        self.assertTextIn(expected, uncommit_email_body)
        subject = (
            'Subject: [Branch %s] Test branch' % self.db_branch.unique_name)
        self.assertTextIn(expected, uncommit_email_body)

        recommit_email_msg = email.message_from_string(recommit_email[2])
        recommit_email_body = recommit_email_msg.get_payload()[0].get_payload(
            decode=True)
        subject = '[Branch %s] Rev 1: second' % self.db_branch.unique_name
        self.assertEmailHeadersEqual(subject, recommit_email_msg['Subject'])
        body_bits = [
            'revno: 1',
            'committer: %s' % author,
            'branch nick: %s' % self.bzr_branch.nick,
            'message:\n  second',
            'added:\n  hello.txt',
            ]
        for bit in body_bits:
            self.assertTextIn(bit, recommit_email_body)
Example #46
0
 def test_runJobHandleErrors_oops_generated_notify_fails(self):
     """A second oops is logged if the notification of the oops fails."""
     job = RaisingJobRaisingNotifyOops('boom')
     runner = JobRunner([job])
     runner.runJobHandleError(job)
     self.assertEqual(2, len(self.oopses))
Example #47
0
 def test_runJobHandleErrors_oops_generated(self):
     """The handle errors method records an oops for raised errors."""
     job = RaisingJob('boom')
     runner = JobRunner([job])
     runner.runJobHandleError(job)
     self.assertEqual(1, len(self.oopses))
Example #48
0
 def test_runJobHandleErrors_user_error_no_oops(self):
     """If the job raises a user error, there is no oops."""
     job = RaisingJobUserError('boom')
     runner = JobRunner([job])
     runner.runJobHandleError(job)
     self.assertEqual(0, len(self.oopses))