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

ara_cli/artefact_scan.py CHANGED
@@ -2,13 +2,21 @@ from textwrap import indent
2
2
  import os
3
3
 
4
4
 
5
- def check_file(file_path, artefact_class):
5
+ def check_file(file_path, artefact_class, classified_artefact_info=None):
6
6
  from pydantic import ValidationError
7
+ from ara_cli.artefact_fuzzy_search import extract_artefact_names_of_classifier
8
+ from ara_cli.file_classifier import FileClassifier
9
+
7
10
  try:
8
11
  with open(file_path, "r", encoding="utf-8") as f:
9
12
  content = f.read()
10
13
  except OSError as e:
11
14
  return False, f"File error: {e}"
15
+
16
+ if not classified_artefact_info:
17
+ file_classifier = FileClassifier(os)
18
+ classified_artefact_info = file_classifier.classify_files()
19
+
12
20
  try:
13
21
  artefact_instance = artefact_class.deserialize(content)
14
22
 
@@ -19,6 +27,23 @@ def check_file(file_path, artefact_class):
19
27
  reason = (f"Filename-Title Mismatch: The file name '{file_name_without_ext}' "
20
28
  f"does not match the artefact title '{artefact_instance.title}'.")
21
29
  return False, reason
30
+
31
+ # Check contribution reference validity
32
+ contribution = artefact_instance.contribution
33
+ if contribution and contribution.artefact_name and contribution.classifier:
34
+
35
+ # Find all artefact names of the referenced classifier
36
+ all_artefact_names = extract_artefact_names_of_classifier(
37
+ classified_files=classified_artefact_info,
38
+ classifier=contribution.classifier
39
+ )
40
+
41
+ # Check if the referenced artefact exists
42
+ if contribution.artefact_name not in all_artefact_names:
43
+ reason = (f"Invalid Contribution Reference: The contribution references "
44
+ f"'{contribution.classifier}' artefact '{contribution.artefact_name}' "
45
+ f"which does not exist.")
46
+ return False, reason
22
47
 
23
48
  return True, None
24
49
  except (ValidationError, ValueError, AssertionError) as e:
@@ -37,7 +62,7 @@ def find_invalid_files(classified_artefact_info, classifier):
37
62
  continue
38
63
  if ".data" in artefact_info["file_path"]:
39
64
  continue
40
- is_valid, reason = check_file(artefact_info["file_path"], artefact_class)
65
+ is_valid, reason = check_file(artefact_info["file_path"], artefact_class, classified_artefact_info)
41
66
  if not is_valid:
42
67
  invalid_files.append((artefact_info["file_path"], reason))
43
68
  return invalid_files
@@ -1,6 +1,6 @@
1
1
  from ara_cli.classifier import Classifier
2
2
  from ara_cli.artefact_models.artefact_model import Artefact
3
- from ara_cli.artefact_fuzzy_search import find_closest_name_match
3
+ from ara_cli.artefact_fuzzy_search import find_closest_name_matches
4
4
  from functools import lru_cache
5
5
  from typing import Optional
6
6
  import textwrap
@@ -17,7 +17,7 @@ class FileClassifier:
17
17
  info["title"] for info in classified_artefacts.get(classifier, [])]
18
18
  if name in all_artefact_names:
19
19
  return name
20
- return find_closest_name_match(name, all_artefact_names)
20
+ return find_closest_name_matches(name, all_artefact_names)
21
21
 
22
22
  @lru_cache(maxsize=None)
23
23
  def read_file_content(self, file_path):
ara_cli/prompt_handler.py CHANGED
@@ -3,7 +3,7 @@ import litellm
3
3
  from ara_cli.classifier import Classifier
4
4
  from ara_cli.artefact_creator import ArtefactCreator
5
5
  from ara_cli.template_manager import TemplatePathManager
6
- from ara_cli.ara_config import ConfigManager
6
+ from ara_cli.ara_config import ConfigManager, LLMConfigItem
7
7
  from ara_cli.file_lister import generate_markdown_listing
8
8
  from os.path import exists, join
9
9
  import os
@@ -20,15 +20,15 @@ class LLMSingleton:
20
20
 
21
21
  def __init__(self, model_id):
22
22
  config = ConfigManager().get_config()
23
- llm_config = config.llm_config
24
- selected_config = llm_config[str(model_id)]
23
+ selected_config = config.llm_config.get(str(model_id))
25
24
 
