@@ -220,8 +220,11 @@ async def test_delete_document_success(tmp_path: Path) -> None:
220220 mock_anyio .to_thread .run_sync .assert_awaited_once ()
221221
222222
223- async def test_delete_document_idempotent () -> None :
223+ async def test_delete_document_idempotent (
224+ tmp_path : Path , monkeypatch : pytest .MonkeyPatch
225+ ) -> None :
224226 """Returns deleted_topics=0 without error when document does not exist."""
227+ monkeypatch .setenv ("EVEROS_ROOT" , str (tmp_path ))
225228 with patch (f"{ _MOD } .knowledge_document_repo" ) as mock_doc_repo :
226229 mock_doc_repo .get_by_doc_id = AsyncMock (return_value = None )
227230
@@ -232,6 +235,58 @@ async def test_delete_document_idempotent() -> None:
232235 assert result .deleted_topics == 0
233236
234237
238+ async def test_delete_document_removes_dir_before_the_index_has_the_row (
239+ tmp_path : Path , monkeypatch : pytest .MonkeyPatch
240+ ) -> None :
241+ """A document created a moment ago has a directory but no SQLite row yet
242+ (the cascade trails the markdown by seconds). Delete must still remove the
243+ directory — and only that directory — and report the topic files it held.
244+ """
245+ monkeypatch .setenv ("EVEROS_ROOT" , str (tmp_path ))
246+ kdir = tmp_path / "app1" / "proj1" / "knowledge"
247+ doc_dir = kdir / "Technology" / "Release_checklist_d_lagging00001"
248+ doc_dir .mkdir (parents = True )
249+ (doc_dir / "index.md" ).write_text ("---\n doc_id: d_lagging00001\n ---\n " )
250+ (doc_dir / "1_before.md" ).write_text ("# before\n " )
251+ (doc_dir / "2_after.md" ).write_text ("# after\n " )
252+ sibling = kdir / "Technology" / "Other_notes_d_sibling000001"
253+ sibling .mkdir ()
254+ (sibling / "index.md" ).write_text ("---\n doc_id: d_sibling000001\n ---\n " )
255+ (kdir / ".taxonomy.md" ).write_text ("# taxonomy\n " )
256+
257+ with patch (f"{ _MOD } .knowledge_document_repo" ) as mock_doc_repo :
258+ mock_doc_repo .get_by_doc_id = AsyncMock (return_value = None )
259+ result = await delete_document ("d_lagging00001" , "app1" , "proj1" )
260+
261+ assert result .deleted_topics == 2
262+ assert not doc_dir .exists ()
263+ assert sibling .is_dir ()
264+ assert (kdir / ".taxonomy.md" ).exists ()
265+
266+
267+ @pytest .mark .parametrize ("doc_id" , ["d_absent0000001" , "*" , "*/_original" ])
268+ async def test_delete_document_unindexed_id_not_on_disk_removes_nothing (
269+ tmp_path : Path , monkeypatch : pytest .MonkeyPatch , doc_id : str
270+ ) -> None :
271+ """No row and no directory for the id → nothing is touched, including
272+ when the id looks like a glob pattern or a path: the name match is literal.
273+ """
274+ monkeypatch .setenv ("EVEROS_ROOT" , str (tmp_path ))
275+ kdir = tmp_path / "app1" / "proj1" / "knowledge"
276+ sibling = kdir / "Technology" / "Other_notes_d_sibling000001"
277+ sibling .mkdir (parents = True )
278+ (sibling / "index.md" ).write_text ("---\n doc_id: d_sibling000001\n ---\n " )
279+ (sibling / "_original" ).mkdir ()
280+
281+ with patch (f"{ _MOD } .knowledge_document_repo" ) as mock_doc_repo :
282+ mock_doc_repo .get_by_doc_id = AsyncMock (return_value = None )
283+ result = await delete_document (doc_id , "app1" , "proj1" )
284+
285+ assert result .deleted_topics == 0
286+ assert (sibling / "index.md" ).exists ()
287+ assert (sibling / "_original" ).is_dir ()
288+
289+
235290# ── list_documents ────────────────────────────────────────────────────────────
236291
237292
0 commit comments