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

@@ -1,18 +1,18 @@
1
1
  ara_cli/__init__.py,sha256=0zl7IegxTid26EBGLav_fXZ4CCIV3H5TfAoFQiOHjvg,148
2
2
  ara_cli/__main__.py,sha256=Z6XYWRLceIoZPvfC-X9EXouSZdtFOOe84kKVWJGA4r4,1861
3
- ara_cli/ara_command_action.py,sha256=dmv2xAUw3UXB1dMfeJ3ibFYJhGjBDbQdXrHRtx_whaM,21981
4
- ara_cli/ara_command_parser.py,sha256=v8LUdkBSI2771gI53PdrxtD8YVjhk-7E8vgTEsTGnRM,17952
3
+ ara_cli/ara_command_action.py,sha256=J613DUTjRxrPG8Jm-fJcIM0QlZTeULmq9Q7DKkDxJHg,22039
4
+ ara_cli/ara_command_parser.py,sha256=HHuLxGeLjsd3R-JcoWJ5MUgaxXqkECHoqE95amlaVXY,18115
5
5
  ara_cli/ara_config.py,sha256=xynwnCrlpBj20ozL7ra8siniNlfM4eh4OlOHi7fkDY8,4237
6
- ara_cli/artefact_autofix.py,sha256=_xCAZFNZHJnIS95QCNxGPezrCBb9v8gIMn9_Ixjcj74,14876
7
- ara_cli/artefact_creator.py,sha256=ysWtlYDbZf_k8ya7ZBXILP_PWZKIssAAJK8kak5mW-E,6056
6
+ ara_cli/artefact_autofix.py,sha256=X1-Rxc7NLwy8ebWJgfY4ILgQ2T7FgTINL7BLs9ApDFU,16111
7
+ ara_cli/artefact_creator.py,sha256=0Ory6cB-Ahkw-BDNb8QHnTbp_OHGABdkb9bhwcEdcIc,6063
8
8
  ara_cli/artefact_deleter.py,sha256=Co4wwCH3yW8H9NrOq7_2p5571EeHr0TsfE-H8KqoOfY,1900
9
9
  ara_cli/artefact_fuzzy_search.py,sha256=iBlDqjZf-_D3VUjFf7ZwkiQbpQDcwRndIU7aG_sRTgE,2668
10
10
  ara_cli/artefact_link_updater.py,sha256=nKdxTpDKqWTOAMD8viKmUaklSFGWzJZ8S8E8xW_ADuM,3775
11
11
  ara_cli/artefact_lister.py,sha256=jhk4n4eqp7hDIq07q43QzS7-36BM3OfZ4EABxCeOGcw,4764
12
12
  ara_cli/artefact_reader.py,sha256=E6DMBvbOYf1OoLf-OyLaiB6K2-gd7iHmjoQZU9Rsy6g,6965
13
13
  ara_cli/artefact_renamer.py,sha256=Hnz_3zD9xxnBa1FHyUE6mIktLk_9ttP2rFRvQIkmz-o,4061
14
- ara_cli/artefact_scan.py,sha256=yg1ozwavWmqsrO9lx4bY3ggCooiPJ_DYQuZuJlFJdqc,3617
15
- ara_cli/chat.py,sha256=Vv4tfzP203hCISd2Q0W0ZZ7ua7dk0jJ1qdl9NiHI5gM,28635
14
+ ara_cli/artefact_scan.py,sha256=PTOKE2UImmFW_wOwGNfLT8IvAU8ZaWuIHD8IvrFxFqg,3563
15
+ ara_cli/chat.py,sha256=CR30wBfKaS8DVCeavEEYFMeCFp3IyRtybQnk-GrCj7U,29341
16
16
  ara_cli/classifier.py,sha256=zWskj7rBYdqYBGjksBm46iTgVU5IIf2PZsJr4qeiwVU,1878
17
17
  ara_cli/codefusionretriever.py,sha256=fCHgXdIBRzkVAnapX-KI2NQ44XbrrF4tEQmn5J6clUI,1980
18
18
  ara_cli/codehierachieretriever.py,sha256=Xd3EgEWWhkSf1TmTWtf8X5_YvyE_4B66nRrqarwSiTU,1182
@@ -29,19 +29,19 @@ ara_cli/prompt_handler.py,sha256=v0254EUk1A2h0y1NmpkllVH6v6Q6i0Yh9qXkQMBXVY4,185
29
29
  ara_cli/prompt_rag.py,sha256=ydlhe4CUqz0jdzlY7jBbpKaf_5fjMrAZKnriKea3ZAg,7485
30
30
  ara_cli/run_file_lister.py,sha256=XbrrDTJXp1LFGx9Lv91SNsEHZPP-PyEMBF_P4btjbDA,2360
31
31
  ara_cli/tag_extractor.py,sha256=4krQyvmLR2ffhe7N7lWC7QjaxXcb90HaQdmjnBiD8ak,2523
32
- ara_cli/template_manager.py,sha256=flMO3yXMUIgBxCgLdRtIhxezPDs5kfh3IASySSa1IKI,6669
32
+ ara_cli/template_manager.py,sha256=YwrN6AYPpl6ZrW8BVQpVXx8yTRf-oNpJUIKeg4NAggs,6606
33
33
  ara_cli/update_config_prompt.py,sha256=Oy9vNTw6UhDohyTEfSKkqE5ifEMPlmWNYkKHgUrK_pY,4607
