From 77e9d9dfa5681bc32e8aec8558d0fa449805e3ac Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Tue, 21 May 2024 05:53:37 +0200
Subject: [PATCH] Fix minor ProcessStep POST handling bugs.

---
 plomtask/http.py       |  4 +++-
 templates/process.html |  2 +-
 tests/processes.py     | 53 +++++++++++++++++++++++++++++++++++++++++-
 tests/utils.py         |  3 ++-
 4 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/plomtask/http.py b/plomtask/http.py
index 4841472..f5e24ed 100644
--- a/plomtask/http.py
+++ b/plomtask/http.py
@@ -341,8 +341,10 @@ class TaskHandler(BaseHTTPRequestHandler):
         process.set_disables(self.conn, self.form_data.get_all_int('disables'))
         process.calendarize = self.form_data.get_all_str('calendarize') != []
         process.save(self.conn)
-        process.explicit_steps = []
         steps: list[tuple[int | None, int, int | None]] = []
+        for step_id in self.form_data.get_all_int('keep_step'):
+            if step_id not in self.form_data.get_all_int('steps'):
+                raise BadFormatException('trying to keep unknown step')
         for step_id in self.form_data.get_all_int('steps'):
             for step_process_id in self.form_data.get_all_int(
                     f'new_step_to_{step_id}'):
diff --git a/templates/process.html b/templates/process.html
index 7ad59b8..a07645e 100644
--- a/templates/process.html
+++ b/templates/process.html
@@ -22,7 +22,7 @@
 </td>
 <td>
 {% if step_node.is_explicit %}
-add: <input name="new_step_to_{{step_id}}" list="candidates" autocomplete="off" />
+add sub-step: <input name="new_step_to_{{step_id}}" list="candidates" autocomplete="off" />
 {% endif %}
 </td>
 </tr>
diff --git a/tests/processes.py b/tests/processes.py
index 578d545..127a3f6 100644
--- a/tests/processes.py
+++ b/tests/processes.py
@@ -243,8 +243,59 @@ class TestsWithServer(TestCaseWithServer):
         self.check_post(form_data, '/process?id=6', 404)
         self.check_post(form_data, '/process?id=5', 302, '/processes')
 
+    def test_do_POST_process_steps(self) -> None:
+        """Test behavior of ProcessStep posting."""
+        form_data_1 = self.post_process(1)
+        self.post_process(2)
+        self.post_process(3)
+        form_data_1['new_top_step'] = [2]
+        self.post_process(1, form_data_1)
+        retrieved_process = Process.by_id(self.db_conn, 1)
+        self.assertEqual(len(retrieved_process.explicit_steps), 1)
+        retrieved_step = retrieved_process.explicit_steps[0]
+        self.assertEqual(retrieved_step.step_process_id, 2)
+        self.assertEqual(retrieved_step.owner_id, 1)
+        self.assertEqual(retrieved_step.parent_step_id, None)
+        form_data_1['new_top_step'] = []
+        self.post_process(1, form_data_1)
+        retrieved_process = Process.by_id(self.db_conn, 1)
+        self.assertEqual(retrieved_process.explicit_steps, [])
+        with self.assertRaises(NotFoundException):
+            ProcessStep.by_id(self.db_conn, retrieved_step.id_)
+        form_data_1['new_top_step'] = [3]
+        self.post_process(1, form_data_1)
+        retrieved_process = Process.by_id(self.db_conn, 1)
+        retrieved_step = retrieved_process.explicit_steps[0]
+        self.assertEqual(retrieved_step.step_process_id, 3)
+        self.assertEqual(retrieved_step.owner_id, 1)
+        self.assertEqual(retrieved_step.parent_step_id, None)
+        form_data_1['new_top_step'] = []
+        form_data_1['steps'] = [retrieved_step.id_]
+        self.post_process(1, form_data_1)
+        retrieved_process = Process.by_id(self.db_conn, 1)
+        self.assertEqual(retrieved_process.explicit_steps, [])
+        form_data_1['steps'] = []
+        form_data_1['keep_step'] = [retrieved_step.id_]
+        self.check_post(form_data_1, '/process?id=1', 400, '/process?id=1')
+        form_data_1['steps'] = [retrieved_step.id_]
+        form_data_1['keep_step'] = [retrieved_step.id_]
+        self.check_post(form_data_1, '/process?id=1', 400, '/process?id=1')
+        form_data_1[f'step_{retrieved_step.id_}_process_id'] = [2]
+        self.post_process(1, form_data_1)
+        retrieved_process = Process.by_id(self.db_conn, 1)
+        self.assertEqual(len(retrieved_process.explicit_steps), 1)
+        retrieved_step = retrieved_process.explicit_steps[0]
+        self.assertEqual(retrieved_step.step_process_id, 2)
+        self.assertEqual(retrieved_step.owner_id, 1)
+        self.assertEqual(retrieved_step.parent_step_id, None)
+        form_data_1['new_top_step'] = ['foo']
+        form_data_1['steps'] = []
+        form_data_1['keep_step'] = []
+        self.check_post(form_data_1, '/process?id=1', 400, '/process?id=1')
+        retrieved_process = Process.by_id(self.db_conn, 1)
+        self.assertEqual(len(retrieved_process.explicit_steps), 1)
+
     def test_do_GET(self) -> None:
         """Test /process and /processes response codes."""
-        self.post_process()
         self.check_get_defaults('/process')
         self.check_get('/processes', 200)
diff --git a/tests/utils.py b/tests/utils.py
index a42b3f3..6f44f61 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -262,5 +262,6 @@ class TestCaseWithServer(TestCaseWithDB):
         """POST basic Process."""
         if not form_data:
             form_data = {'title': 'foo', 'description': 'foo', 'effort': 1.1}
-        self.check_post(form_data, '/process?id=', 302, f'/process?id={id_}')
+        self.check_post(form_data, f'/process?id={id_}', 302,
+                        f'/process?id={id_}')
         return form_data
-- 
2.30.2