import unittest from unittest.mock import MagicMock, patch from semantica.kg.graph_builder import GraphBuilder class TestGraphBuilderExternal(unittest.TestCase): def setUp(self): self.mock_tracker_patcher = patch("semantica.utils.progress_tracker.get_progress_tracker") self.mock_get_tracker = self.mock_tracker_patcher.start() self.mock_tracker = MagicMock() self.mock_get_tracker.return_value = self.mock_tracker self.mock_resolver_patcher = patch("semantica.kg.entity_resolver.EntityResolver") self.mock_resolver_cls = self.mock_resolver_patcher.start() self.mock_conflict_patcher = patch("semantica.conflicts.conflict_detector.ConflictDetector") self.mock_conflict_cls = self.mock_conflict_patcher.start() def tearDown(self): self.mock_tracker_patcher.stop() self.mock_resolver_patcher.stop() self.mock_conflict_patcher.stop() def test_single_source_dict_with_source_id_target_id(self): builder = GraphBuilder(merge_entities=False, resolve_conflicts=False) entities = [ {"id": "drug:1", "name": "Aspirin", "type": "Drug"}, {"id": "disease:1", "name": "Myocardial infarction", "type": "Disease"}, ] relationships = [ {"source_id": "drug:1", "target_id": "disease:1", "type": "TREATS"}, ] source = {"entities": entities, "relationships": relationships} kg = builder.build(source) self.assertEqual(len(kg["entities"]), 2) self.assertEqual(len(kg["relationships"]), 1) rel = kg["relationships"][0] self.assertEqual(rel.get("source"), "drug:1") self.assertEqual(rel.get("target"), "disease:1") self.assertEqual(kg["metadata"]["num_relationships"], 1) def test_sources_list_merge_with_external_relationships(self): builder = GraphBuilder(merge_entities=False, resolve_conflicts=False) source1 = { "entities": [{"id": "1", "name": "A"}], "relationships": [{"source_id": "1", "target_id": "2", "type": "REL_1"}], } source2 = { "entities": [{"id": "2", "name": "B"}], "relationships": [{"source_id": "2", "target_id": "1", "type": "REL_2"}], } kg = builder.build([source1, source2]) self.assertEqual(len(kg["entities"]), 2) self.assertEqual(len(kg["relationships"]), 2) sources = {r["source"] for r in kg["relationships"]} targets = {r["target"] for r in kg["relationships"]} self.assertEqual(sources, {"1", "2"}) self.assertEqual(targets, {"1", "2"}) def test_build_with_explicit_relationships_argument_external_ids(self): builder = GraphBuilder(merge_entities=False, resolve_conflicts=False) entities = [ {"id": "1", "name": "A"}, {"id": "2", "name": "B"}, ] relationships = [ {"source_id": "1", "target_id": "2", "type": "REL"}, ] kg = builder.build(entities, relationships=relationships) self.assertEqual(len(kg["entities"]), 2) self.assertEqual(len(kg["relationships"]), 1) rel = kg["relationships"][0] self.assertEqual(rel.get("source"), "1") self.assertEqual(rel.get("target"), "2") def test_build_single_source_external_graph(self): builder = GraphBuilder(merge_entities=False, resolve_conflicts=False) source = { "entities": [{"id": "1", "name": "A"}], "relationships": [{"source_id": "1", "target_id": "1", "type": "SELF"}], } kg = builder.build_single_source(source) self.assertEqual(len(kg["entities"]), 1) self.assertEqual(len(kg["relationships"]), 1) rel = kg["relationships"][0] self.assertEqual(rel.get("source"), "1") self.assertEqual(rel.get("target"), "1") def test_relationship_key_variants_normalized(self): builder = GraphBuilder(merge_entities=False, resolve_conflicts=False) entities = [ {"id": "1", "name": "A"}, {"id": "2", "name": "B"}, {"id": "3", "name": "C"}, {"id": "4", "name": "D"}, ] relationships = [ {"source_id": "1", "target_id": "2", "type": "R1"}, {"source": "2", "target": "3", "type": "R2"}, {"subject": "3", "object": "4", "type": "R3"}, ] kg = builder.build({"entities": entities, "relationships": relationships}) self.assertEqual(len(kg["relationships"]), 3) ids = {(r["source"], r["target"]) for r in kg["relationships"]} self.assertIn(("1", "2"), ids) self.assertIn(("2", "3"), ids) self.assertIn(("3", "4"), ids) def test_warning_when_all_relationships_dropped(self): builder = GraphBuilder(merge_entities=False, resolve_conflicts=False) source = { "entities": [], "relationships": [{"foo": "x"}, {"bar": "y"}], } with patch.object(builder.logger, "warning") as mock_warning: kg = builder.build(source) self.assertEqual(len(kg["relationships"]), 0) mock_warning.assert_called() args, _ = mock_warning.call_args self.assertIn("All relationships were dropped", args[0]) def test_no_warning_when_some_relationships_kept(self): builder = GraphBuilder(merge_entities=False, resolve_conflicts=False) source = { "entities": [{"id": "1"}, {"id": "2"}], "relationships": [ {"source_id": "1", "target_id": "2", "type": "REL"}, {"foo": "x"}, ], } with patch.object(builder.logger, "warning") as mock_warning: kg = builder.build(source) self.assertEqual(len(kg["relationships"]), 2) mock_warning.assert_not_called() def test_issue_208_minimal_reproduction_shape(self): builder = GraphBuilder( merge_entities=False, entity_resolution_strategy="none", resolve_conflicts=False, ) entities = [ {"id": "e1", "name": "Entity 1"}, {"id": "e2", "name": "Entity 2"}, {"id": "e3", "name": "Entity 3"}, ] relationships = [ {"source_id": "e1", "target_id": "e2", "type": "REL_1"}, {"source_id": "e2", "target_id": "e3", "type": "REL_2"}, ] entity_ids = {e["id"] for e in entities} for r in relationships: self.assertIn(r["source_id"], entity_ids) self.assertIn(r["target_id"], entity_ids) kg = builder.build( sources=[{"entities": entities, "relationships": relationships}], merge_entities=False, ) self.assertEqual(len(kg["entities"]), 3) self.assertEqual(len(kg["relationships"]), 2) pairs = {(r["source"], r["target"]) for r in kg["relationships"]} self.assertIn(("e1", "e2"), pairs) self.assertIn(("e2", "e3"), pairs) def test_issue_206_earnings_call_shape(self): builder = GraphBuilder( merge_entities=False, entity_resolution_strategy="none", resolve_conflicts=False, ) entities = [ { "id": "entity_446_MDA Space Ltd.", "name": "MDA Space Ltd.", "type": "ORGANIZATION", }, { "id": "entity_500_$409.8 million", "name": "$409.8 million", "type": "MONEY", }, ] relationships = [ { "id": None, "source_id": "MDA Space Ltd.", "target_id": "$409.8 million", "type": "HAS_REVENUE", "confidence": 0.975, "metadata": {}, } ] kg = builder.build( sources=[{"entities": entities, "relationships": relationships}], merge_entities=False, ) self.assertEqual(len(kg["entities"]), 2) self.assertEqual(len(kg["relationships"]), 1) rel = kg["relationships"][0] self.assertEqual(rel.get("source"), "MDA Space Ltd.") self.assertEqual(rel.get("target"), "$409.8 million")