Skip to main content
Glama
cbcoutinho

Nextcloud MCP Server

by cbcoutinho
test_notes_api.py8.91 kB
import logging import httpx import pytest from nextcloud_mcp_server.client.notes import NotesClient from tests.client.conftest import create_mock_error_response, create_mock_note_response logger = logging.getLogger(__name__) # Mark all tests in this module as unit tests pytestmark = pytest.mark.unit async def test_notes_api_get_note(mocker): """Test that get_note correctly parses the API response.""" # Create mock response mock_response = create_mock_note_response( note_id=123, title="Test Note", content="Test content", category="Test", etag="abc123", ) # Mock the _make_request method mock_client = mocker.AsyncMock(spec=httpx.AsyncClient) mock_make_request = mocker.patch.object( NotesClient, "_make_request", return_value=mock_response ) # Create client and test client = NotesClient(mock_client, "testuser") note = await client.get_note(note_id=123) # Verify the response was parsed correctly assert note["id"] == 123 assert note["title"] == "Test Note" assert note["content"] == "Test content" assert note["category"] == "Test" assert note["etag"] == "abc123" # Verify the correct API endpoint was called mock_make_request.assert_called_once_with("GET", "/apps/notes/api/v1/notes/123") async def test_notes_api_create_note(mocker): """Test that create_note correctly parses the API response.""" mock_response = create_mock_note_response( note_id=456, title="New Note", content="New content", category="Category", etag="def456", ) mock_client = mocker.AsyncMock(spec=httpx.AsyncClient) mock_make_request = mocker.patch.object( NotesClient, "_make_request", return_value=mock_response ) client = NotesClient(mock_client, "testuser") note = await client.create_note( title="New Note", content="New content", category="Category" ) assert note["id"] == 456 assert note["title"] == "New Note" assert note["content"] == "New content" assert note["category"] == "Category" # Verify the correct API call was made mock_make_request.assert_called_once_with( "POST", "/apps/notes/api/v1/notes", json={"title": "New Note", "content": "New content", "category": "Category"}, ) async def test_notes_api_update(mocker): """Test that update correctly parses the API response and handles etag.""" # Mock the update response (no category passed, so no GET call happens) update_response = create_mock_note_response( note_id=123, title="Updated Title", content="Updated content", category="Test", etag="new_etag", ) mock_client = mocker.AsyncMock(spec=httpx.AsyncClient) # Mock _make_request to return the update response mock_make_request = mocker.patch.object(NotesClient, "_make_request") mock_make_request.return_value = update_response client = NotesClient(mock_client, "testuser") updated_note = await client.update( note_id=123, etag="abc123", title="Updated Title", content="Updated content", ) assert updated_note["id"] == 123 assert updated_note["title"] == "Updated Title" assert updated_note["content"] == "Updated content" assert updated_note["etag"] == "new_etag" # Verify the PUT request was made with the correct etag header (only 1 call since no category) assert mock_make_request.call_count == 1 put_call = mock_make_request.call_args_list[0] assert put_call[0] == ("PUT", "/apps/notes/api/v1/notes/123") assert put_call[1]["headers"]["If-Match"] == '"abc123"' async def test_notes_api_update_conflict(mocker): """Test that update raises HTTPStatusError on 412 conflict.""" # Mock the 412 error response error_response = create_mock_error_response(412, "Precondition Failed") mock_client = mocker.AsyncMock(spec=httpx.AsyncClient) mock_make_request = mocker.patch.object(NotesClient, "_make_request") mock_make_request.side_effect = httpx.HTTPStatusError( "412 Precondition Failed", request=httpx.Request("PUT", "http://test.local"), response=error_response, ) client = NotesClient(mock_client, "testuser") with pytest.raises(httpx.HTTPStatusError) as excinfo: await client.update( note_id=123, etag="old_etag", title="This should fail", ) assert excinfo.value.response.status_code == 412 async def test_notes_api_delete_note(mocker): """Test that delete_note makes the correct API call.""" # Mock get_note response (to fetch category for cleanup) get_response = create_mock_note_response(note_id=123, category="Test") # Mock delete response delete_response = create_mock_note_response(note_id=123) mock_client = mocker.AsyncMock(spec=httpx.AsyncClient) mock_make_request = mocker.patch.object(NotesClient, "_make_request") mock_make_request.side_effect = [get_response, delete_response] client = NotesClient(mock_client, "testuser") await client.delete_note(note_id=123) # Verify DELETE was called assert any(call[0][0] == "DELETE" for call in mock_make_request.call_args_list) async def test_notes_api_delete_nonexistent(mocker): """Test that deleting a non-existent note raises 404.""" # Mock 404 error when fetching note details error_response = create_mock_error_response(404, "Not Found") mock_client = mocker.AsyncMock(spec=httpx.AsyncClient) mock_make_request = mocker.patch.object(NotesClient, "_make_request") mock_make_request.side_effect = httpx.HTTPStatusError( "404 Not Found", request=httpx.Request("GET", "http://test.local"), response=error_response, ) client = NotesClient(mock_client, "testuser") with pytest.raises(httpx.HTTPStatusError) as excinfo: await client.delete_note(note_id=999999999) assert excinfo.value.response.status_code == 404 async def test_notes_api_append_content(mocker): """Test that append_content correctly appends to existing content.""" # Mock get_note response (to fetch current content) get_response = create_mock_note_response( note_id=123, content="Original content", etag="old_etag", ) # Mock update response with appended content update_response = create_mock_note_response( note_id=123, content="Original content\n---\nAppended content", etag="new_etag", ) mock_client = mocker.AsyncMock(spec=httpx.AsyncClient) mock_make_request = mocker.patch.object(NotesClient, "_make_request") # First call: GET (from get_note), second call: PUT (from update) mock_make_request.side_effect = [get_response, update_response] client = NotesClient(mock_client, "testuser") updated_note = await client.append_content(note_id=123, content="Appended content") assert updated_note["content"] == "Original content\n---\nAppended content" assert updated_note["etag"] == "new_etag" async def test_notes_api_append_content_to_empty_note(mocker): """Test that appending to empty note doesn't add separator.""" # Mock get_note response with empty content get_response = create_mock_note_response( note_id=123, content="", etag="old_etag", ) # Mock update response with just the appended text (no separator) update_response = create_mock_note_response( note_id=123, content="First content", etag="new_etag", ) mock_client = mocker.AsyncMock(spec=httpx.AsyncClient) mock_make_request = mocker.patch.object(NotesClient, "_make_request") # First call: GET (from get_note), second call: PUT (from update) mock_make_request.side_effect = [get_response, update_response] client = NotesClient(mock_client, "testuser") updated_note = await client.append_content(note_id=123, content="First content") # For empty notes, no separator should be added assert updated_note["content"] == "First content" async def test_notes_api_append_content_nonexistent_note(mocker): """Test that appending to a non-existent note raises 404.""" error_response = create_mock_error_response(404, "Not Found") mock_client = mocker.AsyncMock(spec=httpx.AsyncClient) mock_make_request = mocker.patch.object(NotesClient, "_make_request") mock_make_request.side_effect = httpx.HTTPStatusError( "404 Not Found", request=httpx.Request("GET", "http://test.local"), response=error_response, ) client = NotesClient(mock_client, "testuser") with pytest.raises(httpx.HTTPStatusError) as excinfo: await client.append_content(note_id=999999999, content="This should fail") assert excinfo.value.response.status_code == 404

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cbcoutinho/nextcloud-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server