Пример #1
0
    def test_accept_deleted_version(self):
        date1 = self._store_fetched_table()
        date2 = self._store_fetched_table()

        self.step.notifications = False
        self.step.stored_data_version = date1
        self.step.save()

        delta = self.run_with_async_db(
            commands.do(
                SetStepDataVersion,
                workflow_id=self.workflow.id,
                step=self.step,
                new_version=date2,
            )
        )

        self.step.stored_objects.get(stored_at=date1).delete()

        self.run_with_async_db(commands.undo(self.workflow.id))
        self.step.refresh_from_db()
        self.assertEqual(self.step.stored_data_version, date1)

        self.run_with_async_db(commands.redo(self.workflow.id))
        self.step.refresh_from_db()
        self.assertEqual(self.step.stored_data_version, date2)
Пример #2
0
    def test_delete_tab(self):
        workflow = Workflow.create_and_init()  # tab-1
        tab2 = workflow.tabs.create(position=1, slug="tab-2")
        workflow.tabs.create(position=2, slug="tab-3")

        self.run_with_async_db(
            commands.do(DeleteTab, workflow_id=workflow.id, tab=tab2)
        )
        tab2.refresh_from_db()  # it is only _soft_-deleted.
        self.assertEqual(tab2.is_deleted, True)
        self.assertEqual(
            list(workflow.live_tabs.values_list("slug", "position")),
            [("tab-1", 0), ("tab-3", 1)],
        )

        self.run_with_async_db(commands.undo(workflow.id))
        tab2.refresh_from_db()
        self.assertEqual(tab2.is_deleted, False)
        self.assertEqual(
            list(workflow.live_tabs.values_list("slug", "position")),
            [("tab-1", 0), ("tab-2", 1), ("tab-3", 2)],
        )

        self.run_with_async_db(commands.redo(workflow.id))
        tab2.refresh_from_db()
        self.assertEqual(tab2.is_deleted, True)
        self.assertEqual(
            list(workflow.live_tabs.values_list("slug", "position")),
            [("tab-1", 0), ("tab-3", 1)],
        )
Пример #3
0
    def test_delete_lone_step(self):
        workflow = Workflow.create_and_init()  # tab-1
        tab1 = workflow.tabs.first()
        step = tab1.steps.create(
            order=0,
            slug="step-1",
            last_relevant_delta_id=workflow.last_delta_id,
            params={"url": ""},
        )

        self.run_with_async_db(
            commands.do(DeleteStep, workflow_id=workflow.id, step=step))
        step.refresh_from_db()  # it is only _soft_-deleted.
        self.assertEqual(step.is_deleted, True)
        self.assertEqual(list(tab1.live_steps.values_list("slug", "order")),
                         [])

        self.run_with_async_db(commands.undo(workflow.id))
        step.refresh_from_db()
        self.assertEqual(step.is_deleted, False)
        self.assertEqual(list(tab1.live_steps.values_list("slug", "order")),
                         [("step-1", 0)])

        self.run_with_async_db(commands.redo(workflow.id))
        step.refresh_from_db()  # it is only _soft_-deleted.
        self.assertEqual(step.is_deleted, True)
        self.assertEqual(list(tab1.live_steps.values_list("slug", "order")),
                         [])
Пример #4
0
    def test_reorder_slugs(self):
        workflow = Workflow.create_and_init()  # tab slug: tab-1
        workflow.tabs.create(position=1, slug="tab-2")
        workflow.tabs.create(position=2, slug="tab-3")

        cmd = self.run_with_async_db(
            commands.do(
                ReorderTabs,
                workflow_id=workflow.id,
                new_order=["tab-3", "tab-1", "tab-2"],
            ))
        self.assertEqual(
            list(workflow.live_tabs.values_list("slug", "position")),
            [("tab-3", 0), ("tab-1", 1), ("tab-2", 2)],
        )

        self.run_with_async_db(commands.undo(workflow.id))
        self.assertEqual(
            list(workflow.live_tabs.values_list("slug", "position")),
            [("tab-1", 0), ("tab-2", 1), ("tab-3", 2)],
        )

        self.run_with_async_db(commands.redo(workflow.id))
        self.assertEqual(
            list(workflow.live_tabs.values_list("slug", "position")),
            [("tab-3", 0), ("tab-1", 1), ("tab-2", 2)],
        )