34
- ara_cli/version.py,sha256=EeE9cgmnRIG8uXViIRiujIL8NEX4pIBGBfOof3tGM-c,146
34
+ ara_cli/version.py,sha256=LCVm4Kujz8s6pok0OYm8qtCVhA5zvKHA96k-vpx5_c4,146
35
35
  ara_cli/artefact_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  ara_cli/artefact_models/artefact_load.py,sha256=dNcwZDW2Dk0bts9YnPZ0ESmWD2NbsLIvl4Z-qQeGmTQ,401
37
37
  ara_cli/artefact_models/artefact_mapping.py,sha256=8aD0spBjkJ8toMAmFawc6UTUxB6-tEEViZXv2I-r88Q,1874
38
- ara_cli/artefact_models/artefact_model.py,sha256=FSn_DBaqvcCpybsq0jzaQOinkGPqTVJ6MOSHY6lZtTg,14979
39
- ara_cli/artefact_models/artefact_templates.py,sha256=kEBmxFfySgO8ISgMRLDKhBl-n8o1mi7p49-Pf686YU0,8345
38
+ ara_cli/artefact_models/artefact_model.py,sha256=qSbcrmFWAYgBqcNl9QARI1_uLQJm-TPVgP5q2AEFnjE,15983
39
+ ara_cli/artefact_models/artefact_templates.py,sha256=8HNM-TsNvKgTpruOBs751yRDXJypTiJhc1tkWCiYG7s,9830
40
40
  ara_cli/artefact_models/businessgoal_artefact_model.py,sha256=jqYFMXjWle0YW9RvcFLDBAwy61bdT5VuDT_6lTOFzMw,4853
41
41
  ara_cli/artefact_models/capability_artefact_model.py,sha256=SZqHx4O2mj4urn77Stnj4_Jxtlq3-LgBBU9SMkByppI,3079
42
42
  ara_cli/artefact_models/epic_artefact_model.py,sha256=IadQWs6SWNcLgwvtOQWmYDyV9xLr3WwAsx-YMFan5fA,5765
43
43
  ara_cli/artefact_models/example_artefact_model.py,sha256=UXrKbaPotg1jwcrVSdCeo-XH4tTD_-U1e3giaBn5_xg,1384
44
- ara_cli/artefact_models/feature_artefact_model.py,sha256=JvgBKsiI1Y5Cs_0Ygn6lttJIjCpEC9XyZieDExUnsvg,18386
44
+ ara_cli/artefact_models/feature_artefact_model.py,sha256=DE8vEpSy2w5DpNcHgmFMZj0zdPTHjYE3R3RIkHS7USI,18450
45
45
  ara_cli/artefact_models/issue_artefact_model.py,sha256=v6CpKnkqiUh6Wch2kkEmyyW49c8ysdy1qz8l1Ft9uJA,2552
46
46
  ara_cli/artefact_models/keyfeature_artefact_model.py,sha256=a3MyAiePN9n_GTN6QkTvamdsaorwVUff6w-9CdRZSlo,4243
47
47
  ara_cli/artefact_models/serialize_helper.py,sha256=Wks30wy-UrwJURetydKykLgJkdGRgXFHkDT24vHe5tU,595
@@ -49,26 +49,16 @@ ara_cli/artefact_models/task_artefact_model.py,sha256=kHMw_Tr-Ud3EeHWpRWy4jI0xFn
49
49
  ara_cli/artefact_models/userstory_artefact_model.py,sha256=u6G8wdeE2EpOsg1OPR-s8uShB4A77GfqN0vkSSuthFI,6582
50
50
  ara_cli/artefact_models/vision_artefact_model.py,sha256=KcNE3QQjyT29ZMMhCQo4pOcXKTkI6pXLvyfqoN2kuUQ,5920
51
51
  ara_cli/templates/agile.artefacts,sha256=nTA8dp98HWKAD-0qhmNpVYIfkVGoJshZqMJGnphiOsE,7932
52
- ara_cli/templates/template.businessgoal,sha256=3OU-y8dOCRbRsB9ovBzwFPxHSbG0dqbkok0uJnZIOd4,524
53
52
  ara_cli/templates/template.businessgoal.prompt_log.md,sha256=xF6bkgj_GqAAqHxJWJiQNt11mEuSGemIqoZ2wOo6dI0,214
54
- ara_cli/templates/template.capability,sha256=NZ7ZhwXqD9ZHwTAHxgPxfZWYicW_BvoU_onBHsMOlpQ,487
55
53
  ara_cli/templates/template.capability.prompt_log.md,sha256=eO8EzrHgb2vYJ-DP1jGzAfDlMo8nY75hZDfhh0s40uQ,208
56
- ara_cli/templates/template.epic,sha256=8dO4KYBB6Tdg9k0KOZWFZXQmUR8RRwBNCo85sMhOARE,632
57
54
  ara_cli/templates/template.epic.prompt_log.md,sha256=70MeWRL8Yl64eXwxPFG9KeMeoSziB2O0vv-tYLI8B3g,190
58
- ara_cli/templates/template.example,sha256=TXZCb5yVNINlEbrtcyjRqtLoHN0llvSxSqKfL-45r3w,386
59
55
  ara_cli/templates/template.example.prompt_log.md,sha256=PiLVx-tE0FqtLcXMsOvgCFDHIQ0EkOYhZRweJL49kHo,199
60
- ara_cli/templates/template.feature,sha256=h8PSBEJgfPPKlBPrE7tAI_8RamIriVdqI7GWRytlJRg,1066
61
56
  ara_cli/templates/template.feature.prompt_log.md,sha256=8xJUgCnYf18cmlDu90r2mF0039g2QOd38B7ZYg2Aefg,199
