ReMe/tests4/unit/test_file_catalog.py
jinliyl 3cb2579ff7
refactor(steps): update auto-memory (#263)
* refactor(steps): update naming conventions in components and configuration

Updated naming conventions across multiple files, changing colon-separated names to underscore-separated format, and added new step definitions along with documentation updates.

Key changes:
- Replaced `Synchronizer` with `AutoMemory` as the counterpart component for cold-write operations
- Updated naming conventions in all related configuration files (e.g., `frontmatter:read` → `frontmatter_read`)
- Added new step definitions such as `submit_slug_updates` and `auto_memory`
- Updated relevant documentation
- Modified log output format for improved readability

* refactor(evolve): Refactor the auto-memory module and update related configurations

- Remove the old slug update commit step file
- Add new auto-memory planner and writer steps
- Update __init__.py to export the new step classes
- Modify the auto_memory configuration structure in default.yaml
- Update the slug field description for clearer explanation of its purpose

* up

* up

* refactor(tests): Move unit test directory from `tests4/unittest` to `tests4/unit`

Additionally, the assertion logic in test files has been updated: direct comparisons of `payload["notes"]` have been replaced with checks verifying the presence of paths and metadata within the response content. Furthermore, some test expectations have been simplified—for example, using `count` instead of asserting against specific note lists.

Specific changes include:
- Updating workflow configurations to align with the new test directory structure
- Modifying assertions across multiple test methods to make them more flexible and maintainable
- Cleaning up and optimizing parts of the test code structure

This is a comprehensive test refactoring effort aimed at improving test readability and robustness.

* Refactor(steps): Update memory writing logic and optimize JSON schema structure

Improved the write strategy description in `auto_memory_writer.yaml` to emphasize using `edit` over `write`.
Adjusted the `json_schema` structure in `base_step.py` to support the new function definition format.
Also corrected grammatical issues in the related documentation.

* Fix: Improve frontend data parsing error handling and update test files

Added capture and handling logic for YAML parsing exceptions, providing more detailed error messages when frontend data format issues occur. Also corrected the description text in a test file.
2026-05-29 12:07:44 +08:00

161 lines
5.1 KiB
Python

"""Tests for FileCatalog backends."""
# pylint: disable=protected-access
import asyncio
import os
import tempfile
import pytest
from reme4.components.file_catalog import LocalFileCatalog
from reme4.schema import FileNode
class temp_chdir:
"""Context manager to temporarily chdir into a path and restore on exit."""
def __init__(self, path):
self.path = path
self.old = None
def __enter__(self):
self.old = os.getcwd()
os.chdir(self.path)
return self
def __exit__(self, *exc):
os.chdir(self.old)
def make_node(path: str, mtime: float = 1.0) -> FileNode:
"""Build a FileNode with the given path and mtime for fixture use."""
return FileNode(path=path, st_mtime=mtime)
# All backends should satisfy the same BaseFileCatalog contract.
BACKENDS = [LocalFileCatalog]
@pytest.mark.parametrize("backend_cls", BACKENDS)
def test_upsert_and_get_nodes(backend_cls):
"""upsert stores nodes; get_nodes returns by path or all."""
async def run():
with tempfile.TemporaryDirectory() as tmpdir, temp_chdir(tmpdir):
catalog = backend_cls()
await catalog.start()
await catalog.upsert([make_node("a.md"), make_node("b.md")])
got_all = await catalog.get_nodes()
assert {n.path for n in got_all} == {"a.md", "b.md"}
got_one = await catalog.get_nodes(["a.md"])
assert len(got_one) == 1
assert got_one[0].path == "a.md"
got_missing = await catalog.get_nodes(["nope.md"])
assert got_missing == []
await catalog.close()
print(f"✓ test_upsert_and_get_nodes[{backend_cls.__name__}] passed")
asyncio.run(run())
@pytest.mark.parametrize("backend_cls", BACKENDS)
def test_upsert_replaces_existing(backend_cls):
"""Re-upserting a node with the same path overwrites the prior entry."""
async def run():
with tempfile.TemporaryDirectory() as tmpdir, temp_chdir(tmpdir):
catalog = backend_cls()
await catalog.start()
await catalog.upsert([make_node("a.md", mtime=1.0)])
await catalog.upsert([make_node("a.md", mtime=2.0)])
nodes = await catalog.get_nodes(["a.md"])
assert len(nodes) == 1
assert nodes[0].st_mtime == 2.0
await catalog.close()
print(f"✓ test_upsert_replaces_existing[{backend_cls.__name__}] passed")
asyncio.run(run())
@pytest.mark.parametrize("backend_cls", BACKENDS)
def test_delete_single_and_list(backend_cls):
"""delete accepts both a single path and a list; missing paths are no-ops."""
async def run():
with tempfile.TemporaryDirectory() as tmpdir, temp_chdir(tmpdir):
catalog = backend_cls()
await catalog.start()
await catalog.upsert([make_node("a.md"), make_node("b.md"), make_node("c.md")])
await catalog.delete("a.md")
assert {n.path for n in await catalog.get_nodes()} == {"b.md", "c.md"}
await catalog.delete(["b.md", "ghost.md"])
assert {n.path for n in await catalog.get_nodes()} == {"c.md"}
await catalog.close()
print(f"✓ test_delete_single_and_list[{backend_cls.__name__}] passed")
asyncio.run(run())
@pytest.mark.parametrize("backend_cls", BACKENDS)
def test_get_nodes_empty_inputs(backend_cls):
"""get_nodes([]) returns []; get_nodes(None) returns all."""
async def run():
with tempfile.TemporaryDirectory() as tmpdir, temp_chdir(tmpdir):
catalog = backend_cls()
await catalog.start()
await catalog.upsert([make_node("a.md")])
assert await catalog.get_nodes([]) == []
assert len(await catalog.get_nodes(None)) == 1
await catalog.close()
print(f"✓ test_get_nodes_empty_inputs[{backend_cls.__name__}] passed")
asyncio.run(run())
@pytest.mark.parametrize("backend_cls", BACKENDS)
def test_persistence_roundtrip(backend_cls):
"""close() dumps; a fresh instance loads the same nodes from disk."""
async def run():
with tempfile.TemporaryDirectory() as tmpdir, temp_chdir(tmpdir):
c1 = backend_cls()
await c1.start()
await c1.upsert([make_node("a.md", mtime=10.0), make_node("b.md", mtime=20.0)])
await c1.close()
c2 = backend_cls()
await c2.start()
nodes = sorted(await c2.get_nodes(), key=lambda n: n.path)
assert [n.path for n in nodes] == ["a.md", "b.md"]
assert [n.st_mtime for n in nodes] == [10.0, 20.0]
await c2.close()
print(f"✓ test_persistence_roundtrip[{backend_cls.__name__}] passed")
asyncio.run(run())
if __name__ == "__main__":
print("\n=== FileCatalog Tests ===")
for backend in BACKENDS:
test_upsert_and_get_nodes(backend)
test_upsert_replaces_existing(backend)
test_delete_single_and_list(backend)
test_get_nodes_empty_inputs(backend)
test_persistence_roundtrip(backend)
print("\n所有测试通过!")