예제 #1
0
    def test_progress_info_without_desc(self):
        sleep_mock = SleepMock()

        offset = datetime.timedelta(seconds=1)
        with mock.patch.object(timezone, 'now', MockDatetimeGenerator(offset=offset)),\
                mock.patch.object(time, 'sleep', sleep_mock):
            linear_processing_task(desc=None, total=3)

        # Add the last entry, executed after time.sleep() so not captured, yet:
        sleep_mock.add_iteration()

        info = sleep_mock.progress_info
        assert info == [
            [0, 1.0, 'linear_processing_task: 0/3it 0% 0/it (Main task)'],
            [
                1, 2.0,
                'linear_processing_task: 1/3it 33% 2.0\xa0seconds/it (Main task)'
            ],
            [
                2, 3.0,
                'linear_processing_task: 2/3it 67% 1.5\xa0seconds/it (Main task)'
            ],
            [
                3, 6.0,
                'linear_processing_task: 3/3it 100% 2.0\xa0seconds/it finished (Main task)'
            ],
        ]
예제 #2
0
    def test_linear_processing_task(self):
        sleep_mock = SleepMock()

        offset = datetime.timedelta(seconds=1)
        with mock.patch.object(timezone, 'now', MockDatetimeGenerator(offset=offset)),\
                mock.patch.object(time, 'sleep', sleep_mock):
            linear_processing_task(desc='Foo Bar', total=10)

        signals = list(
            SignalInfoModel.objects.values_list('signal_name', flat=True))
        assert signals == ['executing', 'complete']

        # Add the last entry, executed after time.sleep() so not captured, yet:
        sleep_mock.add_iteration()

        info = sleep_mock.progress_info
        assert info == [
            [0, 1.0, 'Foo Bar: 0/10it 0% 0/it (Main task)'],
            [1, 2.0, 'Foo Bar: 1/10it 10% 2.0\xa0seconds/it (Main task)'],
            [2, 3.0, 'Foo Bar: 2/10it 20% 1.5\xa0seconds/it (Main task)'],
            [3, 4.0, 'Foo Bar: 3/10it 30% 1.3\xa0seconds/it (Main task)'],
            [4, 5.0, 'Foo Bar: 4/10it 40% 1.2\xa0seconds/it (Main task)'],
            [5, 6.0, 'Foo Bar: 5/10it 50% 1.2\xa0seconds/it (Main task)'],
            [6, 7.0, 'Foo Bar: 6/10it 60% 1.2\xa0seconds/it (Main task)'],
            [7, 8.0, 'Foo Bar: 7/10it 70% 1.1\xa0seconds/it (Main task)'],
            [8, 9.0, 'Foo Bar: 8/10it 80% 1.1\xa0seconds/it (Main task)'],
            [9, 10.0, 'Foo Bar: 9/10it 90% 1.1\xa0seconds/it (Main task)'],
            [
                10, 13.0,
                'Foo Bar: 10/10it 100% 1.3\xa0seconds/it finished (Main task)'
            ],
        ]
예제 #3
0
    def test_parallel_task(self):
        assert TaskModel.objects.count() == 0

        class TimeSleepNoop:
            def __call__(self, *args, **kwargs):
                pass

        offset = datetime.timedelta(seconds=1)
        with mock.patch.object(timezone, 'now', MockDatetimeGenerator(offset=offset)),\
                mock.patch.object(time, 'sleep', TimeSleepNoop()):
            task_result = parallel_task(total=10, task_num=2)

        assert TaskModel.objects.count() == 3

        assert isinstance(task_result, Result)
        main_task_id = task_result.task.id

        main_task_instance = TaskModel.objects.get(pk=main_task_id)
        assert main_task_instance.human_progress_string() == (
            '10/10it 100% 2.5 seconds/it finished')
        assert str(main_task_instance) == (
            'parallel_task: 10/10it 100% 2.5 seconds/it finished (Main task)')

        sub_tasks = TaskModel.objects.filter(
            parent_task=main_task_instance).order_by('update_dt')
        values = list(sub_tasks.values_list('name', 'state__signal_name'))
        assert values == [('parallel_sub_task', 'complete'),
                          ('parallel_sub_task', 'complete')]

        # Note: Huey is in immediate mode, so the tasks executes synchronously!

        sub_tasks1 = sub_tasks[0]
        assert sub_tasks1.human_progress_string(
        ) == '5/5it 100% 1.8 seconds/it finished'
        assert str(sub_tasks1) == (
            'parallel_sub_task: 5/5it 100% 1.8 seconds/it finished (Sub task of parallel_task)'
        )

        sub_tasks2 = sub_tasks[1]
        assert sub_tasks2.human_progress_string(
        ) == '5/5it 100% 1.8 seconds/it finished'
        assert str(sub_tasks2) == (
            'parallel_sub_task: 5/5it 100% 1.8 seconds/it finished (Sub task of parallel_task)'
        )
