def test_no_event_state_change_if_service_fails(
            self, update_db, get_repo_urls, build_image_artifact_build):
        build_image_artifact_build.return_value = 67890

        self.db_advisory_rpm_signed_event = models.Event.create(
            db.session, 'msg-id-123', '12345',
            events.ErrataAdvisoryStateChangedEvent,
            state=EventState.BUILDING.value)

        self.image_a_build = models.ArtifactBuild.create(
            db.session, self.db_advisory_rpm_signed_event,
            'image-a-0.1-1', ArtifactType.IMAGE,
            build_id=12345,
            state=ArtifactBuildState.PLANNED.value,
            original_nvr='image-a-0.1-1', rebuilt_nvr='image-a-0.1-2')
        # Empty json.
        self.image_a_build.build_args = "{}"

        db.session.commit()

        state_changed_event = events.BrewContainerTaskStateChangeEvent(
            'msg-id-890', 'image-a', 'branch', 'target', 12345,
            'BUILD', 'FAILED')

        handler = RebuildImagesOnParentImageBuild()
        with self.assertRaises(RuntimeError):
            handler.handle(state_changed_event)

        # As self.image_b_build starts to be rebuilt, not all images are
        # rebuilt yet.
        self.assertEqual(EventState.BUILDING.value,
                         self.db_advisory_rpm_signed_event.state)
    def test_mark_event_COMPLETE_if_all_builds_done(self, publish):
        self.db_advisory_rpm_signed_event = models.Event.create(
            db.session,
            'msg-id-123',
            '12345',
            events.ErrataAdvisoryStateChangedEvent,
            state=EventState.BUILDING.value)

        self.image_a_build = models.ArtifactBuild.create(
            db.session,
            self.db_advisory_rpm_signed_event,
            'image-a-0.1-1',
            ArtifactType.IMAGE,
            state=ArtifactBuildState.DONE.value)

        self.image_b_build = models.ArtifactBuild.create(
            db.session,
            self.db_advisory_rpm_signed_event,
            'image-b-0.1-1',
            ArtifactType.IMAGE,
            dep_on=self.image_a_build,
            state=ArtifactBuildState.DONE.value)

        self.image_c_build = models.ArtifactBuild.create(
            db.session,
            self.db_advisory_rpm_signed_event,
            'image-c-0.1-1',
            ArtifactType.IMAGE,
            dep_on=self.image_b_build,
            state=ArtifactBuildState.FAILED.value)

        self.image_d_build = models.ArtifactBuild.create(
            db.session,
            self.db_advisory_rpm_signed_event,
            'image-d-0.1-1',
            ArtifactType.IMAGE,
            dep_on=self.image_a_build,
            build_id=12345,
            state=ArtifactBuildState.BUILD.value)

        db.session.commit()

        state_changed_event = events.BrewContainerTaskStateChangeEvent(
            'msg-id-890', 'image-d', 'branch', 'target', 12345, 'BUILD',
            'CLOSED')

        handler = RebuildImagesOnParentImageBuild()
        handler.handle(state_changed_event)

        self.assertEqual(EventState.COMPLETE.value,
                         self.db_advisory_rpm_signed_event.state)
        self.assertEqual(
            "Advisory 12345: 1 of 4 container image(s) failed to rebuild.",
            self.db_advisory_rpm_signed_event.state_reason)
    def test_not_change_state_if_not_all_builds_done(
            self, get_repo_urls, build_image_artifact_build):
        build_image_artifact_build.return_value = 67890

        self.db_advisory_rpm_signed_event = models.Event.create(
            db.session,
            'msg-id-123',
            '12345',
            events.ErrataAdvisoryStateChangedEvent,
            state=EventState.BUILDING.value)

        self.image_a_build = models.ArtifactBuild.create(
            db.session,
            self.db_advisory_rpm_signed_event,
            'image-a-0.1-1',
            ArtifactType.IMAGE,
            build_id=12345,
            state=ArtifactBuildState.BUILD.value)

        self.image_b_build = models.ArtifactBuild.create(
            db.session,
            self.db_advisory_rpm_signed_event,
            'image-b-0.1-1',
            ArtifactType.IMAGE,
            dep_on=self.image_a_build,
            state=ArtifactBuildState.PLANNED.value)

        self.image_c_build = models.ArtifactBuild.create(
            db.session,
            self.db_advisory_rpm_signed_event,
            'image-c-0.1-1',
            ArtifactType.IMAGE,
            dep_on=self.image_b_build,
            state=ArtifactBuildState.FAILED.value)

        db.session.commit()

        state_changed_event = events.BrewContainerTaskStateChangeEvent(
            'msg-id-890', 'image-a', 'branch', 'target', 12345, 'BUILD',
            'CLOSED')

        handler = RebuildImagesOnParentImageBuild()
        handler.handle(state_changed_event)

        # As self.image_b_build starts to be rebuilt, not all images are
        # rebuilt yet.
        self.assertEqual(EventState.BUILDING.value,
                         self.db_advisory_rpm_signed_event.state)
 def setUp(self):
     super(TestRebuildImagesOnParentImageBuild, self).setUp()
     events.BaseEvent.register_parser(BrewTaskStateChangeParser)
     self.handler = RebuildImagesOnParentImageBuild()
