Files
uipath-explainator/tests/test_pipeline.py
xiaomai 5ba2e3217a feat(report): restructure Gemini analysis and overview documentation
Update Gemini response schema to extract detailed overview, logic steps, data dependencies, and
consultation insights.
Add pipeline processing logic and file categorization explanations to OVERVIEW.md.
2026-04-02 10:33:19 +08:00

120 lines
5.1 KiB
Python

from __future__ import annotations
from pathlib import Path
from tempfile import TemporaryDirectory
import sys
import unittest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from uipath_explainator.pipeline import ProjectPipeline
from uipath_explainator.scanner import crawl_dependencies, extract_dependencies, strip_comment_out_blocks
MAIN_XAML = """<?xml version="1.0" encoding="utf-8"?>
<Activity xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.uipath.com/workflow/activities">
<Sequence>
<ui:InvokeWorkflowFile WorkflowFileName="Flows/Active.xaml" />
<ui:CommentOut>
<Sequence>
<ui:InvokeWorkflowFile WorkflowFileName="Flows/Old.xaml" />
</Sequence>
</ui:CommentOut>
</Sequence>
</Activity>
"""
ACTIVE_XAML = """<?xml version="1.0" encoding="utf-8"?>
<Activity xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.uipath.com/workflow/activities"
xmlns:sap2010="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation">
<Sequence>
<ui:InvokeVBA>
<ui:InvokeVBA.CodeFilePath>
<InArgument x:TypeArguments="x:String">["Scripts/Keep.bas"]</InArgument>
</ui:InvokeVBA.CodeFilePath>
</ui:InvokeVBA>
</Sequence>
</Activity>
"""
OLD_XAML = """<?xml version="1.0" encoding="utf-8"?>
<Activity xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:ui="http://schemas.uipath.com/workflow/activities">
<Sequence>
<ui:InvokeVBA CodeFilePath="Scripts/Drop.bas" />
</Sequence>
</Activity>
"""
class StubAnalyzer:
def analyze(self, relative_path: Path, content: str) -> str:
return f"# {relative_path.as_posix()}\n\n{len(content)}"
class PipelineTests(unittest.TestCase):
def test_strip_comment_out_blocks_removes_nested_blocks(self) -> None:
source = "<root><ui:CommentOut><x/><ui:CommentOut><y/></ui:CommentOut></ui:CommentOut><z/></root>"
cleaned = strip_comment_out_blocks(source)
self.assertEqual(cleaned, "<root><z/></root>")
def test_extract_dependencies_reads_property_element_paths(self) -> None:
with TemporaryDirectory() as tmp:
root = Path(tmp)
flows = root / "Flows"
flows.mkdir()
(root / "Main.xaml").write_text(ACTIVE_XAML, encoding="utf-8")
(root / "Scripts").mkdir()
(root / "Scripts" / "Keep.bas").write_text("Sub Keep()", encoding="utf-8")
dependencies, warnings = extract_dependencies(root, root / "Main.xaml")
self.assertEqual(len(warnings), 0)
self.assertEqual(len(dependencies), 1)
self.assertEqual(dependencies[0].target, root / "Scripts" / "Keep.bas")
def test_pipeline_prunes_files_only_reachable_before_comment_cleanup(self) -> None:
with TemporaryDirectory() as tmp:
tmp_path = Path(tmp)
project_root = tmp_path / "project"
output_root = tmp_path / "workspace"
(project_root / "Flows").mkdir(parents=True)
(project_root / "Flows" / "Active.xaml").write_text(ACTIVE_XAML, encoding="utf-8")
(project_root / "Flows" / "Old.xaml").write_text(OLD_XAML, encoding="utf-8")
(project_root / "Scripts").mkdir()
(project_root / "Scripts" / "Keep.bas").write_text("Sub Keep()\nEnd Sub", encoding="utf-8")
(project_root / "Scripts" / "Drop.bas").write_text("Sub Drop()\nEnd Sub", encoding="utf-8")
(project_root / "main.xaml").write_text(MAIN_XAML, encoding="utf-8")
initial_scan = crawl_dependencies(project_root, project_root / "main.xaml")
initial_files = {path.relative_to(project_root).as_posix() for path in initial_scan.files}
self.assertIn("Flows/Old.xaml", initial_files)
self.assertIn("Scripts/Drop.bas", initial_files)
report = ProjectPipeline(project_root, output_root, "main.xaml", force=True).run(StubAnalyzer())
final_files = {path.as_posix() for path in report.final_files}
self.assertIn("main.xaml", final_files)
self.assertIn("Flows/Active.xaml", final_files)
self.assertIn("Scripts/Keep.bas", final_files)
self.assertNotIn("Flows/Old.xaml", final_files)
self.assertNotIn("Scripts/Drop.bas", final_files)
self.assertFalse((output_root / "Flows" / "Old.xaml").exists())
self.assertFalse((output_root / "Scripts" / "Drop.bas").exists())
self.assertTrue((output_root / "Flows" / "Active.xaml.analysis.md").exists())
overview = (output_root / "OVERVIEW.md").read_text(encoding="utf-8")
self.assertIn("## Processing Logic", overview)
self.assertIn("Initial Scan", overview)
self.assertIn("## How To Read This Output", overview)
self.assertIn("## Cleaned XAML Files", overview)
if __name__ == "__main__":
unittest.main()