Folder move added
This commit is contained in:
@@ -96,6 +96,33 @@ class MoveApiGoldenTest(unittest.TestCase):
|
||||
self.assertTrue((self.root1 / "moved.txt").exists())
|
||||
self.assertFalse(src.exists())
|
||||
|
||||
def test_move_directory_success_same_root_and_completed(self) -> None:
|
||||
src_dir = self.root1 / "source-dir"
|
||||
src_dir.mkdir()
|
||||
(src_dir / "nested.txt").write_text("hello", encoding="utf-8")
|
||||
target_parent = self.root1 / "target-parent"
|
||||
target_parent.mkdir()
|
||||
|
||||
response = self._request(
|
||||
"POST",
|
||||
"/api/files/move",
|
||||
{"source": "storage1/source-dir", "destination": "storage1/target-parent/moved-dir"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 202)
|
||||
body = response.json()
|
||||
self.assertEqual(body["status"], "queued")
|
||||
|
||||
detail = self._wait_task(body["task_id"])
|
||||
self.assertEqual(detail["status"], "completed")
|
||||
self.assertEqual(detail["done_items"], 1)
|
||||
self.assertEqual(detail["total_items"], 1)
|
||||
self.assertIsNone(detail["done_bytes"])
|
||||
self.assertIsNone(detail["total_bytes"])
|
||||
self.assertTrue((self.root1 / "target-parent" / "moved-dir").is_dir())
|
||||
self.assertTrue((self.root1 / "target-parent" / "moved-dir" / "nested.txt").exists())
|
||||
self.assertFalse(src_dir.exists())
|
||||
|
||||
def test_move_success_cross_root_create_task_shape_and_completed(self) -> None:
|
||||
src = self.root1 / "source.txt"
|
||||
src.write_text("hello", encoding="utf-8")
|
||||
@@ -116,6 +143,19 @@ class MoveApiGoldenTest(unittest.TestCase):
|
||||
self.assertTrue((self.root2 / "moved.txt").exists())
|
||||
self.assertFalse(src.exists())
|
||||
|
||||
def test_move_directory_cross_root_blocked(self) -> None:
|
||||
src_dir = self.root1 / "source-dir"
|
||||
src_dir.mkdir()
|
||||
|
||||
response = self._request(
|
||||
"POST",
|
||||
"/api/files/move",
|
||||
{"source": "storage1/source-dir", "destination": "storage2/source-dir"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(response.json()["error"]["code"], "invalid_request")
|
||||
|
||||
def test_move_source_not_found(self) -> None:
|
||||
response = self._request(
|
||||
"POST",
|
||||
@@ -126,13 +166,24 @@ class MoveApiGoldenTest(unittest.TestCase):
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(response.json()["error"]["code"], "path_not_found")
|
||||
|
||||
def test_move_source_is_directory_type_conflict(self) -> None:
|
||||
def test_move_directory_source_not_found(self) -> None:
|
||||
response = self._request(
|
||||
"POST",
|
||||
"/api/files/move",
|
||||
{"source": "storage1/missing-dir", "destination": "storage1/out-dir"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(response.json()["error"]["code"], "path_not_found")
|
||||
|
||||
def test_move_source_is_directory_type_conflict_for_file_destination_parent(self) -> None:
|
||||
(self.root1 / "dir").mkdir()
|
||||
(self.root1 / "out.txt").write_text("x", encoding="utf-8")
|
||||
|
||||
response = self._request(
|
||||
"POST",
|
||||
"/api/files/move",
|
||||
{"source": "storage1/dir", "destination": "storage1/out.txt"},
|
||||
{"source": "storage1/dir", "destination": "storage1/out.txt/child"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 409)
|
||||
@@ -151,6 +202,19 @@ class MoveApiGoldenTest(unittest.TestCase):
|
||||
self.assertEqual(response.status_code, 409)
|
||||
self.assertEqual(response.json()["error"]["code"], "already_exists")
|
||||
|
||||
def test_move_directory_destination_exists_already_exists(self) -> None:
|
||||
(self.root1 / "source-dir").mkdir()
|
||||
(self.root1 / "target-dir").mkdir()
|
||||
|
||||
response = self._request(
|
||||
"POST",
|
||||
"/api/files/move",
|
||||
{"source": "storage1/source-dir", "destination": "storage1/target-dir"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 409)
|
||||
self.assertEqual(response.json()["error"]["code"], "already_exists")
|
||||
|
||||
def test_move_traversal_source(self) -> None:
|
||||
response = self._request(
|
||||
"POST",
|
||||
@@ -173,6 +237,33 @@ class MoveApiGoldenTest(unittest.TestCase):
|
||||
self.assertEqual(response.status_code, 403)
|
||||
self.assertEqual(response.json()["error"]["code"], "path_traversal_detected")
|
||||
|
||||
def test_move_directory_destination_inside_source_blocked(self) -> None:
|
||||
src_dir = self.root1 / "source-dir"
|
||||
src_dir.mkdir()
|
||||
(src_dir / "child").mkdir()
|
||||
|
||||
response = self._request(
|
||||
"POST",
|
||||
"/api/files/move",
|
||||
{"source": "storage1/source-dir", "destination": "storage1/source-dir/child/moved-dir"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(response.json()["error"]["code"], "invalid_request")
|
||||
|
||||
def test_move_directory_same_source_destination_blocked(self) -> None:
|
||||
src_dir = self.root1 / "source-dir"
|
||||
src_dir.mkdir()
|
||||
|
||||
response = self._request(
|
||||
"POST",
|
||||
"/api/files/move",
|
||||
{"source": "storage1/source-dir", "destination": "storage1/source-dir"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(response.json()["error"]["code"], "invalid_request")
|
||||
|
||||
def test_move_source_symlink_rejected(self) -> None:
|
||||
target = self.root1 / "real.txt"
|
||||
target.write_text("x", encoding="utf-8")
|
||||
@@ -188,6 +279,22 @@ class MoveApiGoldenTest(unittest.TestCase):
|
||||
self.assertEqual(response.status_code, 409)
|
||||
self.assertEqual(response.json()["error"]["code"], "type_conflict")
|
||||
|
||||
def test_move_directory_source_symlink_rejected(self) -> None:
|
||||
target = self.root1 / "real-dir"
|
||||
target.mkdir()
|
||||
(target / "nested.txt").write_text("x", encoding="utf-8")
|
||||
link = self.root1 / "dir-link"
|
||||
link.symlink_to(target, target_is_directory=True)
|
||||
|
||||
response = self._request(
|
||||
"POST",
|
||||
"/api/files/move",
|
||||
{"source": "storage1/dir-link", "destination": "storage1/out-dir"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 409)
|
||||
self.assertEqual(response.json()["error"]["code"], "type_conflict")
|
||||
|
||||
def test_move_runtime_io_error_failed_task_shape(self) -> None:
|
||||
src = self.root1 / "source.txt"
|
||||
src.write_text("hello", encoding="utf-8")
|
||||
|
||||
Reference in New Issue
Block a user