예제 #4
0
    def test_progress_info_without_total(self):
        sleep_mock = SleepMock()

        offset = datetime.timedelta(seconds=1)
        with mock.patch.object(timezone, 'now', MockDatetimeGenerator(offset=offset)),\
                mock.patch.object(time, 'sleep', sleep_mock):
            linear_processing_task(desc='Without total',
                                   total=3,
                                   no_total=True)

        # Add the last entry, executed after time.sleep() so not captured, yet:
        sleep_mock.add_iteration()

        info = sleep_mock.progress_info
        assert info == [
            [0, 1.0, 'Without total: 0it 0/it (Main task)'],
            [1, 2.0, 'Without total: 1it 2.0\xa0seconds/it (Main task)'],
            [2, 3.0, 'Without total: 2it 1.5\xa0seconds/it (Main task)'],
            [
                3, 6.0,
                'Without total: 3it 2.0\xa0seconds/it finished (Main task)'
            ],
        ]
예제 #5
0
    def test_set_name_by_request(self):
        with self.assertLogs('django_tools'):
            item = baker.make(ItemModel)

            link = ItemLinkModel(item=item, url='http://test.tld/foo/bar')

            offset = datetime.timedelta(seconds=30)
            with patch.object(timezone, 'now', MockDatetimeGenerator(offset)):
                with requests_mock.Mocker() as m:
                    m.get('http://test.tld/foo/bar', text='No title')

                    assert link.last_check is None
                    with self.assertLogs('inventory.models.links',
                                         level=logging.WARNING) as logs:
                        link.full_clean()
                        assert link.page_title is None
                        assert link.name is None
                        assert link.last_check == parse_dt(
                            '2000-01-01T00:00:30+0000')

                    logs = logs.output
                    assert logs == [
                        "WARNING:inventory.models.links:No title found in 'http://test.tld/foo/bar'"
                    ]

                    # We should not create request on every admin save call

                    with self.assertLogs('inventory.models.links',
                                         level=logging.DEBUG) as logs:
                        link.full_clean()
                        assert link.page_title is None
                        assert link.name is None

                    logs = logs.output
                    assert logs == [
                        'DEBUG:inventory.models.links:Last check is 0:00:30 ago.',
                        "INFO:inventory.models.links:Skip request for: 'http://test.tld/foo/bar'"
                    ]

                    # Next try after 1 Min

                    m.get('http://test.tld/foo/bar',
                          text='<title>A <boom>Title</boom>!</title>')
                    with self.assertLogs('inventory.models.links',
                                         level=logging.INFO) as logs:
                        link.full_clean()
                        assert link.page_title == 'A Title!'
                        assert link.name == 'A Title!'

                    logs = logs.output
                    assert logs == [
                        "INFO:inventory.models.links:Found title: 'A <boom>Title</boom>!'"
                    ]

                # Don't make requests, if we have a link name!

                with requests_mock.Mocker():
                    with self.assertLogs('inventory.models.links',
                                         level=logging.DEBUG) as logs:
                        link.full_clean()
                        assert link.page_title == 'A Title!'
                        assert link.name == 'A Title!'

                    logs = logs.output
                    assert logs == [
                        ("DEBUG:inventory.models.links:Skip link request:"
                         " because we have a name: 'A Title!'")
                    ]