class TestRebuildImagesOnParentImageBuild(helpers.ModelsTestCase):
    def setUp(self):
        super(TestRebuildImagesOnParentImageBuild, self).setUp()
        events.BaseEvent.register_parser(BrewTaskStateChangeParser)
        self.handler = RebuildImagesOnParentImageBuild()

    def test_can_handle_brew_container_task_closed_event(self):
        """
        Tests handler can handle brew build container task closed event.
        """
        event = self.get_event_from_msg(get_fedmsg('brew_container_task_closed'))
        self.assertTrue(self.handler.can_handle(event))

    def test_can_handle_brew_container_task_failed_event(self):
        """
        Tests handler can handle brew build container task failed event.
        """
        event = self.get_event_from_msg(get_fedmsg('brew_container_task_failed'))
        self.assertTrue(self.handler.can_handle(event))

    @mock.patch('freshmaker.handlers.ContainerBuildHandler.build_image_artifact_build')
    @mock.patch('freshmaker.handlers.ContainerBuildHandler.get_repo_urls')
    @mock.patch('freshmaker.handlers.ContainerBuildHandler.set_context')
    def test_build_containers_when_dependency_container_is_built(self, set_context, repo_urls, build_image):
        """
        Tests when dependency container is built, rebuild containers depend on it.
        """
        build_image.side_effect = [1, 2, 3]
        repo_urls.return_value = ["url"]
        e1 = models.Event.create(db.session, "test_msg_id", "RHSA-2018-001", events.TestingEvent)
        event = self.get_event_from_msg(get_fedmsg('brew_container_task_closed'))

        base_build = models.ArtifactBuild.create(db.session, e1, 'test-product-docker', ArtifactType.IMAGE, event.task_id)

        build_0 = models.ArtifactBuild.create(db.session, e1, 'docker-up-0', ArtifactType.IMAGE, 0,
                                              dep_on=base_build, state=ArtifactBuildState.PLANNED)
        build_1 = models.ArtifactBuild.create(db.session, e1, 'docker-up-1', ArtifactType.IMAGE, 0,
                                              dep_on=base_build, state=ArtifactBuildState.PLANNED)
        build_2 = models.ArtifactBuild.create(db.session, e1, 'docker-up-2', ArtifactType.IMAGE, 0,
                                              dep_on=base_build, state=ArtifactBuildState.PLANNED)

        self.handler.handle(event)
        self.assertEqual(base_build.state, ArtifactBuildState.DONE.value)
        build_image.assert_has_calls([
            mock.call(build_0, ['url']), mock.call(build_1, ['url']),
            mock.call(build_2, ['url']),
        ])

        set_context.assert_has_calls([
            mock.call(build_0), mock.call(build_1), mock.call(build_2)])

        self.assertEqual(build_0.build_id, 1)
        self.assertEqual(build_1.build_id, 2)
        self.assertEqual(build_2.build_id, 3)

    @mock.patch('freshmaker.handlers.ContainerBuildHandler.build_image_artifact_build')
    @mock.patch('freshmaker.handlers.ContainerBuildHandler.get_repo_urls')
    def test_not_build_containers_when_dependency_container_build_task_failed(
            self, repo_urls, build_image):
        """
        Tests when dependency container build task failed in brew, only update build state in db.
        """
        build_image.side_effect = [1, 2, 3, 4]
        repo_urls.return_value = ["url"]
        e1 = models.Event.create(db.session, "test_msg_id", "RHSA-2018-001", events.TestingEvent)
        event = self.get_event_from_msg(get_fedmsg('brew_container_task_failed'))

        base_build = models.ArtifactBuild.create(
            db.session, e1, 'test-product-docker', ArtifactType.IMAGE, event.task_id,
            original_nvr='foo-1-1')
        base_build.build_args = json.dumps({})

        models.ArtifactBuild.create(db.session, e1, 'docker-up', ArtifactType.IMAGE, 0,
                                    dep_on=base_build, state=ArtifactBuildState.PLANNED)
        self.handler.handle(event)
        self.assertEqual(base_build.state, ArtifactBuildState.BUILD.value)
        self.assertEqual(base_build.build_id, 1)
        event.task_id = 1
        self.handler.handle(event)
        self.assertEqual(base_build.state, ArtifactBuildState.BUILD.value)
        self.assertEqual(base_build.build_id, 2)
        event.task_id = 2
        self.handler.handle(event)
        self.assertEqual(base_build.state, ArtifactBuildState.FAILED.value)
        self.assertEqual(base_build.build_id, 2)
        build_image.assert_called()

    @mock.patch('freshmaker.models.messaging.publish')
    def test_mark_event_COMPLETE_if_all_builds_done(self, publish):
        self.db_advisory_rpm_signed_event = models.Event.create(
            db.session, 'msg-id-123', '12345',
            events.ErrataAdvisoryStateChangedEvent,
            state=EventState.BUILDING.value)

        self.image_a_build = models.ArtifactBuild.create(
            db.session, self.db_advisory_rpm_signed_event,
            'image-a-0.1-1', ArtifactType.IMAGE,
            state=ArtifactBuildState.DONE.value)

        self.image_b_build = models.ArtifactBuild.create(
            db.session, self.db_advisory_rpm_signed_event,
            'image-b-0.1-1', ArtifactType.IMAGE,
            dep_on=self.image_a_build,
            state=ArtifactBuildState.DONE.value)

        self.image_c_build = models.ArtifactBuild.create(
            db.session, self.db_advisory_rpm_signed_event,
            'image-c-0.1-1', ArtifactType.IMAGE,
            dep_on=self.image_b_build,
            state=ArtifactBuildState.FAILED.value)

        self.image_d_build = models.ArtifactBuild.create(
            db.session, self.db_advisory_rpm_signed_event,
            'image-d-0.1-1', ArtifactType.IMAGE,
            dep_on=self.image_a_build,
            build_id=12345,
            state=ArtifactBuildState.BUILD.value)

        db.session.commit()

        state_changed_event = events.BrewContainerTaskStateChangeEvent(
            'msg-id-890', 'image-d', 'branch', 'target', 12345,
            'BUILD', 'CLOSED')

        handler = RebuildImagesOnParentImageBuild()
        handler.handle(state_changed_event)

        self.assertEqual(EventState.COMPLETE.value,
                         self.db_advisory_rpm_signed_event.state)
        self.assertEqual("Advisory 12345: 1 of 4 container image(s) failed to rebuild.",
                         self.db_advisory_rpm_signed_event.state_reason)

    @mock.patch('freshmaker.handlers.ContainerBuildHandler.build_image_artifact_build')
    @mock.patch('freshmaker.handlers.ContainerBuildHandler.get_repo_urls')
    def test_not_change_state_if_not_all_builds_done(
            self, get_repo_urls, build_image_artifact_build):
        build_image_artifact_build.return_value = 67890

        self.db_advisory_rpm_signed_event = models.Event.create(
            db.session, 'msg-id-123', '12345',
            events.ErrataAdvisoryStateChangedEvent,
            state=EventState.BUILDING.value)

        self.image_a_build = models.ArtifactBuild.create(
            db.session, self.db_advisory_rpm_signed_event,
            'image-a-0.1-1', ArtifactType.IMAGE,
            build_id=12345,
            state=ArtifactBuildState.BUILD.value)

        self.image_b_build = models.ArtifactBuild.create(
            db.session, self.db_advisory_rpm_signed_event,
            'image-b-0.1-1', ArtifactType.IMAGE,
            dep_on=self.image_a_build,
            state=ArtifactBuildState.PLANNED.value)

        self.image_c_build = models.ArtifactBuild.create(
            db.session, self.db_advisory_rpm_signed_event,
            'image-c-0.1-1', ArtifactType.IMAGE,
            dep_on=self.image_b_build,
            state=ArtifactBuildState.FAILED.value)

        db.session.commit()

        state_changed_event = events.BrewContainerTaskStateChangeEvent(
            'msg-id-890', 'image-a', 'branch', 'target', 12345,
            'BUILD', 'CLOSED')

        handler = RebuildImagesOnParentImageBuild()
        handler.handle(state_changed_event)

        # As self.image_b_build starts to be rebuilt, not all images are
        # rebuilt yet.
        self.assertEqual(EventState.BUILDING.value,
                         self.db_advisory_rpm_signed_event.state)

    @mock.patch('freshmaker.kojiservice.KojiService')
    @mock.patch('freshmaker.errata.Errata.get_binary_rpm_nvrs')
    def test_mark_build_done_when_container_has_latest_rpms_from_advisory(self, get_binary_rpm_nvrs, KojiService):
        """
        Tests when dependency container build task failed in brew, only update build state in db.
        """
        get_binary_rpm_nvrs.return_value = set(['foo-1.2.1-22.el7'])

        koji_service = KojiService.return_value
        koji_service.get_build_rpms.return_value = [
            {'build_id': 634904, 'nvr': 'foo-debuginfo-1.2.1-22.el7', 'name': 'foo-debuginfo'},
            {'build_id': 634904, 'nvr': 'foo-1.2.1-22.el7', 'name': 'foo'},
            {'build_id': 634904, 'nvr': 'foo-debuginfo-1.1.1-22.el7', 'name': 'foo-debuginfo'},
            {'build_id': 634904, 'nvr': 'foo-1.1.1-22.el7', 'name': 'foo'},
        ]
        koji_service.get_rpms_in_container.return_value = set(
            ['foo-1.2.1-22.el7', 'bar-1.2.3-1.el7']
        )

        e1 = models.Event.create(db.session, "test_msg_id", "2018001", events.ErrataAdvisoryRPMsSignedEvent)
        event = self.get_event_from_msg(get_fedmsg('brew_container_task_closed'))
        build = models.ArtifactBuild.create(db.session, e1, 'test-product-docker', ArtifactType.IMAGE, event.task_id)

        self.handler.handle(event)

        self.assertEqual(build.state, ArtifactBuildState.DONE.value)
        self.assertEqual(build.state_reason, 'Built successfully.')

    @mock.patch('freshmaker.kojiservice.KojiService')
    @mock.patch('freshmaker.errata.Errata.get_binary_rpm_nvrs')
    def test_mark_build_fail_when_container_not_has_latest_rpms_from_advisory(self, get_binary_rpm_nvrs, KojiService):
        """
        Tests when dependency container build task failed in brew, only update build state in db.
        """
        get_binary_rpm_nvrs.return_value = set(['foo-1.2.1-23.el7'])

        koji_service = KojiService.return_value
        koji_service.get_build_rpms.return_value = [
            {'build_id': 634904, 'nvr': 'foo-debuginfo-1.2.1-23.el7', 'name': 'foo-debuginfo'},
            {'build_id': 634904, 'nvr': 'foo-1.2.1-23.el7', 'name': 'foo'},
            {'build_id': 634904, 'nvr': 'foo-debuginfo-1.1.1-22.el7', 'name': 'foo-debuginfo'},
            {'build_id': 634904, 'nvr': 'foo-1.1.1-22.el7', 'name': 'foo'},
        ]
        koji_service.get_rpms_in_container.return_value = set(
            ['foo-1.2.1-22.el7', 'bar-1.2.3-1.el7']
        )

        e1 = models.Event.create(db.session, "test_msg_id", "2018001", events.ErrataAdvisoryRPMsSignedEvent)
        event = self.get_event_from_msg(get_fedmsg('brew_container_task_closed'))
        build = models.ArtifactBuild.create(db.session, e1, 'test-product-docker', ArtifactType.IMAGE, event.task_id)

        self.handler.handle(event)
        self.assertEqual(build.state, ArtifactBuildState.FAILED.value)
        self.assertRegex(build.state_reason, r"The following RPMs in container build.*")

    @mock.patch('freshmaker.handlers.ContainerBuildHandler.build_image_artifact_build')
    @mock.patch('freshmaker.handlers.ContainerBuildHandler.get_repo_urls')
    @mock.patch('freshmaker.handlers.koji.rebuild_images_on_parent_image_build.'
                'RebuildImagesOnParentImageBuild.start_to_build_images',
                side_effect=RuntimeError('something went wrong!'))
    def test_no_event_state_change_if_service_fails(
            self, update_db, get_repo_urls, build_image_artifact_build):
        build_image_artifact_build.return_value = 67890

        self.db_advisory_rpm_signed_event = models.Event.create(
            db.session, 'msg-id-123', '12345',
            events.ErrataAdvisoryStateChangedEvent,
            state=EventState.BUILDING.value)

        self.image_a_build = models.ArtifactBuild.create(
            db.session, self.db_advisory_rpm_signed_event,
            'image-a-0.1-1', ArtifactType.IMAGE,
            build_id=12345,
            state=ArtifactBuildState.PLANNED.value,
            original_nvr='image-a-0.1-1', rebuilt_nvr='image-a-0.1-2')
        # Empty json.
        self.image_a_build.build_args = "{}"

        db.session.commit()

        state_changed_event = events.BrewContainerTaskStateChangeEvent(
            'msg-id-890', 'image-a', 'branch', 'target', 12345,
            'BUILD', 'FAILED')

        handler = RebuildImagesOnParentImageBuild()
        with self.assertRaises(RuntimeError):
            handler.handle(state_changed_event)

        # As self.image_b_build starts to be rebuilt, not all images are
        # rebuilt yet.
        self.assertEqual(EventState.BUILDING.value,
                         self.db_advisory_rpm_signed_event.state)