Пример #5
0
    def test_change_notes(self):
        workflow = Workflow.create_and_init()
        step = workflow.tabs.first().steps.create(
            order=0,
            slug="step-1",
            notes="text1",
            last_relevant_delta_id=workflow.last_delta_id,
        )

        # do
        self.run_with_async_db(
            commands.do(
                SetStepNote,
                workflow_id=workflow.id,
                step=step,
                new_value="text2",
            )
        )
        self.assertEqual(step.notes, "text2")
        step.refresh_from_db()
        self.assertEqual(step.notes, "text2")

        # undo
        self.run_with_async_db(commands.undo(workflow.id))
        step.refresh_from_db()
        self.assertEqual(step.notes, "text1")

        # redo
        self.run_with_async_db(commands.redo(workflow.id))
        step.refresh_from_db()
        self.assertEqual(step.notes, "text2")
Пример #6
0
    def test_change_data_version(self):
        # Create two data versions, use the second
        date1 = self._store_fetched_table()
        date2 = self._store_fetched_table()

        self.wf_module.stored_data_version = date2
        self.wf_module.save()

        self.workflow.refresh_from_db()
        v1 = self.workflow.last_delta_id

        # Change back to first version
        cmd = self.run_with_async_db(
            commands.do(
                ChangeDataVersionCommand,
                workflow_id=self.workflow.id,
                wf_module=self.wf_module,
                new_version=date1,
            ))
        self.assertEqual(self.wf_module.stored_data_version, date1)

        self.workflow.refresh_from_db()
        v2 = cmd.id
        # workflow revision should have been incremented
        self.assertEqual(self.wf_module.last_relevant_delta_id, v2)

        # undo
        self.run_with_async_db(commands.undo(cmd))
        self.assertEqual(self.wf_module.last_relevant_delta_id, v1)
        self.assertEqual(self.wf_module.stored_data_version, date2)

        # redo
        self.run_with_async_db(commands.redo(cmd))
        self.assertEqual(self.wf_module.last_relevant_delta_id, v2)
        self.assertEqual(self.wf_module.stored_data_version, date1)
    def test_change_notes(self):
        workflow = Workflow.create_and_init()
        wf_module = workflow.tabs.first().wf_modules.create(
            order=0,
            slug="step-1",
            notes="text1",
            last_relevant_delta_id=workflow.last_delta_id,
        )

        # do
        cmd = self.run_with_async_db(
            commands.do(
                ChangeWfModuleNotesCommand,
                workflow_id=workflow.id,
                wf_module=wf_module,
                new_value="text2",
            )
        )
        self.assertEqual(wf_module.notes, "text2")
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.notes, "text2")

        # undo
        self.run_with_async_db(commands.undo(cmd))
        self.assertEqual(wf_module.notes, "text1")
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.notes, "text1")

        # redo
        self.run_with_async_db(commands.redo(cmd))
        self.assertEqual(wf_module.notes, "text2")
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.notes, "text2")
Пример #8
0
    def test_accept_deleted_version(self):
        """
        Let the user choose whichever version is desired, even if it does not
        exist.

        The errors will be user-visible ... _later_.
        """
        date1 = self._store_fetched_table()
        date2 = self._store_fetched_table()

        self.wf_module.notifications = False
        self.wf_module.stored_data_version = date1
        self.wf_module.save()

        delta = self.run_with_async_db(
            commands.do(
                ChangeDataVersionCommand,
                workflow_id=self.workflow.id,
                wf_module=self.wf_module,
                new_version=date2,
            ))

        self.wf_module.stored_objects.get(stored_at=date1).delete()

        self.run_with_async_db(commands.undo(delta))
        self.wf_module.refresh_from_db()
        self.assertEqual(self.wf_module.stored_data_version, date1)

        self.run_with_async_db(commands.redo(delta))
        self.wf_module.refresh_from_db()
        self.assertEqual(self.wf_module.stored_data_version, date2)
Пример #9
0
    def test_old_style_params(self):
        all_modules = self.tab.live_steps
        step1 = self.tab.steps.create(order=0, slug="step-1")
        step2 = self.tab.steps.create(order=1, slug="step-2")
        step3 = self.tab.steps.create(order=2, slug="step-3")

        # Let's build a new-style Delta, then overwrite it to be old-style
        cmd = self.run_with_async_db(
            commands.do(
                ReorderSteps,
                workflow_id=self.workflow.id,
                tab=self.tab,
                slugs=["step-3", "step-2", "step-1"],
            ))
        cmd.values_for_backward = {
            "legacy_format": [
                {
                    "id": step1.id,
                    "order": 0
                },
                {
                    "id": step2.id,
                    "order": 1
                },
                {
                    "id": step3.id,
                    "order": 2
                },
            ]
        }
        cmd.values_for_forward = {
            "legacy_format": [
                {
                    "id": step3.id,
                    "order": 0
                },
                {
                    "id": step2.id,
                    "order": 1
                },
                {
                    "id": step1.id,
                    "order": 2
                },
            ]
        }
        cmd.save(update_fields=["values_for_backward", "values_for_forward"])

        self.run_with_async_db(commands.undo(self.workflow.id))
        self.assertEqual(
            list(all_modules.values_list("id", flat=True)),
            [step1.id, step2.id, step3.id],
        )

        self.run_with_async_db(commands.redo(self.workflow.id))
        self.assertEqual(
            list(all_modules.values_list("id", flat=True)),
            [step3.id, step2.id, step1.id],
        )
