ara-cli 0.1.9.66__py3-none-any.whl → 0.1.9.68__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.
- ara_cli/ara_command_action.py +15 -5
- ara_cli/ara_command_parser.py +4 -1
- ara_cli/ara_config.py +9 -19
- ara_cli/artefact_autofix.py +73 -27
- ara_cli/artefact_models/artefact_model.py +1 -4
- ara_cli/artefact_scan.py +11 -1
- ara_cli/prompt_handler.py +2 -1
- ara_cli/version.py +1 -1
- {ara_cli-0.1.9.66.dist-info → ara_cli-0.1.9.68.dist-info}/METADATA +1 -1
- {ara_cli-0.1.9.66.dist-info → ara_cli-0.1.9.68.dist-info}/RECORD +34 -33
- {ara_cli-0.1.9.66.dist-info → ara_cli-0.1.9.68.dist-info}/top_level.txt +1 -0
- tests/__init__.py +0 -0
- tests/test_ara_autofix.py +219 -0
- {ara_cli/tests → tests}/test_artefact_renamer.py +3 -5
- {ara_cli/tests → tests}/test_artefact_scan.py +37 -10
- {ara_cli/tests → tests}/test_chat.py +39 -22
- {ara_cli/tests → tests}/test_file_creator.py +1 -1
- {ara_cli/tests → tests}/test_template_manager.py +37 -21
- ara_cli/tests/test_ara_autofix.py +0 -113
- /ara_cli/{tests → artefact_models}/__init__.py +0 -0
- {ara_cli-0.1.9.66.dist-info → ara_cli-0.1.9.68.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.9.66.dist-info → ara_cli-0.1.9.68.dist-info}/entry_points.txt +0 -0
- {ara_cli/tests → tests}/test_ara_command_action.py +0 -0
- {ara_cli/tests → tests}/test_ara_config.py +0 -0
- {ara_cli/tests → tests}/test_artefact_fuzzy_search.py +0 -0
- {ara_cli/tests → tests}/test_artefact_link_updater.py +0 -0
- {ara_cli/tests → tests}/test_artefact_lister.py +0 -0
- {ara_cli/tests → tests}/test_artefact_reader.py +0 -0
- {ara_cli/tests → tests}/test_classifier.py +0 -0
- {ara_cli/tests → tests}/test_directory_navigator.py +0 -0
- {ara_cli/tests → tests}/test_file_classifier.py +0 -0
- {ara_cli/tests → tests}/test_file_lister.py +0 -0
- {ara_cli/tests → tests}/test_list_filter.py +0 -0
- {ara_cli/tests → tests}/test_tag_extractor.py +0 -0
- {ara_cli/tests → tests}/test_update_config_prompt.py +0 -0
ara_cli/ara_command_action.py
CHANGED
|
@@ -576,22 +576,32 @@ def scan_action(args):
|
|
|
576
576
|
def autofix_action(args):
|
|
577
577
|
from ara_cli.artefact_autofix import parse_report, apply_autofix, read_report_file
|
|
578
578
|
|
|
579
|
-
|
|
579
|
+
# If the user passes --non-deterministic, only_deterministic_fix becomes False.
|
|
580
|
+
# If the user passes --deterministic, only_non_deterministic_fix becomes False.
|
|
581
|
+
# If no flags are passed, both are True, and all fixes are attempted.
|
|
582
|
+
run_deterministic = not args.non_deterministic
|
|
583
|
+
run_non_deterministic = not args.deterministic
|
|
580
584
|
|
|
585
|
+
content = read_report_file()
|
|
581
586
|
if not content:
|
|
582
587
|
return False
|
|
583
588
|
|
|
584
589
|
issues = parse_report(content)
|
|
585
|
-
|
|
586
590
|
if not issues:
|
|
587
591
|
print("No issues found in the report. Nothing to fix.")
|
|
588
592
|
return
|
|
589
593
|
|
|
590
|
-
# print("
|
|
594
|
+
# print("\nStarting autofix process...")
|
|
591
595
|
for classifier, files in issues.items():
|
|
592
596
|
print(f"\nClassifier: {classifier}")
|
|
593
597
|
for file_path, reason in files:
|
|
594
|
-
|
|
595
|
-
|
|
598
|
+
apply_autofix(
|
|
599
|
+
file_path,
|
|
600
|
+
classifier,
|
|
601
|
+
reason,
|
|
602
|
+
deterministic=run_deterministic,
|
|
603
|
+
non_deterministic=run_non_deterministic
|
|
604
|
+
)
|
|
596
605
|
|
|
597
606
|
print("\nAutofix process completed. Please review the changes.")
|
|
607
|
+
|
ara_cli/ara_command_parser.py
CHANGED
|
@@ -229,7 +229,10 @@ def scan_parser(subparsers):
|
|
|
229
229
|
|
|
230
230
|
|
|
231
231
|
def autofix_parser(subparsers):
|
|
232
|
-
subparsers.add_parser("autofix", help="Fix ARA tree with llm models for scanned artefacts with ara scan command.")
|
|
232
|
+
autofix_parser = subparsers.add_parser("autofix", help="Fix ARA tree with llm models for scanned artefacts with ara scan command.")
|
|
233
|
+
determinism_group = autofix_parser.add_mutually_exclusive_group()
|
|
234
|
+
determinism_group.add_argument("--deterministic", "-d", action="store_true", help="Run only deterministic fixes e.g Title-FileName Mismatch fix")
|
|
235
|
+
determinism_group.add_argument("--non-deterministic", "-nd", action="store_true", help="Run only non-deterministic fixes")
|
|
233
236
|
|
|
234
237
|
|
|
235
238
|
class CustomHelpFormatter(argparse.HelpFormatter):
|
ara_cli/ara_config.py
CHANGED
|
@@ -42,9 +42,9 @@ class ARAconfig(BaseModel):
|
|
|
42
42
|
"model": "openai/gpt-4o",
|
|
43
43
|
"temperature": 0.8
|
|
44
44
|
},
|
|
45
|
-
"gpt-4.
|
|
45
|
+
"gpt-4.1": {
|
|
46
46
|
"provider": "openai",
|
|
47
|
-
"model": "openai/gpt-4.
|
|
47
|
+
"model": "openai/gpt-4.1",
|
|
48
48
|
"temperature": 0.8,
|
|
49
49
|
},
|
|
50
50
|
"o3-mini": {
|
|
@@ -52,9 +52,14 @@ class ARAconfig(BaseModel):
|
|
|
52
52
|
"model": "openai/o3-mini",
|
|
53
53
|
"temperature": 1.0,
|
|
54
54
|
},
|
|
55
|
-
"
|
|
55
|
+
"opus-4": {
|
|
56
|
+
"provider": "anthropic",
|
|
57
|
+
"model": "anthropic/claude-opus-4-20250514",
|
|
58
|
+
"temperature": 0.8,
|
|
59
|
+
},
|
|
60
|
+
"sonnet-4": {
|
|
56
61
|
"provider": "anthropic",
|
|
57
|
-
"model": "anthropic/claude-
|
|
62
|
+
"model": "anthropic/claude-sonnet-4-20250514",
|
|
58
63
|
"temperature": 0.8,
|
|
59
64
|
},
|
|
60
65
|
"together-ai-llama-2": {
|
|
@@ -67,21 +72,6 @@ class ARAconfig(BaseModel):
|
|
|
67
72
|
"model": "groq/llama3-70b-8192",
|
|
68
73
|
"temperature": 0.8,
|
|
69
74
|
},
|
|
70
|
-
"opus-4": {
|
|
71
|
-
"provider": "anthropic",
|
|
72
|
-
"model": "anthropic/claude-opus-4-20250514",
|
|
73
|
-
"temperature": 0.8,
|
|
74
|
-
},
|
|
75
|
-
"sonnet-3.7": {
|
|
76
|
-
"provider": "anthropic",
|
|
77
|
-
"model": "anthropic/claude-3-7-sonnet-20250219",
|
|
78
|
-
"temperature": 0.8,
|
|
79
|
-
},
|
|
80
|
-
"sonnet-4": {
|
|
81
|
-
"provider": "anthropic",
|
|
82
|
-
"model": "anthropic/claude-sonnet-4-20250514",
|
|
83
|
-
"temperature": 0.8,
|
|
84
|
-
},
|
|
85
75
|
}
|
|
86
76
|
default_llm: Optional[str] = "gpt-4o"
|
|
87
77
|
|
ara_cli/artefact_autofix.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Dict, List, Tuple
|
|
3
|
+
|
|
1
4
|
def read_report_file():
|
|
2
5
|
file_path = "incompatible_artefacts_report.md"
|
|
3
6
|
try:
|
|
@@ -5,12 +8,11 @@ def read_report_file():
|
|
|
5
8
|
content = f.read()
|
|
6
9
|
except OSError:
|
|
7
10
|
print('Artefact scan results file not found. Did you run the "ara scan" command?')
|
|
8
|
-
return
|
|
9
|
-
|
|
11
|
+
return None
|
|
10
12
|
return content
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
def parse_report(content: str) ->
|
|
15
|
+
def parse_report(content: str) -> Dict[str, List[Tuple[str, str]]]:
|
|
14
16
|
"""
|
|
15
17
|
Parses the incompatible artefacts report and returns structured data.
|
|
16
18
|
Returns a dictionary where keys are artefact classifiers, and values are lists of (file_path, reason) tuples.
|
|
@@ -20,31 +22,27 @@ def parse_report(content: str) -> dict:
|
|
|
20
22
|
current_classifier = None
|
|
21
23
|
|
|
22
24
|
if not lines or lines[0] != "# Artefact Check Report":
|
|
23
|
-
return issues
|
|
25
|
+
return issues
|
|
24
26
|
|
|
25
27
|
if len(lines) >= 3 and lines[2] == "No problems found.":
|
|
26
|
-
return issues
|
|
28
|
+
return issues
|
|
27
29
|
|
|
28
|
-
for line in lines[1:]:
|
|
30
|
+
for line in lines[1:]:
|
|
29
31
|
line = line.strip()
|
|
30
32
|
if not line:
|
|
31
33
|
continue
|
|
32
34
|
|
|
33
|
-
# Classifier başlığı tespiti (## ile başlayan)
|
|
34
35
|
if line.startswith("## "):
|
|
35
36
|
current_classifier = line[3:].strip()
|
|
36
37
|
issues[current_classifier] = []
|
|
37
38
|
|
|
38
|
-
# Dosya listesi tespiti (- ile başlayan)
|
|
39
39
|
elif line.startswith("- ") and current_classifier is not None:
|
|
40
|
-
# Format: "- `file_path`: reason"
|
|
41
40
|
parts = line.split("`", 2)
|
|
42
41
|
if len(parts) < 3:
|
|
43
|
-
continue
|
|
42
|
+
continue
|
|
44
43
|
|
|
45
44
|
file_path = parts[1]
|
|
46
|
-
reason = parts[2].split(
|
|
47
|
-
":", 1)[1].strip() if ":" in parts[2] else ""
|
|
45
|
+
reason = parts[2].split(":", 1)[1].strip() if ":" in parts[2] else ""
|
|
48
46
|
issues[current_classifier].append((file_path, reason))
|
|
49
47
|
|
|
50
48
|
return issues
|
|
@@ -53,7 +51,7 @@ def parse_report(content: str) -> dict:
|
|
|
53
51
|
def read_artefact(file_path):
|
|
54
52
|
"""Reads the artefact text from the given file path."""
|
|
55
53
|
try:
|
|
56
|
-
with open(file_path, 'r') as file:
|
|
54
|
+
with open(file_path, 'r', encoding="utf-8") as file:
|
|
57
55
|
return file.read()
|
|
58
56
|
except FileNotFoundError:
|
|
59
57
|
print(f"File not found: {file_path}")
|
|
@@ -82,7 +80,7 @@ def construct_prompt(artefact_type, reason, file_path, artefact_text):
|
|
|
82
80
|
from ara_cli.artefact_models.artefact_model import ArtefactType
|
|
83
81
|
|
|
84
82
|
prompt = (
|
|
85
|
-
f"Correct the following {artefact_type} artefact to fix the issue: {reason}. "
|
|
83
|
+
f"Correct the following {artefact_type.value} artefact to fix the issue: {reason}. "
|
|
86
84
|
"Provide the corrected artefact. Do not reformulate the artefact, "
|
|
87
85
|
"just fix the pydantic model errors, use correct grammar. "
|
|
88
86
|
"You should follow the name of the file "
|
|
@@ -113,33 +111,81 @@ def run_agent(prompt, artefact_class):
|
|
|
113
111
|
from pydantic_ai import Agent
|
|
114
112
|
# gpt-4o
|
|
115
113
|
# anthropic:claude-3-7-sonnet-20250219
|
|
116
|
-
|
|
114
|
+
# anthropic:claude-4-sonnet-20250514
|
|
115
|
+
agent = Agent(model="anthropic:claude-4-sonnet-20250514",
|
|
117
116
|
result_type=artefact_class, instrument=True)
|
|
118
117
|
result = agent.run_sync(prompt)
|
|
119
118
|
return result.data
|
|
120
119
|
|
|
121
120
|
|
|
122
121
|
def write_corrected_artefact(file_path, corrected_text):
|
|
123
|
-
with open(file_path, 'w') as file:
|
|
122
|
+
with open(file_path, 'w', encoding="utf-8") as file:
|
|
124
123
|
file.write(corrected_text)
|
|
125
124
|
print(f"Fixed artefact at {file_path}")
|
|
126
125
|
|
|
127
126
|
|
|
128
|
-
def
|
|
127
|
+
def fix_title_mismatch(file_path: str, artefact_text: str, artefact_class) -> str:
|
|
128
|
+
"""
|
|
129
|
+
Deterministically fixes the title in the artefact text to match the filename.
|
|
130
|
+
"""
|
|
131
|
+
base_name = os.path.basename(file_path)
|
|
132
|
+
correct_title_underscores, _ = os.path.splitext(base_name)
|
|
133
|
+
correct_title_spaces = correct_title_underscores.replace('_', ' ')
|
|
134
|
+
|
|
135
|
+
title_prefix = artefact_class._title_prefix()
|
|
136
|
+
|
|
137
|
+
lines = artefact_text.splitlines()
|
|
138
|
+
new_lines = []
|
|
139
|
+
title_found_and_replaced = False
|
|
140
|
+
|
|
141
|
+
for line in lines:
|
|
142
|
+
if not title_found_and_replaced and line.strip().startswith(title_prefix):
|
|
143
|
+
new_lines.append(f"{title_prefix} {correct_title_spaces}")
|
|
144
|
+
title_found_and_replaced = True
|
|
145
|
+
else:
|
|
146
|
+
new_lines.append(line)
|
|
147
|
+
|
|
148
|
+
if not title_found_and_replaced:
|
|
149
|
+
print(f"Warning: Title prefix '{title_prefix}' not found in {file_path}. Title could not be fixed.")
|
|
150
|
+
return artefact_text
|
|
151
|
+
|
|
152
|
+
return "\n".join(new_lines)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def apply_autofix(file_path: str, classifier: str, reason: str, deterministic: bool, non_deterministic: bool) -> bool:
|
|
129
156
|
artefact_text = read_artefact(file_path)
|
|
130
157
|
if artefact_text is None:
|
|
131
|
-
return
|
|
158
|
+
return False
|
|
132
159
|
|
|
133
|
-
artefact_type, artefact_class = determine_artefact_type_and_class(
|
|
134
|
-
classifier)
|
|
160
|
+
artefact_type, artefact_class = determine_artefact_type_and_class(classifier)
|
|
135
161
|
if artefact_type is None or artefact_class is None:
|
|
136
|
-
return
|
|
162
|
+
return False
|
|
137
163
|
|
|
138
|
-
|
|
164
|
+
is_deterministic_issue = "Filename-Title Mismatch" in reason
|
|
139
165
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
corrected_text =
|
|
166
|
+
if deterministic and is_deterministic_issue:
|
|
167
|
+
print(f"Attempting deterministic fix for {file_path}...")
|
|
168
|
+
corrected_text = fix_title_mismatch(file_path, artefact_text, artefact_class)
|
|
143
169
|
write_corrected_artefact(file_path, corrected_text)
|
|
144
|
-
|
|
145
|
-
|
|
170
|
+
return True
|
|
171
|
+
|
|
172
|
+
# Attempt non-deterministic fix if requested and the issue is NOT deterministic
|
|
173
|
+
if non_deterministic and not is_deterministic_issue:
|
|
174
|
+
print(f"Attempting non-deterministic (LLM) fix for {file_path}...")
|
|
175
|
+
prompt = construct_prompt(artefact_type, reason, file_path, artefact_text)
|
|
176
|
+
try:
|
|
177
|
+
corrected_artefact = run_agent(prompt, artefact_class)
|
|
178
|
+
corrected_text = corrected_artefact.serialize()
|
|
179
|
+
write_corrected_artefact(file_path, corrected_text)
|
|
180
|
+
return True
|
|
181
|
+
except Exception as e:
|
|
182
|
+
print(f"LLM agent failed to fix artefact at {file_path}: {e}")
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
# Log if a fix was skipped due to flags
|
|
186
|
+
if is_deterministic_issue and not deterministic:
|
|
187
|
+
print(f"Skipping deterministic fix for {file_path} as per request.")
|
|
188
|
+
elif not is_deterministic_issue and not non_deterministic:
|
|
189
|
+
print(f"Skipping non-deterministic fix for {file_path} as per request.")
|
|
190
|
+
|
|
191
|
+
return False
|
|
@@ -49,16 +49,13 @@ class Contribution(BaseModel):
|
|
|
49
49
|
|
|
50
50
|
@model_validator(mode="after")
|
|
51
51
|
def validate_parent(self) -> Self:
|
|
52
|
+
|
|
52
53
|
artefact_name = self.artefact_name
|
|
53
54
|
classifier = self.classifier
|
|
54
55
|
rule = self.rule
|
|
55
56
|
|
|
56
57
|
if artefact_name:
|
|
57
58
|
artefact_name = replace_space_with_underscore(artefact_name)
|
|
58
|
-
if artefact_name and classifier:
|
|
59
|
-
artefact_name = artefact_name.removesuffix(f"_{classifier}")
|
|
60
|
-
artefact_name = artefact_name.removesuffix(f"_{classifier.capitalize()}")
|
|
61
|
-
self.artefact_name = artefact_name
|
|
62
59
|
if not artefact_name or not classifier:
|
|
63
60
|
self.artefact_name = None
|
|
64
61
|
self.classifier = None
|
ara_cli/artefact_scan.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from textwrap import indent
|
|
2
|
+
import os
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def check_file(file_path, artefact_class):
|
|
@@ -9,7 +10,16 @@ def check_file(file_path, artefact_class):
|
|
|
9
10
|
except OSError as e:
|
|
10
11
|
return False, f"File error: {e}"
|
|
11
12
|
try:
|
|
12
|
-
artefact_class.deserialize(content)
|
|
13
|
+
artefact_instance = artefact_class.deserialize(content)
|
|
14
|
+
|
|
15
|
+
base_name = os.path.basename(file_path)
|
|
16
|
+
file_name_without_ext, _ = os.path.splitext(base_name)
|
|
17
|
+
|
|
18
|
+
if artefact_instance.title != file_name_without_ext:
|
|
19
|
+
reason = (f"Filename-Title Mismatch: The file name '{file_name_without_ext}' "
|
|
20
|
+
f"does not match the artefact title '{artefact_instance.title}'.")
|
|
21
|
+
return False, reason
|
|
22
|
+
|
|
13
23
|
return True, None
|
|
14
24
|
except (ValidationError, ValueError, AssertionError) as e:
|
|
15
25
|
return False, str(e)
|
ara_cli/prompt_handler.py
CHANGED
ara_cli/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# version.py
|
|
2
|
-
__version__ = "0.1.9.
|
|
2
|
+
__version__ = "0.1.9.68" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
|
|
@@ -1,9 +1,9 @@
|
|
|
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=
|
|
4
|
-
ara_cli/ara_command_parser.py,sha256=
|
|
5
|
-
ara_cli/ara_config.py,sha256=
|
|
6
|
-
ara_cli/artefact_autofix.py,sha256
|
|
3
|
+
ara_cli/ara_command_action.py,sha256=dD97IeH7x5udKC_APk5TbuVXPiyU2XSBV2uqy52nQ9A,21625
|
|
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
|
|
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
9
|
ara_cli/artefact_fuzzy_search.py,sha256=BBDe-IP75sWZjG6nTNFtVljjL01JlQUy5ccJBZ6Trow,2429
|
|
@@ -11,7 +11,7 @@ ara_cli/artefact_link_updater.py,sha256=itMS_Z64jE8bBly9WA01z8PqkBeNW6ntTO7ryMeC
|
|
|
11
11
|
ara_cli/artefact_lister.py,sha256=jhk4n4eqp7hDIq07q43QzS7-36BM3OfZ4EABxCeOGcw,4764
|
|
12
12
|
ara_cli/artefact_reader.py,sha256=_RqBY1f1DWH2DThXETYuHeTB2ip0wgYuGvHYI_D6EJ4,8062
|
|
13
13
|
ara_cli/artefact_renamer.py,sha256=loIn1DF9kVnjhH7wP1v5qUvt3s0uKeWXuQPrHXenQGE,4025
|
|
14
|
-
ara_cli/artefact_scan.py,sha256=
|
|
14
|
+
ara_cli/artefact_scan.py,sha256=J3aCAOltVr1oS6Lwnv51gtZC_G8faORGTccFY3kkBX4,2368
|
|
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
|
|
@@ -25,16 +25,17 @@ 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=
|
|
28
|
+
ara_cli/prompt_handler.py,sha256=qHq2COt6rIby3tO49IXOt-CLnOlNTgCTFkAUjxrfaTc,17459
|
|
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=
|
|
34
|
+
ara_cli/version.py,sha256=qsRulH7BZ8x-z1VK67VqaUHu1vUfJ7cgIUVvWmlyke4,146
|
|
35
|
+
ara_cli/artefact_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
36
|
ara_cli/artefact_models/artefact_load.py,sha256=dNcwZDW2Dk0bts9YnPZ0ESmWD2NbsLIvl4Z-qQeGmTQ,401
|
|
36
37
|
ara_cli/artefact_models/artefact_mapping.py,sha256=8aD0spBjkJ8toMAmFawc6UTUxB6-tEEViZXv2I-r88Q,1874
|
|
37
|
-
ara_cli/artefact_models/artefact_model.py,sha256=
|
|
38
|
+
ara_cli/artefact_models/artefact_model.py,sha256=nZZS6WyVuODblLnVTH6VZli--eFrDiwNMtjwHTs2KBI,14703
|
|
38
39
|
ara_cli/artefact_models/artefact_templates.py,sha256=Vd7SwoRVKNGKZmxBKS6f9FE1ThUOCqZLScu0ClPfIu8,8321
|
|
39
40
|
ara_cli/artefact_models/businessgoal_artefact_model.py,sha256=jqYFMXjWle0YW9RvcFLDBAwy61bdT5VuDT_6lTOFzMw,4853
|
|
40
41
|
ara_cli/artefact_models/capability_artefact_model.py,sha256=SZqHx4O2mj4urn77Stnj4_Jxtlq3-LgBBU9SMkByppI,3079
|
|
@@ -130,28 +131,28 @@ ara_cli/templates/specification_breakdown_files/template.step.exploration.md,sha
|
|
|
130
131
|
ara_cli/templates/specification_breakdown_files/template.step.md,sha256=nzDRl9Xo-fJKeDKxIRh9njidV1SOd2ZOPi7eM3YG9DU,1052
|
|
131
132
|
ara_cli/templates/specification_breakdown_files/template.technology.exploration.md,sha256=zQyiJcmbUfXdte-5uZwZUpT6ey0zwfZ00P4VwI97jQk,2274
|
|
132
133
|
ara_cli/templates/specification_breakdown_files/template.technology.md,sha256=bySiksz-8xtq0Nnj4svqe2MgUftWrVkbK9AcrDUE3KY,952
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
ara_cli-0.1.9.
|
|
154
|
-
ara_cli-0.1.9.
|
|
155
|
-
ara_cli-0.1.9.
|
|
156
|
-
ara_cli-0.1.9.
|
|
157
|
-
ara_cli-0.1.9.
|
|
134
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
135
|
+
tests/test_ara_autofix.py,sha256=EFVzhVTNPDRVxpoK0CY6OAM88EjZTRXh-uxFGS1AVWk,10695
|
|
136
|
+
tests/test_ara_command_action.py,sha256=Y5MrG6VjXgebliKfdFaCaS8i3GoZCGSLpj3AWCbL5Lk,25695
|
|
137
|
+
tests/test_ara_config.py,sha256=1LWby_iSestTIIqK-1clggL8kmbGGbtlYfsxAHaMMF8,2232
|
|
138
|
+
tests/test_artefact_fuzzy_search.py,sha256=5Sh3_l9QK8-WHn6JpGPU1b6h4QEnl2JoMq1Tdp2cj1U,1261
|
|
139
|
+
tests/test_artefact_link_updater.py,sha256=gN5KFF1uY7OoBh8Mr5jWpqXp02YCU5OSIpSU76Rm4Gs,2137
|
|
140
|
+
tests/test_artefact_lister.py,sha256=VCEOCgDgnAOeUUgIoGAbWgz60hf9UT-tdHg18LGfB34,22656
|
|
141
|
+
tests/test_artefact_reader.py,sha256=660K-d8ed-j8hulsUB_7baPD2-hhbg9TffUR5yVc4Uo,927
|
|
142
|
+
tests/test_artefact_renamer.py,sha256=N9JnnKBEJChTYEdltHoiDnn5UCNQUPj9H9YqOvLzbH0,3458
|
|
143
|
+
tests/test_artefact_scan.py,sha256=7w4wqCj8VEwk676JiSqh0FCAOT-kFq624u2JDrhzpuw,5860
|
|
144
|
+
tests/test_chat.py,sha256=-00mni6Kik_RO8BGUpWqaL4S0wt2MbUBi5jD06dSHJM,47538
|
|
145
|
+
tests/test_classifier.py,sha256=grYGPksydNdPsaEBQxYHZTuTdcJWz7VQtikCKA6BNaQ,1920
|
|
146
|
+
tests/test_directory_navigator.py,sha256=7G0MVrBbtBvbrFUpL0zb_9EkEWi1dulWuHsrQxMJxDY,140
|
|
147
|
+
tests/test_file_classifier.py,sha256=hbGp0-_A_LgQ0pGv1jWDEIyCgvDyfChcvvVfbxjNY2U,10938
|
|
148
|
+
tests/test_file_creator.py,sha256=D3G7MbgE0m8JmZihxnTryxLco6iZdbV--2CGc0L20FM,2109
|
|
149
|
+
tests/test_file_lister.py,sha256=f6B_vIv-wAulKH2ZGgNg4SG79XqGGbfwoIvZlbEnYyM,4306
|
|
150
|
+
tests/test_list_filter.py,sha256=gSRKirTtFuhRS3QlFHqWl89WvCvAdVEnFsCWTYmgB2o,7928
|
|
151
|
+
tests/test_tag_extractor.py,sha256=nSiAYlTKZ7TLAOtcJpwK5zTWHhFYU0tI5xKnivLc1dU,2712
|
|
152
|
+
tests/test_template_manager.py,sha256=q-LMHRG4rHkD6ON6YW4cpZxUx9hul6Or8wVVRC2kb-8,4099
|
|
153
|
+
tests/test_update_config_prompt.py,sha256=vSsLvc18HZdVjVM93qXWVbJt752xTLL6VGjSVCrPufk,6729
|
|
154
|
+
ara_cli-0.1.9.68.dist-info/METADATA,sha256=3k2Jl8M-AkkRS7sc_8YQWmNfHCxhVtdRAT1bdKYVxvk,415
|
|
155
|
+
ara_cli-0.1.9.68.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
156
|
+
ara_cli-0.1.9.68.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
|
|
157
|
+
ara_cli-0.1.9.68.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
|
|
158
|
+
ara_cli-0.1.9.68.dist-info/RECORD,,
|
tests/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from unittest.mock import patch, mock_open, MagicMock
|
|
3
|
+
from ara_cli.artefact_autofix import (
|
|
4
|
+
read_report_file,
|
|
5
|
+
parse_report,
|
|
6
|
+
apply_autofix,
|
|
7
|
+
read_artefact,
|
|
8
|
+
determine_artefact_type_and_class,
|
|
9
|
+
run_agent,
|
|
10
|
+
write_corrected_artefact,
|
|
11
|
+
construct_prompt,
|
|
12
|
+
fix_title_mismatch
|
|
13
|
+
)
|
|
14
|
+
from ara_cli.artefact_models.artefact_model import ArtefactType
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def mock_artefact_type():
|
|
18
|
+
"""Provides a mock for the ArtefactType enum member."""
|
|
19
|
+
mock_type = MagicMock()
|
|
20
|
+
mock_type.value = "feature"
|
|
21
|
+
return mock_type
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def mock_artefact_class():
|
|
25
|
+
"""Provides a mock for the Artefact class."""
|
|
26
|
+
mock_class = MagicMock()
|
|
27
|
+
mock_class._title_prefix.return_value = "Feature:"
|
|
28
|
+
# Mock the serialize method for the agent tests
|
|
29
|
+
mock_class.serialize.return_value = "llm corrected content"
|
|
30
|
+
return mock_class
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_read_report_file_success():
|
|
34
|
+
"""Tests successful reading of the report file."""
|
|
35
|
+
mock_content = "# Artefact Check Report\n- `file.feature`: reason"
|
|
36
|
+
with patch("builtins.open", mock_open(read_data=mock_content)) as m:
|
|
37
|
+
content = read_report_file()
|
|
38
|
+
assert content == mock_content
|
|
39
|
+
m.assert_called_once_with("incompatible_artefacts_report.md", "r", encoding="utf-8")
|
|
40
|
+
|
|
41
|
+
def test_read_report_file_not_found(capsys):
|
|
42
|
+
with patch("builtins.open", side_effect=OSError("File not found")):
|
|
43
|
+
content = read_report_file()
|
|
44
|
+
assert content is None
|
|
45
|
+
assert "Artefact scan results file not found" in capsys.readouterr().out
|
|
46
|
+
|
|
47
|
+
def test_parse_report_with_issues():
|
|
48
|
+
content = "# Artefact Check Report\n\n## feature\n- `path/to/file.feature`: A reason\n"
|
|
49
|
+
expected = {"feature": [("path/to/file.feature", "A reason")]}
|
|
50
|
+
assert parse_report(content) == expected
|
|
51
|
+
|
|
52
|
+
def test_parse_report_no_issues():
|
|
53
|
+
content = "# Artefact Check Report\n\nNo problems found.\n"
|
|
54
|
+
assert parse_report(content) == {}
|
|
55
|
+
|
|
56
|
+
def test_parse_report_invalid_format():
|
|
57
|
+
assert parse_report("This is not a valid report") == {}
|
|
58
|
+
|
|
59
|
+
def test_parse_report_invalid_line_format():
|
|
60
|
+
content = "# Artefact Check Report\n\n## feature\n- an invalid line\n"
|
|
61
|
+
assert parse_report(content) == {"feature": []}
|
|
62
|
+
|
|
63
|
+
def test_read_artefact_success():
|
|
64
|
+
mock_content = "Feature: My Feature"
|
|
65
|
+
with patch("builtins.open", mock_open(read_data=mock_content)) as m:
|
|
66
|
+
content = read_artefact("file.feature")
|
|
67
|
+
assert content == mock_content
|
|
68
|
+
m.assert_called_once_with("file.feature", 'r', encoding="utf-8")
|
|
69
|
+
|
|
70
|
+
def test_read_artefact_file_not_found(capsys):
|
|
71
|
+
with patch("builtins.open", side_effect=FileNotFoundError):
|
|
72
|
+
result = read_artefact("nonexistent.feature")
|
|
73
|
+
assert result is None
|
|
74
|
+
assert "File not found: nonexistent.feature" in capsys.readouterr().out
|
|
75
|
+
|
|
76
|
+
@patch("ara_cli.artefact_models.artefact_mapping.artefact_type_mapping")
|
|
77
|
+
def test_determine_artefact_type_and_class_no_class_found(mock_mapping, capsys):
|
|
78
|
+
mock_mapping.get.return_value = None
|
|
79
|
+
# The function returns (None, None) if the class is not in the mapping.
|
|
80
|
+
artefact_type, artefact_class = determine_artefact_type_and_class("feature")
|
|
81
|
+
assert artefact_type is None
|
|
82
|
+
assert artefact_class is None
|
|
83
|
+
# The print statement inside the function is called before returning, so this check is valid.
|
|
84
|
+
assert "No artefact class found for" in capsys.readouterr().out
|
|
85
|
+
|
|
86
|
+
@patch("ara_cli.artefact_models.artefact_model.ArtefactType", side_effect=ValueError)
|
|
87
|
+
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")
|
|
89
|
+
assert artefact_type is None
|
|
90
|
+
assert artefact_class is None
|
|
91
|
+
assert "Invalid classifier: invalid_classifier" in capsys.readouterr().out
|
|
92
|
+
|
|
93
|
+
def test_write_corrected_artefact():
|
|
94
|
+
with patch("builtins.open", mock_open()) as m:
|
|
95
|
+
write_corrected_artefact("file.feature", "corrected content")
|
|
96
|
+
m.assert_called_once_with("file.feature", 'w', encoding="utf-8")
|
|
97
|
+
m().write.assert_called_once_with("corrected content")
|
|
98
|
+
|
|
99
|
+
def test_construct_prompt_for_task():
|
|
100
|
+
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
|
|
102
|
+
|
|
103
|
+
@patch("ara_cli.artefact_autofix.run_agent")
|
|
104
|
+
@patch("ara_cli.artefact_autofix.determine_artefact_type_and_class", return_value=(None, None))
|
|
105
|
+
@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):
|
|
107
|
+
"""Tests that apply_autofix exits early if the classifier is invalid."""
|
|
108
|
+
result = apply_autofix("file.feature", "invalid", "reason", deterministic=True, non_deterministic=True)
|
|
109
|
+
assert result is False
|
|
110
|
+
mock_read.assert_called_once_with("file.feature")
|
|
111
|
+
mock_determine.assert_called_once_with("invalid")
|
|
112
|
+
mock_run_agent.assert_not_called()
|
|
113
|
+
|
|
114
|
+
@patch("ara_cli.artefact_autofix.run_agent")
|
|
115
|
+
@patch("ara_cli.artefact_autofix.write_corrected_artefact")
|
|
116
|
+
@patch("ara_cli.artefact_autofix.fix_title_mismatch", return_value="fixed text")
|
|
117
|
+
@patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
|
|
118
|
+
@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):
|
|
120
|
+
"""Tests that a deterministic fix is applied when the flag is True."""
|
|
121
|
+
mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
|
|
122
|
+
reason = "Filename-Title Mismatch: some details"
|
|
123
|
+
|
|
124
|
+
result = apply_autofix("file.feature", "feature", reason, deterministic=True, non_deterministic=False)
|
|
125
|
+
|
|
126
|
+
assert result is True
|
|
127
|
+
mock_fix_title.assert_called_once_with("file.feature", "original text", mock_artefact_class)
|
|
128
|
+
mock_write.assert_called_once_with("file.feature", "fixed text")
|
|
129
|
+
mock_run_agent.assert_not_called()
|
|
130
|
+
|
|
131
|
+
@patch("ara_cli.artefact_autofix.run_agent")
|
|
132
|
+
@patch("ara_cli.artefact_autofix.write_corrected_artefact")
|
|
133
|
+
@patch("ara_cli.artefact_autofix.fix_title_mismatch")
|
|
134
|
+
@patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
|
|
135
|
+
@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):
|
|
137
|
+
"""Tests that a deterministic fix is skipped when the flag is False."""
|
|
138
|
+
mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
|
|
139
|
+
reason = "Filename-Title Mismatch: some details"
|
|
140
|
+
|
|
141
|
+
result = apply_autofix("file.feature", "feature", reason, deterministic=False, non_deterministic=True)
|
|
142
|
+
|
|
143
|
+
assert result is False
|
|
144
|
+
mock_fix_title.assert_not_called()
|
|
145
|
+
mock_write.assert_not_called()
|
|
146
|
+
mock_run_agent.assert_not_called()
|
|
147
|
+
|
|
148
|
+
@patch("ara_cli.artefact_autofix.write_corrected_artefact")
|
|
149
|
+
@patch("ara_cli.artefact_autofix.run_agent")
|
|
150
|
+
@patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
|
|
151
|
+
@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):
|
|
153
|
+
"""Tests that an LLM fix is applied when the non-deterministic flag is True."""
|
|
154
|
+
mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
|
|
155
|
+
mock_run_agent.return_value = mock_artefact_class
|
|
156
|
+
reason = "Pydantic validation error"
|
|
157
|
+
|
|
158
|
+
result = apply_autofix("file.feature", "feature", reason, deterministic=False, non_deterministic=True)
|
|
159
|
+
|
|
160
|
+
assert result is True
|
|
161
|
+
mock_run_agent.assert_called_once()
|
|
162
|
+
mock_write.assert_called_once_with("file.feature", "llm corrected content")
|
|
163
|
+
|
|
164
|
+
@patch("ara_cli.artefact_autofix.write_corrected_artefact")
|
|
165
|
+
@patch("ara_cli.artefact_autofix.run_agent")
|
|
166
|
+
@patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
|
|
167
|
+
@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):
|
|
169
|
+
"""Tests that an LLM fix is skipped when the non-deterministic flag is False."""
|
|
170
|
+
mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
|
|
171
|
+
reason = "Pydantic validation error"
|
|
172
|
+
|
|
173
|
+
result = apply_autofix("file.feature", "feature", reason, deterministic=True, non_deterministic=False)
|
|
174
|
+
|
|
175
|
+
assert result is False
|
|
176
|
+
mock_run_agent.assert_not_called()
|
|
177
|
+
mock_write.assert_not_called()
|
|
178
|
+
|
|
179
|
+
@patch("ara_cli.artefact_autofix.run_agent", side_effect=Exception("LLM failed"))
|
|
180
|
+
@patch("ara_cli.artefact_autofix.determine_artefact_type_and_class")
|
|
181
|
+
@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):
|
|
183
|
+
"""Tests that an exception during an LLM fix is handled gracefully."""
|
|
184
|
+
mock_determine.return_value = (mock_artefact_type, mock_artefact_class)
|
|
185
|
+
reason = "Pydantic validation error"
|
|
186
|
+
|
|
187
|
+
result = apply_autofix("file.feature", "feature", reason, deterministic=False, non_deterministic=True)
|
|
188
|
+
|
|
189
|
+
assert result is False
|
|
190
|
+
assert "LLM agent failed to fix artefact at file.feature: LLM failed" in capsys.readouterr().out
|
|
191
|
+
|
|
192
|
+
# === Other Tests ===
|
|
193
|
+
|
|
194
|
+
def test_fix_title_mismatch_success(mock_artefact_class):
|
|
195
|
+
artefact_text = "Feature: wrong title\nSome other content"
|
|
196
|
+
file_path = "path/to/correct_title.feature"
|
|
197
|
+
|
|
198
|
+
expected_text = "Feature: correct title\nSome other content"
|
|
199
|
+
|
|
200
|
+
result = fix_title_mismatch(file_path, artefact_text, mock_artefact_class)
|
|
201
|
+
|
|
202
|
+
assert result == expected_text
|
|
203
|
+
mock_artefact_class._title_prefix.assert_called_once()
|
|
204
|
+
|
|
205
|
+
def test_fix_title_mismatch_prefix_not_found(capsys, mock_artefact_class):
|
|
206
|
+
artefact_text = "No title prefix here"
|
|
207
|
+
file_path = "path/to/correct_title.feature"
|
|
208
|
+
|
|
209
|
+
result = fix_title_mismatch(file_path, artefact_text, mock_artefact_class)
|
|
210
|
+
|
|
211
|
+
assert result == artefact_text # Should return original text
|
|
212
|
+
assert "Warning: Title prefix 'Feature:' not found" in capsys.readouterr().out
|
|
213
|
+
|
|
214
|
+
@patch("pydantic_ai.Agent")
|
|
215
|
+
def test_run_agent_exception_handling(mock_agent_class):
|
|
216
|
+
mock_agent_instance = mock_agent_class.return_value
|
|
217
|
+
mock_agent_instance.run_sync.side_effect = Exception("Agent error")
|
|
218
|
+
with pytest.raises(Exception, match="Agent error"):
|
|
219
|
+
run_agent("prompt", MagicMock())
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
from mock import patch
|
|
2
|
-
from unittest.mock import call, mock_open
|
|
1
|
+
from unittest.mock import call, mock_open, patch, Mock
|
|
3
2
|
from ara_cli.artefact_renamer import ArtefactRenamer
|
|
4
3
|
from ara_cli.classifier import Classifier
|
|
5
|
-
import mock
|
|
6
4
|
import pytest
|
|
7
5
|
import os
|
|
8
6
|
import shutil
|
|
@@ -47,7 +45,7 @@ def test_rename_checks_new_name_provided():
|
|
|
47
45
|
def test_update_title_in_artefact(mock_file, classifier, artefact_name, read_data_prefix, old_title, new_title):
|
|
48
46
|
ar = ArtefactRenamer(os)
|
|
49
47
|
read_data = f"{read_data_prefix}{old_title}\nOther content that remains unchanged."
|
|
50
|
-
mock_file.return_value.read =
|
|
48
|
+
mock_file.return_value.read = Mock(return_value=read_data)
|
|
51
49
|
artefact_path = f"path/to/{classifier}.artefact"
|
|
52
50
|
|
|
53
51
|
# Ensure that the mock for get_artefact_title returns the prefix without an extra colon and space
|
|
@@ -76,7 +74,7 @@ def test_update_title_no_title_line(mock_get_artefact_title, mock_file):
|
|
|
76
74
|
ar = ArtefactRenamer()
|
|
77
75
|
|
|
78
76
|
read_data = "content that remains unchanged."
|
|
79
|
-
mock_file.return_value.read =
|
|
77
|
+
mock_file.return_value.read = Mock(return_value=read_data)
|
|
80
78
|
artefact_path = "path/to/artefact.vision"
|
|
81
79
|
|
|
82
80
|
with pytest.raises(ValueError):
|
|
@@ -5,13 +5,36 @@ from pydantic import ValidationError
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def test_check_file_valid():
|
|
8
|
+
"""Tests the happy path where the file is valid and the title matches."""
|
|
9
|
+
mock_artefact_instance = MagicMock()
|
|
10
|
+
mock_artefact_instance.title = "dummy_path"
|
|
11
|
+
|
|
8
12
|
mock_artefact_class = MagicMock()
|
|
9
|
-
mock_artefact_class.deserialize.return_value =
|
|
13
|
+
mock_artefact_class.deserialize.return_value = mock_artefact_instance
|
|
10
14
|
|
|
11
15
|
with patch("builtins.open", mock_open(read_data="valid content")):
|
|
12
|
-
is_valid, reason = check_file("dummy_path", mock_artefact_class)
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
is_valid, reason = check_file("dummy_path.feature", mock_artefact_class)
|
|
17
|
+
|
|
18
|
+
assert is_valid is True
|
|
19
|
+
assert reason is None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_check_file_title_mismatch():
|
|
23
|
+
"""Tests the case where the filename and artefact title do not match."""
|
|
24
|
+
mock_artefact_instance = MagicMock()
|
|
25
|
+
mock_artefact_instance.title = "wrong_title"
|
|
26
|
+
|
|
27
|
+
mock_artefact_class = MagicMock()
|
|
28
|
+
mock_artefact_class.deserialize.return_value = mock_artefact_instance
|
|
29
|
+
|
|
30
|
+
with patch("builtins.open", mock_open(read_data="content")):
|
|
31
|
+
is_valid, reason = check_file("correct_path.feature", mock_artefact_class)
|
|
32
|
+
|
|
33
|
+
assert is_valid is False
|
|
34
|
+
assert "Filename-Title Mismatch" in reason
|
|
35
|
+
|
|
36
|
+
assert "'correct_path'" in reason
|
|
37
|
+
assert "'wrong_title'" in reason
|
|
15
38
|
|
|
16
39
|
|
|
17
40
|
def test_check_file_value_error():
|
|
@@ -60,26 +83,30 @@ def test_find_invalid_files():
|
|
|
60
83
|
mock_artefact_class = MagicMock()
|
|
61
84
|
with patch("ara_cli.artefact_models.artefact_mapping.artefact_type_mapping", {"test_classifier": mock_artefact_class}):
|
|
62
85
|
artefact_files = {
|
|
63
|
-
"test_classifier": [
|
|
86
|
+
"test_classifier": [
|
|
87
|
+
{"file_path": "file1.txt"}, # Should be checked
|
|
88
|
+
{"file_path": "file2.txt"}, # Should be checked
|
|
89
|
+
{"file_path": "templates/file3.txt"}, # Should be skipped
|
|
90
|
+
{"file_path": "some/path/file.data"} # Should be skipped
|
|
91
|
+
]
|
|
64
92
|
}
|
|
65
93
|
|
|
66
94
|
with patch("ara_cli.artefact_scan.check_file") as mock_check_file:
|
|
67
95
|
mock_check_file.side_effect = [
|
|
68
|
-
(True, None),
|
|
69
|
-
(False, "Invalid content")
|
|
96
|
+
(True, None), # for file1.txt
|
|
97
|
+
(False, "Invalid content") # for file2.txt
|
|
70
98
|
]
|
|
71
99
|
|
|
72
100
|
invalid_files = find_invalid_files(
|
|
73
101
|
artefact_files, "test_classifier")
|
|
74
102
|
assert len(invalid_files) == 1
|
|
75
103
|
assert invalid_files[0] == ("file2.txt", "Invalid content")
|
|
104
|
+
assert mock_check_file.call_count == 2
|
|
76
105
|
mock_check_file.assert_has_calls([
|
|
77
106
|
call("file1.txt", mock_artefact_class),
|
|
78
107
|
call("file2.txt", mock_artefact_class)
|
|
79
108
|
], any_order=False)
|
|
80
109
|
|
|
81
|
-
# Tests for show_results
|
|
82
|
-
|
|
83
110
|
|
|
84
111
|
def test_show_results_no_issues(capsys):
|
|
85
112
|
invalid_artefacts = {}
|
|
@@ -126,4 +153,4 @@ def test_show_results_with_issues(capsys):
|
|
|
126
153
|
call("- `file3.txt`: reason3\n"),
|
|
127
154
|
call("\n")
|
|
128
155
|
]
|
|
129
|
-
handle.write.assert_has_calls(expected_writes, any_order=False)
|
|
156
|
+
handle.write.assert_has_calls(expected_writes, any_order=False)
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
import os
|
|
3
3
|
import tempfile
|
|
4
|
-
import mock
|
|
5
4
|
import glob
|
|
6
5
|
import cmd2
|
|
6
|
+
import ara_cli
|
|
7
7
|
from unittest.mock import patch, MagicMock, mock_open
|
|
8
8
|
from types import SimpleNamespace
|
|
9
9
|
from ara_cli.chat import Chat
|
|
10
10
|
from ara_cli.template_manager import TemplatePathManager
|
|
11
11
|
from ara_cli.ara_config import ConfigManager
|
|
12
12
|
|
|
13
|
-
|
|
14
13
|
def get_default_config():
|
|
15
14
|
return SimpleNamespace(
|
|
16
15
|
ext_code_dirs=[
|
|
@@ -63,7 +62,7 @@ def temp_load_file():
|
|
|
63
62
|
|
|
64
63
|
|
|
65
64
|
def test_handle_existing_chat_no_reset(temp_chat_file):
|
|
66
|
-
with
|
|
65
|
+
with patch('builtins.input', return_value='n'):
|
|
67
66
|
mock_config = get_default_config()
|
|
68
67
|
with patch('ara_cli.prompt_handler.ConfigManager.get_config', return_value=mock_config):
|
|
69
68
|
chat = Chat(temp_chat_file.name, reset=None)
|
|
@@ -71,7 +70,7 @@ def test_handle_existing_chat_no_reset(temp_chat_file):
|
|
|
71
70
|
|
|
72
71
|
|
|
73
72
|
def test_handle_existing_chat_with_reset(temp_chat_file):
|
|
74
|
-
with
|
|
73
|
+
with patch('builtins.input', return_value='y'):
|
|
75
74
|
mock_config = get_default_config()
|
|
76
75
|
with patch('ara_cli.prompt_handler.ConfigManager.get_config', return_value=mock_config):
|
|
77
76
|
chat = Chat(temp_chat_file.name, reset=None)
|
|
@@ -931,36 +930,52 @@ def test_do_LOAD_COMMANDS(monkeypatch, temp_chat_file, commands_name, expected_d
|
|
|
931
930
|
|
|
932
931
|
|
|
933
932
|
@pytest.mark.parametrize("template_name, template_type, default_pattern, custom_template_subdir, expected_directory, expected_pattern", [
|
|
934
|
-
("
|
|
935
|
-
("local_command", "commands", "*.commands.md", "
|
|
936
|
-
("
|
|
933
|
+
("local_command", "commands", "*.commands.md", "custom-prompt-modules", "/mocked_local_templates_path/custom-prompt-modules/commands", "local_command"),
|
|
934
|
+
("local_command", "commands", "*.commands.md", "mocked_custom_modules_path", "/mocked_local_templates_path/mocked_custom_modules_path/commands", "local_command"),
|
|
935
|
+
("local_rule", "rules", "*.rules.md", "custom-prompt-modules", "/mocked_local_templates_path/custom-prompt-modules/rules", "local_rule"),
|
|
936
|
+
("local_rule", "rules", "*.rules.md", "mocked_custom_modules_path", "/mocked_local_templates_path/mocked_custom_modules_path/rules", "local_rule"),
|
|
937
|
+
("local_intention", "intention", "*.intentions.md", "custom-prompt-modules", "/mocked_local_templates_path/custom-prompt-modules/intentions", "local_intention"),
|
|
938
|
+
("local_intention", "intention", "*.intentions.md", "mocked_custom_modules_path", "/mocked_local_templates_path/mocked_custom_modules_path/intentions", "local_intention"),
|
|
939
|
+
("local_blueprint", "blueprint", "*.blueprints.md", "custom-prompt-modules", "/mocked_local_templates_path/custom-prompt-modules/blueprints", "local_blueprint"),
|
|
940
|
+
("local_blueprint", "blueprint", "*.blueprints.md", "mocked_custom_modules_path", "/mocked_local_templates_path/mocked_custom_modules_path/blueprints", "local_blueprint")
|
|
941
|
+
])
|
|
942
|
+
def test_load_template_local(monkeypatch, temp_chat_file, template_name, template_type, default_pattern, custom_template_subdir, expected_directory, expected_pattern):
|
|
943
|
+
expected_base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))
|
|
944
|
+
expected_directory_abs = expected_base_dir + expected_directory
|
|
945
|
+
mock_config = get_default_config()
|
|
946
|
+
with patch('ara_cli.prompt_handler.ConfigManager.get_config', return_value=mock_config):
|
|
947
|
+
chat = Chat(temp_chat_file.name, reset=False)
|
|
937
948
|
|
|
938
|
-
|
|
939
|
-
("local_rule", "rules", "*.rules.md", "custom-prompt-modules", "/project/mocked_local_templates_path/custom-prompt-modules/rules", "local_rule"),
|
|
940
|
-
("local_rule", "rules", "*.rules.md", "mocked_custom_modules_path", "/project/mocked_local_templates_path/mocked_custom_modules_path/rules", "local_rule"),
|
|
949
|
+
mock_local_templates_path = "mocked_local_templates_path"
|
|
941
950
|
|
|
942
|
-
(
|
|
943
|
-
("local_intention", "intention", "*.intentions.md", "custom-prompt-modules", "/project/mocked_local_templates_path/custom-prompt-modules/intentions", "local_intention"),
|
|
944
|
-
("local_intention", "intention", "*.intentions.md", "mocked_custom_modules_path", "/project/mocked_local_templates_path/mocked_custom_modules_path/intentions", "local_intention"),
|
|
951
|
+
monkeypatch.setattr(ConfigManager, 'get_config', lambda: MagicMock(local_prompt_templates_dir=mock_local_templates_path))
|
|
945
952
|
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
953
|
+
config = chat.config
|
|
954
|
+
config.local_prompt_templates_dir = mock_local_templates_path
|
|
955
|
+
config.custom_prompt_templates_subdir = custom_template_subdir
|
|
956
|
+
|
|
957
|
+
chat.config = config
|
|
958
|
+
|
|
959
|
+
with patch.object(chat, '_load_helper') as mock_load_helper:
|
|
960
|
+
chat._load_template_from_global_or_local(template_name, template_type)
|
|
961
|
+
mock_load_helper.assert_called_once_with(expected_directory_abs, expected_pattern, template_type)
|
|
962
|
+
|
|
963
|
+
@pytest.mark.parametrize("template_name, template_type, default_pattern, expected_directory, expected_pattern", [
|
|
964
|
+
("global/test_command", "commands", "*.commands.md", "mocked_template_base_path/prompt-modules/commands/", "test_command"),
|
|
965
|
+
("global/test_rule", "rules", "*.rules.md", "mocked_template_base_path/prompt-modules/rules/", "test_rule"),
|
|
966
|
+
("global/test_intention", "intention", "*.intentions.md", "mocked_template_base_path/prompt-modules/intentions/", "test_intention"),
|
|
967
|
+
("global/test_blueprint", "blueprint", "*.blueprints.md", "mocked_template_base_path/prompt-modules/blueprints/", "test_blueprint"),
|
|
949
968
|
])
|
|
950
|
-
def
|
|
969
|
+
def test_load_template_from_global(monkeypatch, temp_chat_file, template_name, template_type, default_pattern, expected_directory, expected_pattern):
|
|
951
970
|
mock_config = get_default_config()
|
|
952
971
|
with patch('ara_cli.prompt_handler.ConfigManager.get_config', return_value=mock_config):
|
|
953
972
|
chat = Chat(temp_chat_file.name, reset=False)
|
|
954
973
|
|
|
955
974
|
mock_template_base_path = "mocked_template_base_path"
|
|
956
|
-
mock_local_templates_path = "mocked_local_templates_path"
|
|
957
975
|
|
|
958
976
|
monkeypatch.setattr(TemplatePathManager, 'get_template_base_path', lambda: mock_template_base_path)
|
|
959
|
-
monkeypatch.setattr(ConfigManager, 'get_config', lambda: MagicMock(local_prompt_templates_dir=mock_local_templates_path))
|
|
960
977
|
|
|
961
978
|
config = chat.config
|
|
962
|
-
config.local_prompt_templates_dir = mock_local_templates_path
|
|
963
|
-
config.custom_prompt_templates_subdir = custom_template_subdir
|
|
964
979
|
chat.config = config
|
|
965
980
|
|
|
966
981
|
with patch.object(chat, '_load_helper') as mock_load_helper:
|
|
@@ -1042,7 +1057,9 @@ def test_do_LOAD_TEMPLATE(temp_chat_file):
|
|
|
1042
1057
|
with patch('ara_cli.prompt_handler.ConfigManager.get_config', return_value=mock_config):
|
|
1043
1058
|
chat = Chat(temp_chat_file.name, reset=False)
|
|
1044
1059
|
template_name = 'test_template'
|
|
1045
|
-
|
|
1060
|
+
|
|
1061
|
+
module_path = os.path.abspath(os.path.dirname(ara_cli.__file__))
|
|
1062
|
+
directory = f'{module_path}/templates'
|
|
1046
1063
|
pattern = f"template.{template_name}"
|
|
1047
1064
|
file_type = "template"
|
|
1048
1065
|
exclude_pattern = os.path.join(directory, "template.*.prompt_log.md")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from unittest.mock import patch
|
|
1
|
+
from unittest.mock import patch, MagicMock
|
|
2
2
|
from ara_cli.template_manager import SpecificationBreakdownAspects, ArtefactFileManager
|
|
3
3
|
from ara_cli.directory_navigator import DirectoryNavigator
|
|
4
4
|
|
|
@@ -64,27 +64,43 @@ def test_copy_templates_to_directory(aspect, expect_exception, match_str):
|
|
|
64
64
|
mock_copy.assert_called()
|
|
65
65
|
mock_print.assert_called()
|
|
66
66
|
|
|
67
|
+
@pytest.fixture
|
|
68
|
+
def mock_file_manager():
|
|
69
|
+
with patch("ara_cli.template_manager.ArtefactFileManager") as MockFileManager:
|
|
70
|
+
yield MockFileManager.return_value
|
|
67
71
|
|
|
68
|
-
@pytest.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
@pytest.fixture
|
|
73
|
+
def mock_navigator():
|
|
74
|
+
with patch("ara_cli.template_manager.DirectoryNavigator") as MockNavigator:
|
|
75
|
+
mock_nav_instance = MockNavigator.return_value
|
|
76
|
+
yield mock_nav_instance
|
|
77
|
+
|
|
78
|
+
@pytest.fixture
|
|
79
|
+
def mock_classifier():
|
|
80
|
+
with patch("ara_cli.template_manager.Classifier") as MockClassifier:
|
|
81
|
+
MockClassifier.valid_classifiers = ['valid_classifier']
|
|
82
|
+
MockClassifier.is_valid_classifier.return_value = True
|
|
83
|
+
yield MockClassifier
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_create_valid_step_aspect(mock_file_manager, mock_navigator, mock_classifier):
|
|
80
87
|
sba = SpecificationBreakdownAspects()
|
|
81
88
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
# Mock values returned by file_manager methods
|
|
90
|
+
mock_file_manager.get_artefact_file_path.return_value = "/tmp/path/file"
|
|
91
|
+
mock_file_manager.get_data_directory_path.return_value = "/tmp/path/data"
|
|
92
|
+
mock_file_manager.generate_behave_steps.return_value = ["step 1", "step 2"]
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
# Run the function
|
|
95
|
+
sba.create(artefact_name="my_artefact", classifier="valid_classifier", aspect="step")
|
|
96
|
+
|
|
97
|
+
# Assert navigator was used
|
|
98
|
+
mock_navigator.navigate_to_target.assert_called_once()
|
|
99
|
+
|
|
100
|
+
# Assert file_manager method calls
|
|
101
|
+
mock_file_manager.get_artefact_file_path.assert_called_once_with("my_artefact", "valid_classifier")
|
|
102
|
+
mock_file_manager.get_data_directory_path.assert_called_once_with("my_artefact", "valid_classifier")
|
|
103
|
+
mock_file_manager.create_directory.assert_called_once_with("/tmp/path/file", "/tmp/path/data")
|
|
104
|
+
mock_file_manager.copy_aspect_templates_to_directory.assert_called_once()
|
|
105
|
+
mock_file_manager.generate_behave_steps.assert_called_once_with("my_artefact")
|
|
106
|
+
mock_file_manager.save_behave_steps_to_file.assert_called_once_with("my_artefact", ["step 1", "step 2"])
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from unittest.mock import patch, mock_open, MagicMock
|
|
3
|
-
from ara_cli.artefact_autofix import (
|
|
4
|
-
read_report_file,
|
|
5
|
-
parse_report,
|
|
6
|
-
apply_autofix,
|
|
7
|
-
read_artefact,
|
|
8
|
-
determine_artefact_type_and_class,
|
|
9
|
-
run_agent,
|
|
10
|
-
write_corrected_artefact
|
|
11
|
-
)
|
|
12
|
-
from ara_cli.ara_command_action import autofix_action
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_read_report_file():
|
|
16
|
-
mock_content = "# Artefact Check Report\n\n## classifier\n- `file_path`: reason\n"
|
|
17
|
-
with patch("builtins.open", mock_open(read_data=mock_content)) as m:
|
|
18
|
-
content = read_report_file()
|
|
19
|
-
assert content == mock_content
|
|
20
|
-
m.assert_called_once_with(
|
|
21
|
-
"incompatible_artefacts_report.md", "r", encoding="utf-8")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def test_parse_report():
|
|
25
|
-
content = "# Artefact Check Report\n\n## classifier\n- `file_path`: reason\n"
|
|
26
|
-
expected_issues = {"classifier": [("file_path", "reason")]}
|
|
27
|
-
issues = parse_report(content)
|
|
28
|
-
assert issues == expected_issues
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@patch("ara_cli.artefact_autofix.run_agent")
|
|
32
|
-
@patch("ara_cli.artefact_autofix.write_corrected_artefact")
|
|
33
|
-
def test_apply_autofix(mock_write_corrected_artefact, mock_run_agent):
|
|
34
|
-
mock_run_agent.return_value.serialize.return_value = "corrected content"
|
|
35
|
-
with patch("ara_cli.artefact_autofix.read_artefact", return_value="artefact text"):
|
|
36
|
-
with patch("ara_cli.artefact_autofix.determine_artefact_type_and_class", return_value=("ArtefactType", MagicMock())):
|
|
37
|
-
apply_autofix("file_path", "classifier", "reason")
|
|
38
|
-
mock_run_agent.assert_called_once()
|
|
39
|
-
mock_write_corrected_artefact.assert_called_once_with(
|
|
40
|
-
"file_path", "corrected content")
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@patch("ara_cli.artefact_autofix.apply_autofix")
|
|
44
|
-
@patch("ara_cli.artefact_autofix.parse_report")
|
|
45
|
-
@patch("ara_cli.artefact_autofix.read_report_file")
|
|
46
|
-
def test_autofix_action(mock_read_report_file, mock_parse_report, mock_apply_autofix, capsys):
|
|
47
|
-
mock_read_report_file.return_value = "# Artefact Check Report\n\n## classifier\n- `file_path`: reason\n"
|
|
48
|
-
mock_parse_report.return_value = {"classifier": [("file_path", "reason")]}
|
|
49
|
-
|
|
50
|
-
args = MagicMock()
|
|
51
|
-
autofix_action(args)
|
|
52
|
-
|
|
53
|
-
captured = capsys.readouterr()
|
|
54
|
-
assert "Attempting to fix file_path for reason: reason" in captured.out
|
|
55
|
-
mock_apply_autofix.assert_called_once_with(
|
|
56
|
-
"file_path", "classifier", "reason")
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def test_read_artefact():
|
|
60
|
-
mock_content = "artefact content"
|
|
61
|
-
with patch("builtins.open", mock_open(read_data=mock_content)) as m:
|
|
62
|
-
content = read_artefact("file_path")
|
|
63
|
-
assert content == mock_content
|
|
64
|
-
m.assert_called_once_with("file_path", "r")
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def test_read_report_file_not_found(capsys):
|
|
68
|
-
with patch("builtins.open", side_effect=OSError("File not found")):
|
|
69
|
-
content = read_report_file()
|
|
70
|
-
captured = capsys.readouterr()
|
|
71
|
-
assert content is None
|
|
72
|
-
assert "Artefact scan results file not found" in captured.out
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def test_parse_report_no_issues():
|
|
76
|
-
content = "# Artefact Check Report\n\nNo problems found.\n"
|
|
77
|
-
issues = parse_report(content)
|
|
78
|
-
assert issues == {}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def test_parse_report_invalid_format():
|
|
82
|
-
content = "Invalid Format"
|
|
83
|
-
issues = parse_report(content)
|
|
84
|
-
assert issues == {}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def test_determine_artefact_type_and_class_invalid():
|
|
88
|
-
artefact_type, artefact_class = determine_artefact_type_and_class(
|
|
89
|
-
"invalid_classifier")
|
|
90
|
-
assert artefact_type is None
|
|
91
|
-
assert artefact_class is None
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def test_write_corrected_artefact():
|
|
95
|
-
corrected_content = "corrected content"
|
|
96
|
-
with patch("builtins.open", mock_open()) as m:
|
|
97
|
-
write_corrected_artefact("file_path", corrected_content)
|
|
98
|
-
m.assert_called_once_with("file_path", "w")
|
|
99
|
-
handle = m()
|
|
100
|
-
handle.write.assert_called_once_with(corrected_content)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
@patch("ara_cli.artefact_autofix.read_artefact", return_value=None)
|
|
104
|
-
def test_apply_autofix_file_not_found(mock_read_artefact):
|
|
105
|
-
apply_autofix("file_path", "classifier", "reason")
|
|
106
|
-
mock_read_artefact.assert_called_once_with("file_path")
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
@patch("pydantic_ai.Agent")
|
|
110
|
-
def test_run_agent_exception_handling(mock_agent):
|
|
111
|
-
mock_agent.return_value.run_sync.side_effect = Exception("Agent error")
|
|
112
|
-
with pytest.raises(Exception, match="Agent error"):
|
|
113
|
-
run_agent("prompt", MagicMock())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|