62
- ara_cli/templates/template.issue,sha256=Bzk_3m8yKDfV-BpNPmdDO3qoOCZ_gK6Yx84Bf2vaOJU,722
63
57
  ara_cli/templates/template.issue.prompt_log.md,sha256=dqO26R1xlftqb6nf_BLTEfCZx60yDKqqJZzEluB4Qco,193
64
- ara_cli/templates/template.keyfeature,sha256=2naABQSAvvyJDr3rqzq1a3MkqSUjaKopqiN2GJGRfmg,761
65
58
  ara_cli/templates/template.keyfeature.prompt_log.md,sha256=VUXZ72Z7KQ9Gp7NR_uWNVa3qSDqFflbNZay_02-X9DY,208
66
59
  ara_cli/templates/template.steps.prompt_log.md,sha256=sd8g1KtwgpPvU-vGWAFCDXiWHk6glRQlno5yrRN02_8,193
67
- ara_cli/templates/template.task,sha256=t6Lis-nP425XR5CuCG3l8qfTxSt-9zWjdcA9i8Id_ek,435
68
60
  ara_cli/templates/template.task.prompt_log.md,sha256=4kAy1zC0k3_y3SoYb-mg3rQEJCW_c43X7ggOLZ321TE,190
69
- ara_cli/templates/template.userstory,sha256=x6fouctaYl6I9gAyR8KXLVXec7oUT4uFm4EcvYMrwkM,679
70
61
  ara_cli/templates/template.userstory.prompt_log.md,sha256=Yp62iF7zDy2XNIwwJN35jKKSmezinK_JKbSvVuagtmA,205
71
- ara_cli/templates/template.vision,sha256=FZYcXUZtFOLvk0H1UEMhrJrDWZnS2CSmDv9feMkyYjU,594
72
62
  ara_cli/templates/template.vision.prompt_log.md,sha256=CAzBzj3O23CzrPIUq3xzpXGKn3_nAvyBLRUi-5Bnq_0,196
73
63
  ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md,sha256=DtZsdfVDNy9_cGE_Nn_TE2T3oRwr27kecZchOp5uIG0,672
74
64
  ara_cli/templates/prompt-modules/blueprints/empty.blueprint.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -134,14 +124,14 @@ ara_cli/templates/specification_breakdown_files/template.technology.md,sha256=by
134
124
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
125
  tests/test_ara_command_action.py,sha256=JTLqXM9BSMlU33OQgrk_sZnoowFJZKZAx8q-st-wa34,25821
136
126
  tests/test_ara_config.py,sha256=1LWby_iSestTIIqK-1clggL8kmbGGbtlYfsxAHaMMF8,2232
137
- tests/test_artefact_autofix.py,sha256=0Y6z5EXTEDUbZb-xxWNhaVUKnhxVVS3w2YZWTvyvoZs,12103
127
+ tests/test_artefact_autofix.py,sha256=K5h-IcPHZHdbLFgQHoto3K4fgX8cNW-ddHgQwCAIlFc,22145
138
128
  tests/test_artefact_fuzzy_search.py,sha256=5Sh3_l9QK8-WHn6JpGPU1b6h4QEnl2JoMq1Tdp2cj1U,1261
139
129
  tests/test_artefact_link_updater.py,sha256=biqbEp2jCOz8giv72hu2P2hDfeJfJ9OrVGdAv5d9cK4,2191
140
130
  tests/test_artefact_lister.py,sha256=VCEOCgDgnAOeUUgIoGAbWgz60hf9UT-tdHg18LGfB34,22656
141
131
  tests/test_artefact_reader.py,sha256=660K-d8ed-j8hulsUB_7baPD2-hhbg9TffUR5yVc4Uo,927
142
132
  tests/test_artefact_renamer.py,sha256=lSnKCCfoFGgKhTdDZrEaeBq1xJAak1QoqH5aSeOe9Ro,3494
143
133
  tests/test_artefact_scan.py,sha256=m1dws5d5m-dMnCn43BVhnNHHLU75zgVWaKIEK9y76fk,7512
144
- tests/test_chat.py,sha256=-00mni6Kik_RO8BGUpWqaL4S0wt2MbUBi5jD06dSHJM,47538
134
+ tests/test_chat.py,sha256=pUPpE-S5xRdYO_N4KDiY3O8_Js9ohnVLtJb0Cb9ZYqI,49165
145
135
  tests/test_classifier.py,sha256=grYGPksydNdPsaEBQxYHZTuTdcJWz7VQtikCKA6BNaQ,1920
146
136
  tests/test_directory_navigator.py,sha256=7G0MVrBbtBvbrFUpL0zb_9EkEWi1dulWuHsrQxMJxDY,140
147
137
  tests/test_file_classifier.py,sha256=kLWPiePu3F5mkVuI_lK_2QlLh2kXD_Mt2K8KZZ1fAnA,10940
@@ -151,8 +141,8 @@ tests/test_list_filter.py,sha256=fJA3d_SdaOAUkE7jn68MOVS0THXGghy1fye_64Zvo1U,796
151
141
  tests/test_tag_extractor.py,sha256=nSiAYlTKZ7TLAOtcJpwK5zTWHhFYU0tI5xKnivLc1dU,2712
152
142
  tests/test_template_manager.py,sha256=q-LMHRG4rHkD6ON6YW4cpZxUx9hul6Or8wVVRC2kb-8,4099
