from pathlib import Path from semantic_kernel import index_project from sir import EdgeKind, NodeKind, validate_snapshot def test_index_project_builds_valid_snapshot(tmp_path: Path): module = tmp_path / "demo_module.bsl" module.write_text( """ Процедура Проведение() ПроверитьОстатки(); Движения.ОстаткиТоваров.Записать(); КонецПроцедуры Процедура ПроверитьОстатки() Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ Остатки.Номенклатура ИЗ РегистрНакопления.ОстаткиТоваров КАК Остатки"; КонецПроцедуры """, encoding="utf-8", ) snapshot = index_project(tmp_path, project_id="demo") validate_snapshot(snapshot) assert any(node.kind == NodeKind.MODULE for node in snapshot.nodes) assert any(node.kind == NodeKind.PROCEDURE and node.name == "Проведение" for node in snapshot.nodes) assert any(node.kind == NodeKind.QUERY for node in snapshot.nodes) assert any(node.kind == NodeKind.REGISTER and node.name == "ОстаткиТоваров" for node in snapshot.nodes) assert any(edge.kind == EdgeKind.CALLS for edge in snapshot.edges) assert any(edge.kind == EdgeKind.READS_TABLE for edge in snapshot.edges) assert any(edge.kind == EdgeKind.WRITES for edge in snapshot.edges) def test_index_project_builds_integration_endpoint_nodes(tmp_path: Path): module = tmp_path / "integration.bsl" module.write_text( """ Процедура Отправить() Адрес = "https://api.example.local/orders"; Объект = Новый COMОбъект("V83.Application"); КонецПроцедуры """, encoding="utf-8", ) snapshot = index_project(tmp_path, project_id="integrations") assert any(node.kind == NodeKind.INTEGRATION_ENDPOINT for node in snapshot.nodes) assert any(edge.kind == EdgeKind.USES_INTEGRATION for edge in snapshot.edges) def test_index_project_extracts_inline_new_query(tmp_path: Path): module = tmp_path / "query_module.bsl" module.write_text( """ Процедура ПроверитьКонтрагента() Запрос = Новый Запрос("ВЫБРАТЬ Контрагенты.Ссылка ИЗ Справочник.Контрагенты КАК Контрагенты"); КонецПроцедуры """, encoding="utf-8", ) snapshot = index_project(tmp_path, project_id="inline-query") query = next(node for node in snapshot.nodes if node.kind == NodeKind.QUERY) assert "Справочник.Контрагенты" in query.attributes["query_text"] assert any( edge.kind == EdgeKind.READS_TABLE and any(node.lineage_id == edge.target_lineage and node.qualified_name == "Справочник.Контрагенты" for node in snapshot.nodes) for edge in snapshot.edges ) def test_index_project_prefers_same_module_routine_for_duplicate_names(tmp_path: Path): shared = tmp_path / "a_shared.bsl" shared.write_text( """ Процедура ПроверитьОстатки() КонецПроцедуры """, encoding="utf-8", ) document_module = tmp_path / "z_document.bsl" document_module.write_text( """ Процедура Проведение() ПроверитьОстатки(); КонецПроцедуры Процедура ПроверитьОстатки() КонецПроцедуры """, encoding="utf-8", ) snapshot = index_project(tmp_path, project_id="duplicate-routines") local_target = next( node for node in snapshot.nodes if node.qualified_name == "z_document.ПроверитьОстатки" ) assert any( edge.kind == EdgeKind.CALLS and edge.target_lineage == local_target.lineage_id for edge in snapshot.edges ) def test_index_project_records_malformed_bsl_diagnostics(tmp_path: Path): module = tmp_path / "broken.bsl" module.write_text( """ Процедура Проведение() Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ Товары.Ссылка ИЗ Справочник.Номенклатура КАК Товары" """, encoding="utf-8", ) snapshot = index_project(tmp_path, project_id="broken") assert {diagnostic.code for diagnostic in snapshot.diagnostics} == { "BSL_UNCLOSED_QUERY", "BSL_UNCLOSED_ROUTINE", } def test_index_project_skips_invalid_xml_and_records_diagnostic(tmp_path: Path): valid_xml = tmp_path / "valid.xml" valid_xml.write_text( """ """, encoding="utf-8", ) broken_xml = tmp_path / "broken.mdo" broken_xml.write_text("