# tests/test_indexer_advanced.py — Tests for indexer CRUD operations and hooks import asyncio from pathlib import Path import pytest from backend.indexer import ( index, vault_config, _file_lookup, path_index, _index_generation, _on_index_change, _add_file_to_structures, _remove_file_from_structures, update_single_file, remove_single_file, handle_file_move, _index_single_file_sync, _ensure_parent_dirs_in_path_index, find_file_in_index, get_vault_names, set_index_change_hook, _scan_vault, build_index, ) # ═══════════════════════════════════════════════════════════════════ # Hook system # ═══════════════════════════════════════════════════════════════════ class TestIndexChangeHook: def test_set_and_call_hook(self, client): """Hook fires when adding/removing files from the index.""" calls = [] def hook(action, vault, path, file_info): calls.append((action, vault, path)) set_index_change_hook(hook) path = "hook_test_12345.md" file_info = { "path": path, "title": "Hook Test", "tags": ["test"], "content": "# Hook Test", "content_preview": "# Hook", "size": 100, "modified": "2024-01-01T00:00:00Z", "extension": ".md", } _add_file_to_structures("TestVault", file_info) _remove_file_from_structures("TestVault", path) # Hook should have been called at least twice (add + remove) assert len(calls) >= 2, f"Hook calls: {calls}" assert calls[0][0] == "add" assert calls[1][0] == "remove" # Reset hook set_index_change_hook(None) # ═══════════════════════════════════════════════════════════════════ # File CRUD operations # ═══════════════════════════════════════════════════════════════════ class TestFileCRUD: def test_index_single_file_sync(self, test_vault_dir): vault_path = test_vault_dir file_path = Path(vault_path) / "note1.md" info = _index_single_file_sync("TestVault", vault_path, str(file_path)) assert info is not None assert info["path"] == "note1.md" assert "title" in info assert "tags" in info assert "content" in info def test_index_single_file_sync_nonexistent(self, test_vault_dir): info = _index_single_file_sync("TestVault", test_vault_dir, "/nonexistent/file.md") assert info is None def test_index_single_file_sync_unsupported(self, test_vault_dir): bin_file = Path(test_vault_dir) / "test_image.png" bin_file.write_bytes(b"fake png data") info = _index_single_file_sync("TestVault", test_vault_dir, str(bin_file)) assert info is None def test_add_and_remove_file(self, client): path = "crud_test_unique_12345.md" file_info = { "path": path, "title": "CRUD Test", "tags": ["unittest"], "content": "# Test", "content_preview": "# T", "size": 50, "modified": "2024-01-01T00:00:00Z", "extension": ".md", } try: _add_file_to_structures("TestVault", file_info) assert find_file_in_index(path, "TestVault") is not None finally: _remove_file_from_structures("TestVault", path) assert find_file_in_index(path, "TestVault") is None def test_remove_nonexistent_file(self, client): removed = _remove_file_from_structures("TestVault", "crud_nonexistent_12345.md") assert removed is None def test_remove_from_nonexistent_vault(self): removed = _remove_file_from_structures("FakeVault", "test.md") assert removed is None def test_add_file_to_nonexistent_vault(self): old_gen = _index_generation _add_file_to_structures("FakeVaultXYZ", {"path": "x.md", "title": "X", "tags": [], "content": "", "content_preview": "", "size": 0, "modified": "", "extension": ".md"}) assert _index_generation == old_gen def test_ensure_parent_dirs(self, client): """_ensure_parent_dirs_in_path_index creates missing directory entries.""" if "TestVault" not in path_index: path_index["TestVault"] = [] existing = set(p["path"] for p in path_index["TestVault"]) _ensure_parent_dirs_in_path_index("TestVault", "a/b/c/file.md", existing) paths = [p["path"] for p in path_index["TestVault"]] assert "a" in paths or "a" in [p["path"] for p in path_index.get("TestVault", [])] # ═══════════════════════════════════════════════════════════════════ # Path index and lookup # ═══════════════════════════════════════════════════════════════════ class TestPathIndex: def test_path_index_has_entries(self, client): assert "TestVault" in path_index paths = [p["path"] for p in path_index["TestVault"]] assert len(paths) > 0 def test_path_index_has_files_and_dirs(self, client): entries = path_index["TestVault"] file_entries = [e for e in entries if e["type"] == "file"] dir_entries = [e for e in entries if e["type"] == "directory"] assert len(file_entries) > 0 assert len(dir_entries) > 0 # ═══════════════════════════════════════════════════════════════════ # File lookup — _file_lookup # ═══════════════════════════════════════════════════════════════════ class TestFileLookup: def test_lookup_contains_files(self, client): assert len(_file_lookup) > 0 def test_find_file_by_name(self, client): result = find_file_in_index("note1.md", "TestVault") assert result is not None assert result["vault"] == "TestVault" def test_find_file_in_subdirectory(self, client): result = find_file_in_index("Projets/projet.md", "TestVault") assert result is not None assert "projet" in result["path"] # ═══════════════════════════════════════════════════════════════════ # Index generation counter # ═══════════════════════════════════════════════════════════════════ class TestIndexGeneration: def test_generation_starts_positive(self): assert isinstance(_index_generation, int) assert _index_generation >= 0 def test_generation_increments_on_add(self, client): old = _index_generation _add_file_to_structures("TestVault", { "path": "gen_test2.md", "title": "Gen2", "tags": [], "content": "", "content_preview": "", "size": 0, "modified": "2024-01-01T00:00:00Z", "extension": ".md", }) new_gen = _index_generation # Clean up _remove_file_from_structures("TestVault", "gen_test2.md") # Generation should have incremented (at least stayed same if cleanup failed) assert new_gen >= old