Example #1
0
    def test_fetch_wf_module(self, save_result, load_module):
        result = ProcessResult(pd.DataFrame({'A': [1]}), error='hi')

        async def fake_fetch(*args, **kwargs):
            return result

        fake_module = Mock(LoadedModule)
        load_module.return_value = fake_module
        fake_module.fetch.side_effect = fake_fetch

        workflow = Workflow.create_and_init()
        wf_module = workflow.tabs.first().wf_modules.create(
            order=0,
            next_update=parser.parse('Aug 28 1999 2:24PM UTC'),
            update_interval=600)

        now = parser.parse('Aug 28 1999 2:24:02PM UTC')
        due_for_update = parser.parse('Aug 28 1999 2:34PM UTC')

        with self.assertLogs(fetch.__name__, logging.DEBUG):
            self.run_with_async_db(
                fetch.fetch_wf_module(workflow.id, wf_module, now))

        save_result.assert_called_with(workflow.id, wf_module, result)

        wf_module.refresh_from_db()
        self.assertEqual(wf_module.last_update_check, now)
        self.assertEqual(wf_module.next_update, due_for_update)
Example #2
0
    def _test_fetch(self, fn, wf_module, save, load) -> ProcessResult:
        """
        Stub out a `fetch` method for `wf_module`.

        Return result.
        """
        if wf_module.module_version is None:
            # White-box: we aren't testing what happens in the (valid) case
            # that a ModuleVersion has been deleted while in use. Pretend it's
            # there.
            wf_module._module_version = ModuleVersion(spec={'parameters': []})

        try:
            workflow_id = wf_module.workflow_id
        except AttributeError:  # No tab/workflow in database
            workflow_id = 1

        # Mock the module we load, so it calls fn() directly.
        load.return_value = LoadedModule('test', '1', False, fetch_impl=fn)
        load.return_value.fetch = fn
        save.return_value = future_none

        # Mock wf_module.save(), which we aren't testing.
        wf_module.save = Mock()

        with self.assertLogs(fetch.__name__, logging.DEBUG):
            self.run_with_async_db(
                fetch.fetch_wf_module(workflow_id, wf_module, timezone.now()))

        save.assert_called_once()
        self.assertEqual(save.call_args[0][0], workflow_id)
        self.assertEqual(save.call_args[0][1], wf_module)

        result = save.call_args[0][2]
        return result
Example #3
0
    def test_crashing_module(self, load_module):
        async def fake_fetch(*args, **kwargs):
            raise ValueError('boo')

        fake_module = Mock(LoadedModule)
        load_module.return_value = fake_module
        fake_module.fetch.side_effect = fake_fetch

        workflow = Workflow.objects.create()
        tab = workflow.tabs.create(position=0)
        wf_module = tab.wf_modules.create(
            order=0,
            next_update=parser.parse('Aug 28 1999 2:24PM UTC'),
            update_interval=600)

        now = parser.parse('Aug 28 1999 2:34:02PM UTC')
        due_for_update = parser.parse('Aug 28 1999 2:44PM UTC')

        with self.assertLogs(fetch.__name__, level='ERROR') as cm:
            # We should log the actual error
            self.run_with_async_db(
                fetch.fetch_wf_module(workflow.id, wf_module, now))
            self.assertEqual(cm.records[0].exc_info[0], ValueError)

        wf_module.refresh_from_db()
        # [adamhooper, 2018-10-26] while fiddling with tests, I changed the
        # behavior to record the update check even when module fetch fails.
        # Previously, an exception would prevent updating last_update_check,
        # and I think that must be wrong.
        self.assertEqual(wf_module.last_update_check, now)
        self.assertEqual(wf_module.next_update, due_for_update)
Example #4
0
    def test_fetch_wf_module_skip_missed_update(self, load_module):
        workflow = Workflow.objects.create()
        tab = workflow.tabs.create(position=0)
        wf_module = tab.wf_modules.create(
            order=0,
            next_update=parser.parse('Aug 28 1999 2:24PM UTC'),
            update_interval=600)

        load_module.side_effect = Exception('caught')  # least-code test case

        now = parser.parse('Aug 28 1999 2:34:02PM UTC')
        due_for_update = parser.parse('Aug 28 1999 2:44PM UTC')

        with self.assertLogs(fetch.__name__):
            self.run_with_async_db(
                fetch.fetch_wf_module(workflow.id, wf_module, now))

        wf_module.refresh_from_db()
        self.assertEqual(wf_module.next_update, due_for_update)
Example #5
0
    def test_fetch_ignore_wf_module_deleted_when_updating(
            self, save_result, load_module):
        """
        It's okay if wf_module is gone when updating wf_module.next_update.
        """
        workflow = Workflow.create_and_init()
        wf_module = workflow.tabs.first().wf_modules.create(
            order=0,
            next_update=parser.parse('Aug 28 1999 2:24PM UTC'),
            update_interval=600)

        async def fake_fetch(*args, **kwargs):
            return ProcessResult(pd.DataFrame({'A': [1]}))

        fake_module = Mock(LoadedModule)
        load_module.return_value = fake_module
        fake_module.fetch.side_effect = fake_fetch

        # We're testing what happens if wf_module disappears after save, before
        # update. To mock that, delete after fetch, when saving result.
        async def fake_save(workflow_id, wf_module, *args, **kwargs):

            @database_sync_to_async
            def do_delete():
                # We can't just call wf_module.delete(), because that will
                # change wf_module.id, which the code under test will notice.
                # We want to test what happens when wf_module.id is not None
                # and the value is not in the DB. Solution: look up a copy and
                # delete the copy.
                WfModule.objects.get(id=wf_module.id).delete()

            await do_delete()

        save_result.side_effect = fake_save

        now = parser.parse('Aug 28 1999 2:34:02PM UTC')

        # Assert fetch does not crash with
        # DatabaseError: Save with update_fields did not affect any rows
        with self.assertLogs(fetch.__name__, level='DEBUG'):
            self.run_with_async_db(
                fetch.fetch_wf_module(workflow.id, wf_module, now))
Example #6
0
    def test_fetch_poll_when_setting_next_update(self, save_result,
                                                 load_module):
        """
        Handle `.auto_update_data` and `.update_interval` changing mid-fetch.
        """
        workflow = Workflow.create_and_init()
        wf_module = workflow.tabs.first().wf_modules.create(
            order=0,
            auto_update_data=True,
            next_update=parser.parse('Aug 28 1999 2:24PM UTC'),
            update_interval=600)

        async def fake_fetch(*args, **kwargs):
            return ProcessResult(pd.DataFrame({'A': [1]}))

        fake_module = Mock(LoadedModule)
        load_module.return_value = fake_module
        fake_module.fetch.side_effect = fake_fetch

        # We're testing what happens if wf_module disappears after save, before
        # update. To mock that, delete after fetch, when saving result.
        async def fake_save(workflow_id, wf_module, *args, **kwargs):

            @database_sync_to_async
            def change_wf_module_during_fetch():
                WfModule.objects.filter(id=wf_module.id).update(
                    auto_update_data=False,
                    next_update=None,
                )

            await change_wf_module_during_fetch()

        save_result.side_effect = fake_save

        now = parser.parse('Aug 28 1999 2:34:02PM UTC')

        with self.assertLogs(fetch.__name__, level='DEBUG'):
            self.run_with_async_db(
                fetch.fetch_wf_module(workflow.id, wf_module, now))
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.auto_update_data, False)
        self.assertIsNone(wf_module.next_update)