def test_ensure_coverage_changes_status(self):
        """Verify that processing an item that has a preexisting 
        CoverageRecord can change the status of that CoverageRecord.
        """
        always = AlwaysSuccessfulCoverageProvider("Always successful",
                                                  self.input_identifier_types,
                                                  self.output_source)
        persistent = NeverSuccessfulCoverageProvider(
            "Persistent failures", self.input_identifier_types,
            self.output_source)
        transient = TransientFailureCoverageProvider(
            "Persistent failures", self.input_identifier_types,
            self.output_source)

        # Cover the same identifier multiple times, simulating all
        # possible states of a CoverageRecord. The same CoverageRecord
        # is used every time and the status is changed appropriately
        # after every run.
        c1 = persistent.ensure_coverage(self.identifier, force=True)
        eq_(CoverageRecord.PERSISTENT_FAILURE, c1.status)

        c2 = transient.ensure_coverage(self.identifier, force=True)
        eq_(c2, c1)
        eq_(CoverageRecord.TRANSIENT_FAILURE, c1.status)

        c3 = always.ensure_coverage(self.identifier, force=True)
        eq_(c3, c1)
        eq_(CoverageRecord.SUCCESS, c1.status)

        c4 = persistent.ensure_coverage(self.identifier, force=True)
        eq_(c4, c1)
        eq_(CoverageRecord.PERSISTENT_FAILURE, c1.status)
    def test_ensure_coverage_transient_coverage_failure(self):

        provider = TransientFailureCoverageProvider(
            "Transient failure", self.input_identifier_types,
            self.output_source)
        failure = provider.ensure_coverage(self.identifier)
        eq_([failure], self.identifier.coverage_records)
        eq_(CoverageRecord.TRANSIENT_FAILURE, failure.status)
        eq_("Oops!", failure.exception)

        # Timestamp was not updated.
        eq_([], self._db.query(Timestamp).all())
    def test_transient_failure(self):

        # We start with no CoverageRecords and no Timestamp.
        eq_([], self._db.query(CoverageRecord).all())
        eq_([], self._db.query(Timestamp).all())

        provider = TransientFailureCoverageProvider(
            "Transient failure", self.input_identifier_types,
            self.output_source)
        provider.run()

        # We have a CoverageRecord representing the transient failure.
        [failure] = self.identifier.coverage_records
        eq_(CoverageRecord.TRANSIENT_FAILURE, failure.status)

        # The timestamp was set.
        [timestamp] = self._db.query(Timestamp).all()
        eq_("Transient failure", timestamp.service)
    def test_process_batch_and_handle_results(self):

        e1, p1 = self._edition(with_license_pool=True)
        i1 = e1.primary_identifier

        e2, p2 = self._edition(with_license_pool=True)
        i2 = e2.primary_identifier

        success_provider = AlwaysSuccessfulCoverageProvider(
            "Success",
            self.input_identifier_types,
            self.output_source,
            operation="i succeed")

        batch = [i1, i2]
        counts, successes = success_provider.process_batch_and_handle_results(
            batch)

        # Two successes.
        eq_((2, 0, 0), counts)

        # Each represented with a CoverageRecord with status='success'
        assert all(isinstance(x, CoverageRecord) for x in successes)
        eq_([CoverageRecord.SUCCESS] * 2, [x.status for x in successes])

        # Each associated with one of the identifiers...
        eq_(set([i1, i2]), set([x.identifier for x in successes]))

        # ...and with the coverage provider's operation.
        eq_(['i succeed'] * 2, [x.operation for x in successes])

        # Now try a different CoverageProvider which creates transient
        # failures.
        transient_failure_provider = TransientFailureCoverageProvider(
            "Transient failure",
            self.input_identifier_types,
            self.output_source,
            operation="i fail transiently")
        counts, failures = transient_failure_provider.process_batch_and_handle_results(
            batch)
        # Two transient failures.
        eq_((0, 2, 0), counts)

        # New coverage records were added to track the transient
        # failures.
        eq_([CoverageRecord.TRANSIENT_FAILURE] * 2,
            [x.status for x in failures])
        eq_(["i fail transiently"] * 2, [x.operation for x in failures])

        # Another way of getting transient failures is to just ignore every
        # item you're told to process.
        task_ignoring_provider = TaskIgnoringCoverageProvider(
            "Ignores all tasks",
            self.input_identifier_types,
            self.output_source,
            operation="i ignore")
        counts, records = task_ignoring_provider.process_batch_and_handle_results(
            batch)

        eq_((0, 2, 0), counts)
        eq_([CoverageRecord.TRANSIENT_FAILURE] * 2,
            [x.status for x in records])
        eq_(["i ignore"] * 2, [x.operation for x in records])

        # Or you can go really bad and have persistent failures.
        persistent_failure_provider = NeverSuccessfulCoverageProvider(
            "Persistent failure",
            self.input_identifier_types,
            self.output_source,
            operation="i will always fail")
        counts, results = persistent_failure_provider.process_batch_and_handle_results(
            batch)

        # Two persistent failures.
        eq_((0, 0, 2), counts)
        assert all([isinstance(x, CoverageRecord) for x in results])
        eq_(["What did you expect?", "What did you expect?"],
            [x.exception for x in results])
        eq_([CoverageRecord.PERSISTENT_FAILURE] * 2,
            [x.status for x in results])
        eq_(["i will always fail"] * 2, [x.operation for x in results])