Пример #10
0
 def test_redo_after_final_delta(self, send_update):
     send_update.side_effect = async_noop
     workflow = Workflow.create_and_init(name="hello")
     self.run_with_async_db(
         commands.do(SetWorkflowTitle,
                     workflow_id=workflow.id,
                     new_value="1"))
     send_update.reset_mock()
     self.run_with_async_db(commands.redo(workflow.id))
     send_update.assert_not_called()
    def test_reorder_modules(self):
        all_modules = self.tab.live_wf_modules
        v1 = self.delta.id

        step1 = self.tab.wf_modules.create(last_relevant_delta_id=v1,
                                           order=0,
                                           slug="step-1")
        step2 = self.tab.wf_modules.create(last_relevant_delta_id=v1,
                                           order=1,
                                           slug="step-2")
        step3 = self.tab.wf_modules.create(last_relevant_delta_id=v1,
                                           order=2,
                                           slug="step-3")

        cmd = self.run_with_async_db(
            commands.do(
                ReorderModulesCommand,
                workflow_id=self.workflow.id,
                tab=self.tab,
                new_order=[step1.id, step3.id, step2.id],
            ))
        v2 = cmd.id
        self.assertWfModuleVersions([v1, v2, v2])
        step2.refresh_from_db()
        step3.refresh_from_db()
        self.assertEqual(
            list(all_modules.values_list("id", flat=True)),
            [step1.id, step3.id, step2.id],
        )

        # undo
        self.run_with_async_db(commands.undo(cmd))
        self.assertWfModuleVersions([v1, v1, v1])
        step2.refresh_from_db()
        step3.refresh_from_db()
        self.assertEqual(
            list(all_modules.values_list("id", flat=True)),
            [step1.id, step2.id, step3.id],
        )

        # redo
        self.run_with_async_db(commands.redo(cmd))
        self.assertWfModuleVersions([v1, v2, v2])
        step2.refresh_from_db()
        step3.refresh_from_db()
        self.assertEqual(
            list(all_modules.values_list("id", flat=True)),
            [step1.id, step3.id, step2.id],
        )
Пример #12
0
    def test_redo_modify_last_applied_at(self):
        date0 = datetime.datetime(2000, 1, 1)
        date1 = datetime.datetime.now()

        with freeze_time(date0):
            workflow = Workflow.create_and_init()
            delta = self.run_with_async_db(
                commands.do(SetWorkflowTitle,
                            workflow_id=workflow.id,
                            new_value="1"))
            self.run_with_async_db(commands.undo(workflow.id))

        with freeze_time(date1):
            self.run_with_async_db(commands.redo(workflow.id))

        delta.refresh_from_db()
        self.assertEqual(delta.last_applied_at, date1)
Пример #13
0
    def test_add_module(self):
        all_modules = self.tab.live_steps

        v1 = 1
        existing_module = self.tab.steps.create(
            order=0,
            slug="step-1",
            last_relevant_delta_id=v1,
            params={"url": ""},
        )

        # Add a module, insert before the existing one, check to make sure it
        # went there and old one is after
        cmd = self.run_with_async_db(
            commands.do(
                AddStep,
                workflow_id=self.workflow.id,
                tab=self.workflow.tabs.first(),
                slug="step-2",
                module_id_name=self.module_zipfile.module_id,
                position=0,
                param_values={"url": "https://x.com"},
            )
        )
        self.assertEqual(all_modules.count(), 2)
        added_module = all_modules.get(order=0)
        self.assertNotEqual(added_module, existing_module)
        # Test that supplied param is written
        self.assertEqual(added_module.params["url"], "https://x.com")
        bumped_module = all_modules.get(order=1)
        self.assertEqual(bumped_module, existing_module)

        # undo! undo! ahhhhh everything is on fire! undo!
        self.run_with_async_db(commands.undo(self.workflow.id))
        self.assertEqual(all_modules.count(), 1)
        self.assertEqual(all_modules.first(), existing_module)

        # wait no, we wanted that module
        self.run_with_async_db(commands.redo(self.workflow.id))
        self.assertEqual(all_modules.count(), 2)
        added_module = all_modules.get(order=0)
        self.assertNotEqual(added_module, existing_module)
        bumped_module = all_modules.get(order=1)
        self.assertEqual(bumped_module, existing_module)
