ara-cli 0.1.9.94__py3-none-any.whl → 0.1.9.96__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. ara_cli/__init__.py +18 -1
  2. ara_cli/__main__.py +57 -11
  3. ara_cli/ara_command_action.py +31 -19
  4. ara_cli/ara_config.py +17 -2
  5. ara_cli/artefact_autofix.py +171 -23
  6. ara_cli/artefact_creator.py +5 -8
  7. ara_cli/artefact_deleter.py +2 -4
  8. ara_cli/artefact_fuzzy_search.py +13 -6
  9. ara_cli/artefact_models/artefact_templates.py +3 -3
  10. ara_cli/artefact_models/feature_artefact_model.py +25 -0
  11. ara_cli/artefact_reader.py +4 -5
  12. ara_cli/chat.py +79 -37
  13. ara_cli/commands/extract_command.py +4 -11
  14. ara_cli/error_handler.py +134 -0
  15. ara_cli/file_classifier.py +3 -2
  16. ara_cli/file_loaders/document_readers.py +233 -0
  17. ara_cli/file_loaders/file_loaders.py +123 -0
  18. ara_cli/file_loaders/image_processor.py +89 -0
  19. ara_cli/file_loaders/markdown_reader.py +75 -0
  20. ara_cli/file_loaders/text_file_loader.py +9 -11
  21. ara_cli/global_file_lister.py +61 -0
  22. ara_cli/prompt_extractor.py +1 -1
  23. ara_cli/prompt_handler.py +24 -4
  24. ara_cli/template_manager.py +14 -4
  25. ara_cli/update_config_prompt.py +7 -1
  26. ara_cli/version.py +1 -1
  27. {ara_cli-0.1.9.94.dist-info → ara_cli-0.1.9.96.dist-info}/METADATA +2 -1
  28. {ara_cli-0.1.9.94.dist-info → ara_cli-0.1.9.96.dist-info}/RECORD +40 -33
  29. tests/test_ara_command_action.py +66 -52
  30. tests/test_ara_config.py +28 -0
  31. tests/test_artefact_autofix.py +361 -5
  32. tests/test_chat.py +105 -36
  33. tests/test_file_classifier.py +23 -0
  34. tests/test_file_creator.py +3 -5
  35. tests/test_global_file_lister.py +131 -0
  36. tests/test_prompt_handler.py +26 -1
  37. tests/test_template_manager.py +5 -4
  38. {ara_cli-0.1.9.94.dist-info → ara_cli-0.1.9.96.dist-info}/WHEEL +0 -0
  39. {ara_cli-0.1.9.94.dist-info → ara_cli-0.1.9.96.dist-info}/entry_points.txt +0 -0
  40. {ara_cli-0.1.9.94.dist-info → ara_cli-0.1.9.96.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,131 @@
1
+ # tests/test_global_file_lister.py
2
+
3
+ import os
4
+ import pytest
5
+ from unittest.mock import patch
6
+ from io import StringIO
7
+ from ara_cli.global_file_lister import (
8
+ _build_tree,
9
+ _write_tree_to_markdown,
10
+ generate_global_markdown_listing,
11
+ )
12
+
13
+ @pytest.fixture
14
+ def temp_dir_structure(tmp_path):
15
+ """Create a temporary directory structure for comprehensive testing."""
16
+ root = tmp_path / "global_root"
17
+ root.mkdir()
18
+ (root / "src").mkdir()
19
+ (root / "src" / "main.py").touch()
20
+ (root / "src" / "utils.py").touch()
21
+ (root / "docs").mkdir()
22
+ (root / "docs" / "guide.md").touch()
23
+ (root / "docs" / "images").mkdir() # Empty dir, should be ignored
24
+ (root / "tests").mkdir()
25
+ (root / "tests" / "test_main.py").touch()
26
+ (root / "config.txt").touch() # Should be ignored by patterns
27
+ (root / "empty_dir").mkdir() # Should be ignored
28
+ return str(root)
29
+
30
+ class TestBuildTree:
31
+ """Tests for the _build_tree function."""
32
+ def test_build_tree_with_matching_files(self, temp_dir_structure):
33
+ """Tests building a tree, correctly filtering by patterns and excluding empty dirs."""
34
+ patterns = ["*.py", "*.md"]
35
+ tree = _build_tree(temp_dir_structure, patterns)
36
+
37
+ assert sorted(tree["dirs"]["src"]["files"]) == ["main.py", "utils.py"]
38
+ assert tree["dirs"]["docs"]["files"] == ["guide.md"]
39
+ assert tree["dirs"]["tests"]["files"] == ["test_main.py"]
40
+ assert "images" not in tree["dirs"]["docs"]["dirs"] # Empty dir excluded
41
+ assert "empty_dir" not in tree["dirs"] # Empty dir excluded
42
+ assert not tree["files"] # No files in root
43
+
44
+ def test_build_tree_no_matching_files(self, temp_dir_structure):
45
+ """Tests building a tree where no files match the given patterns."""
46
+ patterns = ["*.json", "*.yml"]
47
+ tree = _build_tree(temp_dir_structure, patterns)
48
+ assert not tree["files"] and not tree["dirs"]
49
+
50
+ def test_build_tree_on_empty_directory(self, tmp_path):
51
+ """Tests that an empty directory results in an empty tree."""
52
+ empty_dir = tmp_path / "empty"
53
+ empty_dir.mkdir()
54
+ tree = _build_tree(str(empty_dir), ["*.*"])
55
+ assert not tree["files"] and not tree["dirs"]
56
+
57
+ @patch('os.listdir')
58
+ @patch('sys.stdout', new_callable=StringIO)
59
+ def test_build_tree_handles_os_error(self, mock_stdout, mock_listdir):
60
+ """Tests that an OSError during directory listing is caught and handled."""
61
+ mock_listdir.side_effect = OSError("Permission denied")
62
+ tree = _build_tree("/unreadable_dir", ["*.*"])
63
+ assert "Warning: Could not access path /unreadable_dir" in mock_stdout.getvalue()
64
+ assert not tree["files"] and not tree["dirs"]
65
+
66
+ class TestWriteTreeToMarkdown:
67
+ """Tests for the _write_tree_to_markdown function."""
68
+ def test_write_tree_to_markdown_output_format(self):
69
+ """Verifies that the markdown output is correctly formatted with proper indentation."""
70
+ tree = {
71
+ 'files': ['root_file.md'],
72
+ 'dirs': {
73
+ 'dir1': {'files': ['a.py', 'b.py'], 'dirs': {}},
74
+ 'dir2': {'files': [], 'dirs': {'subdir': {'files': ['c.md'], 'dirs': {}}}}
75
+ }
76
+ }
77
+
78
+ md_file = StringIO()
79
+ _write_tree_to_markdown(md_file, tree, level=1)
80
+ output = md_file.getvalue()
81
+
82
+ # FIX: Added 4 spaces before "### subdir\n" to match the actual output.
83
+ expected = (
84
+ " - [] root_file.md\n"
85
+ "## dir1\n"
86
+ " - [] a.py\n"
87
+ " - [] b.py\n"
88
+ "## dir2\n"
89
+ " ### subdir\n"
90
+ " - [] c.md\n"
91
+ )
92
+ assert output == expected
93
+
94
+ class TestGenerateGlobalMarkdownListing:
95
+ """Tests for the main generate_global_markdown_listing function."""
96
+ def test_generate_listing_with_valid_dirs(self, temp_dir_structure, tmp_path):
97
+ """Tests the end-to-end generation of a markdown file from a valid directory."""
98
+ output_file = tmp_path / "output.md"
99
+ patterns = ["*.py"]
100
+
101
+ generate_global_markdown_listing([temp_dir_structure], patterns, str(output_file))
102
+
103
+ content = output_file.read_text()
104
+ abs_dir = os.path.abspath(temp_dir_structure)
105
+
106
+ assert f"# {abs_dir}\n" in content
107
+ assert "## src\n" in content
108
+ assert " - [] main.py\n" in content
109
+ assert "## tests\n" in content
110
+ assert "guide.md" not in content # Does not match pattern
111
+
112
+ def test_generate_listing_with_nonexistent_dir(self, tmp_path):
113
+ """Tests that a warning is written to the file for a non-existent directory."""
114
+ output_file = tmp_path / "output.md"
115
+ non_existent_dir = "/path/to/nothing"
116
+
117
+ generate_global_markdown_listing([non_existent_dir], ["*.*"], str(output_file))
118
+
119
+ content = output_file.read_text()
120
+ abs_path = os.path.abspath(non_existent_dir)
121
+
122
+ assert f"# {non_existent_dir}\n" in content
123
+ assert f" - !! UYARI: Dizin bulunamadı: {abs_path}" in content
124
+
125
+ def test_generate_listing_with_no_matching_files(self, temp_dir_structure, tmp_path):
126
+ """Tests that the output file is empty if no files match the patterns."""
127
+ output_file = tmp_path / "output.md"
128
+
129
+ generate_global_markdown_listing([temp_dir_structure], ["*.nonexistent"], str(output_file))
130
+
131
+ assert output_file.read_text() == ""
@@ -674,4 +674,29 @@ class TestArtefactAndTemplateHandling:
674
674
  mock_send.assert_called_once_with(final_message_list)
675
675
 
676
676
  log_file = self.root / "ara" / self.mock_classifier / f"{self.mock_param}.data" / f"{self.mock_classifier}.prompt_log.md"
677
- assert "llm response" in log_file.read_text()
677
+ assert "llm response" in log_file.read_text()
678
+
679
+ @patch('ara_cli.global_file_lister.generate_global_markdown_listing')
680
+ def test_generate_config_prompt_global_givens_file(self, mock_global_lister, mock_config_manager, mock_config):
681
+ """Tests that the global givens file is generated correctly when global_dirs are present."""
682
+ prompt_data_path = self.root / "prompt/data"
683
+ prompt_data_path.mkdir(parents=True)
684
+
685
+ # Scenario 1: No global_dirs are configured, should return early and do nothing.
686
+ mock_config.global_dirs = []
687
+ prompt_handler.generate_config_prompt_global_givens_file(str(prompt_data_path), "global.md")
688
+ mock_global_lister.assert_not_called()
689
+
690
+ # Scenario 2: With global_dirs, should call the global lister with correct arguments.
691
+ mock_config.global_dirs = [{"source_dir": "/global/src1"}, {"path": "/global/src2"}]
692
+ mock_config.ara_prompt_given_list_includes = ["*.py", "*.md"]
693
+
694
+ # Use patch to suppress print output during the test
695
+ with patch('builtins.print'):
696
+ prompt_handler.generate_config_prompt_global_givens_file(str(prompt_data_path), "global.md")
697
+
698
+ mock_global_lister.assert_called_once()
699
+ args, _ = mock_global_lister.call_args
700
+ assert args[0] == ["/global/src1", "/global/src2"]
701
+ assert args[1] == ["*.py", "*.md"]
702
+ assert args[2] == os.path.join(prompt_data_path, "global.md")
@@ -9,10 +9,11 @@ import os
9
9
 
10
10
  @pytest.fixture(autouse=True)
11
11
  def navigate_to_ara_directory():
12
- navigator = DirectoryNavigator("ara")
13
- original_directory = navigator.navigate_to_target()
14
- yield
15
- os.chdir(original_directory)
12
+ with patch('builtins.input', return_value='y'):
13
+ navigator = DirectoryNavigator("ara")
14
+ original_directory = navigator.navigate_to_target()
15
+ yield
16
+ os.chdir(original_directory)
16
17
 
17
18
 
18
19
  @pytest.mark.parametrize(