ara-cli 0.1.9.50__py3-none-any.whl → 0.1.9.51__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/__main__.py +3 -1
- ara_cli/analyse_artefacts.py +133 -0
- ara_cli/ara_command_action.py +71 -50
- ara_cli/ara_command_parser.py +5 -0
- ara_cli/ara_config.py +65 -38
- ara_cli/artefact_lister.py +60 -49
- ara_cli/artefact_models/artefact_model.py +9 -7
- ara_cli/artefact_models/feature_artefact_model.py +4 -1
- ara_cli/artefact_reader.py +104 -57
- ara_cli/artefact_scan.py +46 -0
- ara_cli/file_classifier.py +21 -13
- ara_cli/prompt_extractor.py +10 -2
- ara_cli/tag_extractor.py +6 -16
- ara_cli/templates/specification_breakdown_files/template.concept.md +12 -14
- ara_cli/tests/test_ara_command_action.py +242 -108
- ara_cli/tests/test_artefact_lister.py +552 -183
- ara_cli/tests/test_artefact_reader.py +18 -46
- ara_cli/tests/test_artefact_scan.py +126 -0
- ara_cli/tests/test_file_classifier.py +68 -29
- ara_cli/tests/test_tag_extractor.py +42 -61
- ara_cli/version.py +1 -1
- {ara_cli-0.1.9.50.dist-info → ara_cli-0.1.9.51.dist-info}/METADATA +1 -1
- {ara_cli-0.1.9.50.dist-info → ara_cli-0.1.9.51.dist-info}/RECORD +26 -23
- {ara_cli-0.1.9.50.dist-info → ara_cli-0.1.9.51.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.9.50.dist-info → ara_cli-0.1.9.51.dist-info}/entry_points.txt +0 -0
- {ara_cli-0.1.9.50.dist-info → ara_cli-0.1.9.51.dist-info}/top_level.txt +0 -0
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
# Title: <descriptive title of the architecture concept>
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- [functional major decisions](#functional-major-decisions)
|
|
6
|
-
- [non functional major decisions](#non-functional-major-decisions)
|
|
7
|
-
- [Component Diagram in plantuml](#component-diagram-in-plantuml)
|
|
8
|
-
- [Deployment diagram in plantuml](#deployment-diagram-in-plantuml)
|
|
3
|
+
# C4 Context Diagram in mermaid
|
|
4
|
+
```
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
<list of major functional decisions of the concept>
|
|
6
|
+
```
|
|
7
|
+
Description: <description of the purpose and the value of the components of the diagram>
|
|
13
8
|
|
|
14
|
-
##
|
|
15
|
-
|
|
9
|
+
## C4 Container Diagram in mermaid
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Description: <description of the purpose and the value of the components of the diagram>
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
### C4 Component Diagram in mermaid
|
|
18
16
|
```
|
|
19
17
|
|
|
20
18
|
```
|
|
21
19
|
Description: <description of the purpose and the value of the components of the diagram>
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
#### C4 Code Diagram in mermaid
|
|
24
22
|
```
|
|
25
23
|
|
|
26
24
|
```
|
|
27
|
-
Description: <description of the purpose and the value of the
|
|
25
|
+
Description: <description of the purpose and the value of the components of the diagram>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import pytest
|
|
2
|
-
from unittest.mock import patch, MagicMock, call
|
|
2
|
+
from unittest.mock import patch, MagicMock, call, mock_open
|
|
3
3
|
from ara_cli.ara_command_action import (
|
|
4
4
|
check_validity,
|
|
5
5
|
create_action,
|
|
@@ -10,7 +10,8 @@ from ara_cli.ara_command_action import (
|
|
|
10
10
|
read_user_action,
|
|
11
11
|
set_status_action,
|
|
12
12
|
set_user_action,
|
|
13
|
-
classifier_directory_action
|
|
13
|
+
classifier_directory_action,
|
|
14
|
+
scan_action
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
|
|
@@ -100,7 +101,8 @@ def mock_suggest_close_name_matches():
|
|
|
100
101
|
|
|
101
102
|
@pytest.mark.parametrize(
|
|
102
103
|
"condition, error_message",
|
|
103
|
-
[(True, "This should not be printed"),
|
|
104
|
+
[(True, "This should not be printed"),
|
|
105
|
+
(False, "This is a test error message")],
|
|
104
106
|
)
|
|
105
107
|
def test_check_validity(condition, error_message):
|
|
106
108
|
with patch("sys.exit") as mock_exit, patch("builtins.print") as mock_print:
|
|
@@ -345,192 +347,259 @@ def test_list_action_creates_list_filter(
|
|
|
345
347
|
|
|
346
348
|
|
|
347
349
|
@pytest.mark.parametrize(
|
|
348
|
-
"classifier, artefact_name,
|
|
350
|
+
"classifier, artefact_name, artefact_exists, status, expected_output",
|
|
349
351
|
[
|
|
350
352
|
# Case when artefact_name is not found in artefact_names
|
|
351
|
-
("test_classifier", "non_existent_artefact",
|
|
353
|
+
("test_classifier", "non_existent_artefact", False, None, None),
|
|
352
354
|
|
|
353
355
|
# Case when artefact_name is found but no status is available
|
|
354
|
-
("test_classifier", "artefact1",
|
|
356
|
+
("test_classifier", "artefact1", True, None, "No status found"),
|
|
355
357
|
|
|
356
358
|
# Case when artefact_name is found and status is available
|
|
357
|
-
("test_classifier", "artefact2",
|
|
359
|
+
("test_classifier", "artefact2", True, "Active", "Active"),
|
|
358
360
|
]
|
|
359
361
|
)
|
|
360
|
-
def test_read_status_action(classifier, artefact_name,
|
|
362
|
+
def test_read_status_action(classifier, artefact_name, artefact_exists, status, expected_output):
|
|
361
363
|
args = MagicMock()
|
|
362
364
|
args.classifier = classifier
|
|
363
365
|
args.parameter = artefact_name
|
|
364
|
-
|
|
366
|
+
|
|
365
367
|
mock_artefact = MagicMock()
|
|
366
368
|
mock_artefact.status = status
|
|
367
369
|
|
|
370
|
+
# Create mock artefact info
|
|
371
|
+
artefact_info_dicts = []
|
|
372
|
+
if artefact_exists:
|
|
373
|
+
artefact_info_dicts.append({
|
|
374
|
+
"title": artefact_name,
|
|
375
|
+
"file_path": f"/path/to/{artefact_name}.md"
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
all_artefact_names = [info["title"] for info in artefact_info_dicts]
|
|
379
|
+
|
|
368
380
|
with patch('ara_cli.file_classifier.FileClassifier') as MockFileClassifier, \
|
|
369
|
-
patch('ara_cli.artefact_reader.ArtefactReader') as MockArtefactReader, \
|
|
370
381
|
patch('ara_cli.artefact_models.artefact_load.artefact_from_content') as mock_artefact_from_content, \
|
|
371
|
-
patch('ara_cli.ara_command_action.suggest_close_name_matches') as mock_suggest_close_name_matches
|
|
382
|
+
patch('ara_cli.ara_command_action.suggest_close_name_matches') as mock_suggest_close_name_matches, \
|
|
383
|
+
patch('builtins.open', new_callable=MagicMock()) as mock_open, \
|
|
384
|
+
patch('builtins.print') as mock_print:
|
|
372
385
|
|
|
386
|
+
# Configure file classifier mock
|
|
373
387
|
mock_classifier_instance = MockFileClassifier.return_value
|
|
374
|
-
mock_classifier_instance.
|
|
375
|
-
|
|
376
|
-
|
|
388
|
+
mock_classifier_instance.classify_files_new.return_value = {classifier: artefact_info_dicts}
|
|
389
|
+
|
|
390
|
+
# Configure file open mock
|
|
391
|
+
mock_file_handle = MagicMock()
|
|
392
|
+
mock_file_handle.__enter__.return_value.read.return_value = "mock file content"
|
|
393
|
+
mock_open.return_value = mock_file_handle
|
|
394
|
+
|
|
395
|
+
# Configure artefact mock
|
|
377
396
|
mock_artefact_from_content.return_value = mock_artefact
|
|
378
397
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
398
|
+
# Call the function
|
|
399
|
+
read_status_action(args)
|
|
400
|
+
|
|
401
|
+
# Verify behavior
|
|
402
|
+
if not artefact_exists:
|
|
403
|
+
# Should suggest close matches when artefact not found
|
|
404
|
+
mock_suggest_close_name_matches.assert_called_once_with(artefact_name, all_artefact_names)
|
|
405
|
+
mock_open.assert_not_called()
|
|
383
406
|
else:
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
407
|
+
# Should open the file and read content
|
|
408
|
+
mock_open.assert_called_once()
|
|
409
|
+
mock_artefact_from_content.assert_called_once_with("mock file content")
|
|
410
|
+
|
|
411
|
+
if status:
|
|
412
|
+
mock_print.assert_called_once_with(status)
|
|
413
|
+
else:
|
|
414
|
+
mock_print.assert_called_once_with("No status found")
|
|
388
415
|
|
|
389
416
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
("test_classifier", "non_existent_artefact", ["artefact1", "artefact2"], None, None, None),
|
|
394
|
-
("test_classifier", "artefact1", ["artefact1", "artefact2"], "some_content", [], "No user found"),
|
|
395
|
-
("test_classifier", "artefact2", ["artefact1", "artefact2"], "some_content", ["User1", "User2"],
|
|
396
|
-
" - User1\n - User2")
|
|
397
|
-
]
|
|
398
|
-
)
|
|
399
|
-
def test_read_user_action(classifier, artefact_name, artefact_names, content, user_tags, expected_output):
|
|
400
|
-
args = MagicMock()
|
|
401
|
-
args.classifier = classifier
|
|
402
|
-
args.parameter = artefact_name
|
|
403
|
-
|
|
404
|
-
mock_artefact = MagicMock()
|
|
405
|
-
mock_artefact.users = user_tags
|
|
417
|
+
def read_user_action(args):
|
|
418
|
+
from ara_cli.artefact_models.artefact_load import artefact_from_content
|
|
419
|
+
from ara_cli.file_classifier import FileClassifier
|
|
406
420
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
patch('ara_cli.artefact_models.artefact_load.artefact_from_content') as mock_artefact_from_content, \
|
|
410
|
-
patch('ara_cli.ara_command_action.suggest_close_name_matches') as mock_suggest_close_name_matches:
|
|
421
|
+
classifier = args.classifier
|
|
422
|
+
artefact_name = args.parameter
|
|
411
423
|
|
|
412
|
-
|
|
413
|
-
|
|
424
|
+
file_classifier = FileClassifier(os)
|
|
425
|
+
artefact_info = file_classifier.classify_files_new()
|
|
426
|
+
artefact_info_dicts = artefact_info.get(classifier, [])
|
|
414
427
|
|
|
415
|
-
|
|
416
|
-
|
|
428
|
+
all_artefact_names = [artefact_info["title"] for artefact_info in artefact_info_dicts]
|
|
429
|
+
if artefact_name not in all_artefact_names:
|
|
430
|
+
suggest_close_name_matches(artefact_name, all_artefact_names)
|
|
431
|
+
return
|
|
417
432
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
433
|
+
artefact_info = next(filter(
|
|
434
|
+
lambda x: x["title"] == artefact_name, artefact_info_dicts
|
|
435
|
+
))
|
|
436
|
+
|
|
437
|
+
with open(artefact_info["file_path"], 'r') as file:
|
|
438
|
+
content = file.read()
|
|
439
|
+
artefact = artefact_from_content(content)
|
|
440
|
+
|
|
441
|
+
user_tags = artefact.users
|
|
442
|
+
|
|
443
|
+
if not user_tags:
|
|
444
|
+
print("No user found")
|
|
445
|
+
return
|
|
446
|
+
for tag in user_tags:
|
|
447
|
+
print(f" - {tag}")
|
|
432
448
|
|
|
433
449
|
|
|
434
450
|
@pytest.mark.parametrize(
|
|
435
|
-
"classifier, artefact_name, artefact_names, new_status, content, expected_output, should_suggest",
|
|
451
|
+
"classifier, artefact_name, artefact_names, new_status, status_tags, content, expected_output, should_suggest",
|
|
436
452
|
[
|
|
437
453
|
# Case when artefact_name is not found in artefact_names
|
|
438
|
-
("test_classifier", "non_existent_artefact", ["artefact1", "artefact2"], "to-do", None, None, True),
|
|
454
|
+
("test_classifier", "non_existent_artefact", ["artefact1", "artefact2"], "to-do", ["to-do", "review", "done"], None, None, True),
|
|
439
455
|
|
|
440
456
|
# Case when new_status is invalid
|
|
441
|
-
("test_classifier", "artefact1", ["artefact1", "artefact2"], "invalid_status", "some_content", None, False),
|
|
457
|
+
("test_classifier", "artefact1", ["artefact1", "artefact2"], "invalid_status", ["to-do", "review", "done"], "some_content", None, False),
|
|
442
458
|
|
|
443
459
|
# Case when artefact_name is found and status is successfully changed
|
|
444
|
-
("test_classifier", "artefact1", ["artefact1", "artefact2"], "review", "
|
|
460
|
+
("test_classifier", "artefact1", ["artefact1", "artefact2"], "review", ["to-do", "review", "done"], "some_content",
|
|
461
|
+
"Status of task 'artefact1' has been updated to 'review'.", False),
|
|
445
462
|
|
|
446
463
|
# Case when new_status has a leading '@'
|
|
447
|
-
("test_classifier", "artefact1", ["artefact1", "artefact2"], "@done", "
|
|
464
|
+
("test_classifier", "artefact1", ["artefact1", "artefact2"], "@done", ["to-do", "review", "done"], "some_content",
|
|
465
|
+
"Status of task 'artefact1' has been updated to 'done'.", False),
|
|
448
466
|
]
|
|
449
467
|
)
|
|
450
|
-
def test_set_status_action(classifier, artefact_name, artefact_names, new_status, content, expected_output, should_suggest):
|
|
468
|
+
def test_set_status_action(classifier, artefact_name, artefact_names, new_status, status_tags, content, expected_output, should_suggest):
|
|
451
469
|
args = MagicMock()
|
|
452
470
|
args.classifier = classifier
|
|
453
471
|
args.parameter = artefact_name
|
|
454
472
|
args.new_status = new_status
|
|
455
473
|
|
|
474
|
+
# Create artefact info dictionaries for each artefact name
|
|
475
|
+
artefact_info_dicts = [
|
|
476
|
+
{"title": name, "file_path": f"/path/to/{name}.md"}
|
|
477
|
+
for name in artefact_names
|
|
478
|
+
]
|
|
479
|
+
|
|
456
480
|
mock_artefact = MagicMock()
|
|
457
481
|
mock_artefact.serialize.return_value = "serialized_content"
|
|
458
482
|
|
|
459
|
-
with patch('ara_cli.
|
|
460
|
-
patch('ara_cli.
|
|
483
|
+
with patch('ara_cli.artefact_models.artefact_model.ALLOWED_STATUS_VALUES', status_tags), \
|
|
484
|
+
patch('ara_cli.file_classifier.FileClassifier') as MockFileClassifier, \
|
|
461
485
|
patch('ara_cli.artefact_models.artefact_load.artefact_from_content') as mock_artefact_from_content, \
|
|
462
486
|
patch('ara_cli.ara_command_action.suggest_close_name_matches') as mock_suggest_close_name_matches, \
|
|
463
487
|
patch('builtins.open', new_callable=MagicMock) as mock_open, \
|
|
464
488
|
patch('ara_cli.ara_command_action.check_validity') as mock_check_validity:
|
|
465
489
|
|
|
490
|
+
# Configure mock file classifier
|
|
466
491
|
mock_classifier_instance = MockFileClassifier.return_value
|
|
467
|
-
mock_classifier_instance.
|
|
468
|
-
|
|
469
|
-
|
|
492
|
+
mock_classifier_instance.classify_files_new.return_value = {classifier: artefact_info_dicts}
|
|
493
|
+
|
|
494
|
+
# Configure mock file handling
|
|
495
|
+
mock_file_handle = MagicMock()
|
|
496
|
+
mock_file_handle.__enter__.return_value.read.return_value = content
|
|
497
|
+
mock_open.return_value = mock_file_handle
|
|
498
|
+
|
|
499
|
+
# Configure artefact loading
|
|
470
500
|
mock_artefact_from_content.return_value = mock_artefact
|
|
471
501
|
|
|
502
|
+
# Run the function under test
|
|
472
503
|
if expected_output:
|
|
473
504
|
with patch('builtins.print') as mock_print:
|
|
474
505
|
set_status_action(args)
|
|
506
|
+
|
|
507
|
+
# Verify the status was set on the artefact
|
|
508
|
+
assert mock_artefact.status == new_status.lstrip('@') if new_status.startswith('@') else new_status
|
|
509
|
+
|
|
510
|
+
# Verify the file was opened for reading and writing
|
|
511
|
+
expected_file_path = next(info["file_path"] for info in artefact_info_dicts if info["title"] == artefact_name)
|
|
512
|
+
mock_open.assert_any_call(expected_file_path, 'r')
|
|
513
|
+
mock_open.assert_any_call(expected_file_path, 'w')
|
|
514
|
+
|
|
515
|
+
# Verify the serialized content was written
|
|
516
|
+
mock_file_handle.__enter__.return_value.write.assert_called_once_with("serialized_content")
|
|
517
|
+
|
|
518
|
+
# Verify the success message was printed
|
|
475
519
|
mock_print.assert_called_once_with(expected_output)
|
|
476
|
-
mock_open.assert_called_once_with("dummy/path", 'w')
|
|
477
520
|
else:
|
|
478
521
|
set_status_action(args)
|
|
479
522
|
if should_suggest:
|
|
523
|
+
# Should suggest close matches when artefact not found
|
|
480
524
|
mock_suggest_close_name_matches.assert_called_once_with(artefact_name, artefact_names)
|
|
525
|
+
mock_artefact_from_content.assert_not_called()
|
|
481
526
|
else:
|
|
482
|
-
|
|
527
|
+
# Should validate the status
|
|
528
|
+
mock_check_validity.assert_called_once_with(
|
|
529
|
+
new_status.lstrip('@') if new_status.startswith('@') else new_status in status_tags,
|
|
530
|
+
"Invalid status provided. Please provide a valid status."
|
|
531
|
+
)
|
|
483
532
|
|
|
484
533
|
|
|
485
534
|
@pytest.mark.parametrize(
|
|
486
|
-
"
|
|
535
|
+
"classifier, artefact_name, artefact_names, new_user, expected_output",
|
|
487
536
|
[
|
|
488
|
-
|
|
489
|
-
(
|
|
490
|
-
|
|
491
|
-
(
|
|
537
|
+
# Normal case
|
|
538
|
+
("test_classifier", "valid_artefact", ["valid_artefact"], "john_doe", "User of task 'valid_artefact' has been updated to 'john_doe'."),
|
|
539
|
+
# Case with @ prefix in user name
|
|
540
|
+
("test_classifier", "valid_artefact", ["valid_artefact"], "@john_doe", "User of task 'valid_artefact' has been updated to 'john_doe'."),
|
|
541
|
+
# Case where artefact is not found
|
|
542
|
+
("test_classifier", "invalid_artefact", ["valid_artefact"], "john_doe", None),
|
|
492
543
|
],
|
|
493
544
|
)
|
|
494
545
|
def test_set_user_action(
|
|
495
|
-
|
|
496
|
-
mock_artefact_reader,
|
|
497
|
-
mock_artefact,
|
|
498
|
-
mock_suggest_close_name_matches,
|
|
499
|
-
artefact_names,
|
|
500
|
-
artefact_tags,
|
|
501
|
-
new_user,
|
|
502
|
-
expected_tags,
|
|
503
|
-
expected_output,
|
|
546
|
+
classifier, artefact_name, artefact_names, new_user, expected_output
|
|
504
547
|
):
|
|
505
|
-
MockFileClassifier = mock_file_classifier
|
|
506
|
-
MockArtefactReader = mock_artefact_reader
|
|
507
|
-
MockArtefact = mock_artefact
|
|
508
|
-
|
|
509
548
|
args = MagicMock()
|
|
510
|
-
args.classifier =
|
|
511
|
-
args.parameter =
|
|
549
|
+
args.classifier = classifier
|
|
550
|
+
args.parameter = artefact_name
|
|
512
551
|
args.new_user = new_user
|
|
513
552
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
"
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
553
|
+
# Create artefact info dictionaries for each artefact name
|
|
554
|
+
artefact_info_dicts = [
|
|
555
|
+
{"title": name, "file_path": f"/path/to/{name}.md"}
|
|
556
|
+
for name in artefact_names
|
|
557
|
+
]
|
|
558
|
+
|
|
559
|
+
mock_artefact = MagicMock()
|
|
560
|
+
mock_artefact.serialize.return_value = "serialized_content"
|
|
561
|
+
|
|
562
|
+
mock_file_content = "mock file content"
|
|
520
563
|
|
|
521
|
-
|
|
522
|
-
|
|
564
|
+
with patch('ara_cli.file_classifier.FileClassifier') as MockFileClassifier, \
|
|
565
|
+
patch('ara_cli.artefact_models.artefact_load.artefact_from_content') as mock_artefact_from_content, \
|
|
566
|
+
patch('ara_cli.ara_command_action.suggest_close_name_matches') as mock_suggest_close_name_matches, \
|
|
567
|
+
patch('builtins.open', new_callable=MagicMock()) as mock_open, \
|
|
568
|
+
patch('builtins.print') as mock_print:
|
|
569
|
+
|
|
570
|
+
# Configure mocks
|
|
571
|
+
mock_file_classifier_instance = MockFileClassifier.return_value
|
|
572
|
+
mock_file_classifier_instance.classify_files_new.return_value = {classifier: artefact_info_dicts}
|
|
573
|
+
|
|
574
|
+
mock_file_handle = MagicMock()
|
|
575
|
+
mock_file_handle.__enter__.return_value.read.return_value = mock_file_content
|
|
576
|
+
mock_open.return_value = mock_file_handle
|
|
577
|
+
|
|
578
|
+
mock_artefact_from_content.return_value = mock_artefact
|
|
523
579
|
|
|
524
|
-
|
|
580
|
+
# Call the function
|
|
525
581
|
set_user_action(args)
|
|
526
582
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
583
|
+
# Verify behavior
|
|
584
|
+
if artefact_name not in artefact_names:
|
|
585
|
+
# Should suggest close matches when artefact not found
|
|
586
|
+
mock_suggest_close_name_matches.assert_called_once_with(artefact_name, artefact_names)
|
|
587
|
+
mock_artefact_from_content.assert_not_called()
|
|
530
588
|
else:
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
)
|
|
589
|
+
# Should open the file and read content
|
|
590
|
+
expected_file_path = next(info["file_path"] for info in artefact_info_dicts if info["title"] == artefact_name)
|
|
591
|
+
mock_open.assert_any_call(expected_file_path, 'r')
|
|
592
|
+
mock_artefact_from_content.assert_called_once_with(mock_file_content)
|
|
593
|
+
|
|
594
|
+
# Should set the users attribute on the artefact
|
|
595
|
+
assert mock_artefact.users == [new_user.lstrip('@') if new_user.startswith('@') else new_user]
|
|
596
|
+
|
|
597
|
+
# Should write the serialized content back to the file
|
|
598
|
+
mock_open.assert_any_call(expected_file_path, 'w')
|
|
599
|
+
mock_file_handle.__enter__.return_value.write.assert_called_once_with("serialized_content")
|
|
600
|
+
|
|
601
|
+
# Should print a success message
|
|
602
|
+
mock_print.assert_called_once_with(expected_output)
|
|
534
603
|
|
|
535
604
|
|
|
536
605
|
@pytest.mark.parametrize(
|
|
@@ -550,3 +619,68 @@ def test_classifier_directory_action(mock_classifier_get_sub_directory, classifi
|
|
|
550
619
|
classifier_directory_action(args)
|
|
551
620
|
mock_classifier_get_sub_directory.assert_called_once_with(classifier)
|
|
552
621
|
mock_print.assert_called_once_with(expected_subdirectory)
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def test_scan_action_with_issues(capsys):
|
|
625
|
+
args = MagicMock()
|
|
626
|
+
with patch("ara_cli.file_classifier.FileClassifier") as MockFileClassifier, \
|
|
627
|
+
patch("ara_cli.artefact_scan.find_invalid_files") as mock_find_invalid_files, \
|
|
628
|
+
patch("builtins.open", mock_open()) as m:
|
|
629
|
+
|
|
630
|
+
mock_classifier = MockFileClassifier.return_value
|
|
631
|
+
mock_classifier.classify_files_new.return_value = {
|
|
632
|
+
"classifier1": ["file1.txt"],
|
|
633
|
+
"classifier2": ["file2.txt"]
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
def find_invalid_side_effect(artefact_files, classifier):
|
|
637
|
+
if classifier == "classifier1":
|
|
638
|
+
return [("file1.txt", "reason1")]
|
|
639
|
+
elif classifier == "classifier2":
|
|
640
|
+
return []
|
|
641
|
+
|
|
642
|
+
mock_find_invalid_files.side_effect = find_invalid_side_effect
|
|
643
|
+
|
|
644
|
+
scan_action(args)
|
|
645
|
+
|
|
646
|
+
captured = capsys.readouterr()
|
|
647
|
+
expected_output = (
|
|
648
|
+
"\nIncompatible classifier1 Files:\n"
|
|
649
|
+
"\t- file1.txt\n"
|
|
650
|
+
)
|
|
651
|
+
assert captured.out == expected_output
|
|
652
|
+
m.assert_called_once_with("incompatible_artefacts_report.md", "w")
|
|
653
|
+
handle = m()
|
|
654
|
+
expected_writes = [
|
|
655
|
+
call("# Artefact Check Report\n\n"),
|
|
656
|
+
call("## classifier1\n"),
|
|
657
|
+
call("- `file1.txt`: reason1\n"),
|
|
658
|
+
call("\n")
|
|
659
|
+
]
|
|
660
|
+
handle.write.assert_has_calls(expected_writes, any_order=False)
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def test_scan_action_all_good(capsys):
|
|
664
|
+
args = MagicMock()
|
|
665
|
+
with patch("ara_cli.file_classifier.FileClassifier") as MockFileClassifier, \
|
|
666
|
+
patch("ara_cli.artefact_scan.find_invalid_files") as mock_find_invalid_files, \
|
|
667
|
+
patch("builtins.open", mock_open()) as m:
|
|
668
|
+
|
|
669
|
+
mock_classifier = MockFileClassifier.return_value
|
|
670
|
+
mock_classifier.classify_files_new.return_value = {
|
|
671
|
+
"classifier1": ["file1.txt"],
|
|
672
|
+
"classifier2": ["file2.txt"]
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
mock_find_invalid_files.return_value = []
|
|
676
|
+
|
|
677
|
+
scan_action(args)
|
|
678
|
+
|
|
679
|
+
captured = capsys.readouterr()
|
|
680
|
+
assert captured.out == "All files are good!\n"
|
|
681
|
+
m.assert_called_once_with("incompatible_artefacts_report.md", "w")
|
|
682
|
+
handle = m()
|
|
683
|
+
handle.write.assert_has_calls([
|
|
684
|
+
call("# Artefact Check Report\n\n"),
|
|
685
|
+
call("No problems found.\n")
|
|
686
|
+
], any_order=False)
|