Files
basicmachines-co-basic-memory/tests/utils/test_file_utils.py
T
2025-01-03 23:59:09 -06:00

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"