class ModelManipulateTestCase(TestCase):
    @mock.patch.object(timezone, 'now', MockDatetimeGenerator())
    def test_create_or_update(self):

        # create a new entry:

        with AssertModelCleanCalled() as cm:
            instance, created, updated_fields = create_or_update(
                ModelClass=CreateOrUpdateTestModel,
                lookup={'id': 1},
                name='First entry',
                slug='first'
            )
            assert isinstance(instance, CreateOrUpdateTestModel)
            assert instance.id == 1
            assert instance.name == 'First entry'
            assert instance.slug == 'first'
            assert instance.create_dt == parse_dt('2001-01-01T00:00:00+0000')
            assert instance.update_dt == parse_dt('2001-01-01T00:00:00+0000')
            assert created is True
            assert updated_fields is None
        cm.assert_no_missing_cleans()

        # Change only 'slug'

        with AssertModelCleanCalled() as cm:
            instance, created, updated_fields = create_or_update(
                ModelClass=CreateOrUpdateTestModel,
                lookup={'id': 1},
                name='First entry',
                slug='change-value'
            )
            assert isinstance(instance, CreateOrUpdateTestModel)
            assert instance.id == 1
            assert instance.name == 'First entry'
            assert instance.slug == 'change-value'
            assert instance.create_dt == parse_dt('2001-01-01T00:00:00+0000')  # not changed!
            assert instance.update_dt == parse_dt('2002-01-01T00:00:00+0000')
            assert created is False
            assert updated_fields == ['slug']
        cm.assert_no_missing_cleans()

        # Change 'name' and 'slug':

        with AssertModelCleanCalled() as cm:
            instance, created, updated_fields = create_or_update(
                ModelClass=CreateOrUpdateTestModel,
                lookup={'id': 1},
                name='New name !',
                slug='new-slug'
            )
            assert isinstance(instance, CreateOrUpdateTestModel)
            assert instance.id == 1
            assert instance.name == 'New name !'
            assert instance.slug == 'new-slug'
            assert instance.create_dt == parse_dt('2001-01-01T00:00:00+0000')  # not changed!
            assert instance.update_dt == parse_dt('2003-01-01T00:00:00+0000')
            assert created is False
            assert updated_fields == ['name', 'slug']
        cm.assert_no_missing_cleans()

        # Nothing changed:

        instance, created, updated_fields = create_or_update(
            ModelClass=CreateOrUpdateTestModel,
            lookup={'id': 1},
            name='New name !',
            slug='new-slug'
        )
        assert isinstance(instance, CreateOrUpdateTestModel)
        assert instance.id == 1
        assert instance.name == 'New name !'
        assert instance.slug == 'new-slug'
        assert instance.create_dt == parse_dt('2001-01-01T00:00:00+0000')
        assert instance.update_dt == parse_dt('2003-01-01T00:00:00+0000')  # not changed!
        assert created is False
        assert updated_fields == []

    def test_non_valid(self):
        msg = str(validate_slug.message)
        with self.assertRaisesMessage(ValidationError, msg):
            create_or_update(
                ModelClass=CreateOrUpdateTestModel,
                lookup={'id': 1},
                name='foo',
                slug='this is no Slug !'
            )

        # Update existing entry with non-valid values should also not work:

        CreateOrUpdateTestModel(id=1, name='foo', slug='bar')
        with self.assertRaisesMessage(ValidationError, msg):
            create_or_update(
                ModelClass=CreateOrUpdateTestModel,
                lookup={'id': 1},
                name='foo',
                slug='this is no Slug !'
            )

    def test_disable_full_clean(self):
        # Create a new entry without "full_clean()" call:
        with AssertModelCleanCalled() as cm:
            instance, created, updated_fields = create_or_update(
                ModelClass=CreateOrUpdateTestModel,
                lookup={'id': 1},
                call_full_clean=False,
                slug='This is not a valid slug!'
            )
            assert isinstance(instance, CreateOrUpdateTestModel)
            assert instance.id == 1
            assert instance.slug == 'This is not a valid slug!'
            assert created is True
            assert updated_fields is None
        assert cm.called_cleans == []
        assert len(cm.missing_cleans) == 1

        # Change existing without "full_clean()" call:
        with AssertModelCleanCalled() as cm:
            instance, created, updated_fields = create_or_update(
                ModelClass=CreateOrUpdateTestModel,
                lookup={'id': 1},
                call_full_clean=False,
                slug='Also no valid slug!'
            )
            assert isinstance(instance, CreateOrUpdateTestModel)
            assert instance.id == 1
            assert instance.slug == 'Also no valid slug!'
            assert created is False
            assert updated_fields == ['slug']
        assert cm.called_cleans == []
        assert len(cm.missing_cleans) == 1

    @mock.patch.object(timezone, 'now', MockDatetimeGenerator())
    def test_create_or_update_without_lookup(self):
        # create a new entry:

        with AssertModelCleanCalled() as cm:
            instance, created, updated_fields = create_or_update(
                ModelClass=CreateOrUpdateTestModel,
                lookup=None,
                name='First entry',
                slug='first'
            )
            assert isinstance(instance, CreateOrUpdateTestModel)
            assert instance.pk is not None
            assert instance.name == 'First entry'
            assert instance.slug == 'first'
            assert instance.create_dt == parse_dt('2001-01-01T00:00:00+0000')
            assert instance.update_dt == parse_dt('2001-01-01T00:00:00+0000')
            assert created is True
            assert updated_fields is None
        cm.assert_no_missing_cleans()

    @mock.patch.object(timezone, 'now', MockDatetimeGenerator())
    def test_create(self):
        # create a new entry:

        with AssertModelCleanCalled() as cm:
            instance = create(
                ModelClass=CreateOrUpdateTestModel,
                name='First entry',
                slug='first'
            )
            assert isinstance(instance, CreateOrUpdateTestModel)
            assert instance.pk is not None
            assert instance.name == 'First entry'
            assert instance.slug == 'first'
            assert instance.create_dt == parse_dt('2001-01-01T00:00:00+0000')
            assert instance.update_dt == parse_dt('2001-01-01T00:00:00+0000')
        cm.assert_no_missing_cleans()