153
143
  tests/test_update_config_prompt.py,sha256=xsqj1WTn4BsG5Q2t-sNPfu7EoMURFcS-hfb5VSXUnJc,6765
154
- ara_cli-0.1.9.72.dist-info/METADATA,sha256=IJcjoYAVthNFzAuL15h343sheK02UrnbDUJVTD3_2Xs,415
155
- ara_cli-0.1.9.72.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
156
- ara_cli-0.1.9.72.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
157
- ara_cli-0.1.9.72.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
158
- ara_cli-0.1.9.72.dist-info/RECORD,,
144
+ ara_cli-0.1.9.74.dist-info/METADATA,sha256=p8qqnr68Q-czsh9MmSajWmOtJNzngrf-eZFUqCg_b00,415
145
+ ara_cli-0.1.9.74.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
146
+ ara_cli-0.1.9.74.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
147
+ ara_cli-0.1.9.74.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
148
+ ara_cli-0.1.9.74.dist-info/RECORD,,
@@ -10,8 +10,13 @@ from ara_cli.artefact_autofix import (
10
10
  write_corrected_artefact,
11
11
  construct_prompt,
12
12
  fix_title_mismatch,
13
+ ask_for_correct_contribution,
14
+ ask_for_contribution_choice,
15
+ _has_valid_contribution,
16
+ set_closest_contribution,
17
+ fix_contribution,
13
18
  )
14
- from ara_cli.artefact_models.artefact_model import ArtefactType
19
+ from ara_cli.artefact_models.artefact_model import Artefact, ArtefactType, Contribution
15
20
 
16
21
 
17
22
  @pytest.fixture
@@ -38,6 +43,23 @@ def mock_classified_artefact_info():
38
43
  return MagicMock()
39
44
 
40
45
 
46
+ @pytest.fixture
47
+ def mock_artefact_with_contribution():
48
+ """Provides a mock Artefact with a mock Contribution."""
49
+ mock_contribution = MagicMock(spec=Contribution)
50
+ mock_contribution.artefact_name = "some_artefact"
51
+ mock_contribution.classifier = "feature"
52
+ mock_contribution.rule = "some rule"
53
+
54
+ mock_artefact = MagicMock(spec=Artefact)
55
+ mock_artefact.contribution = mock_contribution
56
+ mock_artefact.title = "my_test_artefact"
57
+ mock_artefact._artefact_type.return_value.value = "requirement"
58
+ mock_artefact.serialize.return_value = "serialized artefact text"
59
+
60
+ return mock_artefact
61
+
62
+
41
63
  def test_read_report_file_success():
42
64
  """Tests successful reading of the report file."""
43
65
  mock_content = "# Artefact Check Report\n- `file.feature`: reason"
@@ -96,11 +118,9 @@ def test_read_artefact_file_not_found(capsys):
96
118
  @patch("ara_cli.artefact_models.artefact_mapping.artefact_type_mapping")
97
119
  def test_determine_artefact_type_and_class_no_class_found(mock_mapping, capsys):
98
120
  mock_mapping.get.return_value = None
99
- # The function returns (None, None) if the class is not in the mapping.
100
121
  artefact_type, artefact_class = determine_artefact_type_and_class("feature")
101
122
  assert artefact_type is None
102
123
  assert artefact_class is None
103
- # The print statement inside the function is called before returning, so this check is valid.
104
124
  assert "No artefact class found for" in capsys.readouterr().out
105
125
 
106
126
 
@@ -124,7 +144,8 @@ def test_write_corrected_artefact():
124
144
  def test_construct_prompt_for_task():
125
145
  prompt = construct_prompt(ArtefactType.task, "some reason", "file.task", "text")
126
146
  assert (
127
- "For task artefacts, if the action items looks like template or empty" in prompt
147
+ "For task artefacts, if the action items looks like template or empty"
148
+ in prompt
128
149
  )
129
150
 
130
151
 
@@ -133,28 +154,30 @@ def test_construct_prompt_for_task():
133
154
  "ara_cli.artefact_autofix.determine_artefact_type_and_class",
134
155
  return_value=(None, None),
135
156
  )
136
- @patch("ara_cli.artefact_autofix.read_artefact", return_value="original text")
157
+ @patch("ara_cli.artefact_autofix.read_artefact")
137
158
  def test_apply_autofix_exits_when_classifier_is_invalid(
138
159
  mock_read, mock_determine, mock_run_agent, mock_classified_artefact_info
139
160
  ):
140
161
  """Tests that apply_autofix exits early if the classifier is invalid."""
141
162
  result = apply_autofix(
142
- "file.feature",
143
- "invalid",
144
- "reason",
163
+ file_path="file.feature",
164
+ classifier="invalid",
165
+ reason="reason",
145
166
  deterministic=True,
146
167
  non_deterministic=True,
147
168
  classified_artefact_info=mock_classified_artefact_info,
148
169
  )
149
170
  assert result is False
150
- mock_read.assert_called_once_with("file.feature")
151
171
  mock_determine.assert_called_once_with("invalid")
172
+ mock_read.assert_not_called()
152
173
  mock_run_agent.assert_not_called()
153
174
 
154
175
 
176
+ @patch("ara_cli.artefact_autofix.FileClassifier")
177
+ @patch("ara_cli.artefact_autofix.check_file")
155
178
  @patch("ara_cli.artefact_autofix.run_agent")
156
179
  @patch("ara_cli.artefact_autofix.write_corrected_artefact")
157
- @patch("ara_cli.artefact_autofix.fix_title_mismatch", return_value="fixed text")
180
+ @patch("ara_cli.artefact_autofix.fix_title_mismatch")
158
181
  @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