Пример #14
0
    def test_set_name(self):
        workflow = Workflow.create_and_init()
        tab = workflow.tabs.first()
        tab.name = "foo"
        tab.save(update_fields=["name"])

        self.run_with_async_db(
            commands.do(SetTabName, workflow_id=workflow.id, tab=tab, new_name="bar")
        )
        tab.refresh_from_db()
        self.assertEqual(tab.name, "bar")

        self.run_with_async_db(commands.undo(workflow.id))
        tab.refresh_from_db()
        self.assertEqual(tab.name, "foo")

        self.run_with_async_db(commands.redo(workflow.id))
        tab.refresh_from_db()
        self.assertEqual(tab.name, "bar")
Пример #15
0
    def test_change_title(self):
        workflow = Workflow.create_and_init(name="title1")

        # Change back to second title, see if it saved
        self.run_with_async_db(
            commands.do(SetWorkflowTitle, workflow_id=workflow.id, new_value="title2")
        )
        workflow.refresh_from_db()
        self.assertEqual(workflow.name, "title2")  # test DB change

        # undo
        self.run_with_async_db(commands.undo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.name, "title1")  # test DB change

        # redo
        self.run_with_async_db(commands.redo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.name, "title2")
Пример #16
0
    def test_redo_modify_updated_at(self, send_update):
        send_update.side_effect = async_noop

        workflow = Workflow.create_and_init()
        self.run_with_async_db(
            commands.do(SetWorkflowTitle,
                        workflow_id=workflow.id,
                        new_value="1"))
        self.run_with_async_db(commands.undo(workflow.id))

        date0 = datetime.datetime.now() - datetime.timedelta(days=1)
        workflow.updated_at = date0  # reset
        workflow.save(update_fields=["updated_at"])

        self.run_with_async_db(commands.redo(workflow.id))
        workflow.refresh_from_db()
        self.assertGreater(workflow.updated_at, date0)

        send_update.assert_called()
        update = send_update.call_args[0][1]
        self.assertEqual(update.workflow.updated_at, workflow.updated_at)
Пример #17
0
    def test_adjust_selected_tab_position(self):
        # tab slug: tab-1
        workflow = Workflow.create_and_init(selected_tab_position=2)
        workflow.tabs.create(position=1, slug="tab-2")
        workflow.tabs.create(position=2, slug="tab-3")

        self.run_with_async_db(
            commands.do(
                ReorderTabs,
                workflow_id=workflow.id,
                new_order=["tab-3", "tab-1", "tab-2"],
            ))
        workflow.refresh_from_db()
        self.assertEqual(workflow.selected_tab_position, 0)

        self.run_with_async_db(commands.undo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.selected_tab_position, 2)

        self.run_with_async_db(commands.redo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.selected_tab_position, 0)
Пример #18
0
    def test_redo_nonfirst_history_item(self, send_update):
        send_update.side_effect = async_noop
        workflow = Workflow.create_and_init(name="hello")
        delta1 = self.run_with_async_db(
            commands.do(SetWorkflowTitle,
                        workflow_id=workflow.id,
                        new_value="1"))
        delta2 = self.run_with_async_db(
            commands.do(SetWorkflowTitle,
                        workflow_id=workflow.id,
                        new_value="2"))
        self.run_with_async_db(commands.undo(workflow.id))

        send_update.reset_mock()
        self.run_with_async_db(commands.redo(workflow.id))

        send_update.assert_called()
        workflow.refresh_from_db()
        self.assertEqual(workflow.name, "2")
        self.assertEqual(workflow.last_delta_id, delta2.id)
        self.assertEqual(list(workflow.deltas.values_list("id", flat=True)),
                         [delta1.id, delta2.id])
Пример #19
0
    def test_change_data_version(self):
        # Create two data versions, use the second
        date1 = self._store_fetched_table(stored_at=datetime.datetime(2021, 6, 24))
        date2 = self._store_fetched_table(stored_at=datetime.datetime(2021, 6, 23))

        self.step.stored_data_version = date2
        self.step.save(update_fields=["stored_data_version"])

        self.workflow.refresh_from_db()
        v1 = self.workflow.last_delta_id

        # Change back to first version
        cmd = self.run_with_async_db(
            commands.do(
                SetStepDataVersion,
                workflow_id=self.workflow.id,
                step=self.step,
                new_version=isoparse("2021-06-24T00:00:00.000000Z"),
            )
        )
        self.assertEqual(self.step.stored_data_version, date1)

        self.workflow.refresh_from_db()
        v2 = cmd.id
        # workflow revision should have been incremented
        self.step.refresh_from_db()
        self.assertEqual(self.step.last_relevant_delta_id, v2)

        # undo
        self.run_with_async_db(commands.undo(self.workflow.id))
        self.step.refresh_from_db()
        self.assertEqual(self.step.last_relevant_delta_id, v1)
        self.assertEqual(self.step.stored_data_version, date2)

        # redo
        self.run_with_async_db(commands.redo(self.workflow.id))
        self.step.refresh_from_db()
        self.assertEqual(self.step.last_relevant_delta_id, v2)
        self.assertEqual(self.step.stored_data_version, date1)