예제 #7
0
    def test_normal_user_create_minimal_item(self):
        offset = datetime.timedelta(minutes=1)
        with mock.patch.object(timezone, 'now', MockDatetimeGenerator(offset=offset)),\
                mock.patch.object(NowNode, 'render', return_value='MockedNowNode'), \
                mock.patch.object(CsrfTokenNode, 'render', return_value='MockedCsrfTokenNode'):
            self.client.force_login(self.normaluser)

            response = self.client.get('/admin/inventory/itemmodel/add/')
            assert response.status_code == 200
            self.assert_html_parts(
                response,
                parts=(
                    f'<title>Add Item | PyInventory v{__version__}</title>', ))
            assert_html_response_snapshot(response=response, validate=False)

            assert ItemModel.objects.count() == 0

            post_data = dict(ITEM_FORM_DEFAULTS)
            response = self.client.post(
                path='/admin/inventory/itemmodel/add/',
                data=post_data,
            )
            assert response.status_code == 302, response.content.decode(
                'utf-8')  # Form error?
            self.assertRedirects(response,
                                 expected_url='/admin/inventory/itemmodel/')

            data = list(
                ItemModel.objects.values_list('kind__name', 'name', 'version'))
            assert data == [('kind', 'name', 2)
                            ]  # FIXME: Save call done two times!

            item = ItemModel.objects.first()

            self.assert_messages(
                response,
                expected_messages=[
                    f'The Item “<a href="/admin/inventory/itemmodel/{item.pk}/change/"> - name</a>”'
                    ' was added successfully.'
                ])

            assert item.user_id == self.normaluser.pk

            # Test django-tools VersionProtectBaseModel integration:

            assert item.version == 2  # current Version in DB
            post_data['version'] = 1  # Try to overwrite with older version
            post_data['name'] = 'A new Name!'
            response = self.client.post(
                path=f'/admin/inventory/itemmodel/{item.pk}/change/',
                data=post_data,
            )
            self.assert_html_parts(
                response,
                parts=
                (f'<title>Change Item | PyInventory v{__version__}</title>',
                 '<li>Version error: Overwrite version 2 with 1 is forbidden!</li>',
                 '<pre>- &quot;name&quot;\n+ &quot;A new Name!&quot;</pre>'))
            html = response.content.decode('utf-8')
            html = html.replace(str(item.pk), '<removed-UUID>')
            assert_html_snapshot(got=html, validate=False)