159
182
  @patch("ara_cli.artefact_autofix.read_artefact", return_value="original text")
160
183
  def test_apply_autofix_for_title_mismatch_with_deterministic_flag(
@@ -163,24 +186,31 @@ def test_apply_autofix_for_title_mismatch_with_deterministic_flag(
163
186
  mock_fix_title,
164
187
  mock_write,
165
188
  mock_run_agent,
189
+ mock_check_file,
190
+ mock_file_classifier,
166
191
  mock_artefact_type,
167
192
  mock_artefact_class,
168
193
  mock_classified_artefact_info,
169
194
  ):
170
195
  """Tests that a deterministic fix is applied when the flag is True."""
171
196
  mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
172
- reason = "Filename-Title Mismatch: some details"
197
+ mock_check_file.side_effect = [
198
+ (False, "Filename-Title Mismatch: some details"),
199
+ (True, ""),
200
+ ]
201
+ mock_fix_title.return_value = "fixed text"
173
202
 
174
203
  result = apply_autofix(
175
- "file.feature",
176
- "feature",
177
- reason,
204
+ file_path="file.feature",
205
+ classifier="feature",
206
+ reason="Filename-Title Mismatch: some details",
178
207
  deterministic=True,
179
208
  non_deterministic=False,
180
209
  classified_artefact_info=mock_classified_artefact_info,
181
210
  )
182
211
 
183
212
  assert result is True
213
+ assert mock_check_file.call_count == 2
184
214
  mock_fix_title.assert_called_once_with(
185
215
  file_path="file.feature",
186
216
  artefact_text="original text",
@@ -189,8 +219,10 @@ def test_apply_autofix_for_title_mismatch_with_deterministic_flag(
189
219
  )
190
220
  mock_write.assert_called_once_with("file.feature", "fixed text")
191
221
  mock_run_agent.assert_not_called()
222
+ mock_file_classifier.assert_called_once()
192
223
 
193
224
 
225
+ @patch("ara_cli.artefact_autofix.check_file")
194
226
  @patch("ara_cli.artefact_autofix.run_agent")
195
227
  @patch("ara_cli.artefact_autofix.write_corrected_artefact")
196
228
  @patch("ara_cli.artefact_autofix.fix_title_mismatch")
@@ -202,29 +234,34 @@ def test_apply_autofix_skips_title_mismatch_without_deterministic_flag(
202
234
  mock_fix_title,
203
235
  mock_write,
204
236
  mock_run_agent,
237
+ mock_check_file,
205
238
  mock_artefact_type,
206
239
  mock_artefact_class,
207
240
  mock_classified_artefact_info,
208
241
  ):
209
242
  """Tests that a deterministic fix is skipped when the flag is False."""
210
243
  mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
211
- reason = "Filename-Title Mismatch: some details"
244
+ mock_check_file.return_value = (False, "Filename-Title Mismatch: some details")
212
245
 
213
246
  result = apply_autofix(
214
- "file.feature",
215
- "feature",
216
- reason,
247
+ file_path="file.feature",
248
+ classifier="feature",
249
+ reason="Filename-Title Mismatch: some details",
217
250
  deterministic=False,
218
251
  non_deterministic=True,
219
252
  classified_artefact_info=mock_classified_artefact_info,
220
253
  )
221
254
 
222
255
  assert result is False
256
+ mock_check_file.assert_called_once()
257
+ mock_read.assert_called_once_with("file.feature")
223
258
  mock_fix_title.assert_not_called()
224
259
  mock_write.assert_not_called()
225
260
  mock_run_agent.assert_not_called()
226
261
 
227
262
 
263
+ @patch("ara_cli.artefact_autofix.FileClassifier")
264
+ @patch("ara_cli.artefact_autofix.check_file")
228
265
  @patch("ara_cli.artefact_autofix.write_corrected_artefact")
229
266
  @patch("ara_cli.artefact_autofix.run_agent")
230
267
  @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
@@ -234,27 +271,32 @@ def test_apply_autofix_for_llm_fix_with_non_deterministic_flag(
234
271
  mock_determine,
235
272
  mock_run_agent,
236
273
  mock_write,
274
+ mock_check_file,
275
+ mock_file_classifier,
237
276
  mock_artefact_type,
238
277
  mock_artefact_class,
239
278
  mock_classified_artefact_info,
240
279
  ):
241
280
  """Tests that an LLM fix is applied when the non-deterministic flag is True."""
242
281
  mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
282
+ mock_check_file.side_effect = [(False, "Pydantic validation error"), (True, "")]
243
283
  mock_run_agent.return_value = mock_artefact_class
244
- reason = "Pydantic validation error"
245
284
 
246
285
  result = apply_autofix(
247
- "file.feature",
248
- "feature",
249
- reason,
286
+ file_path="file.feature",
287
+ classifier="feature",
288
+ reason="Pydantic validation error",
250
289
  deterministic=False,
251
290
  non_deterministic=True,
252
291
  classified_artefact_info=mock_classified_artefact_info,
253
292
  )
254
293
 
255
294
  assert result is True
295
+ assert mock_check_file.call_count == 2
296
+ mock_read.assert_called_once_with("file.feature")
256
297
  mock_run_agent.assert_called_once()
257
298
  mock_write.assert_called_once_with("file.feature", "llm corrected content")
299
+ mock_file_classifier.assert_called_once()
258
300
 
259
301
 
260
302
  @patch("ara_cli.artefact_autofix.write_corrected_artefact")
@@ -320,9 +362,6 @@ def test_apply_autofix_llm_exception(
320
362
  )
321
363
 
322
364
 
323
- # === Other Tests ===
324
-
325
-
326
365
  def test_fix_title_mismatch_success(mock_artefact_class):
327
366
  artefact_text = "Feature: wrong title\nSome other content"
328
367
  file_path = "path/to/correct_title.feature"
@@ -351,3 +390,228 @@ def test_run_agent_exception_handling(mock_agent_class):
351
390
  mock_agent_instance.run_sync.side_effect = Exception("Agent error")
352
391
  with pytest.raises(Exception, match="Agent error"):
353
392
  run_agent("prompt", MagicMock())
393
+
394
+
395
+ @patch("builtins.input", side_effect=["1"])
396
+ def test_ask_for_contribution_choice_valid(mock_input):
397
+ """Tests selecting a valid choice."""
398
+ choices = ["choice1", "choice2"]
399
+ # This simpler call now works without causing a TypeError
400
+ result = ask_for_contribution_choice(choices)
401
+ assert result == "choice1"
402
+
403
+ @patch("builtins.input", side_effect=["99"])
404
+ def test_ask_for_contribution_choice_out_of_range(mock_input, capsys):
405
+ """Tests selecting a choice that is out of range."""
406
+ choices = ["choice1", "choice2"]
407
+ result = ask_for_contribution_choice(choices)
408
+ assert result is None
409
+ assert "Invalid choice" in capsys.readouterr().out
410
+
411
+
412
+ @patch("builtins.input", side_effect=["not a number"])
413
+ def test_ask_for_contribution_choice_invalid_input(mock_input, capsys):
414
+ """Tests providing non-numeric input."""
415
+ choices = ["choice1", "choice2"]
416
+ result = ask_for_contribution_choice(choices)
417
+ assert result is None
418
+ assert "Invalid input" in capsys.readouterr().out
419
+
420
+
421
+ @patch("builtins.input", side_effect=["feature my_feature_name"])
422
+ def test_ask_for_correct_contribution_valid(mock_input):
423
+ """Tests providing valid '<classifier> <name>' input."""
424
+ name, classifier = ask_for_correct_contribution(("old_name", "feature"))
425
+ assert name == "my_feature_name"
426
+ assert classifier == "feature"
427
+
428
+
429
+ @patch("builtins.input", side_effect=[""])
430
+ def test_ask_for_correct_contribution_empty_input(mock_input):
431
+ """Tests providing empty input."""
432
+ name, classifier = ask_for_correct_contribution()
433
+ assert name is None
434
+ assert classifier is None
435
+
436
+
437
+ @patch("builtins.input", side_effect=["invalid-one-word-input"])
438
+ def test_ask_for_correct_contribution_invalid_format(mock_input, capsys):
439
+ """Tests providing input with the wrong format."""
440
+ # Fix: Use input that results in a single part after split()
441
+ name, classifier = ask_for_correct_contribution()
442
+ assert name is None
443
+ assert classifier is None
444
+ assert "Invalid input format" in capsys.readouterr().out
445
+
446
+
447
+ def test_has_valid_contribution_true(mock_artefact_with_contribution):
448
+ """Tests with a valid contribution object."""
449
+ # Fix: Check for truthiness, not strict boolean equality
450
+ assert _has_valid_contribution(mock_artefact_with_contribution)
451
+
452
+
453
+ def test_has_valid_contribution_false_no_contribution():
454
+ """Tests when the artefact's contribution is None."""
455
+ mock_artefact = MagicMock(spec=Artefact)
456
+ mock_artefact.contribution = None
457
+ # Fix: Check for falsiness, not strict boolean equality
458
+ assert not _has_valid_contribution(mock_artefact)
459
+
460
+
461
+ @patch("ara_cli.artefact_autofix.FileClassifier")
462
+ @patch("ara_cli.artefact_autofix.extract_artefact_names_of_classifier")
463
+ @patch("ara_cli.artefact_autofix.find_closest_name_matches")
464
+ def test_set_closest_contribution_no_change_needed(
465
+ mock_find, mock_extract, mock_classifier, mock_artefact_with_contribution
466
+ ):
467
+ """Tests the case where the contribution name is already the best match."""
468
+ mock_find.return_value = ["some_artefact"] # Exact match is found
469
+ artefact, changed = set_closest_contribution(mock_artefact_with_contribution)
470
+ assert changed is False
471
+ assert artefact == mock_artefact_with_contribution
472
+
473
+
474
+ @patch("ara_cli.artefact_autofix.FileClassifier")
475
+ @patch("ara_cli.artefact_autofix.extract_artefact_names_of_classifier")
476
+ @patch("ara_cli.artefact_autofix.find_closest_name_matches", return_value=[])
477
+ @patch(
478
+ "ara_cli.artefact_autofix.ask_for_correct_contribution",
479
+ return_value=("new_name", "new_classifier"),
480
+ )
481
+ def test_set_closest_contribution_no_matches_user_provides(
482
+ mock_ask, mock_find, mock_extract, mock_classifier, mock_artefact_with_contribution
483
+ ):
484
+ """Tests when no matches are found and the user provides a new contribution."""
485
+ artefact, changed = set_closest_contribution(mock_artefact_with_contribution)
486
+ assert changed is True
487
+ assert artefact.contribution.artefact_name == "new_name"
488
+ assert artefact.contribution.classifier == "new_classifier"
489
+
490
+
491
+ @patch("ara_cli.artefact_autofix.set_closest_contribution")
492
+ @patch("ara_cli.artefact_autofix.FileClassifier")
493
+ def test_fix_contribution(
494
+ mock_file_classifier, mock_set, mock_artefact_with_contribution
495
+ ):
496
+ """Tests the fix_contribution wrapper function."""
497
+ # Arrange
498
+ mock_artefact_class = MagicMock()
499
+ mock_artefact_class.deserialize.return_value = mock_artefact_with_contribution
500
+ mock_set.return_value = (mock_artefact_with_contribution, True)
501
+
502
+ # Act
503
+ result = fix_contribution(
504
+ file_path="dummy.path",
505
+ artefact_text="original text",
506
+ artefact_class=mock_artefact_class,
507
+ classified_artefact_info={},
508
+ )
509
+
510
+ # Assert
511
+ assert result == "serialized artefact text"
512
+ mock_artefact_class.deserialize.assert_called_once_with("original text")
513
+ mock_set.assert_called_once_with(mock_artefact_with_contribution)
514
+
515
+
516
+ @patch("ara_cli.artefact_autofix.FileClassifier")
517
+ @patch("ara_cli.artefact_autofix.check_file")
518
+ @patch("ara_cli.artefact_autofix.write_corrected_artefact")
519
+ @patch("ara_cli.artefact_autofix.fix_contribution", return_value="fixed text")
520
+ @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
521
+ @patch("ara_cli.artefact_autofix.read_artefact", return_value="original text")
522
+ def test_apply_autofix_for_contribution_mismatch(
523
+ mock_read,
524
+ mock_determine,
525
+ mock_fix_contribution,
526
+ mock_write,
527
+ mock_check_file,
528
+ mock_classifier,
529
+ mock_artefact_type,
530
+ mock_artefact_class,
531
+ mock_classified_artefact_info,
532
+ ):
533
+ """Tests the deterministic fix for 'Invalid Contribution Reference'."""
534
+ mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
535
+ mock_check_file.side_effect = [
536
+ (False, "Invalid Contribution Reference"),
537
+ (True, ""),
538
+ ]
539
+
540
+ result = apply_autofix(
541
+ file_path="file.feature",
542
+ classifier="feature",
543
+ reason="Invalid Contribution Reference",
544
+ classified_artefact_info=mock_classified_artefact_info,
545
+ )
546
+
547
+ assert result is True
548
+ mock_fix_contribution.assert_called_once()
549
+ mock_write.assert_called_once_with("file.feature", "fixed text")
550
+
551
+
552
+ @patch("ara_cli.artefact_autofix.check_file")
553
+ @patch("ara_cli.artefact_autofix.write_corrected_artefact")
554
+ @patch("ara_cli.artefact_autofix.fix_title_mismatch", return_value="original text")
555
+ @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
556
+ @patch("ara_cli.artefact_autofix.read_artefact", return_value="original text")
557
+ def test_apply_autofix_stops_if_no_alteration(
558
+ mock_read,
559
+ mock_determine,
560
+ mock_fix_title,
561
+ mock_write,
562
+ mock_check_file,
563
+ capsys,
564
+ mock_artefact_type,
565
+ mock_artefact_class,
566
+ mock_classified_artefact_info,
567
+ ):
568
+ """Tests that the loop stops if a fix attempt does not change the file content."""
569
+ mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
570
+ mock_check_file.return_value = (False, "Filename-Title Mismatch")
571
+
572
+ result = apply_autofix(
573
+ file_path="file.feature",
574
+ classifier="feature",
575
+ reason="any",
576
+ classified_artefact_info=mock_classified_artefact_info,
577
+ )
578
+
579
+ assert result is False
580
+ mock_fix_title.assert_called_once()
581
+ mock_write.assert_not_called()
582
+ assert (
583
+ "Fixing attempt did not alter the file. Stopping to prevent infinite loop."
584
+ in capsys.readouterr().out
585
+ )
586
+
587
+
588
+ @patch("ara_cli.artefact_autofix.check_file")
589
+ @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
590
+ def test_apply_autofix_single_pass(
591
+ mock_determine,
592
+ mock_check_file,
593
+ capsys,
594
+ mock_artefact_type,
595
+ mock_artefact_class,
596
+ mock_classified_artefact_info,
597
+ ):
598
+ """Tests that single_pass=True runs the loop only once."""
599
+ mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
600
+ # Simulate a failure that won't be fixed to ensure the loop doesn't repeat
601
+ mock_check_file.return_value = (False, "Some unfixable error")
602
+
603
+ apply_autofix(
604
+ file_path="file.feature",
605
+ classifier="feature",
606
+ reason="any",
607
+ single_pass=True,
608
+ deterministic=False, # Disable fixes
609
+ non_deterministic=False,
610
+ classified_artefact_info=mock_classified_artefact_info,
611
+ )
612
+
613
+ output = capsys.readouterr().out
614
+ assert "Single-pass mode enabled" in output
615
+ assert "Attempt 1/1" in output
616
+ assert "Attempt 2/1" not in output
617
+ mock_check_file.assert_called_once()
tests/test_chat.py CHANGED
@@ -485,7 +485,7 @@ def test_load_text_file(temp_chat_file, file_name, file_content, prefix, suffix,
485
485
 
486
486
  assert result is True
487
487
 
488
- mock_file.assert_any_call(file_name, 'r', encoding='utf-8')
488
+ mock_file.assert_any_call(file_name, 'r', encoding='utf-8', errors="replace")
489
489
 
490
490
  mock_file.assert_any_call(chat.chat_name, 'a', encoding='utf-8')
491
491
 
@@ -1052,19 +1052,39 @@ def test_do_SEND(temp_chat_file):
1052
1052
  mock_send_message.assert_called_once()
1053
1053
 
1054
1054
 
1055
- def test_do_LOAD_TEMPLATE(temp_chat_file):
1056
- mock_config = get_default_config()
1055
+ @pytest.mark.parametrize("template_name, artefact_obj, expected_write, expected_print", [
1056
+ ("TestTemplate", MagicMock(serialize=MagicMock(return_value="serialized_content")), "serialized_content", "Loaded TestTemplate artefact template\n"),
1057
+ ("AnotherTemplate", MagicMock(serialize=MagicMock(return_value="other_content")), "other_content", "Loaded AnotherTemplate artefact template\n"),
1058
+ ])
1059
+ def test_do_LOAD_TEMPLATE_success(temp_chat_file, template_name, artefact_obj, expected_write, expected_print, capsys):
1060
+ mock_config = MagicMock() # or use get_default_config() if needed
1057
1061
  with patch('ara_cli.prompt_handler.ConfigManager.get_config', return_value=mock_config):
1058
1062
  chat = Chat(temp_chat_file.name, reset=False)
1059
- template_name = 'test_template'
1060
-
1061
- module_path = os.path.abspath(os.path.dirname(ara_cli.__file__))
1062
- directory = f'{module_path}/templates'
1063
- pattern = f"template.{template_name}"
1064
- file_type = "template"
1065
- exclude_pattern = os.path.join(directory, "template.*.prompt_log.md")
1066
-
1067
- with patch.object(chat, '_load_helper') as mock_load_helper:
1063
+ # Patch the artefact loader to return artefact_obj
1064
+ with patch('ara_cli.artefact_models.artefact_templates.template_artefact_of_type', return_value=artefact_obj) as mock_template_loader, \
1065
+ patch.object(chat, 'add_prompt_tag_if_needed') as mock_add_prompt_tag, \
1066
+ patch("builtins.open", mock_open()) as mock_file:
1068
1067
  chat.do_LOAD_TEMPLATE(template_name)
1069
-
1070
- mock_load_helper.assert_called_once_with(directory, pattern, file_type, exclude_pattern)
1068
+ mock_template_loader.assert_called_once_with(template_name)
1069
+ artefact_obj.serialize.assert_called_once_with()
1070
+ mock_add_prompt_tag.assert_called_once_with(chat.chat_name)
1071
+ mock_file.assert_called_with(chat.chat_name, 'a', encoding='utf-8')
1072
+ mock_file().write.assert_called_once_with(expected_write)
1073
+ out = capsys.readouterr()
1074
+ assert expected_print in out.out
1075
+
1076
+ @pytest.mark.parametrize("template_name", [
1077
+ ("MissingTemplate"),
1078
+ (""),
1079
+ ])
1080
+ def test_do_LOAD_TEMPLATE_missing_artefact(temp_chat_file, template_name):
1081
+ mock_config = MagicMock()
1082
+ with patch('ara_cli.prompt_handler.ConfigManager.get_config', return_value=mock_config):
1083
+ chat = Chat(temp_chat_file.name, reset=False)
1084
+ with patch('ara_cli.artefact_models.artefact_templates.template_artefact_of_type', return_value=None) as mock_template_loader, \
1085
+ patch.object(chat, 'add_prompt_tag_if_needed') as mock_add_prompt_tag, \
1086
+ patch("builtins.open", mock_open()) as mock_file:
1087
+ chat.do_LOAD_TEMPLATE(template_name)
1088
+ mock_template_loader.assert_called_once_with(template_name)
1089
+ mock_add_prompt_tag.assert_not_called()
1090
+ mock_file.assert_not_called()
@@ -1,10 +0,0 @@
1
- @sample_tag
2
- Businessgoal: <descriptive title>
3
-
4
- Contributes to <filename or title of the artefact> <agile requirement artefact category> <(optional in case the contribution is to an artefact that is detailed with rules) using rule <rule as it is formulated>
5
-
6
- In order to <reach primarily a monetary business goal>
7
- As a <business related role>
8
- I want <something that helps me to reach my monetary goal>
9
-
10
- Description: <further optional description to understand the business goal, markdown capable text formatting>
@@ -1,10 +0,0 @@
1
- @sample_tag
2
- Capability: <descriptive title>
3
-
4
- Contributes to <filename or title of the artefact> <agile requirement artefact category> <(optional in case the contribution is to an artefact that is detailed with rules) using rule <rule as it is formulated>
5
-
6
- To be able to <needed capability for stakeholders that are the
7
- enablers/relevant for reaching the business goal>
8
-
9
- Description: <further optional description to understand
10
- the capability, markdown capable text formatting>
@@ -1,15 +0,0 @@
1
- @sample_tag
2
- Epic: <descriptive title>
3
-
4
- Contributes to <filename or title of the artefact> <agile requirement artefact category> <(optional in case the contribution is to an artefact that is detailed with rules) using rule <rule as it is formulated>
5
-
6
- In order to <achieve a benefit>
7
- As a <(user) role>
8
- I want <a certain product behavior>
9
-
10
- Rule: <rule needed to fulfill the wanted product behavior>
11
- Rule: <rule needed to fulfill the wanted product behavior>
12
- Rule: <rule needed to fulfill the wanted product behavior>
13
-
14
- Description: <further optional description to understand
15
- the epic, markdown capable text formatting>