Пример #20
0
    def test_duplicate_nonempty_unrendered_tab(self, send_update,
                                               queue_render):
        send_update.side_effect = async_noop
        queue_render.side_effect = async_noop
        workflow = Workflow.create_and_init()
        init_delta_id = workflow.last_delta_id
        tab = workflow.tabs.first()
        tab.selected_step_position = 1
        tab.save(update_fields=["selected_step_position"])
        # step1 and step2 have not yet been rendered. (But while we're
        # duplicating, conceivably a render could be running; so when we
        # duplicate them, we need to queue a render.)
        step1 = tab.steps.create(
            order=0,
            slug="step-1",
            module_id_name="x",
            params={"p": "s1"},
            last_relevant_delta_id=init_delta_id,
        )
        tab.steps.create(
            order=1,
            slug="step-2",
            module_id_name="y",
            params={"p": "s2"},
            last_relevant_delta_id=init_delta_id,
        )

        cmd = self.run_with_async_db(
            commands.do(
                DuplicateTab,
                workflow_id=workflow.id,
                from_tab=tab,
                slug="tab-2",
                name="Tab 2",
            ))

        # Adds new tab
        cmd.tab.refresh_from_db()
        [step1dup, step2dup] = list(cmd.tab.live_steps.all())
        self.assertFalse(cmd.tab.is_deleted)
        self.assertEqual(cmd.tab.slug, "tab-2")
        self.assertEqual(cmd.tab.name, "Tab 2")
        self.assertEqual(cmd.tab.selected_step_position, 1)
        self.assertEqual(step1dup.order, 0)
        self.assertEqual(step1dup.module_id_name, "x")
        self.assertEqual(step1dup.params, {"p": "s1"})
        self.assertEqual(
            step1dup.last_relevant_delta_id,
            # `cmd.id` would be intuitive, but that would be hard
            # to implement (and we assume we don't need to).
            # (Duplicate also duplicates _cache values_, which
            # means it's expensive to tweak step1's delta ID.)
            step1.last_relevant_delta_id,
        )
        self.assertEqual(step2dup.order, 1)
        self.assertEqual(step2dup.module_id_name, "y")
        self.assertEqual(step2dup.params, {"p": "s2"})
        self.assertNotEqual(step1dup.id, step1.id)
        delta = send_update.mock_calls[0][1][1]
        self.assertEqual(delta.tabs["tab-2"].step_ids,
                         [step1dup.id, step2dup.id])
        self.assertEqual(set(delta.steps.keys()),
                         set([step1dup.id, step2dup.id]))
        step1update = delta.steps[step1dup.id]
        self.assertEqual(step1update.last_relevant_delta_id,
                         step1.last_relevant_delta_id)
        # We should call render: we don't know whether there's a render queued;
        # and these new steps are in need of render.
        queue_render.assert_called_with(workflow.id, cmd.id)
        queue_render.reset_mock()  # so we can assert next time

        # undo
        self.run_with_async_db(commands.undo(workflow.id))
        cmd.tab.refresh_from_db()
        self.assertTrue(cmd.tab.is_deleted)
        delta = send_update.mock_calls[1][1][1]
        self.assertEqual(delta.clear_tab_slugs, frozenset(["tab-2"]))
        self.assertEqual(delta.clear_step_ids,
                         frozenset([step1dup.id, step2dup.id]))
        # No need to call render(): these modules can't possibly have changed,
        # and nobody cares what's in their cache.
        queue_render.assert_not_called()

        # redo
        self.run_with_async_db(commands.redo(workflow.id))
        # Need to call render() again -- these modules are still out-of-date
        queue_render.assert_called_with(workflow.id, cmd.id)
Пример #21
0
 def test_redo_with_no_history(self, websockets_notify):
     websockets_notify.side_effect = async_noop
     workflow = Workflow.create_and_init(name="hello")
     self.run_with_async_db(commands.redo(workflow.id))
     websockets_notify.assert_not_called()