예제 #8
0
    def test_auto_group_items(self):
        self.client.force_login(self.normaluser)

        offset = datetime.timedelta(minutes=1)
        with mock.patch.object(timezone, 'now',
                               MockDatetimeGenerator(offset=offset)):
            for main_item_no in range(1, 3):
                main_item = ItemModel.objects.create(
                    id=f'00000000-000{main_item_no}-0000-0000-000000000000',
                    user=self.normaluser,
                    name=f'main item {main_item_no}')
                main_item.full_clean()
                for sub_item_no in range(1, 3):
                    sub_item = ItemModel.objects.create(
                        id=
                        f'00000000-000{main_item_no}-000{sub_item_no}-0000-000000000000',
                        user=self.normaluser,
                        parent=main_item,
                        name=f'sub item {main_item_no}.{sub_item_no}')
                    sub_item.full_clean()

        names = list(
            ItemModel.objects.order_by('id').values_list('name', flat=True))
        assert names == [
            'main item 1',
            'sub item 1.1',
            'sub item 1.2',
            'main item 2',
            'sub item 2.1',
            'sub item 2.2',
        ]

        # Default mode, without any GET parameter -> group "automatic":

        with mock.patch.object(NowNode, 'render', return_value='MockedNowNode'), \
                mock.patch.object(CsrfTokenNode, 'render', return_value='MockedCsrfTokenNode'), \
                self.assertLogs(logger='inventory', level=logging.DEBUG) as logs:
            response = self.client.get(path='/admin/inventory/itemmodel/', )
            assert response.status_code == 200
        self.assert_html_parts(
            response,
            parts=(
                f'<title>Select Item to change | PyInventory v{__version__}</title>',
                '<a href="/admin/inventory/itemmodel/00000000-0001-0000-0000-000000000000/change/">'
                'main item 1</a>',
                '<li><a href="/admin/inventory/itemmodel/00000000-0001-0001-0000-000000000000/change/">'
                'sub item 1.1</a></li>',
            ))
        assert logs.output == [
            'INFO:inventory.admin.item:Group items: True (auto mode: True)',
            'DEBUG:inventory.admin.item:Display sub items inline',
            'DEBUG:inventory.admin.item:Display sub items inline'
        ]
        assert_html_response_snapshot(response=response, validate=False)

        # Search should disable grouping:

        with mock.patch.object(NowNode, 'render', return_value='MockedNowNode'), \
                mock.patch.object(CsrfTokenNode, 'render', return_value='MockedCsrfTokenNode'), \
                self.assertLogs(logger='inventory', level=logging.DEBUG) as logs:
            response = self.client.get(
                path='/admin/inventory/itemmodel/?q=sub+item+2.', )
            assert response.status_code == 200
        self.assert_html_parts(
            response,
            parts=(
                '<input type="text" size="40" name="q" value="sub item 2." id="searchbar" autofocus>',
                '2 results (<a href="?">6 total</a>)',
                '<a href="/admin/inventory/itemmodel/00000000-0002-0001-0000-000000000000/change/">'
                'sub item 2.1</a>',
                '<a href="/admin/inventory/itemmodel/00000000-0002-0002-0000-000000000000/change/">'
                'sub item 2.2</a>',
            ))
        assert logs.output == [
            # grouping disabled?
            'INFO:inventory.admin.item:Group items: False (auto mode: True)'
        ]
        assert_html_response_snapshot(response=response, validate=False)