Пример #1
0
    def save(self, *args, **kwargs):
        is_publishable = (
            self.partner.has_marketing_site
            and waffle.switch_is_active('publish_program_to_marketing_site'))

        if is_publishable:
            publisher = ProgramMarketingSitePublisher(self.partner)
            previous_obj = Program.objects.get(id=self.id) if self.id else None

            with transaction.atomic():
                super(Program, self).save(*args, **kwargs)
                publisher.publish_obj(self, previous_obj=previous_obj)
        else:
            super(Program, self).save(*args, **kwargs)
class ProgramMarketingSitePublisherTests(MarketingSitePublisherTestMixin):
    """
    Tests covering program-specific publishing logic.
    """
    def setUp(self):
        super().setUp()

        self.partner = PartnerFactory()
        self.publisher = ProgramMarketingSitePublisher(self.partner)

        self.api_root = self.publisher.client.api_url
        self.username = self.publisher.client.username

        self.obj = ProgramFactory()

    @mock.patch.object(ProgramMarketingSitePublisher,
                       'serialize_obj',
                       return_value={'uuid': 'foo'})
    @mock.patch.object(ProgramMarketingSitePublisher,
                       'node_id',
                       return_value='node_id')
    @mock.patch.object(ProgramMarketingSitePublisher,
                       'create_node',
                       return_value='node_id')
    @mock.patch.object(ProgramMarketingSitePublisher,
                       'edit_node',
                       return_value=None)
    @mock.patch.object(ProgramMarketingSitePublisher,
                       'update_node_alias',
                       return_value=None)
    @mock.patch.object(ProgramMarketingSitePublisher,
                       'get_and_delete_alias',
                       return_value=None)
    def test_publish_obj(self, mock_get_and_delete_alias,
                         mock_update_node_alias, mock_edit_node,
                         mock_create_node, *args):  # pylint: disable=unused-argument
        """
        Verify that the publisher only attempts to publish programs of certain types,
        only attempts an edit when any one of a set of trigger fields is changed,
        and always follows publication with an attempt to update the node alias.
        """
        # Publication isn't supported for programs of this type.
        self.publisher.publish_obj(self.obj)

        mocked_methods = (mock_create_node, mock_edit_node,
                          mock_update_node_alias)
        for mocked_method in mocked_methods:
            assert not mocked_method.called

        types_to_publish = ('XSeries', 'MicroMasters',
                            'Professional Certificate')

        for name in types_to_publish:
            for mocked_method in mocked_methods:
                mocked_method.reset_mock()

            # Publication is supported for programs of this type. No previous object
            # is provided, so node creation should occur.
            self.obj.type.name = name
            self.publisher.publish_obj(self.obj)

            assert mock_create_node.called
            assert not mock_edit_node.called
            assert mock_get_and_delete_alias.called
            assert mock_update_node_alias.called

            for mocked_method in mocked_methods:
                mocked_method.reset_mock()

            # A previous object is provided, but none of the trigger fields have
            # changed. Editing should not occur.
            self.publisher.publish_obj(self.obj, previous_obj=self.obj)

            for mocked_method in mocked_methods:
                assert not mocked_method.called

            # Trigger fields have changed. Editing should occur.
            previous_obj = ProgramFactory()
            self.publisher.publish_obj(self.obj, previous_obj=previous_obj)

            assert not mock_create_node.called
            assert mock_edit_node.called
            assert mock_get_and_delete_alias.called
            assert mock_update_node_alias.called

    @responses.activate
    def test_serialize_obj(self):
        """
        Verify that the publisher serializes data required to publish programs.
        """
        self.mock_api_client()

        actual = self.publisher.serialize_obj(self.obj)
        expected = {
            'field_uuid': str(self.obj.uuid),
            'author': {
                'id': self.user_id
            },
            'status': 1,
            'title': self.obj.title,
            'type': str(self.obj.type).lower().replace(' ', '_'),
            'uuid': str(self.obj.uuid),
        }

        assert actual == expected

        self.obj.status = ProgramStatus.Unpublished

        actual = self.publisher.serialize_obj(self.obj)
        expected['status'] = 0

        assert actual == expected

    @responses.activate
    def test_update_node_alias(self):
        """
        Verify that the publisher attempts to create a new alias when necessary
        and deletes an old alias, if one existed, and that appropriate exceptions
        are raised for non-200 status codes.
        """
        # No previous object is provided. Create a new node and make sure
        # title alias created, by default, based on the title is deleted
        # and a new alias based on marketing slug is created.
        self.mock_api_client()
        self.mock_get_alias_form()
        self.mock_get_delete_form(self.obj.title)
        self.mock_delete_alias()
        self.mock_get_delete_form(self.obj.marketing_slug)
        self.mock_add_alias()

        self.publisher.update_node_alias(self.obj, self.node_id, None)

        assert responses.calls[-1].request.url == '{}/add'.format(
            self.publisher.alias_api_base)

        responses.reset()

        # Same scenario, but this time a non-200 status code is returned during
        # alias creation. An exception should be raised.
        self.mock_api_client()
        self.mock_get_alias_form()
        self.mock_get_delete_form(self.obj.title)
        self.mock_delete_alias()
        self.mock_get_delete_form(self.obj.marketing_slug)
        self.mock_add_alias(status=500)

        with pytest.raises(AliasCreateError):
            self.publisher.update_node_alias(self.obj, self.node_id, None)

        responses.reset()

        # A previous object is provided, but the marketing slug hasn't changed.
        # Neither alias creation nor alias deletion should occur.
        self.mock_api_client()
        self.mock_get_delete_form(self.obj.marketing_slug)

        self.publisher.update_node_alias(self.obj, self.node_id, self.obj)

        responses.reset()

        # A previous object is provided, and the marketing slug has changed.
        # Both alias creation and alias deletion should occur.
        previous_obj = ProgramFactory()

        self.mock_api_client()
        self.mock_get_delete_form(self.obj.marketing_slug)
        self.mock_get_delete_form(previous_obj.marketing_slug)
        self.mock_delete_alias_form()
        self.mock_delete_alias()
        self.mock_get_alias_form()
        self.mock_get_delete_form(self.obj.marketing_slug)
        self.mock_add_alias()

        self.publisher.update_node_alias(self.obj, self.node_id, previous_obj)

        assert any('/add' in call.request.url for call in responses.calls)
        assert any(
            '/list/{}'.format(previous_obj.marketing_slug) in call.request.url
            for call in responses.calls)

        responses.reset()

        # Same scenario, but this time a non-200 status code is returned during
        # alias deletion. An exception should be raised.
        self.mock_api_client()
        self.mock_get_delete_form(self.obj.marketing_slug)
        self.mock_get_delete_form(previous_obj.marketing_slug)
        self.mock_delete_alias_form()
        self.mock_delete_alias(status=500)

        with pytest.raises(AliasDeleteError):
            self.publisher.update_node_alias(self.obj, self.node_id,
                                             previous_obj)

    def test_alias(self):
        """
        Verify that aliases are constructed correctly.
        """
        actual = self.publisher.alias(self.obj)
        expected = '{type}/{slug}'.format(type=self.obj.type.slug,
                                          slug=self.obj.marketing_slug)

        assert actual == expected