Пример #22
0
    def test_change_parameters(self):
        # Setup: workflow with loadurl module
        #
        # loadurl is a good choice because it has three parameters, two of
        # which are useful.
        workflow = Workflow.create_and_init()

        ModuleVersion.create_or_replace_from_spec({
            "id_name":
            "loadurl",
            "name":
            "loadurl",
            "category":
            "Clean",
            "parameters": [
                {
                    "id_name": "url",
                    "type": "string"
                },
                {
                    "id_name": "has_header",
                    "type": "checkbox",
                    "name": "HH"
                },
                {
                    "id_name": "version_select",
                    "type": "custom"
                },
            ],
        })

        params1 = {
            "url": "http://example.org",
            "has_header": True,
            "version_select": "",
        }

        wf_module = workflow.tabs.first().wf_modules.create(
            module_id_name="loadurl",
            order=0,
            slug="step-1",
            last_relevant_delta_id=workflow.last_delta_id,
            params=params1,
        )

        # Create and apply delta. It should change params.
        cmd = self.run_with_async_db(
            commands.do(
                ChangeParametersCommand,
                workflow_id=workflow.id,
                wf_module=wf_module,
                new_values={
                    "url": "http://example.com/foo",
                    "has_header": False
                },
            ))
        wf_module.refresh_from_db()

        params2 = {
            "url": "http://example.com/foo",
            "has_header": False,
            "version_select": "",
        }
        self.assertEqual(wf_module.params, params2)

        # undo
        self.run_with_async_db(commands.undo(cmd))
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.params, params1)

        # redo
        self.run_with_async_db(commands.redo(cmd))
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.params, params2)
Пример #23
0
    def test_duplicate_empty_tab(self, ws_notify, queue_render):
        ws_notify.side_effect = async_noop
        workflow = Workflow.create_and_init()
        tab = workflow.tabs.first()

        cmd = self.run_with_async_db(
            commands.do(
                DuplicateTabCommand,
                workflow_id=workflow.id,
                from_tab=tab,
                slug="tab-2",
                name="Tab 2",
            )
        )

        # Adds new tab
        cmd.tab.refresh_from_db()
        self.assertFalse(cmd.tab.is_deleted)
        self.assertEqual(cmd.tab.slug, "tab-2")
        self.assertEqual(cmd.tab.name, "Tab 2")
        ws_notify.assert_called_with(
            workflow.id,
            {
                "updateWorkflow": {
                    "name": workflow.name,
                    "public": False,
                    "last_update": cmd.datetime.isoformat(),
                    "tab_slugs": ["tab-1", "tab-2"],
                },
                "updateTabs": {
                    "tab-2": {
                        "slug": "tab-2",
                        "name": "Tab 2",
                        "wf_module_ids": [],
                        "selected_wf_module_position": None,
                    }
                },
                "updateWfModules": {},
            },
        )

        # Backward: should delete tab
        self.run_with_async_db(commands.undo(cmd))
        cmd.tab.refresh_from_db()
        self.assertTrue(cmd.tab.is_deleted)
        ws_notify.assert_called_with(
            workflow.id,
            {
                "updateWorkflow": {
                    "name": workflow.name,
                    "public": False,
                    "last_update": workflow.last_delta.datetime.isoformat(),
                    "tab_slugs": ["tab-1"],
                },
                "clearTabSlugs": ["tab-2"],
                "clearWfModuleIds": [],
            },
        )

        # Forward: should bring us back
        self.run_with_async_db(commands.redo(cmd))
        cmd.tab.refresh_from_db()
        self.assertFalse(cmd.tab.is_deleted)
        ws_notify.assert_called_with(
            workflow.id,
            {
                "updateWorkflow": {
                    "name": workflow.name,
                    "public": False,
                    "last_update": cmd.datetime.isoformat(),
                    "tab_slugs": ["tab-1", "tab-2"],
                },
                "updateTabs": {
                    "tab-2": {
                        "slug": "tab-2",
                        "name": "Tab 2",
                        "wf_module_ids": [],
                        "selected_wf_module_position": None,
                    }
                },
                "updateWfModules": {},
            },
        )

        # There should never be a render: we aren't changing any module
        # outputs.
        queue_render.assert_not_called()
