@@ -349,3 +349,156 @@ async def test_delete_nonexistent_relation(relation_repository):
349349 """Test deleting a relation that doesn't exist."""
350350 result = await relation_repository .delete_by_fields (relation_type = "nonexistent" )
351351 assert result is False
352+
353+
354+ # -------------------------------------------------------------------------
355+ # Tests for add_all_ignore_duplicates
356+ # -------------------------------------------------------------------------
357+
358+
359+ @pytest .mark .asyncio
360+ async def test_add_all_ignore_duplicates_basic (
361+ relation_repository : RelationRepository , sample_entity : Entity , related_entity : Entity
362+ ):
363+ """Test bulk inserting relations with ON CONFLICT DO NOTHING."""
364+ relations = [
365+ Relation (
366+ from_id = sample_entity .id ,
367+ to_id = related_entity .id ,
368+ to_name = related_entity .title ,
369+ relation_type = "links_to" ,
370+ ),
371+ Relation (
372+ from_id = sample_entity .id ,
373+ to_id = related_entity .id ,
374+ to_name = related_entity .title ,
375+ relation_type = "references" ,
376+ ),
377+ ]
378+
379+ inserted = await relation_repository .add_all_ignore_duplicates (relations )
380+
381+ # Both should be inserted
382+ assert inserted == 2
383+
384+ # Verify they exist
385+ found = await relation_repository .find_by_entities (sample_entity .id , related_entity .id )
386+ assert len (found ) == 2
387+ relation_types = {r .relation_type for r in found }
388+ assert relation_types == {"links_to" , "references" }
389+
390+
391+ @pytest .mark .asyncio
392+ async def test_add_all_ignore_duplicates_skips_duplicates (
393+ relation_repository : RelationRepository , sample_entity : Entity , related_entity : Entity
394+ ):
395+ """Test that duplicate relations are silently ignored."""
396+ # Same relation appearing multiple times (common when same [[link]] appears twice in doc)
397+ relations = [
398+ Relation (
399+ from_id = sample_entity .id ,
400+ to_id = None , # Unresolved
401+ to_name = "Some Target" ,
402+ relation_type = "links_to" ,
403+ ),
404+ Relation (
405+ from_id = sample_entity .id ,
406+ to_id = None ,
407+ to_name = "Some Target" , # Duplicate!
408+ relation_type = "links_to" ,
409+ ),
410+ Relation (
411+ from_id = sample_entity .id ,
412+ to_id = None ,
413+ to_name = "Some Target" , # Triple duplicate!
414+ relation_type = "links_to" ,
415+ ),
416+ ]
417+
418+ inserted = await relation_repository .add_all_ignore_duplicates (relations )
419+
420+ # Only 1 should be inserted (duplicates ignored)
421+ assert inserted == 1
422+
423+ # Verify only one exists
424+ all_relations = await relation_repository .find_all ()
425+ matching = [r for r in all_relations if r .to_name == "Some Target" ]
426+ assert len (matching ) == 1
427+
428+
429+ @pytest .mark .asyncio
430+ async def test_add_all_ignore_duplicates_empty_list (relation_repository : RelationRepository ):
431+ """Test with empty list returns 0."""
432+ inserted = await relation_repository .add_all_ignore_duplicates ([])
433+ assert inserted == 0
434+
435+
436+ @pytest .mark .asyncio
437+ async def test_add_all_ignore_duplicates_mixed (
438+ relation_repository : RelationRepository , sample_entity : Entity , related_entity : Entity
439+ ):
440+ """Test with mix of new and duplicate relations."""
441+ # First, insert one relation
442+ first_relation = Relation (
443+ from_id = sample_entity .id ,
444+ to_id = None ,
445+ to_name = "Existing Target" ,
446+ relation_type = "links_to" ,
447+ )
448+ await relation_repository .add_all_ignore_duplicates ([first_relation ])
449+
450+ # Now try to insert a mix of new and duplicate
451+ relations = [
452+ Relation (
453+ from_id = sample_entity .id ,
454+ to_id = None ,
455+ to_name = "Existing Target" , # Duplicate of first_relation
456+ relation_type = "links_to" ,
457+ ),
458+ Relation (
459+ from_id = sample_entity .id ,
460+ to_id = None ,
461+ to_name = "New Target 1" , # New
462+ relation_type = "links_to" ,
463+ ),
464+ Relation (
465+ from_id = sample_entity .id ,
466+ to_id = None ,
467+ to_name = "New Target 2" , # New
468+ relation_type = "references" ,
469+ ),
470+ ]
471+
472+ inserted = await relation_repository .add_all_ignore_duplicates (relations )
473+
474+ # Only 2 new ones should be inserted
475+ assert inserted == 2
476+
477+ # Verify total count
478+ all_relations = await relation_repository .find_all ()
479+ from_sample = [r for r in all_relations if r .from_id == sample_entity .id ]
480+ assert len (from_sample ) == 3 # 1 existing + 2 new
481+
482+
483+ @pytest .mark .asyncio
484+ async def test_add_all_ignore_duplicates_with_context (
485+ relation_repository : RelationRepository , sample_entity : Entity , related_entity : Entity
486+ ):
487+ """Test that context field is properly inserted."""
488+ relations = [
489+ Relation (
490+ from_id = sample_entity .id ,
491+ to_id = related_entity .id ,
492+ to_name = related_entity .title ,
493+ relation_type = "links_to" ,
494+ context = "some context here" ,
495+ ),
496+ ]
497+
498+ inserted = await relation_repository .add_all_ignore_duplicates (relations )
499+ assert inserted == 1
500+
501+ # Verify context was saved
502+ found = await relation_repository .find_by_entities (sample_entity .id , related_entity .id )
503+ assert len (found ) == 1
504+ assert found [0 ].context == "some context here"
0 commit comments