26
25
  if not selected_config:
27
26
  raise ValueError(f"No configuration found for the model: {model_id}")
28
27
 
29
28
  LLMSingleton._model = model_id
30
-
31
- self.config_parameters = selected_config
29
+
30
+ # Typesafe for None values inside the config.
31
+ self.config_parameters = selected_config.model_dump(exclude_none=True)
32
32
 
33
33
  LLMSingleton._instance = self
34
34
 
@@ -74,15 +74,14 @@ def read_string_from_file(path):
74
74
  def send_prompt(prompt):
75
75
  chat = LLMSingleton.get_instance()
76
76
 
77
- # remove provider from config parameters
78
77
  config_parameters = chat.config_parameters.copy()
79
- del config_parameters["provider"]
78
+ if "provider" in config_parameters:
79
+ del config_parameters["provider"]
80
80
 
81
81
  completion = litellm.completion(
82
82
  **config_parameters,
83
83
  messages=prompt,
84
- stream=True,
85
- max_tokens=32768
84
+ stream=True
86
85
  )
87
86
  for chunk in completion:
88
87
  yield chunk
@@ -436,4 +435,4 @@ def generate_config_prompt_givens_file(prompt_data_path, config_prompt_givens_na
436
435
 
437
436
  # Write the updated listing back to the file
438
437
  with open(config_prompt_givens_path, 'w', encoding='utf-8') as file:
439
- file.write("".join(updated_listing))
438
+ file.write("".join(updated_listing))
ara_cli/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # version.py
2
- __version__ = "0.1.9.69" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
2
+ __version__ = "0.1.9.70" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ara_cli
3
- Version: 0.1.9.69
3
+ Version: 0.1.9.70
4
4
  Requires-Dist: litellm
5
5
  Requires-Dist: llama-index
6
6
  Requires-Dist: llama-index-llms-openai
@@ -1,37 +1,37 @@
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=dD97IeH7x5udKC_APk5TbuVXPiyU2XSBV2uqy52nQ9A,21625
3
+ ara_cli/ara_command_action.py,sha256=aq074NT63xsuyIJgGnM0htp7kpyolaLwtauXjQEPljA,21852
4
4
  ara_cli/ara_command_parser.py,sha256=v8LUdkBSI2771gI53PdrxtD8YVjhk-7E8vgTEsTGnRM,17952
5
- ara_cli/ara_config.py,sha256=gOeaFR5Bkxk1EbN7qvJuHeaLICgy7irCREOIQD6_98M,3832
6
- ara_cli/artefact_autofix.py,sha256=-7XHl7j5O1kwWeBeIybnA4CI8v6SlXRNkV1KQ9nSWLQ,6817
5
+ ara_cli/ara_config.py,sha256=EBVxVXJauWNmLVSDeTdNvncO0z1avR3hN9mkJhPmaU0,4183
6
+ ara_cli/artefact_autofix.py,sha256=F4Vp-SI6YI0rKeVswOlg23OemH7w4CEJ9-ChcqIpn4U,14866
7
7
  ara_cli/artefact_creator.py,sha256=tUNCNvfFYMheyF_viyrQhm2-43AkbHFoQaHui9ntvws,6002
8
8
  ara_cli/artefact_deleter.py,sha256=Co4wwCH3yW8H9NrOq7_2p5571EeHr0TsfE-H8KqoOfY,1900
9
- ara_cli/artefact_fuzzy_search.py,sha256=BBDe-IP75sWZjG6nTNFtVljjL01JlQUy5ccJBZ6Trow,2429
9
+ ara_cli/artefact_fuzzy_search.py,sha256=iBlDqjZf-_D3VUjFf7ZwkiQbpQDcwRndIU7aG_sRTgE,2668
10
10
  ara_cli/artefact_link_updater.py,sha256=itMS_Z64jE8bBly9WA01z8PqkBeNW6ntTO7ryMeCTRg,3703
11
11
  ara_cli/artefact_lister.py,sha256=jhk4n4eqp7hDIq07q43QzS7-36BM3OfZ4EABxCeOGcw,4764
12
- ara_cli/artefact_reader.py,sha256=_RqBY1f1DWH2DThXETYuHeTB2ip0wgYuGvHYI_D6EJ4,8062
12
+ ara_cli/artefact_reader.py,sha256=zVR3DZ4z6vjKPQnFZVbPS75MkluaoLkz0lWmg_hXxTY,8136
13
13
  ara_cli/artefact_renamer.py,sha256=loIn1DF9kVnjhH7wP1v5qUvt3s0uKeWXuQPrHXenQGE,4025
14
- ara_cli/artefact_scan.py,sha256=J3aCAOltVr1oS6Lwnv51gtZC_G8faORGTccFY3kkBX4,2368
14
+ ara_cli/artefact_scan.py,sha256=ZB5mZkFto3K5xg_nZsQekWz5Ly7X0WyHnBsvPL3sC6I,3599
15
15
  ara_cli/chat.py,sha256=7xTtPEDk052_wmIzoti7GavEJ1vpRxe5c084WQ1C7dg,28617
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
19
19
  ara_cli/commandline_completer.py,sha256=b00Dqb5n7SecpxYIDLxAfYhp8X6e3c8a5qYz6ko0i3E,1192
20
20
  ara_cli/directory_navigator.py,sha256=6QbSAjJrJ5a6Lutol9J4HFgVDMiAQ672ny9TATrh04U,3318
21
- ara_cli/file_classifier.py,sha256=JsY7Y_D8WL-fiWz57zwzttg6SEajxWVxpDkFG_149-Q,3967
21
+ ara_cli/file_classifier.py,sha256=26MPVCaZlQkRDa62FoQ6ZqawOiaNgspUvcFOF93bHzo,3971
22
22
  ara_cli/file_lister.py,sha256=VFpUmHU1d6sQvJWSeuFqkZZ0Ci3ZYCUtAUfvgWypaYU,2314
23
23
  ara_cli/filename_validator.py,sha256=Aw9PL8d5-Ymhp3EY6lDrUBk3cudaNqo1Uw5RzPpI1jA,118
24
24
  ara_cli/list_filter.py,sha256=Not17hIngI37gZsLtIKxopB-BmyWoOGlBzSqBwh-Zpc,5273
25
25
  ara_cli/output_suppressor.py,sha256=ZByUwLH2DxOb-eJ31KQbtIziBKdykoyxvwxZ0tSammA,371
26
26
  ara_cli/prompt_chat.py,sha256=kd_OINDQFit6jN04bb7mzgY259JBbRaTaNp9F-webkc,1346
27
27
  ara_cli/prompt_extractor.py,sha256=a8LwPj6U8sG_v3SqDXQyPvDZQds4kHnYSO8eGissYJA,7503
28
- ara_cli/prompt_handler.py,sha256=eUkiA4CnBlSBQPZRQ78i7HqllvIDYlJgrSqQmwQHtp8,17621
28
+ ara_cli/prompt_handler.py,sha256=ukYWR_AMDd8nfGkeUKyYQ8PO_-PfO8x9tV1kNcNWOT4,17672
29
29
  ara_cli/prompt_rag.py,sha256=vmlt4-rSboWibwgO_KUF79TK99YXT5KXjmbD9FeWdZY,7449
30
30
  ara_cli/run_file_lister.py,sha256=XbrrDTJXp1LFGx9Lv91SNsEHZPP-PyEMBF_P4btjbDA,2360
31
31
  ara_cli/tag_extractor.py,sha256=4krQyvmLR2ffhe7N7lWC7QjaxXcb90HaQdmjnBiD8ak,2523
32
32
  ara_cli/template_manager.py,sha256=YXPj2jGNDb-diIHFEK_vGJ-ZucodnXSGAPofKTnOofI,6633
33
33
  ara_cli/update_config_prompt.py,sha256=PZgNIN3dTw6p80GyX8Sp5apkAhSoykwnkEbHo3IOkUo,4571
34
- ara_cli/version.py,sha256=47jiKD51bfE-X5CaysuEIPJuxXII3Vvv_DNLT7glr-I,146
34
+ ara_cli/version.py,sha256=_1Hbcx99RFW-ZzwbZEWia-OGS77hOyexYcSKcpQKm_Y,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
@@ -41,7 +41,7 @@ ara_cli/artefact_models/businessgoal_artefact_model.py,sha256=jqYFMXjWle0YW9RvcF
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=BeLsq9ZinzIDwss8nurIbxJZQ2qPgyRKRHrRlrl-zHA,15583
44
+ ara_cli/artefact_models/feature_artefact_model.py,sha256=JvgBKsiI1Y5Cs_0Ygn6lttJIjCpEC9XyZieDExUnsvg,18386
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=0XCruO70-fyfLfTn7pnt8NrSQe79eYNUAjuQaV8K6_8,586
@@ -132,27 +132,27 @@ ara_cli/templates/specification_breakdown_files/template.step.md,sha256=nzDRl9Xo
132
132
  ara_cli/templates/specification_breakdown_files/template.technology.exploration.md,sha256=zQyiJcmbUfXdte-5uZwZUpT6ey0zwfZ00P4VwI97jQk,2274
133
133
  ara_cli/templates/specification_breakdown_files/template.technology.md,sha256=bySiksz-8xtq0Nnj4svqe2MgUftWrVkbK9AcrDUE3KY,952
134
134
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
- tests/test_ara_autofix.py,sha256=EFVzhVTNPDRVxpoK0CY6OAM88EjZTRXh-uxFGS1AVWk,10695
136
135
  tests/test_ara_command_action.py,sha256=Y5MrG6VjXgebliKfdFaCaS8i3GoZCGSLpj3AWCbL5Lk,25695
137
136
  tests/test_ara_config.py,sha256=1LWby_iSestTIIqK-1clggL8kmbGGbtlYfsxAHaMMF8,2232
137
+ tests/test_artefact_autofix.py,sha256=0Y6z5EXTEDUbZb-xxWNhaVUKnhxVVS3w2YZWTvyvoZs,12103
138
138
  tests/test_artefact_fuzzy_search.py,sha256=5Sh3_l9QK8-WHn6JpGPU1b6h4QEnl2JoMq1Tdp2cj1U,1261
139
139
  tests/test_artefact_link_updater.py,sha256=gN5KFF1uY7OoBh8Mr5jWpqXp02YCU5OSIpSU76Rm4Gs,2137
140
140
  tests/test_artefact_lister.py,sha256=VCEOCgDgnAOeUUgIoGAbWgz60hf9UT-tdHg18LGfB34,22656
141
141
  tests/test_artefact_reader.py,sha256=660K-d8ed-j8hulsUB_7baPD2-hhbg9TffUR5yVc4Uo,927
142
142
  tests/test_artefact_renamer.py,sha256=N9JnnKBEJChTYEdltHoiDnn5UCNQUPj9H9YqOvLzbH0,3458
143
- tests/test_artefact_scan.py,sha256=7w4wqCj8VEwk676JiSqh0FCAOT-kFq624u2JDrhzpuw,5860
143
+ tests/test_artefact_scan.py,sha256=R-L3JDWV1xH9fneGJoyDEjy2lvKpJLBck5ISTF2MmNs,7476
144
144
  tests/test_chat.py,sha256=-00mni6Kik_RO8BGUpWqaL4S0wt2MbUBi5jD06dSHJM,47538
145
145
  tests/test_classifier.py,sha256=grYGPksydNdPsaEBQxYHZTuTdcJWz7VQtikCKA6BNaQ,1920
146
146
  tests/test_directory_navigator.py,sha256=7G0MVrBbtBvbrFUpL0zb_9EkEWi1dulWuHsrQxMJxDY,140
147
- tests/test_file_classifier.py,sha256=hbGp0-_A_LgQ0pGv1jWDEIyCgvDyfChcvvVfbxjNY2U,10938
147
+ tests/test_file_classifier.py,sha256=kLWPiePu3F5mkVuI_lK_2QlLh2kXD_Mt2K8KZZ1fAnA,10940
148
148
  tests/test_file_creator.py,sha256=D3G7MbgE0m8JmZihxnTryxLco6iZdbV--2CGc0L20FM,2109
149
149
  tests/test_file_lister.py,sha256=f6B_vIv-wAulKH2ZGgNg4SG79XqGGbfwoIvZlbEnYyM,4306
150
150
  tests/test_list_filter.py,sha256=gSRKirTtFuhRS3QlFHqWl89WvCvAdVEnFsCWTYmgB2o,7928
151
151
  tests/test_tag_extractor.py,sha256=nSiAYlTKZ7TLAOtcJpwK5zTWHhFYU0tI5xKnivLc1dU,2712
152
152
  tests/test_template_manager.py,sha256=q-LMHRG4rHkD6ON6YW4cpZxUx9hul6Or8wVVRC2kb-8,4099
153
153
  tests/test_update_config_prompt.py,sha256=vSsLvc18HZdVjVM93qXWVbJt752xTLL6VGjSVCrPufk,6729
154
- ara_cli-0.1.9.69.dist-info/METADATA,sha256=rInVDCWbgC4qvLyE2StAFpOXWahBfD7OBkfE5csu6SQ,415
155
- ara_cli-0.1.9.69.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
156
- ara_cli-0.1.9.69.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
157
- ara_cli-0.1.9.69.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
158
- ara_cli-0.1.9.69.dist-info/RECORD,,
154
+ ara_cli-0.1.9.70.dist-info/METADATA,sha256=im6yqXsHO5Nxn5Jhckz1n16ZUWxDqGiQcDsYLi3xJoA,415
155
+ ara_cli-0.1.9.70.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
156
+ ara_cli-0.1.9.70.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
157
+ ara_cli-0.1.9.70.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
158
+ ara_cli-0.1.9.70.dist-info/RECORD,,
@@ -9,10 +9,11 @@ from ara_cli.artefact_autofix import (
9
9
  run_agent,
10
10
  write_corrected_artefact,
11
11
  construct_prompt,
12
- fix_title_mismatch
12
+ fix_title_mismatch,
13
13
  )
14
14
  from ara_cli.artefact_models.artefact_model import ArtefactType
15
15
 
16
+
16
17
  @pytest.fixture
17
18
  def mock_artefact_type():
18
19
  """Provides a mock for the ArtefactType enum member."""
@@ -20,6 +21,7 @@ def mock_artefact_type():
20
21
  mock_type.value = "feature"
21
22
  return mock_type
22
23
 
24
+
23
25
  @pytest.fixture
24
26
  def mock_artefact_class():
25
27
  """Provides a mock for the Artefact class."""
@@ -30,13 +32,22 @@ def mock_artefact_class():
30
32
  return mock_class
31
33
 
32
34
 
35
+ @pytest.fixture
36
+ def mock_classified_artefact_info():
37
+ """Provides a mock for the classified artefact info dictionary."""
38
+ return MagicMock()
39
+
40
+
33
41
  def test_read_report_file_success():
34
42
  """Tests successful reading of the report file."""
35
43
  mock_content = "# Artefact Check Report\n- `file.feature`: reason"
36
44
  with patch("builtins.open", mock_open(read_data=mock_content)) as m:
37
45
  content = read_report_file()
38
46
  assert content == mock_content
39
- m.assert_called_once_with("incompatible_artefacts_report.md", "r", encoding="utf-8")
47
+ m.assert_called_once_with(
48
+ "incompatible_artefacts_report.md", "r", encoding="utf-8"
49
+ )
50
+
40
51
 
41
52
  def test_read_report_file_not_found(capsys):
42
53
  with patch("builtins.open", side_effect=OSError("File not found")):
@@ -44,28 +55,36 @@ def test_read_report_file_not_found(capsys):
44
55
  assert content is None
45
56
  assert "Artefact scan results file not found" in capsys.readouterr().out
46
57
 
58
+
47
59
  def test_parse_report_with_issues():
48
- content = "# Artefact Check Report\n\n## feature\n- `path/to/file.feature`: A reason\n"
60
+ content = (
61
+ "# Artefact Check Report\n\n## feature\n- `path/to/file.feature`: A reason\n"
62
+ )
49
63
  expected = {"feature": [("path/to/file.feature", "A reason")]}
50
64
  assert parse_report(content) == expected
51
65
 
66
+
52
67
  def test_parse_report_no_issues():
53
68
  content = "# Artefact Check Report\n\nNo problems found.\n"
54
69
  assert parse_report(content) == {}
55
70
 
71
+
56
72
  def test_parse_report_invalid_format():
57
73
  assert parse_report("This is not a valid report") == {}
58
74
 
75
+
59
76
  def test_parse_report_invalid_line_format():
60
77
  content = "# Artefact Check Report\n\n## feature\n- an invalid line\n"
61
78
  assert parse_report(content) == {"feature": []}
62
79
 
80
+
63
81
  def test_read_artefact_success():
64
82
  mock_content = "Feature: My Feature"
65
83
  with patch("builtins.open", mock_open(read_data=mock_content)) as m:
66
84
  content = read_artefact("file.feature")
67
85
  assert content == mock_content
68
- m.assert_called_once_with("file.feature", 'r', encoding="utf-8")
86
+ m.assert_called_once_with("file.feature", "r", encoding="utf-8")
87
+
69
88
 
70
89
  def test_read_artefact_file_not_found(capsys):
71
90
  with patch("builtins.open", side_effect=FileNotFoundError):
@@ -73,6 +92,7 @@ def test_read_artefact_file_not_found(capsys):
73
92
  assert result is None
74
93
  assert "File not found: nonexistent.feature" in capsys.readouterr().out
75
94
 
95
+
76
96
  @patch("ara_cli.artefact_models.artefact_mapping.artefact_type_mapping")
77
97
  def test_determine_artefact_type_and_class_no_class_found(mock_mapping, capsys):
78
98
  mock_mapping.get.return_value = None
@@ -83,134 +103,248 @@ def test_determine_artefact_type_and_class_no_class_found(mock_mapping, capsys):
83
103
  # The print statement inside the function is called before returning, so this check is valid.
84
104
  assert "No artefact class found for" in capsys.readouterr().out
85
105
 
106
+
86
107
  @patch("ara_cli.artefact_models.artefact_model.ArtefactType", side_effect=ValueError)
87
108
  def test_determine_artefact_type_and_class_invalid(mock_artefact_type_enum, capsys):
88
- artefact_type, artefact_class = determine_artefact_type_and_class("invalid_classifier")
109
+ artefact_type, artefact_class = determine_artefact_type_and_class(
110
+ "invalid_classifier"
111
+ )
89
112
  assert artefact_type is None
90
113
  assert artefact_class is None
91
114
  assert "Invalid classifier: invalid_classifier" in capsys.readouterr().out
92
115
 
116
+
93
117
  def test_write_corrected_artefact():
94
118
  with patch("builtins.open", mock_open()) as m:
95
119
  write_corrected_artefact("file.feature", "corrected content")
96
- m.assert_called_once_with("file.feature", 'w', encoding="utf-8")
120
+ m.assert_called_once_with("file.feature", "w", encoding="utf-8")
97
121
  m().write.assert_called_once_with("corrected content")
98
122
 
123
+
99
124
  def test_construct_prompt_for_task():
100
125
  prompt = construct_prompt(ArtefactType.task, "some reason", "file.task", "text")
101
- assert "For task artefacts, if the action items looks like template or empty" in prompt
126
+ assert (
127
+ "For task artefacts, if the action items looks like template or empty" in prompt
128
+ )
129
+
102
130
 
103
131
  @patch("ara_cli.artefact_autofix.run_agent")
104
- @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class", return_value=(None, None))
132
+ @patch(
133
+ "ara_cli.artefact_autofix.determine_artefact_type_and_class",
134
+ return_value=(None, None),
135
+ )
105
136
  @patch("ara_cli.artefact_autofix.read_artefact", return_value="original text")
106
- def test_apply_autofix_exits_when_classifier_is_invalid(mock_read, mock_determine, mock_run_agent):
137
+ def test_apply_autofix_exits_when_classifier_is_invalid(
138
+ mock_read, mock_determine, mock_run_agent, mock_classified_artefact_info
139
+ ):
107
140
  """Tests that apply_autofix exits early if the classifier is invalid."""
108
- result = apply_autofix("file.feature", "invalid", "reason", deterministic=True, non_deterministic=True)
141
+ result = apply_autofix(
142
+ "file.feature",
143
+ "invalid",
144
+ "reason",
145
+ deterministic=True,
146
+ non_deterministic=True,
147
+ classified_artefact_info=mock_classified_artefact_info,
148
+ )
109
149
  assert result is False
110
150
  mock_read.assert_called_once_with("file.feature")
111
151
  mock_determine.assert_called_once_with("invalid")
112
152
  mock_run_agent.assert_not_called()
113
153
 
154
+
114
155
  @patch("ara_cli.artefact_autofix.run_agent")
115
156
  @patch("ara_cli.artefact_autofix.write_corrected_artefact")
116
157
  @patch("ara_cli.artefact_autofix.fix_title_mismatch", return_value="fixed text")
117
158
  @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
118
159
  @patch("ara_cli.artefact_autofix.read_artefact", return_value="original text")
119
- def test_apply_autofix_for_title_mismatch_with_deterministic_flag(mock_read, mock_determine, mock_fix_title, mock_write, mock_run_agent, mock_artefact_type, mock_artefact_class):
160
+ def test_apply_autofix_for_title_mismatch_with_deterministic_flag(
161
+ mock_read,
162
+ mock_determine,
163
+ mock_fix_title,
164
+ mock_write,
165
+ mock_run_agent,
166
+ mock_artefact_type,
167
+ mock_artefact_class,
168
+ mock_classified_artefact_info,
169
+ ):
120
170
  """Tests that a deterministic fix is applied when the flag is True."""
121
171
  mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
122
172
  reason = "Filename-Title Mismatch: some details"
123
-
124
- result = apply_autofix("file.feature", "feature", reason, deterministic=True, non_deterministic=False)
173
+
174
+ result = apply_autofix(
175
+ "file.feature",
176
+ "feature",
177
+ reason,
178
+ deterministic=True,
179
+ non_deterministic=False,
180
+ classified_artefact_info=mock_classified_artefact_info,
181
+ )
125
182
 
126
183
  assert result is True
127
- mock_fix_title.assert_called_once_with("file.feature", "original text", mock_artefact_class)
184
+ mock_fix_title.assert_called_once_with(
185
+ file_path="file.feature",
186
+ artefact_text="original text",
187
+ artefact_class=mock_artefact_class,
188
+ classified_artefact_info=mock_classified_artefact_info,
189
+ )
128
190
  mock_write.assert_called_once_with("file.feature", "fixed text")
129
191
  mock_run_agent.assert_not_called()
130
192
 
193
+
131
194
  @patch("ara_cli.artefact_autofix.run_agent")
132
195
  @patch("ara_cli.artefact_autofix.write_corrected_artefact")
133
196
  @patch("ara_cli.artefact_autofix.fix_title_mismatch")
134
197
  @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
135
198
  @patch("ara_cli.artefact_autofix.read_artefact", return_value="original text")
136
- def test_apply_autofix_skips_title_mismatch_without_deterministic_flag(mock_read, mock_determine, mock_fix_title, mock_write, mock_run_agent, mock_artefact_type, mock_artefact_class):
199
+ def test_apply_autofix_skips_title_mismatch_without_deterministic_flag(
200
+ mock_read,
201
+ mock_determine,
202
+ mock_fix_title,
203
+ mock_write,
204
+ mock_run_agent,
205
+ mock_artefact_type,
206
+ mock_artefact_class,
207
+ mock_classified_artefact_info,
208
+ ):
137
209
  """Tests that a deterministic fix is skipped when the flag is False."""
138
210
  mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
139
211
  reason = "Filename-Title Mismatch: some details"
140
-
141
- result = apply_autofix("file.feature", "feature", reason, deterministic=False, non_deterministic=True)
212
+
213
+ result = apply_autofix(
214
+ "file.feature",
215
+ "feature",
216
+ reason,
217
+ deterministic=False,
218
+ non_deterministic=True,
219
+ classified_artefact_info=mock_classified_artefact_info,
220
+ )
142
221
 
143
222
  assert result is False
144
223
  mock_fix_title.assert_not_called()
145
224
  mock_write.assert_not_called()
146
225
  mock_run_agent.assert_not_called()
147
226
 
227
+
148
228
  @patch("ara_cli.artefact_autofix.write_corrected_artefact")
149
229
  @patch("ara_cli.artefact_autofix.run_agent")
150
230
  @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
151
231
  @patch("ara_cli.artefact_autofix.read_artefact", return_value="original text")
152
- def test_apply_autofix_for_llm_fix_with_non_deterministic_flag(mock_read, mock_determine, mock_run_agent, mock_write, mock_artefact_type, mock_artefact_class):
232
+ def test_apply_autofix_for_llm_fix_with_non_deterministic_flag(
233
+ mock_read,
234
+ mock_determine,
235
+ mock_run_agent,
236
+ mock_write,
237
+ mock_artefact_type,
238
+ mock_artefact_class,
239
+ mock_classified_artefact_info,
240
+ ):
153
241
  """Tests that an LLM fix is applied when the non-deterministic flag is True."""
154
242
  mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
155
243
  mock_run_agent.return_value = mock_artefact_class
156
244
  reason = "Pydantic validation error"
157
245
 
158
- result = apply_autofix("file.feature", "feature", reason, deterministic=False, non_deterministic=True)
246
+ result = apply_autofix(
247
+ "file.feature",
248
+ "feature",
249
+ reason,
250
+ deterministic=False,
251
+ non_deterministic=True,
252
+ classified_artefact_info=mock_classified_artefact_info,
253
+ )
159
254
 
160
255
  assert result is True
161
256
  mock_run_agent.assert_called_once()
162
257
  mock_write.assert_called_once_with("file.feature", "llm corrected content")
163
258
 
259
+
164
260
  @patch("ara_cli.artefact_autofix.write_corrected_artefact")
165
261
  @patch("ara_cli.artefact_autofix.run_agent")
166
262
  @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
167
263
  @patch("ara_cli.artefact_autofix.read_artefact", return_value="original text")
168
- def test_apply_autofix_skips_llm_fix_without_non_deterministic_flag(mock_read, mock_determine, mock_run_agent, mock_write, mock_artefact_type, mock_artefact_class):
264
+ def test_apply_autofix_skips_llm_fix_without_non_deterministic_flag(
265
+ mock_read,
266
+ mock_determine,
267
+ mock_run_agent,
268
+ mock_write,
269
+ mock_artefact_type,
270
+ mock_artefact_class,
271
+ mock_classified_artefact_info,
272
+ ):
169
273
  """Tests that an LLM fix is skipped when the non-deterministic flag is False."""
170
274
  mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
171
275
  reason = "Pydantic validation error"
172
276
 
173
- result = apply_autofix("file.feature", "feature", reason, deterministic=True, non_deterministic=False)
277
+ result = apply_autofix(
278
+ "file.feature",
279
+ "feature",
280
+ reason,
281
+ deterministic=True,
282
+ non_deterministic=False,
283
+ classified_artefact_info=mock_classified_artefact_info,
284
+ )
174
285
 
175
286
  assert result is False
176
287
  mock_run_agent.assert_not_called()
177
288
  mock_write.assert_not_called()
178
289
 
290
+
179
291
  @patch("ara_cli.artefact_autofix.run_agent", side_effect=Exception("LLM failed"))
180
292
  @patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
181
293
  @patch("ara_cli.artefact_autofix.read_artefact", return_value="original text")
182
- def test_apply_autofix_llm_exception(mock_read, mock_determine, mock_run_agent, capsys, mock_artefact_type, mock_artefact_class):
294
+ def test_apply_autofix_llm_exception(
295
+ mock_read,
296
+ mock_determine,
297
+ mock_run_agent,
298
+ capsys,
299
+ mock_artefact_type,
300
+ mock_artefact_class,
301
+ mock_classified_artefact_info,
302
+ ):
183
303
  """Tests that an exception during an LLM fix is handled gracefully."""
184
304
  mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
185
305
  reason = "Pydantic validation error"
186
306
 
187
- result = apply_autofix("file.feature", "feature", reason, deterministic=False, non_deterministic=True)
307
+ result = apply_autofix(
308
+ "file.feature",
309
+ "feature",
310
+ reason,
311
+ deterministic=False,
312
+ non_deterministic=True,
313
+ classified_artefact_info=mock_classified_artefact_info,
314
+ )
188
315
 
189
316
  assert result is False
190
- assert "LLM agent failed to fix artefact at file.feature: LLM failed" in capsys.readouterr().out
317
+ assert (
318
+ "LLM agent failed to fix artefact at file.feature: LLM failed"
319
+ in capsys.readouterr().out
320
+ )
321
+
191
322
 
192
323
  # === Other Tests ===
193
324
 
325
+
194
326
  def test_fix_title_mismatch_success(mock_artefact_class):
195
327
  artefact_text = "Feature: wrong title\nSome other content"
196
328
  file_path = "path/to/correct_title.feature"
197
-
329
+
198
330
  expected_text = "Feature: correct title\nSome other content"
199
-
331
+
200
332
  result = fix_title_mismatch(file_path, artefact_text, mock_artefact_class)
201
-
333
+
202
334
  assert result == expected_text
203
335
  mock_artefact_class._title_prefix.assert_called_once()
204
336
 
337
+
205
338
  def test_fix_title_mismatch_prefix_not_found(capsys, mock_artefact_class):
206
339
  artefact_text = "No title prefix here"
207
340
  file_path = "path/to/correct_title.feature"
208
341
 
209
342
  result = fix_title_mismatch(file_path, artefact_text, mock_artefact_class)
210
-
211
- assert result == artefact_text # Should return original text
343
+
344
+ assert result == artefact_text # Should return original text
212
345
  assert "Warning: Title prefix 'Feature:' not found" in capsys.readouterr().out
213
346
 
347
+
214
348
  @patch("pydantic_ai.Agent")
215
349
  def test_run_agent_exception_handling(mock_agent_class):
216
350
  mock_agent_instance = mock_agent_class.return_value