"""Tests for the move_note MCP tool.""" import pytest from basic_memory.mcp.tools.move_note import move_note from basic_memory.mcp.tools.write_note import write_note from basic_memory.mcp.tools.read_note import read_note @pytest.mark.asyncio async def test_move_note_success(app, client): """Test successfully moving a note to a new location.""" # Create initial note await write_note( title="Test Note", folder="source", content="# Test Note\nOriginal content here.", ) # Move note result = await move_note( identifier="source/test-note", destination_path="target/MovedNote.md", ) assert isinstance(result, str) assert "✅ Note moved successfully" in result # Verify original location no longer exists try: await read_note("source/test-note") assert False, "Original note should not exist after move" except Exception: pass # Expected - note should not exist at original location # Verify note exists at new location with same content content = await read_note("target/moved-note") assert "# Test Note" in content assert "Original content here" in content assert "permalink: target/moved-note" in content @pytest.mark.asyncio async def test_move_note_with_folder_creation(client): """Test moving note creates necessary folders.""" # Create initial note await write_note( title="Deep Note", folder="", content="# Deep Note\nContent in root folder.", ) # Move to deeply nested path result = await move_note( identifier="deep-note", destination_path="deeply/nested/folder/DeepNote.md", ) assert isinstance(result, str) assert "✅ Note moved successfully" in result # Verify note exists at new location content = await read_note("deeply/nested/folder/deep-note") assert "# Deep Note" in content assert "Content in root folder" in content @pytest.mark.asyncio async def test_move_note_with_observations_and_relations(app, client): """Test moving note preserves observations and relations.""" # Create note with complex semantic content await write_note( title="Complex Entity", folder="source", content="""# Complex Entity ## Observations - [note] Important observation #tag1 - [feature] Key feature #feature ## Relations - relation to [[SomeOtherEntity]] - depends on [[Dependency]] Some additional content. """, ) # Move note result = await move_note( identifier="source/complex-entity", destination_path="target/MovedComplex.md", ) assert isinstance(result, str) assert "✅ Note moved successfully" in result # Verify moved note preserves all content content = await read_note("target/moved-complex") assert "Important observation #tag1" in content assert "Key feature #feature" in content assert "[[SomeOtherEntity]]" in content assert "[[Dependency]]" in content assert "Some additional content" in content @pytest.mark.asyncio async def test_move_note_by_title(client): """Test moving note using title as identifier.""" # Create note with unique title await write_note( title="UniqueTestTitle", folder="source", content="# UniqueTestTitle\nTest content.", ) # Move using title as identifier result = await move_note( identifier="UniqueTestTitle", destination_path="target/MovedByTitle.md", ) assert isinstance(result, str) assert "✅ Note moved successfully" in result # Verify note exists at new location content = await read_note("target/moved-by-title") assert "# UniqueTestTitle" in content assert "Test content" in content @pytest.mark.asyncio async def test_move_note_by_file_path(client): """Test moving note using file path as identifier.""" # Create initial note await write_note( title="PathTest", folder="source", content="# PathTest\nContent for path test.", ) # Move using file path as identifier result = await move_note( identifier="source/PathTest.md", destination_path="target/MovedByPath.md", ) assert isinstance(result, str) assert "✅ Note moved successfully" in result # Verify note exists at new location content = await read_note("target/moved-by-path") assert "# PathTest" in content assert "Content for path test" in content @pytest.mark.asyncio async def test_move_note_nonexistent_note(client): """Test moving a note that doesn't exist.""" result = await move_note( identifier="nonexistent/note", destination_path="target/SomeFile.md", ) # Should return user-friendly error message string assert isinstance(result, str) assert "# Move Failed - Note Not Found" in result assert "could not be found for moving" in result assert "Search for the note first" in result @pytest.mark.asyncio async def test_move_note_invalid_destination_path(client): """Test moving note with invalid destination path.""" # Create initial note await write_note( title="TestNote", folder="source", content="# TestNote\nTest content.", ) # Test absolute path (should be rejected by validation) result = await move_note( identifier="source/test-note", destination_path="/absolute/path.md", ) # Should return user-friendly error message string assert isinstance(result, str) assert "# Move Failed" in result assert "/absolute/path.md" in result or "Invalid" in result or "path" in result @pytest.mark.asyncio async def test_move_note_destination_exists(client): """Test moving note to existing destination.""" # Create source note await write_note( title="SourceNote", folder="source", content="# SourceNote\nSource content.", ) # Create destination note await write_note( title="DestinationNote", folder="target", content="# DestinationNote\nDestination content.", ) # Try to move source to existing destination result = await move_note( identifier="source/source-note", destination_path="target/DestinationNote.md", ) # Should return user-friendly error message string assert isinstance(result, str) assert "# Move Failed" in result assert "already exists" in result or "Destination" in result @pytest.mark.asyncio async def test_move_note_same_location(client): """Test moving note to the same location.""" # Create initial note await write_note( title="SameLocationTest", folder="test", content="# SameLocationTest\nContent here.", ) # Try to move to same location result = await move_note( identifier="test/same-location-test", destination_path="test/SameLocationTest.md", ) # Should return user-friendly error message string assert isinstance(result, str) assert "# Move Failed" in result assert "already exists" in result or "same" in result or "Destination" in result @pytest.mark.asyncio async def test_move_note_rename_only(client): """Test moving note within same folder (rename operation).""" # Create initial note await write_note( title="OriginalName", folder="test", content="# OriginalName\nContent to rename.", ) # Rename within same folder await move_note( identifier="test/original-name", destination_path="test/NewName.md", ) # Verify original is gone try: await read_note("test/original-name") assert False, "Original note should not exist after rename" except Exception: pass # Expected # Verify new name exists with same content content = await read_note("test/new-name") assert "# OriginalName" in content # Title in content remains same assert "Content to rename" in content assert "permalink: test/new-name" in content @pytest.mark.asyncio async def test_move_note_complex_filename(client): """Test moving note with spaces in filename.""" # Create note with spaces in name await write_note( title="Meeting Notes 2025", folder="meetings", content="# Meeting Notes 2025\nMeeting content with dates.", ) # Move to new location result = await move_note( identifier="meetings/meeting-notes-2025", destination_path="archive/2025/meetings/Meeting Notes 2025.md", ) assert isinstance(result, str) assert "✅ Note moved successfully" in result # Verify note exists at new location with correct content content = await read_note("archive/2025/meetings/meeting-notes-2025") assert "# Meeting Notes 2025" in content assert "Meeting content with dates" in content @pytest.mark.asyncio async def test_move_note_with_tags(app, client): """Test moving note with tags preserves tags.""" # Create note with tags await write_note( title="Tagged Note", folder="source", content="# Tagged Note\nContent with tags.", tags=["important", "work", "project"], ) # Move note result = await move_note( identifier="source/tagged-note", destination_path="target/MovedTaggedNote.md", ) assert isinstance(result, str) assert "✅ Note moved successfully" in result # Verify tags are preserved in correct YAML format content = await read_note("target/moved-tagged-note") assert "- important" in content assert "- work" in content assert "- project" in content @pytest.mark.asyncio async def test_move_note_empty_string_destination(client): """Test moving note with empty destination path.""" # Create initial note await write_note( title="TestNote", folder="source", content="# TestNote\nTest content.", ) # Test empty destination path result = await move_note( identifier="source/test-note", destination_path="", ) # Should return user-friendly error message string assert isinstance(result, str) assert "# Move Failed" in result assert "empty" in result or "Invalid" in result or "path" in result @pytest.mark.asyncio async def test_move_note_parent_directory_path(client): """Test moving note with parent directory in destination path.""" # Create initial note await write_note( title="TestNote", folder="source", content="# TestNote\nTest content.", ) # Test parent directory path result = await move_note( identifier="source/test-note", destination_path="../parent/file.md", ) # Should return user-friendly error message string assert isinstance(result, str) assert "# Move Failed" in result assert "parent" in result or "Invalid" in result or "path" in result or ".." in result @pytest.mark.asyncio async def test_move_note_identifier_variations(client): """Test that various identifier formats work for moving.""" # Create a note to test different identifier formats await write_note( title="Test Document", folder="docs", content="# Test Document\nContent for testing identifiers.", ) # Test with permalink identifier result = await move_note( identifier="docs/test-document", destination_path="moved/TestDocument.md", ) assert isinstance(result, str) assert "✅ Note moved successfully" in result # Verify it moved correctly content = await read_note("moved/test-document") assert "# Test Document" in content assert "Content for testing identifiers" in content @pytest.mark.asyncio async def test_move_note_preserves_frontmatter(app, client): """Test that moving preserves custom frontmatter.""" # Create note with custom frontmatter by first creating it normally await write_note( title="Custom Frontmatter Note", folder="source", content="# Custom Frontmatter Note\nContent with custom metadata.", ) # Move the note result = await move_note( identifier="source/custom-frontmatter-note", destination_path="target/MovedCustomNote.md", ) assert isinstance(result, str) assert "✅ Note moved successfully" in result # Verify the moved note has proper frontmatter structure content = await read_note("target/moved-custom-note") assert "title: Custom Frontmatter Note" in content assert "type: note" in content assert "permalink: target/moved-custom-note" in content assert "# Custom Frontmatter Note" in content assert "Content with custom metadata" in content