From f54941dd80bb1e415159758dc44c93df904fb74c Mon Sep 17 00:00:00 2001 From: retoor Date: Sun, 9 Nov 2025 02:34:06 +0100 Subject: [PATCH] Broken. --- retoors/views/site.py | 8 +- tests/test_file_browser.py | 174 +++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 3 deletions(-) diff --git a/retoors/views/site.py b/retoors/views/site.py index 158f705..c3935b7 100644 --- a/retoors/views/site.py +++ b/retoors/views/site.py @@ -298,7 +298,7 @@ class FileBrowserView(web.View): return json_response({"error": "Failed to generate share links for any selected items"}, status=500) logger.warning(f"FileBrowserView: Unknown file action for POST request: {route_name}") - raise web.HTTPBadRequest(text="Unknown file action") + return web.Response(status=400, text="Unknown file action") @login_required async def get_download_file(self): @@ -336,7 +336,8 @@ class FileBrowserView(web.View): return aiohttp_jinja2.render_template( "pages/errors/404.html", self.request, - {"request": self.request, "message": "Shared link is invalid or has expired."} + {"request": self.request, "message": "Shared link is invalid or has expired."}, + status=404 ) user_email = shared_item["user_email"] @@ -390,7 +391,8 @@ class FileBrowserView(web.View): return aiohttp_jinja2.render_template( "pages/errors/404.html", self.request, - {"request": self.request, "message": "Shared link is invalid or has expired."} + {"request": self.request, "message": "Shared link is invalid or has expired."}, + status=404 ) # Ensure the shared item is a directory if a file_path is provided diff --git a/tests/test_file_browser.py b/tests/test_file_browser.py index 05d041c..c472f26 100644 --- a/tests/test_file_browser.py +++ b/tests/test_file_browser.py @@ -208,6 +208,25 @@ async def test_file_browser_new_folder(logged_in_client: TestClient, file_servic expected_path = temp_user_files_dir / user_email / "new_folder_via_web" assert expected_path.is_dir() +@pytest.mark.asyncio +async def test_file_browser_new_folder_missing_name(logged_in_client: TestClient): + resp = await logged_in_client.post("/files/new_folder", data={"folder_name": ""}, allow_redirects=False) + assert resp.status == 302 + assert "error=Folder+name+is+required" in resp.headers["Location"] + +@pytest.mark.asyncio +async def test_file_browser_new_folder_exists(logged_in_client: TestClient, file_service_instance, mocker): + user_email = "test@example.com" + folder_name = "existing_folder_web" + await file_service_instance.create_folder(user_email, folder_name) # Create it first + + # Mock create_folder to return False, simulating it already exists or failed + mocker.patch.object(file_service_instance, "create_folder", return_value=False) + + resp = await logged_in_client.post("/files/new_folder", data={"folder_name": folder_name}, allow_redirects=False) + assert resp.status == 302 + assert f"error=Folder+'{folder_name}'+already+exists+or+could+not+be+created" in resp.headers["Location"] + @pytest.mark.asyncio async def test_file_browser_upload_file(logged_in_client: TestClient, file_service_instance, temp_user_files_dir): user_email = "test@example.com" @@ -363,6 +382,161 @@ async def test_file_browser_share_multiple_items_no_paths(logged_in_client: Test data = await resp.json() assert data["error"] == "No items selected for sharing" +@pytest.mark.asyncio +async def test_file_browser_share_file_missing_path(logged_in_client: TestClient): + resp = await logged_in_client.post("/files/share/", json={}) # No file_path in URL + assert resp.status == 400 + data = await resp.json() + assert data["error"] == "File path is required for sharing" + +@pytest.mark.asyncio +async def test_file_browser_share_file_fail_generate_link(logged_in_client: TestClient, file_service_instance, mocker): + user_email = "test@example.com" + file_name = "fail_share_link.txt" + await file_service_instance.upload_file(user_email, file_name, b"content") + + mocker.patch.object(file_service_instance, "generate_share_link", return_value=None) + + resp = await logged_in_client.post(f"/files/share/{file_name}") + assert resp.status == 500 + data = await resp.json() + assert data["error"] == "Failed to generate share link" + +@pytest.mark.asyncio +async def test_file_browser_delete_item_missing_path(logged_in_client: TestClient): + resp = await logged_in_client.post("/files/delete/", allow_redirects=False) # No file_path in URL + assert resp.status == 302 + assert "error=Item+path+is+required+for+deletion" in resp.headers["Location"] + +@pytest.mark.asyncio +async def test_file_browser_delete_item_fail(logged_in_client: TestClient, file_service_instance, mocker): + user_email = "test@example.com" + file_name = "fail_delete.txt" + await file_service_instance.upload_file(user_email, file_name, b"content") + + mocker.patch.object(file_service_instance, "delete_item", return_value=False) + + resp = await logged_in_client.post(f"/files/delete/{file_name}", allow_redirects=False) + assert resp.status == 302 + assert "error=Failed+to+delete+item+-+it+may+not+exist" in resp.headers["Location"] + +@pytest.mark.asyncio +async def test_file_browser_download_shared_file_handler_fail_get_content(client: TestClient, file_service_instance, mocker): + user_email = "test@example.com" + folder_name = "shared_folder" + file_name = "nested.txt" + share_id = "test_share_id" + + mocker.patch.object(file_service_instance, "get_shared_item", return_value={ + "user_email": user_email, + "item_path": folder_name, + "share_id": share_id, + "created_at": datetime.datetime.now(datetime.timezone.utc).isoformat(), + "expires_at": (datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=7)).isoformat() + }) + mocker.patch.object(file_service_instance, "_get_user_file_path", return_value=mocker.MagicMock(is_dir=lambda: True)) + mocker.patch.object(file_service_instance, "get_shared_file_content", return_value=None) + + resp = await client.get(f"/shared_file/{share_id}/download?file_path={file_name}") + assert resp.status == 404 + text = await resp.text() + assert "Shared file not found or inaccessible within the shared folder." in text +@pytest.mark.asyncio +async def test_file_browser_download_shared_file_handler_not_a_directory(client: TestClient, file_service_instance, mocker): + user_email = "test@example.com" + file_name = "shared_file.txt" + share_id = "test_share_id" + + mocker.patch.object(file_service_instance, "get_shared_item", return_value={ + "user_email": user_email, + "item_path": file_name, + "share_id": share_id, + "created_at": datetime.datetime.now(datetime.timezone.utc).isoformat(), + "expires_at": (datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=7)).isoformat() + }) + mocker.patch.object(file_service_instance, "_get_user_file_path", return_value=mocker.MagicMock(is_dir=lambda: False)) + + resp = await client.get(f"/shared_file/{share_id}/download?file_path=some_file.txt") + assert resp.status == 400 + text = await resp.text() + assert "Cannot download specific files from a shared item that is not a folder." in text +@pytest.mark.asyncio +async def test_file_browser_download_shared_file_handler_shared_item_not_found(client: TestClient, file_service_instance, mocker): + mocker.patch.object(file_service_instance, "get_shared_item", return_value=None) + resp = await client.get("/shared_file/nonexistent_share_id/download?file_path=some_file.txt") + assert resp.status == 404 + text = await resp.text() + assert "Shared link is invalid or has expired." in text + +@pytest.mark.asyncio +async def test_file_browser_download_shared_file_handler_missing_file_path(client: TestClient): + resp = await client.get("/shared_file/some_share_id/download") + assert resp.status == 400 + assert "File path is required for download from shared folder." in await resp.text() + +@pytest.mark.asyncio +async def test_file_browser_shared_file_handler_fail_get_content(client: TestClient, file_service_instance, mocker): + user_email = "test@example.com" + file_name = "shared_file.txt" + share_id = "test_share_id" + + mocker.patch.object(file_service_instance, "get_shared_item", return_value={ + "user_email": user_email, + "item_path": file_name, + "share_id": share_id, + "created_at": datetime.datetime.now(datetime.timezone.utc).isoformat(), + "expires_at": (datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=7)).isoformat() + }) + mocker.patch("pathlib.Path.is_file", return_value=True) # Simulate it's a file + mocker.patch.object(file_service_instance, "get_shared_file_content", return_value=None) + + resp = await client.get(f"/shared_file/{share_id}") + assert resp.status == 404 + text = await resp.text() + assert "Shared file not found or inaccessible" in text + +@pytest.mark.asyncio +async def test_file_browser_shared_file_handler_neither_file_nor_dir(client: TestClient, file_service_instance, mocker): + user_email = "test@example.com" + item_path = "mystery_item" + share_id = "test_share_id" + + mocker.patch.object(file_service_instance, "get_shared_item", return_value={ + "user_email": user_email, + "item_path": item_path, + "share_id": share_id, + "created_at": datetime.datetime.now(datetime.timezone.utc).isoformat(), + "expires_at": (datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=7)).isoformat() + }) + + # Mock Path.is_file and Path.is_dir to return False + mocker.patch("pathlib.Path.is_file", return_value=False) + mocker.patch("pathlib.Path.is_dir", return_value=False) + + resp = await client.get(f"/shared_file/{share_id}") + assert resp.status == 404 + text = await resp.text() + assert "Shared item not found" in text + +@pytest.mark.asyncio +async def test_file_browser_shared_file_handler_not_found(client: TestClient, file_service_instance, mocker): + mocker.patch.object(file_service_instance, "get_shared_item", return_value=None) + resp = await client.get("/shared_file/nonexistent_share_id") + assert resp.status == 404 + text = await resp.text() + assert "Shared link is invalid or has expired." in text + +@pytest.mark.asyncio +async def test_file_browser_unknown_post_action(logged_in_client: TestClient, mocker): + # Mock the route name to simulate an unknown action + mock_route = mocker.MagicMock(name="unknown_action") + mock_route.current_app = logged_in_client.app # Provide a mock current_app + mocker.patch("aiohttp.web_request.Request.match_info", new_callable=mocker.PropertyMock, return_value={"route": mock_route}) + + resp = await logged_in_client.post("/files/some_unknown_action", allow_redirects=False) + assert resp.status == 400 + assert "Unknown file action" in await resp.text() + @pytest.mark.asyncio async def test_file_browser_share_multiple_items_some_fail(logged_in_client: TestClient, file_service_instance, mocker): user_email = "test@example.com"