ara-cli 0.1.9.73__py3-none-any.whl → 0.1.9.75__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/ara_command_action.py +15 -15
- ara_cli/ara_command_parser.py +2 -1
- ara_cli/ara_config.py +181 -73
- ara_cli/artefact_autofix.py +130 -68
- ara_cli/artefact_creator.py +1 -1
- ara_cli/artefact_models/artefact_model.py +26 -7
- ara_cli/artefact_models/artefact_templates.py +47 -31
- ara_cli/artefact_models/businessgoal_artefact_model.py +23 -25
- ara_cli/artefact_models/epic_artefact_model.py +23 -24
- ara_cli/artefact_models/feature_artefact_model.py +76 -46
- ara_cli/artefact_models/keyfeature_artefact_model.py +21 -24
- ara_cli/artefact_models/task_artefact_model.py +73 -13
- ara_cli/artefact_models/userstory_artefact_model.py +22 -24
- ara_cli/artefact_models/vision_artefact_model.py +23 -42
- ara_cli/artefact_scan.py +55 -17
- ara_cli/chat.py +23 -5
- ara_cli/prompt_handler.py +4 -4
- ara_cli/tag_extractor.py +43 -28
- ara_cli/template_manager.py +3 -8
- ara_cli/version.py +1 -1
- {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/METADATA +1 -1
- {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/RECORD +29 -39
- tests/test_ara_config.py +420 -36
- tests/test_artefact_autofix.py +289 -25
- tests/test_artefact_scan.py +296 -35
- tests/test_chat.py +35 -15
- ara_cli/templates/template.businessgoal +0 -10
- ara_cli/templates/template.capability +0 -10
- ara_cli/templates/template.epic +0 -15
- ara_cli/templates/template.example +0 -6
- ara_cli/templates/template.feature +0 -26
- ara_cli/templates/template.issue +0 -14
- ara_cli/templates/template.keyfeature +0 -15
- ara_cli/templates/template.task +0 -6
- ara_cli/templates/template.userstory +0 -17
- ara_cli/templates/template.vision +0 -14
- {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/entry_points.txt +0 -0
- {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/top_level.txt +0 -0
tests/test_artefact_scan.py
CHANGED
|
@@ -1,7 +1,253 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
from unittest.mock import patch, MagicMock, mock_open, call
|
|
3
|
-
from ara_cli.artefact_scan import
|
|
4
|
-
|
|
3
|
+
from ara_cli.artefact_scan import (
|
|
4
|
+
check_file,
|
|
5
|
+
find_invalid_files,
|
|
6
|
+
show_results,
|
|
7
|
+
is_contribution_valid,
|
|
8
|
+
is_rule_valid,
|
|
9
|
+
check_contribution,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.parametrize("contribution", [None, False, 0, "", []])
|
|
14
|
+
def test_check_contribution_none_contribution(contribution):
|
|
15
|
+
# Should return (True, None) if contribution is falsey
|
|
16
|
+
result = check_contribution(
|
|
17
|
+
contribution, classified_artefact_info={}, file_path="irrelevant"
|
|
18
|
+
)
|
|
19
|
+
assert result == (True, None)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_check_contribution_invalid_contribution(monkeypatch):
|
|
23
|
+
# If is_contribution_valid returns False, should return (False, custom_reason)
|
|
24
|
+
contribution = MagicMock()
|
|
25
|
+
contribution.classifier = "some_cls"
|
|
26
|
+
contribution.artefact_name = "some_art"
|
|
27
|
+
with patch(
|
|
28
|
+
"ara_cli.artefact_scan.is_contribution_valid", return_value=False
|
|
29
|
+
) as mock_is_contrib, patch("ara_cli.artefact_scan.is_rule_valid") as mock_is_rule:
|
|
30
|
+
result = check_contribution(
|
|
31
|
+
contribution, classified_artefact_info={}, file_path="f"
|
|
32
|
+
)
|
|
33
|
+
assert result == (
|
|
34
|
+
False,
|
|
35
|
+
"Invalid Contribution Reference: The contribution references "
|
|
36
|
+
"'some_cls' artefact 'some_art' which does not exist.",
|
|
37
|
+
)
|
|
38
|
+
mock_is_contrib.assert_called_once_with(contribution, {})
|
|
39
|
+
mock_is_rule.assert_not_called()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_check_contribution_invalid_rule(monkeypatch):
|
|
43
|
+
# is_contribution_valid returns True, is_rule_valid returns False
|
|
44
|
+
contribution = MagicMock()
|
|
45
|
+
contribution.classifier = "c"
|
|
46
|
+
contribution.artefact_name = "a"
|
|
47
|
+
contribution.rule = "r"
|
|
48
|
+
with patch(
|
|
49
|
+
"ara_cli.artefact_scan.is_contribution_valid", return_value=True
|
|
50
|
+
) as mock_is_contrib, patch(
|
|
51
|
+
"ara_cli.artefact_scan.is_rule_valid", return_value=False
|
|
52
|
+
) as mock_is_rule:
|
|
53
|
+
result = check_contribution(
|
|
54
|
+
contribution, classified_artefact_info={"x": 1}, file_path="myfile"
|
|
55
|
+
)
|
|
56
|
+
assert result == (
|
|
57
|
+
False,
|
|
58
|
+
"Rule Mismatch: The contribution references rule 'r' which the parent c 'a' does not have.",
|
|
59
|
+
)
|
|
60
|
+
mock_is_contrib.assert_called_once_with(contribution, {"x": 1})
|
|
61
|
+
mock_is_rule.assert_called_once_with(contribution, {"x": 1})
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_check_contribution_all_valid(monkeypatch):
|
|
65
|
+
# Both is_contribution_valid and is_rule_valid return True
|
|
66
|
+
contribution = MagicMock()
|
|
67
|
+
contribution.classifier = "c"
|
|
68
|
+
contribution.artefact_name = "a"
|
|
69
|
+
contribution.rule = "r"
|
|
70
|
+
with patch(
|
|
71
|
+
"ara_cli.artefact_scan.is_contribution_valid", return_value=True
|
|
72
|
+
) as mock_is_contrib, patch(
|
|
73
|
+
"ara_cli.artefact_scan.is_rule_valid", return_value=True
|
|
74
|
+
) as mock_is_rule:
|
|
75
|
+
result = check_contribution(
|
|
76
|
+
contribution, classified_artefact_info={1: 2}, file_path="p"
|
|
77
|
+
)
|
|
78
|
+
assert result == (True, None)
|
|
79
|
+
mock_is_contrib.assert_called_once_with(contribution, {1: 2})
|
|
80
|
+
mock_is_rule.assert_called_once_with(contribution, {1: 2})
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@pytest.mark.parametrize(
|
|
84
|
+
"contribution_attrs,expected",
|
|
85
|
+
[
|
|
86
|
+
(None, True), # contribution is None
|
|
87
|
+
({"artefact_name": None, "classifier": "foo"}, True), # artefact_name is None
|
|
88
|
+
(
|
|
89
|
+
{"artefact_name": "", "classifier": "foo"},
|
|
90
|
+
True,
|
|
91
|
+
), # artefact_name is empty string
|
|
92
|
+
({"artefact_name": "bar", "classifier": None}, True), # classifier is None
|
|
93
|
+
(
|
|
94
|
+
{"artefact_name": "bar", "classifier": ""},
|
|
95
|
+
True,
|
|
96
|
+
), # classifier is empty string
|
|
97
|
+
],
|
|
98
|
+
)
|
|
99
|
+
def test_is_rule_valid_short_circuits(contribution_attrs, expected):
|
|
100
|
+
if contribution_attrs is None:
|
|
101
|
+
contribution = None
|
|
102
|
+
else:
|
|
103
|
+
contribution = MagicMock()
|
|
104
|
+
for k, v in contribution_attrs.items():
|
|
105
|
+
setattr(contribution, k, v)
|
|
106
|
+
with patch("ara_cli.artefact_reader.ArtefactReader.read_artefact") as mock_read:
|
|
107
|
+
result = is_rule_valid(contribution, classified_artefact_info={})
|
|
108
|
+
assert result is expected
|
|
109
|
+
mock_read.assert_not_called()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_is_rule_valid_rule_is_none():
|
|
113
|
+
"""Should return True if contribution.rule is None or falsey."""
|
|
114
|
+
contribution = MagicMock()
|
|
115
|
+
contribution.artefact_name = "foo"
|
|
116
|
+
contribution.classifier = "bar"
|
|
117
|
+
contribution.rule = None
|
|
118
|
+
with patch("ara_cli.artefact_reader.ArtefactReader.read_artefact") as mock_read:
|
|
119
|
+
result = is_rule_valid(contribution, classified_artefact_info={})
|
|
120
|
+
assert result is True
|
|
121
|
+
mock_read.assert_not_called()
|
|
122
|
+
|
|
123
|
+
# Also test rule as empty string
|
|
124
|
+
contribution.rule = ""
|
|
125
|
+
with patch("ara_cli.artefact_reader.ArtefactReader.read_artefact") as mock_read:
|
|
126
|
+
result = is_rule_valid(contribution, classified_artefact_info={})
|
|
127
|
+
assert result is True
|
|
128
|
+
mock_read.assert_not_called()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@pytest.mark.parametrize(
|
|
132
|
+
"parent,expected",
|
|
133
|
+
[
|
|
134
|
+
(None, True), # parent is None
|
|
135
|
+
(MagicMock(rules=None), True), # parent.rules is None
|
|
136
|
+
],
|
|
137
|
+
)
|
|
138
|
+
def test_is_rule_valid_parent_or_rules_none(parent, expected):
|
|
139
|
+
contribution = MagicMock()
|
|
140
|
+
contribution.artefact_name = "foo"
|
|
141
|
+
contribution.classifier = "bar"
|
|
142
|
+
contribution.rule = "r1"
|
|
143
|
+
with patch(
|
|
144
|
+
"ara_cli.artefact_reader.ArtefactReader.read_artefact", return_value=parent
|
|
145
|
+
) as mock_read:
|
|
146
|
+
result = is_rule_valid(contribution, classified_artefact_info={})
|
|
147
|
+
assert result is expected
|
|
148
|
+
mock_read.assert_called_once_with("foo", "bar")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_is_rule_valid_rule_not_in_parent_rules():
|
|
152
|
+
"""Should return False if rule is not in parent.rules."""
|
|
153
|
+
contribution = MagicMock()
|
|
154
|
+
contribution.artefact_name = "foo"
|
|
155
|
+
contribution.classifier = "bar"
|
|
156
|
+
contribution.rule = "missing_rule"
|
|
157
|
+
parent = MagicMock()
|
|
158
|
+
parent.rules = ["rule1", "rule2"]
|
|
159
|
+
with patch(
|
|
160
|
+
"ara_cli.artefact_reader.ArtefactReader.read_artefact", return_value=parent
|
|
161
|
+
) as mock_read:
|
|
162
|
+
result = is_rule_valid(contribution, classified_artefact_info={})
|
|
163
|
+
assert result is False
|
|
164
|
+
mock_read.assert_called_once_with("foo", "bar")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_is_rule_valid_rule_in_parent_rules():
|
|
168
|
+
"""Should return True if rule is in parent.rules."""
|
|
169
|
+
contribution = MagicMock()
|
|
170
|
+
contribution.artefact_name = "foo"
|
|
171
|
+
contribution.classifier = "bar"
|
|
172
|
+
contribution.rule = "my_rule"
|
|
173
|
+
parent = MagicMock()
|
|
174
|
+
parent.rules = ["my_rule", "other_rule"]
|
|
175
|
+
with patch(
|
|
176
|
+
"ara_cli.artefact_reader.ArtefactReader.read_artefact", return_value=parent
|
|
177
|
+
) as mock_read:
|
|
178
|
+
result = is_rule_valid(contribution, classified_artefact_info={})
|
|
179
|
+
assert result is True
|
|
180
|
+
mock_read.assert_called_once_with("foo", "bar")
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@pytest.mark.parametrize(
|
|
184
|
+
"contribution_attrs,expected",
|
|
185
|
+
[
|
|
186
|
+
# contribution is None
|
|
187
|
+
(None, True),
|
|
188
|
+
# artefact_name missing/None/empty
|
|
189
|
+
({"artefact_name": None, "classifier": "foo"}, True),
|
|
190
|
+
({"artefact_name": "", "classifier": "foo"}, True),
|
|
191
|
+
# classifier missing/None/empty
|
|
192
|
+
({"artefact_name": "bar", "classifier": None}, True),
|
|
193
|
+
({"artefact_name": "bar", "classifier": ""}, True),
|
|
194
|
+
],
|
|
195
|
+
)
|
|
196
|
+
def test_is_contribution_valid_short_circuits(contribution_attrs, expected):
|
|
197
|
+
classified_artefact_info = {"dummy": []}
|
|
198
|
+
if contribution_attrs is None:
|
|
199
|
+
contribution = None
|
|
200
|
+
else:
|
|
201
|
+
contribution = MagicMock()
|
|
202
|
+
for k, v in contribution_attrs.items():
|
|
203
|
+
setattr(contribution, k, v)
|
|
204
|
+
with patch(
|
|
205
|
+
"ara_cli.artefact_fuzzy_search.extract_artefact_names_of_classifier"
|
|
206
|
+
) as mock_extract:
|
|
207
|
+
result = is_contribution_valid(contribution, classified_artefact_info)
|
|
208
|
+
assert result is expected
|
|
209
|
+
# The extract function should NOT be called in these cases
|
|
210
|
+
mock_extract.assert_not_called()
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def test_is_contribution_valid_references_existing_artefact():
|
|
214
|
+
"""
|
|
215
|
+
contribution with valid artefact_name and classifier,
|
|
216
|
+
artefact_name IS in list returned by extract_artefact_names_of_classifier.
|
|
217
|
+
"""
|
|
218
|
+
contribution = MagicMock()
|
|
219
|
+
contribution.artefact_name = "valid_art"
|
|
220
|
+
contribution.classifier = "valid_clf"
|
|
221
|
+
classified_artefact_info = {"valid_clf": [{"name": "valid_art"}]}
|
|
222
|
+
with patch(
|
|
223
|
+
"ara_cli.artefact_fuzzy_search.extract_artefact_names_of_classifier",
|
|
224
|
+
return_value=["valid_art"],
|
|
225
|
+
) as mock_extract:
|
|
226
|
+
result = is_contribution_valid(contribution, classified_artefact_info)
|
|
227
|
+
assert result is True
|
|
228
|
+
mock_extract.assert_called_once_with(
|
|
229
|
+
classified_files=classified_artefact_info, classifier="valid_clf"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def test_is_contribution_valid_references_missing_artefact():
|
|
234
|
+
"""
|
|
235
|
+
contribution with valid artefact_name and classifier,
|
|
236
|
+
artefact_name is NOT in list returned by extract_artefact_names_of_classifier.
|
|
237
|
+
"""
|
|
238
|
+
contribution = MagicMock()
|
|
239
|
+
contribution.artefact_name = "missing_art"
|
|
240
|
+
contribution.classifier = "some_clf"
|
|
241
|
+
classified_artefact_info = {"some_clf": [{"name": "another"}]}
|
|
242
|
+
with patch(
|
|
243
|
+
"ara_cli.artefact_fuzzy_search.extract_artefact_names_of_classifier",
|
|
244
|
+
return_value=["another", "something_else"],
|
|
245
|
+
) as mock_extract:
|
|
246
|
+
result = is_contribution_valid(contribution, classified_artefact_info)
|
|
247
|
+
assert result is False
|
|
248
|
+
mock_extract.assert_called_once_with(
|
|
249
|
+
classified_files=classified_artefact_info, classifier="some_clf"
|
|
250
|
+
)
|
|
5
251
|
|
|
6
252
|
|
|
7
253
|
def test_check_file_valid():
|
|
@@ -16,8 +262,10 @@ def test_check_file_valid():
|
|
|
16
262
|
|
|
17
263
|
with patch("builtins.open", mock_open(read_data="valid content")):
|
|
18
264
|
is_valid, reason = check_file("dummy_path.feature", mock_artefact_class)
|
|
19
|
-
|
|
20
|
-
assert
|
|
265
|
+
|
|
266
|
+
assert (
|
|
267
|
+
reason is None
|
|
268
|
+
), f"Reason for invalid found, expected none to be found. The reason found: {reason}"
|
|
21
269
|
assert is_valid is True, "File detected as invalid, expected to be valid"
|
|
22
270
|
|
|
23
271
|
|
|
@@ -51,8 +299,7 @@ def test_check_file_value_error():
|
|
|
51
299
|
|
|
52
300
|
def test_check_file_assertion_error():
|
|
53
301
|
mock_artefact_class = MagicMock()
|
|
54
|
-
mock_artefact_class.deserialize.side_effect = AssertionError(
|
|
55
|
-
"Assertion error")
|
|
302
|
+
mock_artefact_class.deserialize.side_effect = AssertionError("Assertion error")
|
|
56
303
|
|
|
57
304
|
with patch("builtins.open", mock_open(read_data="invalid content")):
|
|
58
305
|
is_valid, reason = check_file("dummy_path", mock_artefact_class)
|
|
@@ -78,6 +325,7 @@ def test_check_file_unexpected_error():
|
|
|
78
325
|
assert is_valid is False
|
|
79
326
|
assert "Unexpected error: Exception('Unexpected error')" in reason
|
|
80
327
|
|
|
328
|
+
|
|
81
329
|
# Tests for find_invalid_files
|
|
82
330
|
|
|
83
331
|
|
|
@@ -86,32 +334,37 @@ def test_find_invalid_files():
|
|
|
86
334
|
mock_artefact_class = MagicMock()
|
|
87
335
|
classified_info = {
|
|
88
336
|
"test_classifier": [
|
|
89
|
-
{"file_path": "file1.txt"},
|
|
90
|
-
{"file_path": "file2.txt"},
|
|
91
|
-
{"file_path": "templates/file3.txt"},
|
|
92
|
-
{"file_path": "some/path/file.data"}
|
|
337
|
+
{"file_path": "file1.txt"}, # Should be checked
|
|
338
|
+
{"file_path": "file2.txt"}, # Should be checked
|
|
339
|
+
{"file_path": "templates/file3.txt"}, # Should be skipped
|
|
340
|
+
{"file_path": "some/path/file.data"}, # Should be skipped
|
|
93
341
|
]
|
|
94
342
|
}
|
|
95
|
-
|
|
96
|
-
with patch(
|
|
97
|
-
|
|
343
|
+
|
|
344
|
+
with patch(
|
|
345
|
+
"ara_cli.artefact_models.artefact_mapping.artefact_type_mapping",
|
|
346
|
+
{"test_classifier": mock_artefact_class},
|
|
347
|
+
):
|
|
98
348
|
with patch("ara_cli.artefact_scan.check_file") as mock_check_file:
|
|
99
349
|
mock_check_file.side_effect = [
|
|
100
|
-
(True, None),
|
|
101
|
-
(False, "Invalid content")
|
|
350
|
+
(True, None), # for file1.txt
|
|
351
|
+
(False, "Invalid content"), # for file2.txt
|
|
102
352
|
]
|
|
103
353
|
|
|
104
354
|
invalid_files = find_invalid_files(classified_info, "test_classifier")
|
|
105
|
-
|
|
355
|
+
|
|
106
356
|
assert len(invalid_files) == 1
|
|
107
357
|
assert invalid_files[0] == ("file2.txt", "Invalid content")
|
|
108
358
|
assert mock_check_file.call_count == 2
|
|
109
|
-
|
|
359
|
+
|
|
110
360
|
# Check that check_file was called with the correct parameters
|
|
111
|
-
mock_check_file.assert_has_calls(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
361
|
+
mock_check_file.assert_has_calls(
|
|
362
|
+
[
|
|
363
|
+
call("file1.txt", mock_artefact_class, classified_info),
|
|
364
|
+
call("file2.txt", mock_artefact_class, classified_info),
|
|
365
|
+
],
|
|
366
|
+
any_order=False,
|
|
367
|
+
)
|
|
115
368
|
|
|
116
369
|
|
|
117
370
|
def test_show_results_no_issues(capsys):
|
|
@@ -120,18 +373,20 @@ def test_show_results_no_issues(capsys):
|
|
|
120
373
|
show_results(invalid_artefacts)
|
|
121
374
|
captured = capsys.readouterr()
|
|
122
375
|
assert captured.out == "All files are good!\n"
|
|
123
|
-
m.assert_called_once_with(
|
|
376
|
+
m.assert_called_once_with(
|
|
377
|
+
"incompatible_artefacts_report.md", "w", encoding="utf-8"
|
|
378
|
+
)
|
|
124
379
|
handle = m()
|
|
125
|
-
handle.write.assert_has_calls(
|
|
126
|
-
call("# Artefact Check Report\n\n"),
|
|
127
|
-
|
|
128
|
-
|
|
380
|
+
handle.write.assert_has_calls(
|
|
381
|
+
[call("# Artefact Check Report\n\n"), call("No problems found.\n")],
|
|
382
|
+
any_order=False,
|
|
383
|
+
)
|
|
129
384
|
|
|
130
385
|
|
|
131
386
|
def test_show_results_with_issues(capsys):
|
|
132
387
|
invalid_artefacts = {
|
|
133
388
|
"classifier1": [("file1.txt", "reason1"), ("file2.txt", "reason2")],
|
|
134
|
-
"classifier2": [("file3.txt", "reason3")]
|
|
389
|
+
"classifier2": [("file3.txt", "reason3")],
|
|
135
390
|
}
|
|
136
391
|
with patch("builtins.open", mock_open()) as m:
|
|
137
392
|
show_results(invalid_artefacts)
|
|
@@ -147,7 +402,9 @@ def test_show_results_with_issues(capsys):
|
|
|
147
402
|
"\t\treason3\n"
|
|
148
403
|
)
|
|
149
404
|
assert captured.out == expected_output
|
|
150
|
-
m.assert_called_once_with(
|
|
405
|
+
m.assert_called_once_with(
|
|
406
|
+
"incompatible_artefacts_report.md", "w", encoding="utf-8"
|
|
407
|
+
)
|
|
151
408
|
handle = m()
|
|
152
409
|
expected_writes = [
|
|
153
410
|
call("# Artefact Check Report\n\n"),
|
|
@@ -157,7 +414,7 @@ def test_show_results_with_issues(capsys):
|
|
|
157
414
|
call("\n"),
|
|
158
415
|
call("## classifier2\n"),
|
|
159
416
|
call("- `file3.txt`: reason3\n"),
|
|
160
|
-
call("\n")
|
|
417
|
+
call("\n"),
|
|
161
418
|
]
|
|
162
419
|
handle.write.assert_has_calls(expected_writes, any_order=False)
|
|
163
420
|
|
|
@@ -166,7 +423,7 @@ def test_check_file_with_invalid_contribution():
|
|
|
166
423
|
"""Tests file with invalid contribution reference."""
|
|
167
424
|
mock_artefact_instance = MagicMock()
|
|
168
425
|
mock_artefact_instance.title = "dummy_path"
|
|
169
|
-
|
|
426
|
+
|
|
170
427
|
# Set up invalid contribution
|
|
171
428
|
mock_contribution = MagicMock()
|
|
172
429
|
mock_contribution.classifier = "test_classifier"
|
|
@@ -178,12 +435,16 @@ def test_check_file_with_invalid_contribution():
|
|
|
178
435
|
|
|
179
436
|
# Mock classified_artefact_info
|
|
180
437
|
classified_info = {"test_classifier": [{"name": "existing_artefact"}]}
|
|
181
|
-
|
|
438
|
+
|
|
182
439
|
# Mock extract_artefact_names_of_classifier to return a list without the referenced artefact
|
|
183
440
|
with patch("builtins.open", mock_open(read_data="valid content")):
|
|
184
|
-
with patch(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
441
|
+
with patch(
|
|
442
|
+
"ara_cli.artefact_fuzzy_search.extract_artefact_names_of_classifier",
|
|
443
|
+
return_value=["existing_artefact"],
|
|
444
|
+
):
|
|
445
|
+
is_valid, reason = check_file(
|
|
446
|
+
"dummy_path.feature", mock_artefact_class, classified_info
|
|
447
|
+
)
|
|
448
|
+
|
|
188
449
|
assert is_valid is False
|
|
189
450
|
assert "Invalid Contribution Reference" in reason
|
tests/test_chat.py
CHANGED
|
@@ -13,8 +13,8 @@ from ara_cli.ara_config import ConfigManager
|
|
|
13
13
|
def get_default_config():
|
|
14
14
|
return SimpleNamespace(
|
|
15
15
|
ext_code_dirs=[
|
|
16
|
-
{"
|
|
17
|
-
{"
|
|
16
|
+
{"source_dir": "./src"},
|
|
17
|
+
{"source_dir": "./tests"},
|
|
18
18
|
],
|
|
19
19
|
glossary_dir="./glossary",
|
|
20
20
|
doc_dir="./docs",
|
|
@@ -1052,19 +1052,39 @@ def test_do_SEND(temp_chat_file):
|
|
|
1052
1052
|
mock_send_message.assert_called_once()
|
|
1053
1053
|
|
|
1054
1054
|
|
|
1055
|
-
|
|
1056
|
-
|
|
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
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
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
|
-
|
|
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>
|
ara_cli/templates/template.epic
DELETED
|
@@ -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>
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
@sample_tag
|
|
2
|
-
Example: <descriptive title>
|
|
3
|
-
|
|
4
|
-
Illustrates <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
|
-
Description: <further optional description to understand the rule, no format defined, the example artefact is only a placeholder>
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
@sample_tag
|
|
2
|
-
Feature: <descriptive title>
|
|
3
|
-
|
|
4
|
-
As a <user>
|
|
5
|
-
I want to <do something | need something>
|
|
6
|
-
So that <I can achieve something>
|
|
7
|
-
|
|
8
|
-
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>
|
|
9
|
-
|
|
10
|
-
Description: <further optional description to understand
|
|
11
|
-
the rule, no format defined, the example artefact is only a placeholder>
|
|
12
|
-
|
|
13
|
-
Scenario: <descriptive scenario title>
|
|
14
|
-
Given <precondition>
|
|
15
|
-
When <action>
|
|
16
|
-
Then <expected result>
|
|
17
|
-
|
|
18
|
-
Scenario Outline: <descriptive scenario title>
|
|
19
|
-
Given <precondition>
|
|
20
|
-
When <action>
|
|
21
|
-
Then <expected result>
|
|
22
|
-
|
|
23
|
-
Examples:
|
|
24
|
-
| descriptive scenario title | precondition | action | expected result |
|
|
25
|
-
| <example title 1> | <example precond. 1> | <example action 1> | <example result 1> |
|
|
26
|
-
| <example title 2> | <example precond. 2> | <example action 2> | <example result 2> |
|
ara_cli/templates/template.issue
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
@sample_tag
|
|
2
|
-
Issue: <descriptive issue>
|
|
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> | blank | filled with text that does not refer to an artefact or rule (then this a free issue)
|
|
5
|
-
|
|
6
|
-
*Optional descriptions of the issue in Gherkin style*
|
|
7
|
-
|
|
8
|
-
Given <descriptive text of the starting conditions where the issue occurs>
|
|
9
|
-
When <action under which the issues occurs>
|
|
10
|
-
Then <resulting behavior in contrast to the expected behavior>
|
|
11
|
-
|
|
12
|
-
*or optional free text description*
|
|
13
|
-
|
|
14
|
-
Description: <further free text description to understand the issue, no format defined>
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
@sample_tag
|
|
2
|
-
Keyfeature: <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 <support a capability or business goal>
|
|
7
|
-
As a <main stakeholder who will benefit>
|
|
8
|
-
I want <a product feature that helps me doing something so that I can achieve my named goal>
|
|
9
|
-
|
|
10
|
-
Description: <further optional description to understand the capability, markdown capable text formatting, best practice is using
|
|
11
|
-
GIVEN any precondition
|
|
12
|
-
AND another precondition
|
|
13
|
-
WHEN some action takes place
|
|
14
|
-
THEN some result is to be expected
|
|
15
|
-
AND some other result is to be expected>
|
ara_cli/templates/template.task
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
@to-do
|
|
2
|
-
Task: <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> | blank | filled with text that does not refer to an artefact or rule (then this is a free task)
|
|
5
|
-
|
|
6
|
-
Description: <further optional description to understand the task, no format defined>
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
@sample_tag
|
|
2
|
-
Userstory: <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
|
-
Estimate: <story points, scale?>
|
|
7
|
-
|
|
8
|
-
In order to <achieve a benefit>
|
|
9
|
-
As a <(user) role>
|
|
10
|
-
I want <a certain product behavior>
|
|
11
|
-
|
|
12
|
-
Rule: <rule needed to fulfill the wanted product behavior>
|
|
13
|
-
Rule: <rule needed to fulfill the wanted product behavior>
|
|
14
|
-
Rule: <rule needed to fulfill the wanted product behavior>
|
|
15
|
-
|
|
16
|
-
Description: <further optional description to understand
|
|
17
|
-
the user story, markdown capable text formatting>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
@sample_tag
|
|
2
|
-
Vision: <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
|
-
For <target customer>
|
|
7
|
-
Who <needs something>
|
|
8
|
-
The <product name> is a <product category>
|
|
9
|
-
That <key benefit, compelling reason to buy>
|
|
10
|
-
Unlike <primary competitive alternative>
|
|
11
|
-
Our product <statement of primary differentiation>
|
|
12
|
-
|
|
13
|
-
Description: <further optional description to understand
|
|
14
|
-
the vision, markdown capable text formatting>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|