Пример #24
0
    def test_add_module(self):
        existing_module = self.tab.wf_modules.create(
            order=0,
            slug="step-1",
            last_relevant_delta_id=self.delta.id,
            params={"url": ""},
        )

        all_modules = self.tab.live_wf_modules

        self.workflow.refresh_from_db()
        v1 = self.workflow.last_delta_id

        # Add a module, insert before the existing one, check to make sure it
        # went there and old one is after
        cmd = self.run_with_async_db(
            commands.do(
                AddModuleCommand,
                workflow_id=self.workflow.id,
                tab=self.workflow.tabs.first(),
                slug="step-2",
                module_id_name=self.module_version.id_name,
                position=0,
                param_values={"url": "https://x.com"},
            ))
        self.assertEqual(all_modules.count(), 2)
        added_module = all_modules.get(order=0)
        self.assertNotEqual(added_module, existing_module)
        # Test that supplied param is written
        self.assertEqual(added_module.params["url"], "https://x.com")
        bumped_module = all_modules.get(order=1)
        self.assertEqual(bumped_module, existing_module)

        # workflow revision should have been incremented
        self.workflow.refresh_from_db()
        self.assertGreater(self.workflow.last_delta_id, v1)

        # Check the delta chain (short, but should be sweet)
        self.workflow.refresh_from_db()
        self.assertEqual(self.workflow.last_delta, cmd)
        self.assertEqual(cmd.prev_delta_id, self.delta.id)
        with self.assertRaises(Delta.DoesNotExist):
            cmd.next_delta

        # undo! undo! ahhhhh everything is on fire! undo!
        self.run_with_async_db(commands.undo(cmd))
        self.assertEqual(all_modules.count(), 1)
        self.assertEqual(all_modules.first(), existing_module)

        # wait no, we wanted that module
        self.run_with_async_db(commands.redo(cmd))
        self.assertEqual(all_modules.count(), 2)
        added_module = all_modules.get(order=0)
        self.assertNotEqual(added_module, existing_module)
        bumped_module = all_modules.get(order=1)
        self.assertEqual(bumped_module, existing_module)

        # Undo and test deleting the un-applied command. Should delete dangling
        # WfModule too
        self.run_with_async_db(commands.undo(cmd))
        self.assertEqual(all_modules.count(), 1)
        self.assertEqual(all_modules.first(), existing_module)
        cmd.delete_with_successors()
        with self.assertRaises(WfModule.DoesNotExist):
            all_modules.get(pk=added_module.id)  # should be gone
Пример #25
0
 def test_redo_with_no_history(self, send_update):
     send_update.side_effect = async_noop
     workflow = Workflow.create_and_init(name="hello")
     self.run_with_async_db(commands.redo(workflow.id))
     send_update.assert_not_called()
    def test_change_parameters(self):
        # Setup: workflow with loadurl module
        #
        # loadurl is a good choice because it has three parameters, two of
        # which are useful.
        workflow = Workflow.create_and_init()

        module_zipfile = create_module_zipfile(
            "loadsomething",
            spec_kwargs={
                "parameters": [
                    {"id_name": "url", "type": "string"},
                    {"id_name": "has_header", "type": "checkbox", "name": "HH"},
                    {"id_name": "version_select", "type": "custom"},
                ]
            },
        )

        params1 = {
            "url": "http://example.org",
            "has_header": True,
            "version_select": "",
        }

        wf_module = workflow.tabs.first().wf_modules.create(
            module_id_name="loadurl",
            order=0,
            slug="step-1",
            last_relevant_delta_id=workflow.last_delta_id,
            params=params1,
            cached_migrated_params=params1,
            cached_migrated_params_module_version=module_zipfile.version,
        )

        # Create and apply delta. It should change params.
        self.kernel.migrate_params.side_effect = lambda m, p: p
        with self.assertLogs(level=logging.INFO):
            cmd = self.run_with_async_db(
                commands.do(
                    ChangeParametersCommand,
                    workflow_id=workflow.id,
                    wf_module=wf_module,
                    new_values={"url": "http://example.com/foo", "has_header": False},
                )
            )
        wf_module.refresh_from_db()

        params2 = {
            "url": "http://example.com/foo",
            "has_header": False,
            "version_select": "",
        }
        self.assertEqual(wf_module.params, params2)

        # undo
        with self.assertLogs(level=logging.INFO):
            # building clientside.Update will migrate_params(), so we need
            # to capture logs.
            self.run_with_async_db(commands.undo(cmd))
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.params, params1)

        # redo
        with self.assertLogs(level=logging.INFO):
            # building clientside.Update will migrate_params(), so we need
            # to capture logs.
            self.run_with_async_db(commands.redo(cmd))
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.params, params2)
Пример #27
0
    def test_delete_custom_report_blocks(self, send_update):
        future_none = asyncio.Future()
        future_none.set_result(None)
        send_update.return_value = future_none

        workflow = Workflow.create_and_init(has_custom_report=True)  # tab-1
        tab1 = workflow.tabs.first()
        step1 = tab1.steps.create(
            order=0,
            slug="step-1",
            last_relevant_delta_id=workflow.last_delta_id,
            params={"url": ""},
        )
        step2 = tab1.steps.create(
            order=0,
            slug="step-2",
            last_relevant_delta_id=workflow.last_delta_id,
            params={"url": ""},
        )

        # Report will include the step twice, and have another step elsewhere
        # that should not be touched
        block1 = workflow.blocks.create(position=0,
                                        slug="block-step-1-1",
                                        block_type="Chart",
                                        step=step1)
        block2 = workflow.blocks.create(position=1,
                                        slug="block-step-2",
                                        block_type="Chart",
                                        step=step2)
        block3 = workflow.blocks.create(position=2,
                                        slug="block-step-1-2",
                                        block_type="Chart",
                                        step=step1)

        self.run_with_async_db(
            commands.do(DeleteStep, workflow_id=workflow.id, step=step1))

        with self.assertRaises(Block.DoesNotExist):
            block1.refresh_from_db()
        with self.assertRaises(Block.DoesNotExist):
            block3.refresh_from_db()
        block2.refresh_from_db()
        self.assertEqual(block2.position, 0)

        send_update.assert_called()
        update = send_update.call_args[0][1]
        self.assertEqual(update.workflow.block_slugs, ["block-step-2"])
        self.assertEqual(update.tabs,
                         {"tab-1": clientside.TabUpdate(step_ids=[step2.id])})
        self.assertEqual(update.clear_step_ids, frozenset([step1.id]))
        self.assertEqual(update.blocks, {})

        self.run_with_async_db(commands.undo(workflow.id))
        # The old blocks are deleted. We expect new blocks with new IDs.
        with self.assertRaises(Block.DoesNotExist):
            block1.refresh_from_db()
        with self.assertRaises(Block.DoesNotExist):
            block3.refresh_from_db()
        new_block1 = workflow.blocks.get(slug=block1.slug)
        new_block3 = workflow.blocks.get(slug=block3.slug)
        self.assertEqual(new_block1.step_id, step1.id)
        self.assertEqual(new_block3.step_id, step1.id)
        block2.refresh_from_db()
        self.assertEqual(new_block1.position, 0)
        self.assertEqual(block2.position, 1)
        self.assertEqual(new_block3.position, 2)
        send_update.assert_called()
        update = send_update.call_args[0][1]
        self.assertEqual(
            update.workflow.block_slugs,
            ["block-step-1-1", "block-step-2", "block-step-1-2"],
        )
        self.assertEqual(
            update.tabs,
            {"tab-1": clientside.TabUpdate(step_ids=[step1.id, step2.id])})
        self.assertEqual(
            update.blocks,
            {
                "block-step-1-1": clientside.ChartBlock("step-1"),
                "block-step-1-2": clientside.ChartBlock("step-1"),
            },
        )

        self.run_with_async_db(commands.redo(workflow.id))
        block2.refresh_from_db()
        self.assertEqual(block2.position, 0)
