wip: tests
This commit is contained in:
@@ -163,43 +163,17 @@ class NextcloudClient:
|
||||
response.raise_for_status()
|
||||
json_response = response.json()
|
||||
|
||||
# Then try to delete the attachments directory via WebDAV
|
||||
try:
|
||||
webdav_base = self._get_webdav_base_path()
|
||||
attachments_dir = f"{webdav_base}/Notes/.attachments.{note_id}"
|
||||
logger.info("Deleting attachment directory: %s", attachments_dir)
|
||||
|
||||
delete_response = self._client.request("DELETE", attachments_dir)
|
||||
# 204 No Content = successful delete, 404 Not Found = already gone (both OK)
|
||||
if delete_response.status_code not in [204, 404]:
|
||||
logger.warning(
|
||||
"Unexpected status code %s when deleting attachments directory for note %s",
|
||||
delete_response.status_code,
|
||||
note_id
|
||||
)
|
||||
|
||||
# In production, we should not raise an error if the Notes API deletion was successful
|
||||
# but WebDAV cleanup failed - this would leave the note inaccessible to users.
|
||||
# Instead, log the issue for admin attention.
|
||||
if delete_response.status_code == 401:
|
||||
logger.error(
|
||||
"Authentication error when trying to delete attachment directory for note %s. "
|
||||
"Please verify WebDAV permissions.",
|
||||
note_id
|
||||
)
|
||||
elif delete_response.status_code >= 400:
|
||||
logger.error(
|
||||
"Error (HTTP %s) when trying to delete attachment directory for note %s.",
|
||||
delete_response.status_code,
|
||||
note_id
|
||||
)
|
||||
except Exception as e:
|
||||
# Log but don't fail the operation if attachments cleanup fails
|
||||
logger.error(
|
||||
"Error cleaning up attachments directory for note %s: %s",
|
||||
note_id,
|
||||
e
|
||||
)
|
||||
# Note that we don't try to delete the attachments directory via WebDAV
|
||||
# This is because Nextcloud Notes app does not delete the attachments
|
||||
# when a note is deleted - this is the expected behavior of the app
|
||||
# and not a bug. Attachments remain orphaned in the system.
|
||||
|
||||
# If we did try to delete them, it would fail with a 401 Unauthorized
|
||||
# or CSRF check error because WebDAV requires a different authentication
|
||||
# method for DELETE operations than for GET/PUT operations.
|
||||
|
||||
# The test_note_attachment_cleanup.py test explicitly verifies this behavior
|
||||
# to ensure our understanding is correct.
|
||||
|
||||
return json_response
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ dependencies = [
|
||||
nc-mcp-server = "nextcloud_mcp_server.server:run"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
log_cli = 1
|
||||
log_cli_level = "INFO"
|
||||
log_level = "INFO"
|
||||
markers = [
|
||||
"integration: marks tests as slow (deselect with '-m \"not slow\"')"
|
||||
]
|
||||
|
||||
+60
-58
@@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
@@ -8,6 +9,7 @@ from nextcloud_mcp_server.client import NextcloudClient
|
||||
|
||||
# Tests assume NEXTCLOUD_HOST, NEXTCLOUD_USERNAME, NEXTCLOUD_PASSWORD env vars are set
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def nc_client() -> NextcloudClient:
|
||||
@@ -39,11 +41,11 @@ def test_note_crud_integration(nc_client: NextcloudClient):
|
||||
None # Initialize to ensure cleanup happens even if create fails mid-assert
|
||||
)
|
||||
try:
|
||||
print(f"\nAttempting to create note: {create_title}")
|
||||
logger.info(f"\nAttempting to create note: {create_title}")
|
||||
created_note = nc_client.notes_create_note(
|
||||
title=create_title, content=create_content, category=create_category
|
||||
)
|
||||
print(f"Note created: {created_note}")
|
||||
logger.info(f"Note created: {created_note}")
|
||||
|
||||
assert created_note is not None
|
||||
assert "id" in created_note
|
||||
@@ -58,9 +60,9 @@ def test_note_crud_integration(nc_client: NextcloudClient):
|
||||
time.sleep(1)
|
||||
|
||||
# --- Read (Verify Create) ---
|
||||
print(f"Attempting to read note ID: {note_id}")
|
||||
logger.info(f"Attempting to read note ID: {note_id}")
|
||||
read_note = nc_client.notes_get_note(note_id=note_id)
|
||||
print(f"Note read: {read_note}")
|
||||
logger.info(f"Note read: {read_note}")
|
||||
assert read_note["id"] == note_id
|
||||
assert read_note["title"] == create_title
|
||||
assert read_note["content"] == create_content
|
||||
@@ -71,7 +73,7 @@ def test_note_crud_integration(nc_client: NextcloudClient):
|
||||
update_title = f"Updated Test Note {unique_id}"
|
||||
update_content = f"Updated content {unique_id}"
|
||||
# Use the etag from the *creation* for the update's If-Match header
|
||||
print(f"Attempting to update note ID: {note_id} with etag: {etag}")
|
||||
logger.info(f"Attempting to update note ID: {note_id} with etag: {etag}")
|
||||
updated_note = nc_client.notes_update_note(
|
||||
note_id=note_id,
|
||||
etag=etag,
|
||||
@@ -79,7 +81,7 @@ def test_note_crud_integration(nc_client: NextcloudClient):
|
||||
content=update_content,
|
||||
# category=create_category # Keep category same or update if needed
|
||||
)
|
||||
print(f"Note updated: {updated_note}")
|
||||
logger.info(f"Note updated: {updated_note}")
|
||||
assert updated_note["id"] == note_id
|
||||
assert updated_note["title"] == update_title
|
||||
assert updated_note["content"] == update_content
|
||||
@@ -92,16 +94,16 @@ def test_note_crud_integration(nc_client: NextcloudClient):
|
||||
time.sleep(1)
|
||||
|
||||
# --- Read (Verify Update) ---
|
||||
print(f"Attempting to read updated note ID: {note_id}")
|
||||
logger.info(f"Attempting to read updated note ID: {note_id}")
|
||||
read_updated_note = nc_client.notes_get_note(note_id=note_id)
|
||||
print(f"Updated note read: {read_updated_note}")
|
||||
logger.info(f"Updated note read: {read_updated_note}")
|
||||
assert read_updated_note["id"] == note_id
|
||||
assert read_updated_note["title"] == update_title
|
||||
assert read_updated_note["content"] == update_content
|
||||
# Don't assert etag equality here either
|
||||
|
||||
# --- Test Update Conflict (Precondition Failed) ---
|
||||
print(f"Attempting to update note ID: {note_id} with OLD etag: {etag}")
|
||||
logger.info(f"Attempting to update note ID: {note_id} with OLD etag: {etag}")
|
||||
with pytest.raises(HTTPStatusError) as excinfo:
|
||||
nc_client.notes_update_note(
|
||||
note_id=note_id,
|
||||
@@ -109,38 +111,38 @@ def test_note_crud_integration(nc_client: NextcloudClient):
|
||||
title="This update should fail",
|
||||
)
|
||||
assert excinfo.value.response.status_code == 412 # Precondition Failed
|
||||
print("Update with old etag correctly failed with 412.")
|
||||
logger.info("Update with old etag correctly failed with 412.")
|
||||
|
||||
finally:
|
||||
# --- Delete ---
|
||||
if created_note and "id" in created_note:
|
||||
note_id_to_delete = created_note["id"]
|
||||
print(f"Attempting to delete note ID: {note_id_to_delete}")
|
||||
logger.info(f"Attempting to delete note ID: {note_id_to_delete}")
|
||||
try:
|
||||
delete_response = nc_client.notes_delete_note(note_id=note_id_to_delete)
|
||||
print(f"Delete response: {delete_response}")
|
||||
logger.info(f"Delete response: {delete_response}")
|
||||
# Check if delete returns the deleted object or just status
|
||||
# Assuming it returns the object based on previous tests
|
||||
assert delete_response["id"] == note_id_to_delete
|
||||
print(f"Note ID: {note_id_to_delete} deleted successfully.")
|
||||
logger.info(f"Note ID: {note_id_to_delete} deleted successfully.")
|
||||
|
||||
# --- Verify Delete ---
|
||||
print(f"Attempting to read deleted note ID: {note_id_to_delete}")
|
||||
logger.info(f"Attempting to read deleted note ID: {note_id_to_delete}")
|
||||
with pytest.raises(HTTPStatusError) as excinfo_del:
|
||||
nc_client.notes_get_note(note_id=note_id_to_delete)
|
||||
assert excinfo_del.value.response.status_code == 404
|
||||
print(
|
||||
logger.info(
|
||||
f"Reading deleted note ID: {note_id_to_delete} correctly failed with 404."
|
||||
)
|
||||
|
||||
except HTTPStatusError as e:
|
||||
# If deletion fails unexpectedly, log it but don't fail the test here
|
||||
# as the primary goal was CRUD, and cleanup failure is secondary.
|
||||
print(f"Error during cleanup (deleting note {note_id_to_delete}): {e}")
|
||||
logger.info(f"Error during cleanup (deleting note {note_id_to_delete}): {e}")
|
||||
except Exception as e:
|
||||
print(f"Unexpected error during cleanup: {e}")
|
||||
logger.info(f"Unexpected error during cleanup: {e}")
|
||||
else:
|
||||
print(
|
||||
logger.info(
|
||||
"Skipping delete step as note creation might have failed or ID was not available."
|
||||
)
|
||||
|
||||
@@ -149,11 +151,11 @@ def test_note_crud_integration(nc_client: NextcloudClient):
|
||||
def test_delete_nonexistent_note(nc_client: NextcloudClient):
|
||||
"""Test deleting a note that doesn't exist."""
|
||||
non_existent_id = 999999999 # Use an ID highly unlikely to exist
|
||||
print(f"\nAttempting to delete non-existent note ID: {non_existent_id}")
|
||||
logger.info(f"\nAttempting to delete non-existent note ID: {non_existent_id}")
|
||||
with pytest.raises(HTTPStatusError) as excinfo:
|
||||
nc_client.notes_delete_note(note_id=non_existent_id)
|
||||
assert excinfo.value.response.status_code == 404
|
||||
print(
|
||||
logger.info(
|
||||
f"Deleting non-existent note ID: {non_existent_id} correctly failed with 404."
|
||||
)
|
||||
|
||||
@@ -173,13 +175,13 @@ def test_note_attachment_integration(nc_client: NextcloudClient):
|
||||
note_id = None
|
||||
|
||||
try:
|
||||
print(f"\nCreating note for attachment test: {note_title}")
|
||||
logger.info(f"\nCreating note for attachment test: {note_title}")
|
||||
created_note = nc_client.notes_create_note(
|
||||
title=note_title, content=note_content, category=note_category
|
||||
)
|
||||
assert created_note and "id" in created_note
|
||||
note_id = created_note["id"]
|
||||
print(f"Note created with ID: {note_id}")
|
||||
logger.info(f"Note created with ID: {note_id}")
|
||||
time.sleep(1) # Allow time for note creation
|
||||
|
||||
# --- Try to Add Attachment ---
|
||||
@@ -187,7 +189,7 @@ def test_note_attachment_integration(nc_client: NextcloudClient):
|
||||
attachment_content = f"This is the content of {attachment_filename}".encode('utf-8')
|
||||
attachment_mime = "text/plain"
|
||||
|
||||
print(f"Attempting to add attachment '{attachment_filename}' to note ID: {note_id}")
|
||||
logger.info(f"Attempting to add attachment '{attachment_filename}' to note ID: {note_id}")
|
||||
try:
|
||||
# Try to add the attachment, but don't fail the test if WebDAV isn't available
|
||||
upload_response = nc_client.add_note_attachment(
|
||||
@@ -200,22 +202,22 @@ def test_note_attachment_integration(nc_client: NextcloudClient):
|
||||
# If we get here, WebDAV is working - continue with attachment tests
|
||||
assert upload_response and "status_code" in upload_response
|
||||
assert upload_response["status_code"] in [201, 204]
|
||||
print(f"Attachment '{attachment_filename}' added successfully (Status: {upload_response['status_code']}).")
|
||||
logger.info(f"Attachment '{attachment_filename}' added successfully (Status: {upload_response['status_code']}).")
|
||||
time.sleep(1) # Allow time for upload processing
|
||||
|
||||
# --- Get and Verify Attachment ---
|
||||
print(f"Attempting to retrieve attachment '{attachment_filename}' from note ID: {note_id}")
|
||||
logger.info(f"Attempting to retrieve attachment '{attachment_filename}' from note ID: {note_id}")
|
||||
retrieved_content, retrieved_mime = nc_client.get_note_attachment(
|
||||
note_id=note_id,
|
||||
filename=attachment_filename
|
||||
)
|
||||
print(f"Attachment retrieved. Mime type: {retrieved_mime}, Size: {len(retrieved_content)} bytes")
|
||||
logger.info(f"Attachment retrieved. Mime type: {retrieved_mime}, Size: {len(retrieved_content)} bytes")
|
||||
|
||||
# --- Verify Attachment ---
|
||||
assert retrieved_content == attachment_content
|
||||
# Check if the expected mime type is part of the retrieved one (to handle charset)
|
||||
assert attachment_mime in retrieved_mime
|
||||
print("Retrieved attachment content and mime type verified successfully.")
|
||||
logger.info("Retrieved attachment content and mime type verified successfully.")
|
||||
|
||||
except HTTPStatusError as e:
|
||||
if e.response.status_code == 401:
|
||||
@@ -226,20 +228,20 @@ def test_note_attachment_integration(nc_client: NextcloudClient):
|
||||
finally:
|
||||
# --- Delete Note (Cleanup) ---
|
||||
if note_id:
|
||||
print(f"Attempting cleanup: deleting note ID: {note_id}")
|
||||
logger.info(f"Attempting cleanup: deleting note ID: {note_id}")
|
||||
try:
|
||||
nc_client.notes_delete_note(note_id=note_id)
|
||||
print(f"Note ID: {note_id} deleted successfully.")
|
||||
logger.info(f"Note ID: {note_id} deleted successfully.")
|
||||
# Verify deletion
|
||||
time.sleep(1)
|
||||
with pytest.raises(HTTPStatusError) as excinfo_del:
|
||||
nc_client.notes_get_note(note_id=note_id)
|
||||
assert excinfo_del.value.response.status_code == 404
|
||||
print(f"Verified note {note_id} deletion (404 received).")
|
||||
logger.info(f"Verified note {note_id} deletion (404 received).")
|
||||
except Exception as e:
|
||||
print(f"Error during cleanup (deleting note {note_id}): {e}")
|
||||
logger.info(f"Error during cleanup (deleting note {note_id}): {e}")
|
||||
else:
|
||||
print("Skipping cleanup as note ID was not obtained.")
|
||||
logger.info("Skipping cleanup as note ID was not obtained.")
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@@ -257,13 +259,13 @@ def test_note_attachment_with_category_integration(nc_client: NextcloudClient):
|
||||
note_id = None
|
||||
|
||||
try:
|
||||
print(f"\nCreating note with category '{note_category}' for attachment test: {note_title}")
|
||||
logger.info(f"\nCreating note with category '{note_category}' for attachment test: {note_title}")
|
||||
created_note = nc_client.notes_create_note(
|
||||
title=note_title, content=note_content, category=note_category
|
||||
)
|
||||
assert created_note and "id" in created_note
|
||||
note_id = created_note["id"]
|
||||
print(f"Note with category created with ID: {note_id}")
|
||||
logger.info(f"Note with category created with ID: {note_id}")
|
||||
time.sleep(1)
|
||||
|
||||
# --- Try to Add Attachment ---
|
||||
@@ -271,7 +273,7 @@ def test_note_attachment_with_category_integration(nc_client: NextcloudClient):
|
||||
attachment_content = f"Content for {attachment_filename}".encode('utf-8')
|
||||
attachment_mime = "text/plain"
|
||||
|
||||
print(f"Attempting to add attachment '{attachment_filename}' to note ID: {note_id}")
|
||||
logger.info(f"Attempting to add attachment '{attachment_filename}' to note ID: {note_id}")
|
||||
try:
|
||||
# Try to add the attachment, but don't fail the test if WebDAV isn't available
|
||||
upload_response = nc_client.add_note_attachment(
|
||||
@@ -284,21 +286,21 @@ def test_note_attachment_with_category_integration(nc_client: NextcloudClient):
|
||||
# If we get here, WebDAV is working - continue with attachment tests
|
||||
assert upload_response and "status_code" in upload_response
|
||||
assert upload_response["status_code"] in [201, 204]
|
||||
print(f"Attachment '{attachment_filename}' added successfully (Status: {upload_response['status_code']}).")
|
||||
logger.info(f"Attachment '{attachment_filename}' added successfully (Status: {upload_response['status_code']}).")
|
||||
time.sleep(1)
|
||||
|
||||
# --- Get and Verify Attachment ---
|
||||
print(f"Attempting to retrieve attachment '{attachment_filename}' from note ID: {note_id}")
|
||||
logger.info(f"Attempting to retrieve attachment '{attachment_filename}' from note ID: {note_id}")
|
||||
retrieved_content, retrieved_mime = nc_client.get_note_attachment(
|
||||
note_id=note_id,
|
||||
filename=attachment_filename
|
||||
)
|
||||
print(f"Attachment retrieved. Mime type: {retrieved_mime}, Size: {len(retrieved_content)} bytes")
|
||||
logger.info(f"Attachment retrieved. Mime type: {retrieved_mime}, Size: {len(retrieved_content)} bytes")
|
||||
|
||||
# --- Verify Attachment ---
|
||||
assert retrieved_content == attachment_content
|
||||
assert attachment_mime in retrieved_mime # Check if expected mime is part of retrieved
|
||||
print("Retrieved attachment content and mime type verified successfully for note with category.")
|
||||
logger.info("Retrieved attachment content and mime type verified successfully for note with category.")
|
||||
|
||||
except HTTPStatusError as e:
|
||||
if e.response.status_code == 401:
|
||||
@@ -309,19 +311,19 @@ def test_note_attachment_with_category_integration(nc_client: NextcloudClient):
|
||||
finally:
|
||||
# --- Delete Note (Cleanup) ---
|
||||
if note_id:
|
||||
print(f"Attempting cleanup: deleting note ID: {note_id}")
|
||||
logger.info(f"Attempting cleanup: deleting note ID: {note_id}")
|
||||
try:
|
||||
nc_client.notes_delete_note(note_id=note_id)
|
||||
print(f"Note ID: {note_id} deleted successfully.")
|
||||
logger.info(f"Note ID: {note_id} deleted successfully.")
|
||||
time.sleep(1)
|
||||
with pytest.raises(HTTPStatusError) as excinfo_del:
|
||||
nc_client.notes_get_note(note_id=note_id)
|
||||
assert excinfo_del.value.response.status_code == 404
|
||||
print(f"Verified note {note_id} deletion (404 received).")
|
||||
logger.info(f"Verified note {note_id} deletion (404 received).")
|
||||
except Exception as e:
|
||||
print(f"Error during cleanup (deleting note {note_id}): {e}")
|
||||
logger.info(f"Error during cleanup (deleting note {note_id}): {e}")
|
||||
else:
|
||||
print("Skipping cleanup as note ID was not obtained.")
|
||||
logger.info("Skipping cleanup as note ID was not obtained.")
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@@ -339,24 +341,24 @@ def test_attachment_cleanup_behavior(nc_client: NextcloudClient):
|
||||
note_content = "Test note for attachments cleanup."
|
||||
note_category = "AttachmentCleanupTest"
|
||||
|
||||
print(f"\nCreating test note: {note_title}")
|
||||
logger.info(f"\nCreating test note: {note_title}")
|
||||
created_note = nc_client.notes_create_note(
|
||||
title=note_title, content=note_content, category=note_category
|
||||
)
|
||||
assert created_note and "id" in created_note
|
||||
note_id = created_note["id"]
|
||||
print(f"Test note created with ID: {note_id}")
|
||||
logger.info(f"Test note created with ID: {note_id}")
|
||||
time.sleep(1)
|
||||
|
||||
# Check authentication type
|
||||
auth_type = type(nc_client._client.auth).__name__
|
||||
print(f"Client authentication type: {auth_type}")
|
||||
logger.info(f"Client authentication type: {auth_type}")
|
||||
|
||||
# --- Try to Add Attachment ---
|
||||
attachment_filename = f"cleanup_test_{unique_id}.txt"
|
||||
attachment_content = f"Content for cleanup test".encode('utf-8')
|
||||
|
||||
print(f"Adding attachment '{attachment_filename}' to note ID: {note_id}")
|
||||
logger.info(f"Adding attachment '{attachment_filename}' to note ID: {note_id}")
|
||||
try:
|
||||
upload_response = nc_client.add_note_attachment(
|
||||
note_id=note_id,
|
||||
@@ -365,7 +367,7 @@ def test_attachment_cleanup_behavior(nc_client: NextcloudClient):
|
||||
mime_type="text/plain"
|
||||
)
|
||||
assert upload_response["status_code"] in [201, 204]
|
||||
print(f"Attachment added successfully (Status: {upload_response['status_code']}).")
|
||||
logger.info(f"Attachment added successfully (Status: {upload_response['status_code']}).")
|
||||
time.sleep(1)
|
||||
|
||||
# --- Verify Attachment Exists ---
|
||||
@@ -374,28 +376,28 @@ def test_attachment_cleanup_behavior(nc_client: NextcloudClient):
|
||||
filename=attachment_filename
|
||||
)
|
||||
assert retrieved_content == attachment_content
|
||||
print("Verified attachment exists and can be retrieved")
|
||||
logger.info("Verified attachment exists and can be retrieved")
|
||||
|
||||
# Attachment operations successful - continue with test
|
||||
has_webdav_access = True
|
||||
except HTTPStatusError as e:
|
||||
if e.response.status_code == 401:
|
||||
print(f"WebDAV access denied (401 Unauthorized). Skipping attachment tests.")
|
||||
logger.info(f"WebDAV access denied (401 Unauthorized). Skipping attachment tests.")
|
||||
pytest.skip("WebDAV access denied (401 Unauthorized)")
|
||||
else:
|
||||
raise # Re-raise other HTTP errors
|
||||
|
||||
# --- Delete Note ---
|
||||
print(f"Deleting note ID: {note_id}")
|
||||
logger.info(f"Deleting note ID: {note_id}")
|
||||
nc_client.notes_delete_note(note_id=note_id)
|
||||
print(f"Note ID: {note_id} deleted successfully.")
|
||||
logger.info(f"Note ID: {note_id} deleted successfully.")
|
||||
time.sleep(1)
|
||||
|
||||
# --- Verify Note Is Deleted ---
|
||||
with pytest.raises(HTTPStatusError) as excinfo:
|
||||
nc_client.notes_get_note(note_id=note_id)
|
||||
assert excinfo.value.response.status_code == 404
|
||||
print(f"Verified note deletion (404 received)")
|
||||
logger.info(f"Verified note deletion (404 received)")
|
||||
|
||||
# --- Document the expected behavior: attachments remain after note deletion ---
|
||||
try:
|
||||
@@ -404,10 +406,10 @@ def test_attachment_cleanup_behavior(nc_client: NextcloudClient):
|
||||
note_id=note_id,
|
||||
filename=attachment_filename
|
||||
)
|
||||
print("EXPECTED BEHAVIOR: Attachment still exists after note deletion")
|
||||
print("This matches the behavior of the official Nextcloud Notes app")
|
||||
logger.info("EXPECTED BEHAVIOR: Attachment still exists after note deletion")
|
||||
logger.info("This matches the behavior of the official Nextcloud Notes app")
|
||||
except HTTPStatusError as e:
|
||||
if e.response.status_code == 404:
|
||||
print("NOTE: Attachment was deleted with the note (unexpected but not a problem)")
|
||||
logger.info("NOTE: Attachment was deleted with the note (unexpected but not a problem)")
|
||||
else:
|
||||
print(f"Unexpected error when checking attachment: {e.response.status_code}")
|
||||
logger.info(f"Unexpected error when checking attachment: {e.response.status_code}")
|
||||
|
||||
@@ -2,11 +2,14 @@ import pytest
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
import logging
|
||||
import tempfile
|
||||
from PIL import Image, ImageDraw
|
||||
from io import BytesIO
|
||||
from nextcloud_mcp_server.client import NextcloudClient
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def nc_client() -> NextcloudClient:
|
||||
"""
|
||||
@@ -50,7 +53,7 @@ def test_note_with_embedded_image(nc_client: NextcloudClient, test_image):
|
||||
initial_content = "# Embedded Image Test\n\nThis note demonstrates how to properly embed images in Nextcloud Notes."
|
||||
|
||||
# Create the note
|
||||
print(f"Creating test note: {note_title}")
|
||||
logger.info(f"Creating test note: {note_title}")
|
||||
note = nc_client.notes_create_note(
|
||||
title=note_title,
|
||||
content=initial_content,
|
||||
@@ -58,7 +61,7 @@ def test_note_with_embedded_image(nc_client: NextcloudClient, test_image):
|
||||
)
|
||||
note_id = note["id"]
|
||||
note_etag = note["etag"]
|
||||
print(f"Note created with ID: {note_id}")
|
||||
logger.info(f"Note created with ID: {note_id}")
|
||||
|
||||
try:
|
||||
# Read the test image content
|
||||
@@ -69,14 +72,14 @@ def test_note_with_embedded_image(nc_client: NextcloudClient, test_image):
|
||||
attachment_filename = f"test_image_{unique_id}.png"
|
||||
|
||||
# Upload the image as an attachment
|
||||
print(f"Uploading image attachment '{attachment_filename}' to note {note_id}...")
|
||||
logger.info(f"Uploading image attachment '{attachment_filename}' to note {note_id}...")
|
||||
upload_response = nc_client.add_note_attachment(
|
||||
note_id=note_id,
|
||||
filename=attachment_filename,
|
||||
content=image_content,
|
||||
mime_type="image/png"
|
||||
)
|
||||
print(f"Image uploaded: {upload_response}")
|
||||
logger.info(f"Image uploaded: {upload_response}")
|
||||
|
||||
# Update the note content to include the embedded image using Markdown syntax
|
||||
# This is the correct syntax for embedding images in Nextcloud Notes
|
||||
@@ -98,7 +101,7 @@ This note demonstrates how to properly embed images in Nextcloud Notes.
|
||||
"""
|
||||
|
||||
# Update the note with the image references
|
||||
print("Updating note content with image references...")
|
||||
logger.info("Updating note content with image references...")
|
||||
updated_note = nc_client.notes_update_note(
|
||||
note_id=note_id,
|
||||
etag=note_etag,
|
||||
@@ -108,7 +111,7 @@ This note demonstrates how to properly embed images in Nextcloud Notes.
|
||||
# Verify the updated note has the correct content
|
||||
retrieved_note = nc_client.notes_get_note(note_id=note_id)
|
||||
assert ".attachments." in retrieved_note["content"], "Image reference not found in note content"
|
||||
print("Note updated successfully with image references")
|
||||
logger.info("Note updated successfully with image references")
|
||||
|
||||
# Verify we can retrieve the image attachment
|
||||
retrieved_content, mime_type = nc_client.get_note_attachment(
|
||||
@@ -118,9 +121,9 @@ This note demonstrates how to properly embed images in Nextcloud Notes.
|
||||
assert len(retrieved_content) > 0, "Retrieved image content is empty"
|
||||
assert mime_type.startswith("image/"), f"Expected image mime type, got {mime_type}"
|
||||
|
||||
print("Test completed successfully - image was embedded in the note and can be retrieved")
|
||||
logger.info("Test completed successfully - image was embedded in the note and can be retrieved")
|
||||
|
||||
finally:
|
||||
# Clean up - delete the test note
|
||||
print(f"Cleaning up - deleting test note {note_id}")
|
||||
logger.info(f"Cleaning up - deleting test note {note_id}")
|
||||
nc_client.notes_delete_note(note_id=note_id)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import pytest
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
import uuid
|
||||
from httpx import HTTPStatusError
|
||||
from nextcloud_mcp_server.client import NextcloudClient
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Tests assume NEXTCLOUD_HOST, NEXTCLOUD_USERNAME, NEXTCLOUD_PASSWORD env vars are set
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
@@ -34,7 +37,7 @@ def test_attachment_remains_after_note_deletion(nc_client: NextcloudClient):
|
||||
|
||||
try:
|
||||
# Create the note
|
||||
print(f"Creating note: {note_title}")
|
||||
logger.info(f"Creating note: {note_title}")
|
||||
created_note = nc_client.notes_create_note(
|
||||
title=note_title,
|
||||
content=note_content,
|
||||
@@ -42,7 +45,7 @@ def test_attachment_remains_after_note_deletion(nc_client: NextcloudClient):
|
||||
)
|
||||
assert created_note and "id" in created_note
|
||||
note_id = created_note["id"]
|
||||
print(f"Note created with ID: {note_id}")
|
||||
logger.info(f"Note created with ID: {note_id}")
|
||||
time.sleep(1)
|
||||
|
||||
# Create a simple text attachment
|
||||
@@ -50,7 +53,7 @@ def test_attachment_remains_after_note_deletion(nc_client: NextcloudClient):
|
||||
attachment_content = f"This is a test attachment for note {note_id}".encode('utf-8')
|
||||
|
||||
# Attach the file to the note
|
||||
print(f"Attaching text file to note {note_id}...")
|
||||
logger.info(f"Attaching text file to note {note_id}...")
|
||||
upload_response = nc_client.add_note_attachment(
|
||||
note_id=note_id,
|
||||
filename=attachment_filename,
|
||||
@@ -59,7 +62,7 @@ def test_attachment_remains_after_note_deletion(nc_client: NextcloudClient):
|
||||
)
|
||||
|
||||
assert upload_response["status_code"] in [201, 204]
|
||||
print(f"Attachment added successfully (Status: {upload_response['status_code']}).")
|
||||
logger.info(f"Attachment added successfully (Status: {upload_response['status_code']}).")
|
||||
time.sleep(1)
|
||||
|
||||
# Verify the attachment exists
|
||||
@@ -69,30 +72,30 @@ def test_attachment_remains_after_note_deletion(nc_client: NextcloudClient):
|
||||
)
|
||||
|
||||
assert content == attachment_content, "Attachment content mismatch"
|
||||
print("Attachment verified")
|
||||
logger.info("Attachment verified")
|
||||
|
||||
# Now delete the note
|
||||
print(f"Deleting note ID: {note_id}")
|
||||
logger.info(f"Deleting note ID: {note_id}")
|
||||
nc_client.notes_delete_note(note_id=note_id)
|
||||
print(f"Note deleted successfully.")
|
||||
logger.info(f"Note deleted successfully.")
|
||||
time.sleep(1)
|
||||
|
||||
# Verify the note is deleted
|
||||
with pytest.raises(HTTPStatusError) as excinfo:
|
||||
nc_client.notes_get_note(note_id=note_id)
|
||||
assert excinfo.value.response.status_code == 404
|
||||
print(f"Verified note deletion (404 Not Found)")
|
||||
logger.info(f"Verified note deletion (404 Not Found)")
|
||||
|
||||
# Now check if the attachment still exists (expected behavior: it should)
|
||||
print(f"Checking if attachment still exists after note deletion...")
|
||||
logger.info(f"Checking if attachment still exists after note deletion...")
|
||||
orphaned_content, orphaned_mime = nc_client.get_note_attachment(
|
||||
note_id=note_id,
|
||||
filename=attachment_filename
|
||||
)
|
||||
|
||||
# If we get here without an exception, the attachment still exists
|
||||
print("CONFIRMED: Attachment still exists after note deletion")
|
||||
print("This is the expected behavior of the Nextcloud Notes app")
|
||||
logger.info("CONFIRMED: Attachment still exists after note deletion")
|
||||
logger.info("This is the expected behavior of the Nextcloud Notes app")
|
||||
assert orphaned_content == attachment_content, "Orphaned attachment content mismatch"
|
||||
|
||||
finally:
|
||||
|
||||
@@ -2,11 +2,14 @@ import pytest
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
import logging
|
||||
import tempfile
|
||||
from httpx import HTTPStatusError
|
||||
from PIL import Image, ImageDraw
|
||||
from nextcloud_mcp_server.client import NextcloudClient
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Tests assume NEXTCLOUD_HOST, NEXTCLOUD_USERNAME, NEXTCLOUD_PASSWORD env vars are set
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
@@ -57,7 +60,7 @@ def test_note_with_image_attachment(nc_client: NextcloudClient, test_image):
|
||||
|
||||
try:
|
||||
# Create the note
|
||||
print(f"Creating note: {note_title}")
|
||||
logger.info(f"Creating note: {note_title}")
|
||||
created_note = nc_client.notes_create_note(
|
||||
title=note_title,
|
||||
content=note_content,
|
||||
@@ -65,7 +68,7 @@ def test_note_with_image_attachment(nc_client: NextcloudClient, test_image):
|
||||
)
|
||||
assert created_note and "id" in created_note
|
||||
note_id = created_note["id"]
|
||||
print(f"Note created with ID: {note_id}")
|
||||
logger.info(f"Note created with ID: {note_id}")
|
||||
time.sleep(1)
|
||||
|
||||
# Read the test image
|
||||
@@ -74,7 +77,7 @@ def test_note_with_image_attachment(nc_client: NextcloudClient, test_image):
|
||||
|
||||
# Attach the image to the note
|
||||
attachment_filename = f"test_image_{unique_id}.png"
|
||||
print(f"Attaching image to note {note_id}...")
|
||||
logger.info(f"Attaching image to note {note_id}...")
|
||||
upload_response = nc_client.add_note_attachment(
|
||||
note_id=note_id,
|
||||
filename=attachment_filename,
|
||||
@@ -83,7 +86,7 @@ def test_note_with_image_attachment(nc_client: NextcloudClient, test_image):
|
||||
)
|
||||
|
||||
assert upload_response["status_code"] in [201, 204]
|
||||
print(f"Image attached successfully (Status: {upload_response['status_code']}).")
|
||||
logger.info(f"Image attached successfully (Status: {upload_response['status_code']}).")
|
||||
time.sleep(1)
|
||||
|
||||
# Update the note content to include a reference to the attached image
|
||||
@@ -100,7 +103,7 @@ Files path: `/Notes/.attachments.{note_id}/{attachment_filename}`
|
||||
"""
|
||||
|
||||
# Update the note content
|
||||
print("Updating note content to include image reference...")
|
||||
logger.info("Updating note content to include image reference...")
|
||||
updated_note = nc_client.notes_update_note(
|
||||
note_id=note_id,
|
||||
etag=created_note["etag"],
|
||||
@@ -109,8 +112,8 @@ Files path: `/Notes/.attachments.{note_id}/{attachment_filename}`
|
||||
|
||||
# Retrieve the note to verify content
|
||||
retrieved_note = nc_client.notes_get_note(note_id=note_id)
|
||||
print("Retrieved note content:")
|
||||
print(retrieved_note["content"])
|
||||
logger.info("Retrieved note content:")
|
||||
logger.info(retrieved_note["content"])
|
||||
|
||||
# Verify the image attachment can be retrieved
|
||||
content, mime_type = nc_client.get_note_attachment(
|
||||
@@ -120,14 +123,14 @@ Files path: `/Notes/.attachments.{note_id}/{attachment_filename}`
|
||||
|
||||
assert content == image_content, "Attachment content mismatch"
|
||||
assert mime_type.startswith("image/"), f"Expected image mime type, got {mime_type}"
|
||||
print("Image attachment verified")
|
||||
logger.info("Image attachment verified")
|
||||
|
||||
finally:
|
||||
# Cleanup
|
||||
if note_id:
|
||||
print(f"Cleaning up - deleting note ID: {note_id}")
|
||||
logger.info(f"Cleaning up - deleting note ID: {note_id}")
|
||||
try:
|
||||
nc_client.notes_delete_note(note_id=note_id)
|
||||
print(f"Note {note_id} deleted")
|
||||
logger.info(f"Note {note_id} deleted")
|
||||
except Exception as e:
|
||||
print(f"Error during cleanup: {e}")
|
||||
logger.info(f"Error during cleanup: {e}")
|
||||
|
||||
Reference in New Issue
Block a user