ara-cli 0.1.9.69__py3-none-any.whl → 0.1.10.8__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.

Potentially problematic release.


This version of ara-cli might be problematic. Click here for more details.

Files changed (150) hide show
  1. ara_cli/__init__.py +18 -2
  2. ara_cli/__main__.py +248 -62
  3. ara_cli/ara_command_action.py +155 -86
  4. ara_cli/ara_config.py +226 -80
  5. ara_cli/ara_subcommands/__init__.py +0 -0
  6. ara_cli/ara_subcommands/autofix.py +26 -0
  7. ara_cli/ara_subcommands/chat.py +27 -0
  8. ara_cli/ara_subcommands/classifier_directory.py +16 -0
  9. ara_cli/ara_subcommands/common.py +100 -0
  10. ara_cli/ara_subcommands/create.py +75 -0
  11. ara_cli/ara_subcommands/delete.py +22 -0
  12. ara_cli/ara_subcommands/extract.py +22 -0
  13. ara_cli/ara_subcommands/fetch_templates.py +14 -0
  14. ara_cli/ara_subcommands/list.py +65 -0
  15. ara_cli/ara_subcommands/list_tags.py +25 -0
  16. ara_cli/ara_subcommands/load.py +48 -0
  17. ara_cli/ara_subcommands/prompt.py +136 -0
  18. ara_cli/ara_subcommands/read.py +47 -0
  19. ara_cli/ara_subcommands/read_status.py +20 -0
  20. ara_cli/ara_subcommands/read_user.py +20 -0
  21. ara_cli/ara_subcommands/reconnect.py +27 -0
  22. ara_cli/ara_subcommands/rename.py +22 -0
  23. ara_cli/ara_subcommands/scan.py +14 -0
  24. ara_cli/ara_subcommands/set_status.py +22 -0
  25. ara_cli/ara_subcommands/set_user.py +22 -0
  26. ara_cli/ara_subcommands/template.py +16 -0
  27. ara_cli/artefact_autofix.py +649 -68
  28. ara_cli/artefact_creator.py +8 -11
  29. ara_cli/artefact_deleter.py +2 -4
  30. ara_cli/artefact_fuzzy_search.py +22 -10
  31. ara_cli/artefact_link_updater.py +4 -4
  32. ara_cli/artefact_lister.py +29 -55
  33. ara_cli/artefact_models/artefact_data_retrieval.py +23 -0
  34. ara_cli/artefact_models/artefact_load.py +11 -3
  35. ara_cli/artefact_models/artefact_model.py +146 -39
  36. ara_cli/artefact_models/artefact_templates.py +70 -44
  37. ara_cli/artefact_models/businessgoal_artefact_model.py +23 -25
  38. ara_cli/artefact_models/epic_artefact_model.py +34 -26
  39. ara_cli/artefact_models/feature_artefact_model.py +203 -64
  40. ara_cli/artefact_models/keyfeature_artefact_model.py +21 -24
  41. ara_cli/artefact_models/serialize_helper.py +1 -1
  42. ara_cli/artefact_models/task_artefact_model.py +83 -15
  43. ara_cli/artefact_models/userstory_artefact_model.py +37 -27
  44. ara_cli/artefact_models/vision_artefact_model.py +23 -42
  45. ara_cli/artefact_reader.py +92 -91
  46. ara_cli/artefact_renamer.py +8 -4
  47. ara_cli/artefact_scan.py +66 -3
  48. ara_cli/chat.py +622 -162
  49. ara_cli/chat_agent/__init__.py +0 -0
  50. ara_cli/chat_agent/agent_communicator.py +62 -0
  51. ara_cli/chat_agent/agent_process_manager.py +211 -0
  52. ara_cli/chat_agent/agent_status_manager.py +73 -0
  53. ara_cli/chat_agent/agent_workspace_manager.py +76 -0
  54. ara_cli/commands/__init__.py +0 -0
  55. ara_cli/commands/command.py +7 -0
  56. ara_cli/commands/extract_command.py +15 -0
  57. ara_cli/commands/load_command.py +65 -0
  58. ara_cli/commands/load_image_command.py +34 -0
  59. ara_cli/commands/read_command.py +117 -0
  60. ara_cli/completers.py +144 -0
  61. ara_cli/directory_navigator.py +37 -4
  62. ara_cli/error_handler.py +134 -0
  63. ara_cli/file_classifier.py +6 -5
  64. ara_cli/file_lister.py +1 -1
  65. ara_cli/file_loaders/__init__.py +0 -0
  66. ara_cli/file_loaders/binary_file_loader.py +33 -0
  67. ara_cli/file_loaders/document_file_loader.py +34 -0
  68. ara_cli/file_loaders/document_reader.py +245 -0
  69. ara_cli/file_loaders/document_readers.py +233 -0
  70. ara_cli/file_loaders/file_loader.py +50 -0
  71. ara_cli/file_loaders/file_loaders.py +123 -0
  72. ara_cli/file_loaders/image_processor.py +89 -0
  73. ara_cli/file_loaders/markdown_reader.py +75 -0
  74. ara_cli/file_loaders/text_file_loader.py +187 -0
  75. ara_cli/global_file_lister.py +51 -0
  76. ara_cli/list_filter.py +1 -1
  77. ara_cli/output_suppressor.py +1 -1
  78. ara_cli/prompt_extractor.py +215 -88
  79. ara_cli/prompt_handler.py +521 -134
  80. ara_cli/prompt_rag.py +2 -2
  81. ara_cli/tag_extractor.py +83 -38
  82. ara_cli/template_loader.py +245 -0
  83. ara_cli/template_manager.py +18 -13
  84. ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
  85. ara_cli/templates/prompt-modules/commands/extract_general.commands.md +12 -0
  86. ara_cli/templates/prompt-modules/commands/extract_markdown.commands.md +11 -0
  87. ara_cli/templates/prompt-modules/commands/extract_python.commands.md +13 -0
  88. ara_cli/templates/prompt-modules/commands/feature_add_or_modifiy_specified_behavior.commands.md +36 -0
  89. ara_cli/templates/prompt-modules/commands/feature_generate_initial_specified_bevahior.commands.md +53 -0
  90. ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
  91. ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
  92. ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
  93. ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
  94. ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
  95. ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
  96. ara_cli/update_config_prompt.py +9 -3
  97. ara_cli/version.py +1 -1
  98. ara_cli-0.1.10.8.dist-info/METADATA +241 -0
  99. ara_cli-0.1.10.8.dist-info/RECORD +193 -0
  100. tests/test_ara_command_action.py +73 -59
  101. tests/test_ara_config.py +341 -36
  102. tests/test_artefact_autofix.py +1060 -0
  103. tests/test_artefact_link_updater.py +3 -3
  104. tests/test_artefact_lister.py +52 -132
  105. tests/test_artefact_renamer.py +2 -2
  106. tests/test_artefact_scan.py +327 -33
  107. tests/test_chat.py +2063 -498
  108. tests/test_file_classifier.py +24 -1
  109. tests/test_file_creator.py +3 -5
  110. tests/test_file_lister.py +1 -1
  111. tests/test_global_file_lister.py +131 -0
  112. tests/test_list_filter.py +2 -2
  113. tests/test_prompt_handler.py +746 -0
  114. tests/test_tag_extractor.py +19 -13
  115. tests/test_template_loader.py +192 -0
  116. tests/test_template_manager.py +5 -4
  117. tests/test_update_config_prompt.py +2 -2
  118. ara_cli/ara_command_parser.py +0 -327
  119. ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
  120. ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
  121. ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
  122. ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
  123. ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
  124. ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
  125. ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
  126. ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
  127. ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
  128. ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
  129. ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
  130. ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
  131. ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
  132. ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
  133. ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
  134. ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
  135. ara_cli/templates/template.businessgoal +0 -10
  136. ara_cli/templates/template.capability +0 -10
  137. ara_cli/templates/template.epic +0 -15
  138. ara_cli/templates/template.example +0 -6
  139. ara_cli/templates/template.feature +0 -26
  140. ara_cli/templates/template.issue +0 -14
  141. ara_cli/templates/template.keyfeature +0 -15
  142. ara_cli/templates/template.task +0 -6
  143. ara_cli/templates/template.userstory +0 -17
  144. ara_cli/templates/template.vision +0 -14
  145. ara_cli-0.1.9.69.dist-info/METADATA +0 -16
  146. ara_cli-0.1.9.69.dist-info/RECORD +0 -158
  147. tests/test_ara_autofix.py +0 -219
  148. {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/WHEEL +0 -0
  149. {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/entry_points.txt +0 -0
  150. {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/top_level.txt +0 -0
@@ -25,7 +25,7 @@ def test_update_links_in_related_artefacts(mock_isfile, mock_listdir):
25
25
  mock_file_handles = {filename: mock_open(read_data=content).return_value
26
26
  for filename, content in file_contents.items()}
27
27
  mock_open_function = mock_open()
28
- mock_open_function.side_effect = lambda file_path, mode='r': mock_file_handles[os.path.basename(file_path)]
28
+ mock_open_function.side_effect = lambda file_path, mode='r', encoding='utf-8': mock_file_handles[os.path.basename(file_path)]
29
29
 
30
30
  with patch("builtins.open", mock_open_function):
31
31
  # Instantiate the ArtefactLinkUpdater with the mocked file system
@@ -38,8 +38,8 @@ def test_update_links_in_related_artefacts(mock_isfile, mock_listdir):
38
38
  # Verify that the open function was called correctly for each file
39
39
  for filename in file_contents.keys():
40
40
  expected_file_path = os.path.join(dir_path, filename)
41
- mock_open_function.assert_any_call(expected_file_path, 'r')
42
- mock_open_function.assert_any_call(expected_file_path, 'w')
41
+ mock_open_function.assert_any_call(expected_file_path, 'r', encoding='utf-8')
42
+ mock_open_function.assert_any_call(expected_file_path, 'w', encoding='utf-8')
43
43
 
44
44
  # Verify the content of the files was updated correctly
45
45
  for filename, content in file_contents.items():
@@ -2,6 +2,11 @@ import pytest
2
2
  from unittest.mock import MagicMock, patch
3
3
  from ara_cli.artefact_lister import ArtefactLister
4
4
  from ara_cli.list_filter import ListFilter
5
+ from ara_cli.artefact_models.artefact_data_retrieval import (
6
+ artefact_content_retrieval,
7
+ artefact_path_retrieval,
8
+ artefact_tags_retrieval,
9
+ )
5
10
 
6
11
 
7
12
  @pytest.fixture
@@ -9,66 +14,6 @@ def artefact_lister():
9
14
  return ArtefactLister()
10
15
 
11
16
 
12
- @pytest.mark.parametrize(
13
- "users, status, tags, expected_tags",
14
- [
15
- # Normal case with all fields populated
16
- (
17
- ["john", "alice"],
18
- "in-progress",
19
- ["important", "urgent"],
20
- ["user_john", "user_alice", "in-progress", "important", "urgent"]
21
- ),
22
- # Case with empty users
23
- (
24
- [],
25
- "to-do",
26
- ["feature", "backend"],
27
- ["to-do", "feature", "backend"]
28
- ),
29
- # Case with empty tags
30
- (
31
- ["bob"],
32
- "done",
33
- [],
34
- ["user_bob", "done"]
35
- ),
36
- # Case with all empty fields
37
- (
38
- [],
39
- "",
40
- [],
41
- [""]
42
- ),
43
- # Case with None values for tags (should handle gracefully)
44
- (
45
- ["admin"],
46
- "closed",
47
- None,
48
- ["user_admin", "closed"]
49
- ),
50
- ]
51
- )
52
- def test_artefact_tags_retrieval(users, status, tags, expected_tags):
53
- # Create a mock artefact with the specified attributes
54
- artefact_mock = MagicMock()
55
- artefact_mock.users = users
56
- artefact_mock.status = status
57
- artefact_mock.tags = tags if tags is not None else []
58
-
59
- # Call the method under test
60
- result = ArtefactLister.artefact_tags_retrieval(artefact_mock)
61
-
62
- # Verify the result
63
- assert result == expected_tags
64
-
65
-
66
- def test_artefact_tags_retrieval_with_none():
67
- # Test with None artefact
68
- result = ArtefactLister.artefact_tags_retrieval(None)
69
- assert result == []
70
-
71
-
72
17
  @pytest.mark.parametrize(
73
18
  "classified_files, list_filter, filter_result",
74
19
  [
@@ -76,39 +21,36 @@ def test_artefact_tags_retrieval_with_none():
76
21
  (
77
22
  {"type1": [MagicMock(), MagicMock()]},
78
23
  None,
79
- {"type1": [MagicMock(), MagicMock()]}
24
+ {"type1": [MagicMock(), MagicMock()]},
80
25
  ),
81
26
  # Case 2: Filter with include tags
82
27
  (
83
28
  {"type1": [MagicMock(), MagicMock()]},
84
29
  ListFilter(include_tags=["tag1"]),
85
- {"type1": [MagicMock()]}
30
+ {"type1": [MagicMock()]},
86
31
  ),
87
32
  # Case 3: Filter with exclude tags
88
33
  (
89
34
  {"type1": [MagicMock(), MagicMock()], "type2": [MagicMock()]},
90
35
  ListFilter(exclude_tags=["tag2"]),
91
- {"type1": [MagicMock()], "type2": []}
36
+ {"type1": [MagicMock()], "type2": []},
92
37
  ),
93
38
  # Case 4: Empty result after filtering
94
39
  (
95
40
  {"type1": [MagicMock(), MagicMock()]},
96
41
  ListFilter(include_tags=["nonexistent"]),
97
- {"type1": []}
42
+ {"type1": []},
98
43
  ),
99
44
  # Case 5: Multiple artefact types
100
45
  (
101
46
  {"type1": [MagicMock()], "type2": [MagicMock(), MagicMock()]},
102
47
  ListFilter(exclude_extension=[".txt"]),
103
- {"type1": [], "type2": [MagicMock()]}
48
+ {"type1": [], "type2": [MagicMock()]},
104
49
  ),
105
50
  ],
106
51
  )
107
52
  def test_filter_artefacts(
108
- artefact_lister,
109
- classified_files,
110
- list_filter,
111
- filter_result
53
+ artefact_lister, classified_files, list_filter, filter_result
112
54
  ):
113
55
  # Mock the filter_list function
114
56
  with patch("ara_cli.artefact_lister.filter_list") as mock_filter_list:
@@ -121,9 +63,9 @@ def test_filter_artefacts(
121
63
  mock_filter_list.assert_called_once_with(
122
64
  list_to_filter=classified_files,
123
65
  list_filter=list_filter,
124
- content_retrieval_strategy=ArtefactLister.artefact_content_retrieval,
125
- file_path_retrieval=ArtefactLister.artefact_path_retrieval,
126
- tag_retrieval=ArtefactLister.artefact_tags_retrieval
66
+ content_retrieval_strategy=artefact_content_retrieval,
67
+ file_path_retrieval=artefact_path_retrieval,
68
+ tag_retrieval=artefact_tags_retrieval,
127
69
  )
128
70
 
129
71
  # Verify the structure matches (don't compare the actual MagicMock objects)
@@ -132,30 +74,6 @@ def test_filter_artefacts(
132
74
  assert len(result[key]) == len(filter_result[key])
133
75
 
134
76
 
135
- @pytest.mark.parametrize("artefact_content", ["content1", "content2", "content3"])
136
- def test_artefact_content_retrieval(artefact_content):
137
- artefact_mock = MagicMock()
138
- artefact_mock.serialize.return_value = artefact_content # Mock serialize()
139
-
140
- content = ArtefactLister.artefact_content_retrieval(artefact_mock)
141
- assert content == artefact_content
142
-
143
-
144
- @pytest.mark.parametrize(
145
- "file_path",
146
- [
147
- ("./ara/userstories/test.userstory"),
148
- ("./ara/epics/test.epic"),
149
- ],
150
- )
151
- def test_artefact_path_retrieval(file_path):
152
- artefact_mock = MagicMock()
153
- artefact_mock.file_path = file_path
154
-
155
- path = ArtefactLister.artefact_path_retrieval(artefact_mock)
156
- assert path == file_path
157
-
158
-
159
77
  @pytest.mark.parametrize(
160
78
  "tags, navigate_to_target, list_filter, mock_artefacts, filtered_artefacts, expected_filtered",
161
79
  [
@@ -476,20 +394,21 @@ def test_list_data_artefact_found_data_exists(artefact_lister):
476
394
  classified_artefacts = {
477
395
  "epic": [
478
396
  {"title": "Epic1", "file_path": "path/to/Epic1.epic"},
479
- {"title": "Epic2", "file_path": "path/to/Epic2.epic"}
397
+ {"title": "Epic2", "file_path": "path/to/Epic2.epic"},
480
398
  ]
481
399
  }
482
-
483
- with patch("ara_cli.artefact_lister.FileClassifier") as mock_file_classifier, \
484
- patch("ara_cli.artefact_lister.suggest_close_name_matches") as mock_suggest, \
485
- patch("ara_cli.artefact_lister.os") as mock_os, \
486
- patch("ara_cli.artefact_lister.list_files_in_directory") as mock_list_files:
400
+
401
+ with patch("ara_cli.artefact_lister.FileClassifier") as mock_file_classifier, patch(
402
+ "ara_cli.artefact_lister.suggest_close_name_matches"
403
+ ) as mock_suggest, patch("ara_cli.artefact_lister.os") as mock_os, patch(
404
+ "ara_cli.artefact_lister.list_files_in_directory"
405
+ ) as mock_list_files:
487
406
 
488
407
  # Configure mocks
489
408
  mock_classifier_instance = MagicMock()
490
409
  mock_file_classifier.return_value = mock_classifier_instance
491
410
  mock_classifier_instance.classify_files.return_value = classified_artefacts
492
-
411
+
493
412
  mock_os.path.splitext.return_value = ("path/to/Epic1", ".epic")
494
413
  mock_os.path.exists.return_value = True
495
414
 
@@ -510,20 +429,21 @@ def test_list_data_artefact_found_data_not_exists(artefact_lister):
510
429
  classified_artefacts = {
511
430
  "epic": [
512
431
  {"title": "Epic1", "file_path": "path/to/Epic1.epic"},
513
- {"title": "Epic2", "file_path": "path/to/Epic2.epic"}
432
+ {"title": "Epic2", "file_path": "path/to/Epic2.epic"},
514
433
  ]
515
434
  }
516
-
517
- with patch("ara_cli.artefact_lister.FileClassifier") as mock_file_classifier, \
518
- patch("ara_cli.artefact_lister.suggest_close_name_matches") as mock_suggest, \
519
- patch("ara_cli.artefact_lister.os") as mock_os, \
520
- patch("ara_cli.artefact_lister.list_files_in_directory") as mock_list_files:
435
+
436
+ with patch("ara_cli.artefact_lister.FileClassifier") as mock_file_classifier, patch(
437
+ "ara_cli.artefact_lister.suggest_close_name_matches"
438
+ ) as mock_suggest, patch("ara_cli.artefact_lister.os") as mock_os, patch(
439
+ "ara_cli.artefact_lister.list_files_in_directory"
440
+ ) as mock_list_files:
521
441
 
522
442
  # Configure mocks
523
443
  mock_classifier_instance = MagicMock()
524
444
  mock_file_classifier.return_value = mock_classifier_instance
525
445
  mock_classifier_instance.classify_files.return_value = classified_artefacts
526
-
446
+
527
447
  mock_os.path.splitext.return_value = ("path/to/Epic1", ".epic")
528
448
  mock_os.path.exists.return_value = False
529
449
 
@@ -544,14 +464,15 @@ def test_list_data_artefact_not_found(artefact_lister):
544
464
  classified_artefacts = {
545
465
  "epic": [
546
466
  {"title": "Epic1", "file_path": "path/to/Epic1.epic"},
547
- {"title": "Epic2", "file_path": "path/to/Epic2.epic"}
467
+ {"title": "Epic2", "file_path": "path/to/Epic2.epic"},
548
468
  ]
549
469
  }
550
-
551
- with patch("ara_cli.artefact_lister.FileClassifier") as mock_file_classifier, \
552
- patch("ara_cli.artefact_lister.suggest_close_name_matches") as mock_suggest, \
553
- patch("ara_cli.artefact_lister.os") as mock_os, \
554
- patch("ara_cli.artefact_lister.list_files_in_directory") as mock_list_files:
470
+
471
+ with patch("ara_cli.artefact_lister.FileClassifier") as mock_file_classifier, patch(
472
+ "ara_cli.artefact_lister.suggest_close_name_matches"
473
+ ) as mock_suggest, patch("ara_cli.artefact_lister.os") as mock_os, patch(
474
+ "ara_cli.artefact_lister.list_files_in_directory"
475
+ ) as mock_list_files:
555
476
 
556
477
  # Configure mocks
557
478
  mock_classifier_instance = MagicMock()
@@ -562,10 +483,7 @@ def test_list_data_artefact_not_found(artefact_lister):
562
483
  artefact_lister.list_data(classifier, artefact_name, list_filter)
563
484
 
564
485
  # Verify interactions
565
- mock_suggest.assert_called_once_with(
566
- artefact_name,
567
- ["Epic1", "Epic2"]
568
- )
486
+ mock_suggest.assert_called_once_with(artefact_name, ["Epic1", "Epic2"])
569
487
  mock_os.path.splitext.assert_not_called()
570
488
  mock_os.path.exists.assert_not_called()
571
489
  mock_list_files.assert_not_called()
@@ -578,20 +496,21 @@ def test_list_data_with_filter(artefact_lister):
578
496
  classified_artefacts = {
579
497
  "userstory": [
580
498
  {"title": "Story1", "file_path": "path/to/Story1.userstory"},
581
- {"title": "Story2", "file_path": "path/to/Story2.userstory"}
499
+ {"title": "Story2", "file_path": "path/to/Story2.userstory"},
582
500
  ]
583
501
  }
584
-
585
- with patch("ara_cli.artefact_lister.FileClassifier") as mock_file_classifier, \
586
- patch("ara_cli.artefact_lister.suggest_close_name_matches") as mock_suggest, \
587
- patch("ara_cli.artefact_lister.os") as mock_os, \
588
- patch("ara_cli.artefact_lister.list_files_in_directory") as mock_list_files:
502
+
503
+ with patch("ara_cli.artefact_lister.FileClassifier") as mock_file_classifier, patch(
504
+ "ara_cli.artefact_lister.suggest_close_name_matches"
505
+ ) as mock_suggest, patch("ara_cli.artefact_lister.os") as mock_os, patch(
506
+ "ara_cli.artefact_lister.list_files_in_directory"
507
+ ) as mock_list_files:
589
508
 
590
509
  # Configure mocks
591
510
  mock_classifier_instance = MagicMock()
592
511
  mock_file_classifier.return_value = mock_classifier_instance
593
512
  mock_classifier_instance.classify_files.return_value = classified_artefacts
594
-
513
+
595
514
  mock_os.path.splitext.return_value = ("path/to/Story1", ".userstory")
596
515
  mock_os.path.exists.return_value = True
597
516
 
@@ -610,11 +529,12 @@ def test_list_data_empty_artefact_list(artefact_lister):
610
529
  artefact_name = "Epic1"
611
530
  list_filter = None
612
531
  classified_artefacts = {"epic": []}
613
-
614
- with patch("ara_cli.artefact_lister.FileClassifier") as mock_file_classifier, \
615
- patch("ara_cli.artefact_lister.suggest_close_name_matches") as mock_suggest, \
616
- patch("ara_cli.artefact_lister.os") as mock_os, \
617
- patch("ara_cli.artefact_lister.list_files_in_directory") as mock_list_files:
532
+
533
+ with patch("ara_cli.artefact_lister.FileClassifier") as mock_file_classifier, patch(
534
+ "ara_cli.artefact_lister.suggest_close_name_matches"
535
+ ) as mock_suggest, patch("ara_cli.artefact_lister.os") as mock_os, patch(
536
+ "ara_cli.artefact_lister.list_files_in_directory"
537
+ ) as mock_list_files:
618
538
 
619
539
  # Configure mocks
620
540
  mock_classifier_instance = MagicMock()
@@ -53,9 +53,9 @@ def test_update_title_in_artefact(mock_file, classifier, artefact_name, read_dat
53
53
  ar._update_title_in_artefact(artefact_path, new_title, classifier)
54
54
 
55
55
  # Check that the file was opened for reading
56
- mock_file.assert_any_call(artefact_path, 'r')
56
+ mock_file.assert_any_call(artefact_path, 'r', encoding='utf-8')
57
57
  # Check that the file was opened for writing
58
- mock_file.assert_any_call(artefact_path, 'w')
58
+ mock_file.assert_any_call(artefact_path, 'w', encoding='utf-8')
59
59
  # Check that the file write was called with the correct new content
60
60
  expected_content = read_data.replace(f"{read_data_prefix}{old_title}", f"{read_data_prefix}{new_title}")
61
61
  mock_file().write.assert_called_with(expected_content)