Пример #28
0
    def test_duplicate_empty_tab(self, send_update, queue_render):
        send_update.side_effect = async_noop
        workflow = Workflow.create_and_init()
        tab = workflow.tabs.first()

        cmd = self.run_with_async_db(
            commands.do(
                DuplicateTab,
                workflow_id=workflow.id,
                from_tab=tab,
                slug="tab-2",
                name="Tab 2",
            ))

        # Adds new tab
        cmd.tab.refresh_from_db()
        self.assertFalse(cmd.tab.is_deleted)
        self.assertEqual(cmd.tab.slug, "tab-2")
        self.assertEqual(cmd.tab.name, "Tab 2")
        workflow.refresh_from_db()
        send_update.assert_called_with(
            workflow.id,
            clientside.Update(
                workflow=clientside.WorkflowUpdate(
                    updated_at=workflow.updated_at,
                    tab_slugs=["tab-1", "tab-2"]),
                tabs={
                    "tab-2":
                    clientside.TabUpdate(
                        slug="tab-2",
                        name="Tab 2",
                        step_ids=[],
                        selected_step_index=None,
                    )
                },
            ),
        )

        # Backward: should delete tab
        self.run_with_async_db(commands.undo(workflow.id))
        cmd.tab.refresh_from_db()
        self.assertTrue(cmd.tab.is_deleted)
        workflow.refresh_from_db()
        send_update.assert_called_with(
            workflow.id,
            clientside.Update(
                workflow=clientside.WorkflowUpdate(
                    updated_at=workflow.updated_at, tab_slugs=["tab-1"]),
                clear_tab_slugs=frozenset(["tab-2"]),
            ),
        )

        # Forward: should bring us back
        self.run_with_async_db(commands.redo(workflow.id))
        cmd.tab.refresh_from_db()
        self.assertFalse(cmd.tab.is_deleted)
        workflow.refresh_from_db()
        send_update.assert_called_with(
            workflow.id,
            clientside.Update(
                workflow=clientside.WorkflowUpdate(
                    updated_at=workflow.updated_at,
                    tab_slugs=["tab-1", "tab-2"]),
                tabs={
                    "tab-2":
                    clientside.TabUpdate(
                        slug="tab-2",
                        name="Tab 2",
                        step_ids=[],
                        selected_step_index=None,
                    )
                },
            ),
        )

        # There should never be a render: we aren't changing any module
        # outputs.
        queue_render.assert_not_called()