mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
251 lines
5.7 KiB
Python
251 lines
5.7 KiB
Python
"""Tests for file utilities."""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from basic_memory.utils.file_utils import (
|
|
compute_checksum,
|
|
ensure_directory,
|
|
write_file_atomic,
|
|
add_frontmatter,
|
|
parse_frontmatter,
|
|
has_frontmatter,
|
|
remove_frontmatter,
|
|
parse_content_with_frontmatter,
|
|
FileError,
|
|
FileWriteError,
|
|
ParseError,
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_compute_checksum():
|
|
"""Test checksum computation."""
|
|
content = "test content"
|
|
checksum = await compute_checksum(content)
|
|
assert isinstance(checksum, str)
|
|
assert len(checksum) == 64 # SHA-256 produces 64 char hex string
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_compute_checksum_error():
|
|
"""Test checksum error handling."""
|
|
with pytest.raises(FileError):
|
|
# Try to hash an object that can't be encoded
|
|
await compute_checksum(object()) # pyright: ignore [reportArgumentType]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_ensure_directory(tmp_path: Path):
|
|
"""Test directory creation."""
|
|
test_dir = tmp_path / "test_dir"
|
|
await ensure_directory(test_dir)
|
|
assert test_dir.exists()
|
|
assert test_dir.is_dir()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_write_file_atomic(tmp_path: Path):
|
|
"""Test atomic file writing."""
|
|
test_file = tmp_path / "test.txt"
|
|
content = "test content"
|
|
|
|
await write_file_atomic(test_file, content)
|
|
assert test_file.exists()
|
|
assert test_file.read_text() == content
|
|
|
|
# Temp file should be cleaned up
|
|
assert not test_file.with_suffix(".tmp").exists()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_write_file_atomic_error(tmp_path: Path):
|
|
"""Test atomic write error handling."""
|
|
# Try to write to a directory that doesn't exist
|
|
test_file = tmp_path / "nonexistent" / "test.txt"
|
|
|
|
with pytest.raises(FileWriteError):
|
|
await write_file_atomic(test_file, "test content")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_frontmatter():
|
|
"""Test adding frontmatter."""
|
|
content = "test content"
|
|
metadata = {"title": "Test", "tags": ["a", "b"]}
|
|
|
|
result = await add_frontmatter(content, metadata)
|
|
|
|
# Should have frontmatter delimiters
|
|
assert result.startswith("---\n")
|
|
assert "---\n\n" in result
|
|
|
|
# Should include metadata
|
|
assert "title: Test" in result
|
|
assert "- a\n- b" in result or "['a', 'b']" in result
|
|
|
|
# Should preserve content
|
|
assert result.endswith(f"test content")
|
|
|
|
|
|
def test_has_frontmatter():
|
|
"""Test frontmatter detection."""
|
|
# Valid frontmatter
|
|
assert has_frontmatter("""---
|
|
title: Test
|
|
---
|
|
content""")
|
|
|
|
# Just content
|
|
assert not has_frontmatter("Just content")
|
|
|
|
# Empty content
|
|
assert not has_frontmatter("")
|
|
|
|
# Just delimiter
|
|
assert not has_frontmatter("---")
|
|
|
|
# Delimiter not at start
|
|
assert not has_frontmatter("""
|
|
Some text
|
|
---
|
|
title: Test
|
|
---""")
|
|
|
|
# Invalid format
|
|
assert not has_frontmatter("--title: test--")
|
|
|
|
|
|
def test_parse_frontmatter():
|
|
"""Test parsing frontmatter."""
|
|
# Valid frontmatter
|
|
content = """---
|
|
title: Test
|
|
tags:
|
|
- a
|
|
- b
|
|
---
|
|
content"""
|
|
|
|
result = parse_frontmatter(content)
|
|
assert result == {"title": "Test", "tags": ["a", "b"]}
|
|
|
|
# Empty frontmatter
|
|
content = """---
|
|
---
|
|
content"""
|
|
result = parse_frontmatter(content)
|
|
assert result == None or result == {}
|
|
|
|
# Invalid YAML
|
|
with pytest.raises(ParseError):
|
|
parse_frontmatter("""---
|
|
[invalid yaml]
|
|
---
|
|
content""")
|
|
|
|
# No frontmatter
|
|
with pytest.raises(ParseError):
|
|
parse_frontmatter("Just content")
|
|
|
|
# Incomplete frontmatter
|
|
with pytest.raises(ParseError):
|
|
parse_frontmatter("""---
|
|
title: Test
|
|
content""")
|
|
|
|
|
|
def test_remove_frontmatter():
|
|
"""Test removing frontmatter."""
|
|
# With frontmatter
|
|
content = """---
|
|
title: Test
|
|
---
|
|
test content"""
|
|
assert remove_frontmatter(content) == "test content"
|
|
|
|
# No frontmatter
|
|
content = "test content"
|
|
assert remove_frontmatter(content) == "test content"
|
|
|
|
# Only frontmatter
|
|
content = """---
|
|
title: Test
|
|
---
|
|
"""
|
|
assert remove_frontmatter(content) == ""
|
|
|
|
# frontmatter missing some fields
|
|
assert remove_frontmatter("""---
|
|
title: Test
|
|
content""") == "---\ntitle: Test\ncontent"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_parse_content_with_frontmatter():
|
|
"""Test combined frontmatter and content parsing."""
|
|
# Full document
|
|
content = """---
|
|
title: Test
|
|
tags:
|
|
- a
|
|
- b
|
|
---
|
|
test content"""
|
|
|
|
frontmatter, body = await parse_content_with_frontmatter(content)
|
|
assert frontmatter == {"title": "Test", "tags": ["a", "b"]}
|
|
assert body == "test content"
|
|
|
|
# No frontmatter
|
|
content = "test content"
|
|
frontmatter, body = await parse_content_with_frontmatter(content)
|
|
assert frontmatter == {}
|
|
assert body == "test content"
|
|
|
|
# Empty document
|
|
frontmatter, body = await parse_content_with_frontmatter("")
|
|
assert frontmatter == {}
|
|
assert body == ""
|
|
|
|
# Only frontmatter
|
|
content = """---
|
|
title: Test
|
|
---
|
|
"""
|
|
frontmatter, body = await parse_content_with_frontmatter(content)
|
|
assert frontmatter == {"title": "Test"}
|
|
assert body == ""
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_frontmatter_whitespace_handling():
|
|
"""Test frontmatter handling with various whitespace."""
|
|
# Extra newlines before frontmatter
|
|
content = """
|
|
|
|
---
|
|
title: Test
|
|
---
|
|
content"""
|
|
assert has_frontmatter(content.strip())
|
|
frontmatter = parse_frontmatter(content.strip())
|
|
assert frontmatter == {"title": "Test"}
|
|
|
|
# Extra newlines after frontmatter
|
|
content = """---
|
|
title: Test
|
|
---
|
|
|
|
|
|
content"""
|
|
result = await add_frontmatter("content", {"title": "Test"})
|
|
assert result.count("\n\n") == 1 # Should normalize to single blank line
|
|
|
|
# Spaces around content
|
|
content = """---
|
|
title: Test
|
|
---
|
|
content """
|
|
assert remove_frontmatter(content).strip() == "content" |