ci: [skip ci] Move tests to subdirs

This commit is contained in:
Chris Coutinho
2025-10-14 02:08:45 +02:00
parent ab40127811
commit 72ace9da9e
17 changed files with 3 additions and 8 deletions
+264
View File
@@ -0,0 +1,264 @@
import logging
import time
import uuid
import pytest
from httpx import HTTPStatusError
from nextcloud_mcp_server.client import NextcloudClient
logger = logging.getLogger(__name__)
# Mark all tests in this module as integration tests
pytestmark = pytest.mark.integration
async def test_category_change_cleans_up_old_attachments_directory(
nc_client: NextcloudClient,
):
"""
Tests that when a note's category is changed, the old attachment directory is properly cleaned up.
"""
note_id = None
initial_category = "CategoryTest1"
new_category = "CategoryTest2"
unique_suffix = uuid.uuid4().hex[:8]
note_title = f"Category Cleanup Test {unique_suffix}"
attachment_filename = f"cleanup_test_{unique_suffix}.txt"
attachment_content = f"Content for {attachment_filename}".encode("utf-8")
try:
# 1. Create note with initial category
logger.info(f"Creating note '{note_title}' in category '{initial_category}'")
created_note = await nc_client.notes.create_note(
title=note_title, content="Initial content", category=initial_category
)
note_id = created_note["id"]
etag1 = created_note["etag"]
logger.info(f"Note created with ID: {note_id}, Etag: {etag1}")
time.sleep(1)
# 2. Add attachment (passing initial category)
logger.info(
f"Adding attachment '{attachment_filename}' to note {note_id} (in {initial_category})"
)
upload_response = await nc_client.webdav.add_note_attachment(
note_id=note_id,
filename=attachment_filename,
content=attachment_content,
category=initial_category,
mime_type="text/plain",
)
assert upload_response["status_code"] in [201, 204]
logger.info("Attachment added successfully.")
time.sleep(1)
# 3. Verify attachment retrieval from initial category
logger.info(
f"Verifying attachment retrieval from initial category '{initial_category}'"
)
retrieved_content1, _ = await nc_client.webdav.get_note_attachment(
note_id=note_id, filename=attachment_filename, category=initial_category
)
assert retrieved_content1 == attachment_content
logger.info("Attachment retrieved successfully from initial category.")
# 4. Construct and check the WebDAV path for the initial category's attachment directory
initial_webdav_path = f"Notes/{initial_category}/.attachments.{note_id}"
logger.info(f"Initial WebDAV path for attachments: {initial_webdav_path}")
# Here we would check if the directory exists, but the WebDAV client doesn't directly
# expose directory listing functionality, so we'll infer from attachment retrieval success
# 5. Update note category
logger.info(
f"Updating note {note_id} category from '{initial_category}' to '{new_category}'"
)
current_note_data = await nc_client.notes.get_note(note_id=note_id)
current_etag = current_note_data["etag"]
updated_note = await nc_client.notes.update(
note_id=note_id,
etag=current_etag,
category=new_category,
title=note_title,
content="Updated content",
)
etag3 = updated_note["etag"]
assert updated_note["category"] == new_category
logger.info(f"Note category updated successfully. New Etag: {etag3}")
time.sleep(1)
# 6. Verify attachment retrieval from new category
logger.info(
f"Verifying attachment retrieval from new category '{new_category}'"
)
retrieved_content2, _ = await nc_client.webdav.get_note_attachment(
note_id=note_id, filename=attachment_filename, category=new_category
)
assert retrieved_content2 == attachment_content
logger.info("Attachment retrieved successfully from new category.")
# 7. Try to retrieve from old category - this should fail
logger.info(
f"Trying to retrieve attachment from old category '{initial_category}' - should fail"
)
try:
await nc_client.webdav.get_note_attachment(
note_id=note_id, filename=attachment_filename, category=initial_category
)
# If we get here, it means the old directory still exists (a problem)
logger.error(
"ISSUE DETECTED: Was able to retrieve attachment from old category path!"
)
assert False, (
"Old category attachment directory still exists and accessible!"
)
except HTTPStatusError as e:
# This is the expected outcome - old directory should be gone
logger.info(
f"Correctly got error accessing old category path: {e.response.status_code}"
)
assert e.response.status_code == 404, (
f"Expected 404, got {e.response.status_code}"
)
logger.info(
"Verified old category attachment directory is not accessible (good!)"
)
# 7.1 Directly check old attachment directory existence using WebDAV PROPFIND
logger.info(
"Directly checking if old attachment directory exists in WebDAV"
)
webdav_base = nc_client._get_webdav_base_path()
old_attachment_dir_path = (
f"{webdav_base}/Notes/{initial_category}/.attachments.{note_id}"
)
propfind_headers = {"Depth": "0", "OCS-APIRequest": "true"}
try:
propfind_resp = await nc_client._client.request(
"PROPFIND", old_attachment_dir_path, headers=propfind_headers
)
status = propfind_resp.status_code
if status in [
200,
207,
]: # Success codes indicate the directory exists (a problem)
logger.error(
f"Old attachment directory still exists! PROPFIND returned {status}"
)
assert False, (
f"Expected old attachment directory to be gone, but it still exists (PROPFIND returned {status})!"
)
# If we got another status code (like 404), it's also good - the directory doesn't exist
logger.info(
f"Verified old attachment directory does not exist (PROPFIND returned {status})"
)
except HTTPStatusError as e:
# 404 is expected - directory should not exist
assert e.response.status_code == 404, (
f"Expected PROPFIND to fail with 404, got {e.response.status_code}"
)
logger.info(
"Verified old attachment directory does not exist via PROPFIND (404 received)"
)
finally:
# 8. Cleanup: Delete the note
if note_id:
logger.info(f"Cleaning up note ID: {note_id}")
try:
await nc_client.notes.delete_note(note_id=note_id)
logger.info(f"Note {note_id} deleted.")
time.sleep(1)
# 9. Verify both old and new attachment paths are gone
logger.info("Verifying all attachment paths are gone")
with pytest.raises(HTTPStatusError) as excinfo_new:
await nc_client.webdav.get_note_attachment(
note_id=note_id,
filename=attachment_filename,
category=new_category,
)
assert excinfo_new.value.response.status_code == 404
with pytest.raises(HTTPStatusError) as excinfo_old:
await nc_client.webdav.get_note_attachment(
note_id=note_id,
filename=attachment_filename,
category=initial_category,
)
assert excinfo_old.value.response.status_code == 404
# 9.1 Directly verify directories don't exist using WebDAV PROPFIND
logger.info(
"Directly verifying attachment directories don't exist via PROPFIND"
)
webdav_base = nc_client._get_webdav_base_path()
# Check new category attachment directory
new_attachment_dir_path = (
f"{webdav_base}/Notes/{new_category}/.attachments.{note_id}"
)
propfind_headers = {"Depth": "0", "OCS-APIRequest": "true"}
try:
propfind_resp = await nc_client._client.request(
"PROPFIND", new_attachment_dir_path, headers=propfind_headers
)
status = propfind_resp.status_code
if status in [
200,
207,
]: # Success codes indicate the directory exists (a problem)
logger.error(
f"New category attachment directory still exists! PROPFIND returned {status}"
)
assert False, (
f"Expected new category attachment directory to be gone, but it still exists (PROPFIND returned {status})!"
)
# If we got another status code (like 404), it's also good - the directory doesn't exist
logger.info(
f"Verified new category attachment directory does not exist (PROPFIND returned {status})"
)
except HTTPStatusError as e:
assert e.response.status_code == 404, (
f"Expected PROPFIND to fail with 404, got {e.response.status_code}"
)
logger.info(
"Verified new category attachment directory is gone via PROPFIND"
)
# Check old category attachment directory
old_attachment_dir_path = (
f"{webdav_base}/Notes/{initial_category}/.attachments.{note_id}"
)
try:
propfind_resp = await nc_client._client.request(
"PROPFIND", old_attachment_dir_path, headers=propfind_headers
)
status = propfind_resp.status_code
if status in [
200,
207,
]: # Success codes indicate the directory exists (a problem)
logger.error(
f"Old category attachment directory still exists! PROPFIND returned {status}"
)
assert False, (
f"Expected old category attachment directory to be gone, but it still exists (PROPFIND returned {status})!"
)
# If we got another status code (like 404), it's also good - the directory doesn't exist
logger.info(
f"Verified old category attachment directory does not exist (PROPFIND returned {status})"
)
except HTTPStatusError as e:
assert e.response.status_code == 404, (
f"Expected PROPFIND to fail with 404, got {e.response.status_code}"
)
logger.info(
"Verified old category attachment directory is gone via PROPFIND"
)
logger.info(
"Verified all attachment directories are properly cleaned up."
)
except Exception as e:
logger.error(f"Error during cleanup for note {note_id}: {e}")
@@ -0,0 +1,273 @@
"""Integration tests for WebDAV operations."""
import logging
import uuid
import pytest
from httpx import HTTPStatusError
from nextcloud_mcp_server.client import NextcloudClient
logger = logging.getLogger(__name__)
# Mark all tests in this module as integration tests
pytestmark = pytest.mark.integration
@pytest.fixture
async def test_base_path(nc_client: NextcloudClient):
"""Base path for test files/directories."""
test_dir = f"mcp_test_{uuid.uuid4().hex[:8]}"
await nc_client.webdav.create_directory(test_dir)
yield test_dir
await nc_client.webdav.delete_resource(test_dir)
async def test_create_and_delete_directory(
nc_client: NextcloudClient, test_base_path: str
):
"""Test creating and deleting directories."""
test_dir = f"{test_base_path}/test_directory"
try:
# Create directory
result = await nc_client.webdav.create_directory(test_dir)
assert result["status_code"] == 201 # Created
logger.info(f"Created directory: {test_dir}")
# Verify directory exists by listing parent
parent_listing = await nc_client.webdav.list_directory(test_base_path)
dir_names = [item["name"] for item in parent_listing]
assert "test_directory" in dir_names
# Delete directory
delete_result = await nc_client.webdav.delete_resource(test_dir)
assert delete_result["status_code"] in [204, 404] # No Content or Not Found
logger.info(f"Deleted directory: {test_dir}")
finally:
# Cleanup: ensure directory is deleted
try:
await nc_client.webdav.delete_resource(test_dir)
except Exception:
pass
async def test_write_read_delete_file(nc_client: NextcloudClient, test_base_path: str):
"""Test writing, reading, and deleting files."""
test_file = f"{test_base_path}/test_file.txt"
test_content = f"Test content {uuid.uuid4().hex}"
try:
# Create base directory first
await nc_client.webdav.create_directory(test_base_path)
# Write file
write_result = await nc_client.webdav.write_file(
test_file, test_content.encode("utf-8"), content_type="text/plain"
)
assert write_result["status_code"] in [200, 201, 204] # Success codes
logger.info(f"Wrote file: {test_file}")
# Read file back
content, content_type = await nc_client.webdav.read_file(test_file)
assert content.decode("utf-8") == test_content
assert "text/plain" in content_type
logger.info(f"Read file: {test_file}")
# Verify file appears in directory listing
listing = await nc_client.webdav.list_directory(test_base_path)
file_names = [item["name"] for item in listing]
assert "test_file.txt" in file_names
# Delete file
delete_result = await nc_client.webdav.delete_resource(test_file)
assert delete_result["status_code"] in [204, 404] # No Content or Not Found
logger.info(f"Deleted file: {test_file}")
finally:
# Cleanup
try:
await nc_client.webdav.delete_resource(test_file)
await nc_client.webdav.delete_resource(test_base_path)
except Exception:
pass
async def test_list_directory_empty_and_populated(
nc_client: NextcloudClient, test_base_path: str
):
"""Test listing empty and populated directories."""
try:
# Create base directory
await nc_client.webdav.create_directory(test_base_path)
# List empty directory
empty_listing = await nc_client.webdav.list_directory(test_base_path)
assert isinstance(empty_listing, list)
assert len(empty_listing) == 0
logger.info(f"Empty directory listing: {len(empty_listing)} items")
# Add some files and directories
await nc_client.webdav.create_directory(f"{test_base_path}/subdir1")
await nc_client.webdav.create_directory(f"{test_base_path}/subdir2")
await nc_client.webdav.write_file(
f"{test_base_path}/file1.txt", b"content1", content_type="text/plain"
)
await nc_client.webdav.write_file(
f"{test_base_path}/file2.md",
b"# Markdown content",
content_type="text/markdown",
)
# List populated directory
populated_listing = await nc_client.webdav.list_directory(test_base_path)
assert len(populated_listing) == 4 # 2 dirs + 2 files
# Check that we have both files and directories
names = [item["name"] for item in populated_listing]
assert "subdir1" in names
assert "subdir2" in names
assert "file1.txt" in names
assert "file2.md" in names
# Check metadata is present
for item in populated_listing:
assert "name" in item
assert "path" in item
assert "is_directory" in item
assert "size" in item
assert "content_type" in item
assert "last_modified" in item
logger.info(f"Populated directory listing: {len(populated_listing)} items")
finally:
# Cleanup
try:
await nc_client.webdav.delete_resource(f"{test_base_path}/file1.txt")
await nc_client.webdav.delete_resource(f"{test_base_path}/file2.md")
await nc_client.webdav.delete_resource(f"{test_base_path}/subdir1")
await nc_client.webdav.delete_resource(f"{test_base_path}/subdir2")
await nc_client.webdav.delete_resource(test_base_path)
except Exception:
pass
async def test_read_nonexistent_file(nc_client: NextcloudClient):
"""Test reading a file that doesn't exist."""
nonexistent_file = f"nonexistent_{uuid.uuid4().hex}.txt"
with pytest.raises(HTTPStatusError) as exc_info:
await nc_client.webdav.read_file(nonexistent_file)
assert exc_info.value.response.status_code == 404
logger.info(f"Correctly got 404 for nonexistent file: {nonexistent_file}")
async def test_delete_nonexistent_resource(nc_client: NextcloudClient):
"""Test deleting a resource that doesn't exist."""
nonexistent_resource = f"nonexistent_{uuid.uuid4().hex}"
result = await nc_client.webdav.delete_resource(nonexistent_resource)
assert result["status_code"] == 404
logger.info(f"Correctly got 404 for nonexistent resource: {nonexistent_resource}")
async def test_create_nested_directories(
nc_client: NextcloudClient, test_base_path: str
):
"""Test creating nested directory structures."""
nested_path = f"{test_base_path}/level1/level2/level3"
try:
# Create nested directories (should create parent directories automatically)
result = await nc_client.webdav.create_directory(nested_path, True)
assert result["status_code"] == 201
# Verify the structure was created
level1_listing = await nc_client.webdav.list_directory(
f"{test_base_path}/level1"
)
assert len(level1_listing) == 1
assert level1_listing[0]["name"] == "level2"
assert level1_listing[0]["is_directory"] is True
level2_listing = await nc_client.webdav.list_directory(
f"{test_base_path}/level1/level2"
)
assert len(level2_listing) == 1
assert level2_listing[0]["name"] == "level3"
assert level2_listing[0]["is_directory"] is True
logger.info(f"Created nested directory structure: {nested_path}")
finally:
# Cleanup - delete from deepest to shallowest
try:
await nc_client.webdav.delete_resource(nested_path)
await nc_client.webdav.delete_resource(f"{test_base_path}/level1/level2")
await nc_client.webdav.delete_resource(f"{test_base_path}/level1")
except Exception:
pass
async def test_overwrite_existing_file(nc_client: NextcloudClient, test_base_path: str):
"""Test overwriting an existing file."""
test_file = f"{test_base_path}/overwrite_test.txt"
original_content = "Original content"
new_content = "New content after overwrite"
try:
# Create base directory
await nc_client.webdav.create_directory(test_base_path)
# Write original file
await nc_client.webdav.write_file(
test_file, original_content.encode("utf-8"), content_type="text/plain"
)
# Verify original content
content, _ = await nc_client.webdav.read_file(test_file)
assert content.decode("utf-8") == original_content
# Overwrite with new content
overwrite_result = await nc_client.webdav.write_file(
test_file, new_content.encode("utf-8"), content_type="text/plain"
)
assert overwrite_result["status_code"] in [200, 204] # OK or No Content
# Verify new content
content, _ = await nc_client.webdav.read_file(test_file)
assert content.decode("utf-8") == new_content
logger.info(f"Successfully overwrote file: {test_file}")
finally:
# Cleanup
try:
await nc_client.webdav.delete_resource(test_file)
await nc_client.webdav.delete_resource(test_base_path)
except Exception:
pass
async def test_list_root_directory(nc_client: NextcloudClient):
"""Test listing the root directory."""
root_listing = await nc_client.webdav.list_directory("")
# Root directory should exist and be listable
assert isinstance(root_listing, list)
# Should have at least some default folders/files
assert len(root_listing) >= 0
# Check structure of items
for item in root_listing:
assert "name" in item
assert "path" in item
assert "is_directory" in item
assert "size" in item
assert "content_type" in item
assert "last_modified" in item
logger.info(f"Root directory contains {len(root_listing)} items")