ara-cli 0.1.10.0__py3-none-any.whl → 0.1.13.3__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/__init__.py +51 -6
- ara_cli/__main__.py +270 -103
- ara_cli/ara_command_action.py +106 -63
- ara_cli/ara_config.py +187 -128
- ara_cli/ara_subcommands/__init__.py +0 -0
- ara_cli/ara_subcommands/autofix.py +26 -0
- ara_cli/ara_subcommands/chat.py +27 -0
- ara_cli/ara_subcommands/classifier_directory.py +16 -0
- ara_cli/ara_subcommands/common.py +100 -0
- ara_cli/ara_subcommands/config.py +221 -0
- ara_cli/ara_subcommands/convert.py +43 -0
- ara_cli/ara_subcommands/create.py +75 -0
- ara_cli/ara_subcommands/delete.py +22 -0
- ara_cli/ara_subcommands/extract.py +22 -0
- ara_cli/ara_subcommands/fetch.py +41 -0
- ara_cli/ara_subcommands/fetch_agents.py +22 -0
- ara_cli/ara_subcommands/fetch_scripts.py +19 -0
- ara_cli/ara_subcommands/fetch_templates.py +19 -0
- ara_cli/ara_subcommands/list.py +139 -0
- ara_cli/ara_subcommands/list_tags.py +25 -0
- ara_cli/ara_subcommands/load.py +48 -0
- ara_cli/ara_subcommands/prompt.py +136 -0
- ara_cli/ara_subcommands/read.py +47 -0
- ara_cli/ara_subcommands/read_status.py +20 -0
- ara_cli/ara_subcommands/read_user.py +20 -0
- ara_cli/ara_subcommands/reconnect.py +27 -0
- ara_cli/ara_subcommands/rename.py +22 -0
- ara_cli/ara_subcommands/scan.py +14 -0
- ara_cli/ara_subcommands/set_status.py +22 -0
- ara_cli/ara_subcommands/set_user.py +22 -0
- ara_cli/ara_subcommands/template.py +16 -0
- ara_cli/artefact_autofix.py +154 -63
- ara_cli/artefact_converter.py +256 -0
- ara_cli/artefact_models/artefact_model.py +106 -25
- ara_cli/artefact_models/artefact_templates.py +20 -10
- ara_cli/artefact_models/epic_artefact_model.py +11 -2
- ara_cli/artefact_models/feature_artefact_model.py +31 -1
- ara_cli/artefact_models/userstory_artefact_model.py +15 -3
- ara_cli/artefact_scan.py +2 -2
- ara_cli/chat.py +283 -80
- ara_cli/chat_agent/__init__.py +0 -0
- ara_cli/chat_agent/agent_process_manager.py +155 -0
- ara_cli/chat_script_runner/__init__.py +0 -0
- ara_cli/chat_script_runner/script_completer.py +23 -0
- ara_cli/chat_script_runner/script_finder.py +41 -0
- ara_cli/chat_script_runner/script_lister.py +36 -0
- ara_cli/chat_script_runner/script_runner.py +36 -0
- ara_cli/chat_web_search/__init__.py +0 -0
- ara_cli/chat_web_search/web_search.py +263 -0
- ara_cli/commands/agent_run_command.py +98 -0
- ara_cli/commands/fetch_agents_command.py +106 -0
- ara_cli/commands/fetch_scripts_command.py +43 -0
- ara_cli/commands/fetch_templates_command.py +39 -0
- ara_cli/commands/fetch_templates_commands.py +39 -0
- ara_cli/commands/list_agents_command.py +39 -0
- ara_cli/commands/read_command.py +17 -4
- ara_cli/completers.py +180 -0
- ara_cli/constants.py +2 -0
- ara_cli/directory_navigator.py +37 -4
- ara_cli/file_loaders/text_file_loader.py +2 -2
- ara_cli/global_file_lister.py +5 -15
- ara_cli/llm_utils.py +58 -0
- ara_cli/prompt_chat.py +20 -4
- ara_cli/prompt_extractor.py +199 -76
- ara_cli/prompt_handler.py +160 -59
- ara_cli/tag_extractor.py +38 -18
- ara_cli/template_loader.py +3 -2
- ara_cli/template_manager.py +52 -21
- ara_cli/templates/global-scripts/hello_global.py +1 -0
- ara_cli/templates/prompt-modules/commands/add_scenarios_for_new_behaviour.feature_creation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/align_feature_with_implementation_changes.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/analyze_codebase_and_plan_tasks.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/choose_best_parent_artefact.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/create_tasks_from_artefact_content.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/create_tests_for_uncovered_modules.test_generation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/derive_features_from_video_description.feature_creation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/describe_agent_capabilities.agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
- ara_cli/templates/prompt-modules/commands/execute_scoped_todos_in_task.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/explain_single_file_purpose.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/extract_file_information_bullets.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/extract_general.commands.md +12 -0
- ara_cli/templates/prompt-modules/commands/extract_markdown.commands.md +11 -0
- ara_cli/templates/prompt-modules/commands/extract_python.commands.md +13 -0
- ara_cli/templates/prompt-modules/commands/feature_add_or_modifiy_specified_behavior.commands.md +36 -0
- ara_cli/templates/prompt-modules/commands/feature_generate_initial_specified_bevahior.commands.md +53 -0
- ara_cli/templates/prompt-modules/commands/fix_failing_behave_step_definitions.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/fix_failing_pytest_tests.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/general_instruction_policy.commands.md +47 -0
- ara_cli/templates/prompt-modules/commands/generate_and_fix_pytest_tests.test_generation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
- ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
- ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
- ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
- ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
- ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
- ara_cli/templates/prompt-modules/commands/suggest_next_story_child_tasks.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/summarize_or_transcribe_media.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/update_feature_to_match_implementation.feature_creation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/update_user_story_with_requirements.interview_agent.commands.md +1 -0
- ara_cli/version.py +1 -1
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/METADATA +34 -1
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/RECORD +123 -54
- tests/test_ara_command_action.py +31 -19
- tests/test_ara_config.py +177 -90
- tests/test_artefact_autofix.py +170 -97
- tests/test_artefact_autofix_integration.py +495 -0
- tests/test_artefact_converter.py +357 -0
- tests/test_artefact_extraction.py +564 -0
- tests/test_artefact_scan.py +1 -1
- tests/test_chat.py +162 -126
- tests/test_chat_givens_images.py +603 -0
- tests/test_chat_script_runner.py +454 -0
- tests/test_global_file_lister.py +1 -1
- tests/test_llm_utils.py +164 -0
- tests/test_prompt_chat.py +343 -0
- tests/test_prompt_extractor.py +683 -0
- tests/test_prompt_handler.py +12 -4
- tests/test_tag_extractor.py +19 -13
- tests/test_web_search.py +467 -0
- ara_cli/ara_command_parser.py +0 -605
- ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
- ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
- ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
- ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
- ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
- ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
- ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
- ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
- ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
- ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
- ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
- ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
- ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
- ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
- ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
- ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/entry_points.txt +0 -0
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/top_level.txt +0 -0
tests/test_artefact_autofix.py
CHANGED
|
@@ -85,6 +85,7 @@ def mock_contribution():
|
|
|
85
85
|
m.rule = "my_rule"
|
|
86
86
|
return m
|
|
87
87
|
|
|
88
|
+
|
|
88
89
|
@pytest.fixture
|
|
89
90
|
def mock_artefact(mock_contribution):
|
|
90
91
|
m = MagicMock()
|
|
@@ -177,8 +178,7 @@ def test_write_corrected_artefact():
|
|
|
177
178
|
def test_construct_prompt_for_task():
|
|
178
179
|
prompt = construct_prompt(ArtefactType.task, "some reason", "file.task", "text")
|
|
179
180
|
assert (
|
|
180
|
-
"For task artefacts, if the action items looks like template or empty"
|
|
181
|
-
in prompt
|
|
181
|
+
"For task artefacts, if the action items looks like template or empty" in prompt
|
|
182
182
|
)
|
|
183
183
|
|
|
184
184
|
|
|
@@ -417,9 +417,9 @@ def test_fix_title_mismatch_prefix_not_found(capsys, mock_artefact_class):
|
|
|
417
417
|
assert "Warning: Title prefix 'Feature:' not found" in capsys.readouterr().out
|
|
418
418
|
|
|
419
419
|
|
|
420
|
-
@patch("
|
|
421
|
-
def test_run_agent_exception_handling(
|
|
422
|
-
mock_agent_instance =
|
|
420
|
+
@patch("ara_cli.llm_utils.create_pydantic_ai_agent")
|
|
421
|
+
def test_run_agent_exception_handling(mock_create_agent):
|
|
422
|
+
mock_agent_instance = mock_create_agent.return_value
|
|
423
423
|
mock_agent_instance.run_sync.side_effect = Exception("Agent error")
|
|
424
424
|
with pytest.raises(Exception, match="Agent error"):
|
|
425
425
|
run_agent("prompt", MagicMock())
|
|
@@ -433,6 +433,7 @@ def test_ask_for_contribution_choice_valid(mock_input):
|
|
|
433
433
|
result = ask_for_contribution_choice(choices)
|
|
434
434
|
assert result == "choice1"
|
|
435
435
|
|
|
436
|
+
|
|
436
437
|
@patch("builtins.input", side_effect=["99"])
|
|
437
438
|
def test_ask_for_contribution_choice_out_of_range(mock_input, capsys):
|
|
438
439
|
"""Tests selecting a choice that is out of range."""
|
|
@@ -638,7 +639,7 @@ def test_apply_autofix_single_pass(
|
|
|
638
639
|
classifier="feature",
|
|
639
640
|
reason="any",
|
|
640
641
|
single_pass=True,
|
|
641
|
-
deterministic=False,
|
|
642
|
+
deterministic=False, # Disable fixes
|
|
642
643
|
non_deterministic=False,
|
|
643
644
|
classified_artefact_info=mock_classified_artefact_info,
|
|
644
645
|
)
|
|
@@ -652,7 +653,9 @@ def test_apply_autofix_single_pass(
|
|
|
652
653
|
|
|
653
654
|
@patch("ara_cli.artefact_autofix._update_rule")
|
|
654
655
|
@patch("ara_cli.artefact_autofix.populate_classified_artefact_info")
|
|
655
|
-
def test_fix_rule_with_rule(
|
|
656
|
+
def test_fix_rule_with_rule(
|
|
657
|
+
mock_populate, mock_update_rule, mock_artefact, mock_contribution, capsys
|
|
658
|
+
):
|
|
656
659
|
# Contribution has a rule
|
|
657
660
|
artefact_class = MagicMock()
|
|
658
661
|
artefact_class.deserialize.return_value = mock_artefact
|
|
@@ -680,9 +683,12 @@ def test_fix_rule_with_rule(mock_populate, mock_update_rule, mock_artefact, mock
|
|
|
680
683
|
# Result is the serialized text
|
|
681
684
|
assert result == "serialized-text"
|
|
682
685
|
|
|
686
|
+
|
|
683
687
|
@patch("ara_cli.artefact_autofix._update_rule")
|
|
684
688
|
@patch("ara_cli.artefact_autofix.populate_classified_artefact_info")
|
|
685
|
-
def test_fix_rule_without_rule(
|
|
689
|
+
def test_fix_rule_without_rule(
|
|
690
|
+
mock_populate, mock_update_rule, mock_artefact, mock_contribution, capsys
|
|
691
|
+
):
|
|
686
692
|
# Contribution rule becomes None after update
|
|
687
693
|
mock_contribution.rule = None
|
|
688
694
|
artefact_class = MagicMock()
|
|
@@ -700,6 +706,7 @@ def test_fix_rule_without_rule(mock_populate, mock_update_rule, mock_artefact, m
|
|
|
700
706
|
assert "without a rule" in capsys.readouterr().out
|
|
701
707
|
assert result == "serialized-text"
|
|
702
708
|
|
|
709
|
+
|
|
703
710
|
@patch("ara_cli.artefact_autofix.populate_classified_artefact_info")
|
|
704
711
|
def test_fix_rule_contribution_none_raises(mock_populate):
|
|
705
712
|
# artefact.contribution is None: should assert
|
|
@@ -717,38 +724,43 @@ def test_fix_rule_contribution_none_raises(mock_populate):
|
|
|
717
724
|
classified_artefact_info={},
|
|
718
725
|
)
|
|
719
726
|
|
|
727
|
+
|
|
720
728
|
def test_populate_classified_artefact_info_force_true():
|
|
721
729
|
"""Test populate_classified_artefact_info with force=True"""
|
|
722
|
-
with patch(
|
|
730
|
+
with patch("ara_cli.artefact_autofix.FileClassifier") as mock_classifier:
|
|
723
731
|
mock_instance = mock_classifier.return_value
|
|
724
732
|
mock_instance.classify_files.return_value = {"new": "data"}
|
|
725
|
-
|
|
733
|
+
|
|
726
734
|
result = populate_classified_artefact_info({"old": "data"}, force=True)
|
|
727
|
-
|
|
735
|
+
|
|
728
736
|
assert result == {"new": "data"}
|
|
729
737
|
mock_classifier.assert_called_once()
|
|
730
738
|
|
|
739
|
+
|
|
731
740
|
def test_populate_classified_artefact_info_none_input():
|
|
732
741
|
"""Test populate_classified_artefact_info with None input"""
|
|
733
|
-
with patch(
|
|
742
|
+
with patch("ara_cli.artefact_autofix.FileClassifier") as mock_classifier:
|
|
734
743
|
mock_instance = mock_classifier.return_value
|
|
735
744
|
mock_instance.classify_files.return_value = {"classified": "data"}
|
|
736
|
-
|
|
745
|
+
|
|
737
746
|
result = populate_classified_artefact_info(None)
|
|
738
|
-
|
|
747
|
+
|
|
739
748
|
assert result == {"classified": "data"}
|
|
740
749
|
mock_classifier.assert_called_once()
|
|
741
750
|
|
|
751
|
+
|
|
742
752
|
def test_parse_report_empty_content():
|
|
743
753
|
"""Test parse_report with empty content"""
|
|
744
754
|
assert parse_report("") == {}
|
|
745
755
|
|
|
756
|
+
|
|
746
757
|
def test_parse_report_missing_reason():
|
|
747
758
|
"""Test parse_report with missing reason in issue line"""
|
|
748
759
|
content = "# Artefact Check Report\n\n## feature\n- `file.feature`\n"
|
|
749
760
|
expected = {"feature": [("file.feature", "")]}
|
|
750
761
|
assert parse_report(content) == expected
|
|
751
762
|
|
|
763
|
+
|
|
752
764
|
def test_parse_report_multiple_classifiers():
|
|
753
765
|
"""Test parse_report with multiple classifiers"""
|
|
754
766
|
content = (
|
|
@@ -758,38 +770,44 @@ def test_parse_report_multiple_classifiers():
|
|
|
758
770
|
)
|
|
759
771
|
expected = {
|
|
760
772
|
"feature": [("file1.feature", "reason1")],
|
|
761
|
-
"task": [("file2.task", "reason2")]
|
|
773
|
+
"task": [("file2.task", "reason2")],
|
|
762
774
|
}
|
|
763
775
|
assert parse_report(content) == expected
|
|
764
776
|
|
|
777
|
+
|
|
765
778
|
def test_construct_prompt_non_task_artefact():
|
|
766
779
|
"""Test construct_prompt for non-task artefact types"""
|
|
767
|
-
prompt = construct_prompt(
|
|
780
|
+
prompt = construct_prompt(
|
|
781
|
+
ArtefactType.feature, "some reason", "file.feature", "text"
|
|
782
|
+
)
|
|
768
783
|
assert "For task artefacts" not in prompt
|
|
769
784
|
assert "some reason" in prompt
|
|
770
785
|
assert "file.feature" in prompt
|
|
771
786
|
|
|
772
|
-
|
|
773
|
-
|
|
787
|
+
|
|
788
|
+
@patch("ara_cli.llm_utils.create_pydantic_ai_agent")
|
|
789
|
+
def test_run_agent_success(mock_create_agent):
|
|
774
790
|
"""Test successful run_agent execution"""
|
|
775
|
-
mock_agent_instance =
|
|
791
|
+
mock_agent_instance = mock_create_agent.return_value
|
|
776
792
|
mock_result = MagicMock()
|
|
777
793
|
mock_result.output = "agent output"
|
|
778
794
|
mock_agent_instance.run_sync.return_value = mock_result
|
|
779
|
-
|
|
795
|
+
|
|
780
796
|
result = run_agent("test prompt", MagicMock())
|
|
781
|
-
|
|
797
|
+
|
|
782
798
|
assert result == "agent output"
|
|
783
|
-
|
|
799
|
+
mock_create_agent.assert_called_once()
|
|
800
|
+
|
|
784
801
|
|
|
785
802
|
def test_write_corrected_artefact_with_print(capsys):
|
|
786
803
|
"""Test write_corrected_artefact prints success message"""
|
|
787
804
|
with patch("builtins.open", mock_open()) as m:
|
|
788
805
|
write_corrected_artefact("file.feature", "corrected content")
|
|
789
|
-
|
|
806
|
+
|
|
790
807
|
captured = capsys.readouterr()
|
|
791
808
|
assert "Fixed artefact at file.feature" in captured.out
|
|
792
809
|
|
|
810
|
+
|
|
793
811
|
# Tests for the new scenario placeholder functions
|
|
794
812
|
def test_extract_scenario_block():
|
|
795
813
|
"""Test _extract_scenario_block function"""
|
|
@@ -798,15 +816,16 @@ def test_extract_scenario_block():
|
|
|
798
816
|
"Scenario: Test scenario",
|
|
799
817
|
" Given something",
|
|
800
818
|
" When something",
|
|
801
|
-
"Scenario: Another scenario"
|
|
819
|
+
"Scenario: Another scenario",
|
|
802
820
|
]
|
|
803
|
-
|
|
821
|
+
|
|
804
822
|
scenario_lines, next_index = _extract_scenario_block(lines, 1)
|
|
805
|
-
|
|
823
|
+
|
|
806
824
|
assert len(scenario_lines) == 3
|
|
807
825
|
assert scenario_lines[0] == "Scenario: Test scenario"
|
|
808
826
|
assert next_index == 4
|
|
809
827
|
|
|
828
|
+
|
|
810
829
|
def test_is_scenario_boundary():
|
|
811
830
|
"""Test _is_scenario_boundary function"""
|
|
812
831
|
assert _is_scenario_boundary("Scenario: test")
|
|
@@ -815,49 +834,50 @@ def test_is_scenario_boundary():
|
|
|
815
834
|
assert _is_scenario_boundary("Feature: test")
|
|
816
835
|
assert not _is_scenario_boundary("Given something")
|
|
817
836
|
|
|
837
|
+
|
|
818
838
|
def test_process_scenario_block_no_placeholders():
|
|
819
839
|
"""Test _process_scenario_block with no placeholders"""
|
|
820
|
-
scenario_lines = [
|
|
821
|
-
|
|
822
|
-
" Given something",
|
|
823
|
-
" When something"
|
|
824
|
-
]
|
|
825
|
-
|
|
840
|
+
scenario_lines = [" Scenario: Test", " Given something", " When something"]
|
|
841
|
+
|
|
826
842
|
result = _process_scenario_block(scenario_lines)
|
|
827
|
-
|
|
843
|
+
|
|
828
844
|
assert result == scenario_lines
|
|
829
845
|
|
|
846
|
+
|
|
830
847
|
def test_process_scenario_block_with_placeholders():
|
|
831
848
|
"""Test _process_scenario_block with placeholders"""
|
|
832
849
|
scenario_lines = [
|
|
833
850
|
" Scenario: Test",
|
|
834
851
|
" Given something with <placeholder>",
|
|
835
|
-
" When something with <another>"
|
|
852
|
+
" When something with <another>",
|
|
836
853
|
]
|
|
837
|
-
|
|
854
|
+
|
|
838
855
|
result = _process_scenario_block(scenario_lines)
|
|
839
|
-
|
|
856
|
+
|
|
840
857
|
assert "Scenario Outline:" in result[0]
|
|
841
858
|
assert "Examples:" in result[-3]
|
|
842
859
|
|
|
860
|
+
|
|
843
861
|
def test_get_line_indentation():
|
|
844
862
|
"""Test _get_line_indentation function"""
|
|
845
863
|
assert _get_line_indentation(" indented line") == " "
|
|
846
864
|
assert _get_line_indentation("no indent") == ""
|
|
847
865
|
assert _get_line_indentation(" two spaces") == " "
|
|
848
866
|
|
|
867
|
+
|
|
849
868
|
def test_extract_placeholders_from_scenario():
|
|
850
869
|
"""Test _extract_placeholders_from_scenario function"""
|
|
851
870
|
step_lines = [
|
|
852
871
|
" Given something with <placeholder1>",
|
|
853
872
|
" When something with <placeholder2>",
|
|
854
|
-
" Then something normal"
|
|
873
|
+
" Then something normal",
|
|
855
874
|
]
|
|
856
|
-
|
|
875
|
+
|
|
857
876
|
placeholders = _extract_placeholders_from_scenario(step_lines)
|
|
858
|
-
|
|
877
|
+
|
|
859
878
|
assert placeholders == {"placeholder1", "placeholder2"}
|
|
860
879
|
|
|
880
|
+
|
|
861
881
|
def test_extract_placeholders_with_docstring():
|
|
862
882
|
"""Test _extract_placeholders_from_scenario ignoring docstrings"""
|
|
863
883
|
step_lines = [
|
|
@@ -865,196 +885,249 @@ def test_extract_placeholders_with_docstring():
|
|
|
865
885
|
' """',
|
|
866
886
|
" Some docstring with <not_a_placeholder>",
|
|
867
887
|
' """',
|
|
868
|
-
" When something with <placeholder2>"
|
|
888
|
+
" When something with <placeholder2>",
|
|
869
889
|
]
|
|
870
|
-
|
|
890
|
+
|
|
871
891
|
placeholders = _extract_placeholders_from_scenario(step_lines)
|
|
872
|
-
|
|
892
|
+
|
|
873
893
|
assert placeholders == {"placeholder1", "placeholder2"}
|
|
874
894
|
|
|
895
|
+
|
|
875
896
|
def test_update_docstring_state():
|
|
876
897
|
"""Test _update_docstring_state function"""
|
|
877
898
|
assert _update_docstring_state('"""', False) == True
|
|
878
899
|
assert _update_docstring_state('"""', True) == False
|
|
879
|
-
assert _update_docstring_state(
|
|
880
|
-
assert _update_docstring_state(
|
|
900
|
+
assert _update_docstring_state("normal line", False) == False
|
|
901
|
+
assert _update_docstring_state("normal line", True) == True
|
|
902
|
+
|
|
881
903
|
|
|
882
904
|
def test_convert_to_scenario_outline():
|
|
883
905
|
"""Test _convert_to_scenario_outline function"""
|
|
884
906
|
scenario_lines = [
|
|
885
907
|
" Scenario: Test scenario",
|
|
886
908
|
" Given something",
|
|
887
|
-
" When something"
|
|
909
|
+
" When something",
|
|
888
910
|
]
|
|
889
911
|
placeholders = {"placeholder1", "placeholder2"}
|
|
890
|
-
|
|
912
|
+
|
|
891
913
|
result = _convert_to_scenario_outline(scenario_lines, placeholders, " ")
|
|
892
|
-
|
|
914
|
+
|
|
893
915
|
assert "Scenario Outline: Test scenario" in result[0]
|
|
894
916
|
assert "Examples:" in result[-3]
|
|
895
917
|
|
|
918
|
+
|
|
896
919
|
def test_create_examples_table():
|
|
897
920
|
"""Test _create_examples_table function"""
|
|
898
921
|
placeholders = {"param1", "param2"}
|
|
899
|
-
|
|
922
|
+
|
|
900
923
|
result = _create_examples_table(placeholders, " ")
|
|
901
|
-
|
|
924
|
+
|
|
902
925
|
assert len(result) == 3
|
|
903
926
|
assert "Examples:" in result[0]
|
|
904
927
|
assert "| param1 | param2 |" in result[1]
|
|
905
928
|
assert "<param1_value>" in result[2]
|
|
906
929
|
|
|
930
|
+
|
|
907
931
|
def test_fix_scenario_placeholder_mismatch_no_scenarios():
|
|
908
932
|
"""Test fix_scenario_placeholder_mismatch with no scenarios"""
|
|
909
933
|
artefact_text = "Feature: Test\nBackground:\n Given something"
|
|
910
|
-
|
|
911
|
-
result = fix_scenario_placeholder_mismatch(
|
|
912
|
-
|
|
934
|
+
|
|
935
|
+
result = fix_scenario_placeholder_mismatch(
|
|
936
|
+
"file.feature", artefact_text, MagicMock()
|
|
937
|
+
)
|
|
938
|
+
|
|
913
939
|
assert result == artefact_text
|
|
914
940
|
|
|
941
|
+
|
|
915
942
|
def test_fix_scenario_placeholder_mismatch_with_placeholders():
|
|
916
943
|
"""Test fix_scenario_placeholder_mismatch converting to outline"""
|
|
917
944
|
artefact_text = """Feature: Test
|
|
918
945
|
Scenario: Test scenario
|
|
919
946
|
Given something with <placeholder>
|
|
920
947
|
When something happens"""
|
|
921
|
-
|
|
922
|
-
result = fix_scenario_placeholder_mismatch(
|
|
923
|
-
|
|
948
|
+
|
|
949
|
+
result = fix_scenario_placeholder_mismatch(
|
|
950
|
+
"file.feature", artefact_text, MagicMock()
|
|
951
|
+
)
|
|
952
|
+
|
|
924
953
|
assert "Scenario Outline:" in result
|
|
925
954
|
assert "Examples:" in result
|
|
926
955
|
assert "<placeholder>" in result
|
|
927
956
|
|
|
957
|
+
|
|
928
958
|
def test_should_skip_issue_non_deterministic_false():
|
|
929
959
|
"""Test should_skip_issue when non_deterministic is False"""
|
|
930
960
|
result = should_skip_issue(None, True, False, "file.txt")
|
|
931
961
|
assert result == True
|
|
932
962
|
|
|
963
|
+
|
|
933
964
|
def test_should_skip_issue_deterministic_false():
|
|
934
965
|
"""Test should_skip_issue when deterministic is False"""
|
|
935
966
|
result = should_skip_issue("some_issue", False, True, "file.txt")
|
|
936
967
|
assert result == True
|
|
937
968
|
|
|
969
|
+
|
|
938
970
|
def test_should_skip_issue_no_skip():
|
|
939
971
|
"""Test should_skip_issue when no skip is needed"""
|
|
940
972
|
result = should_skip_issue("some_issue", True, True, "file.txt")
|
|
941
973
|
assert result == False
|
|
942
974
|
|
|
975
|
+
|
|
943
976
|
def test_determine_attempt_count_single_pass():
|
|
944
977
|
"""Test determine_attempt_count with single_pass=True"""
|
|
945
978
|
result = determine_attempt_count(True, "file.txt")
|
|
946
979
|
assert result == 1
|
|
947
980
|
|
|
981
|
+
|
|
948
982
|
def test_determine_attempt_count_multiple_pass():
|
|
949
983
|
"""Test determine_attempt_count with single_pass=False"""
|
|
950
984
|
result = determine_attempt_count(False, "file.txt")
|
|
951
985
|
assert result == 3
|
|
952
986
|
|
|
987
|
+
|
|
953
988
|
def test_apply_deterministic_fix_with_issue():
|
|
954
989
|
"""Test apply_deterministic_fix when deterministic issue exists"""
|
|
955
990
|
mock_fix_function = MagicMock(return_value="fixed_text")
|
|
956
991
|
deterministic_markers = {"test_issue": mock_fix_function}
|
|
957
|
-
|
|
992
|
+
|
|
958
993
|
result = apply_deterministic_fix(
|
|
959
|
-
True,
|
|
960
|
-
|
|
994
|
+
True,
|
|
995
|
+
"test_issue",
|
|
996
|
+
"file.txt",
|
|
997
|
+
"original",
|
|
998
|
+
MagicMock(),
|
|
999
|
+
{},
|
|
1000
|
+
deterministic_markers,
|
|
1001
|
+
"corrected",
|
|
961
1002
|
)
|
|
962
|
-
|
|
1003
|
+
|
|
963
1004
|
assert result == "fixed_text"
|
|
964
1005
|
mock_fix_function.assert_called_once()
|
|
965
1006
|
|
|
1007
|
+
|
|
966
1008
|
def test_apply_deterministic_fix_no_issue():
|
|
967
1009
|
"""Test apply_deterministic_fix when no deterministic issue"""
|
|
968
1010
|
result = apply_deterministic_fix(
|
|
969
|
-
True, None, "file.txt", "original", MagicMock(),
|
|
970
|
-
{}, {}, "corrected"
|
|
1011
|
+
True, None, "file.txt", "original", MagicMock(), {}, {}, "corrected"
|
|
971
1012
|
)
|
|
972
|
-
|
|
1013
|
+
|
|
973
1014
|
assert result == "corrected"
|
|
974
1015
|
|
|
975
|
-
|
|
976
|
-
@patch(
|
|
1016
|
+
|
|
1017
|
+
@patch("ara_cli.artefact_autofix.construct_prompt")
|
|
1018
|
+
@patch("ara_cli.artefact_autofix.run_agent")
|
|
977
1019
|
def test_apply_non_deterministic_fix_success(mock_run_agent, mock_construct_prompt):
|
|
978
1020
|
"""Test apply_non_deterministic_fix successful execution"""
|
|
979
1021
|
mock_construct_prompt.return_value = "test prompt"
|
|
980
1022
|
mock_artefact = MagicMock()
|
|
981
1023
|
mock_artefact.serialize.return_value = "fixed_text"
|
|
982
1024
|
mock_run_agent.return_value = mock_artefact
|
|
983
|
-
|
|
1025
|
+
|
|
984
1026
|
result = apply_non_deterministic_fix(
|
|
985
|
-
True,
|
|
986
|
-
|
|
1027
|
+
True,
|
|
1028
|
+
None,
|
|
1029
|
+
"corrected",
|
|
1030
|
+
MagicMock(),
|
|
1031
|
+
"reason",
|
|
1032
|
+
"file.txt",
|
|
1033
|
+
"original",
|
|
1034
|
+
MagicMock(),
|
|
987
1035
|
)
|
|
988
|
-
|
|
1036
|
+
|
|
989
1037
|
assert result == "fixed_text"
|
|
990
1038
|
|
|
1039
|
+
|
|
991
1040
|
def test_apply_non_deterministic_fix_with_deterministic_issue():
|
|
992
1041
|
"""Test apply_non_deterministic_fix when deterministic issue exists"""
|
|
993
1042
|
result = apply_non_deterministic_fix(
|
|
994
|
-
True,
|
|
995
|
-
"
|
|
1043
|
+
True,
|
|
1044
|
+
"some_issue",
|
|
1045
|
+
"corrected",
|
|
1046
|
+
MagicMock(),
|
|
1047
|
+
"reason",
|
|
1048
|
+
"file.txt",
|
|
1049
|
+
"original",
|
|
1050
|
+
MagicMock(),
|
|
996
1051
|
)
|
|
997
|
-
|
|
1052
|
+
|
|
998
1053
|
assert result == "corrected"
|
|
999
1054
|
|
|
1000
|
-
|
|
1001
|
-
@patch(
|
|
1002
|
-
|
|
1055
|
+
|
|
1056
|
+
@patch("ara_cli.artefact_autofix.construct_prompt")
|
|
1057
|
+
@patch("ara_cli.artefact_autofix.run_agent", side_effect=Exception("LLM Error"))
|
|
1058
|
+
def test_apply_non_deterministic_fix_exception(
|
|
1059
|
+
mock_run_agent, mock_construct_prompt, capsys
|
|
1060
|
+
):
|
|
1003
1061
|
"""Test apply_non_deterministic_fix with exception"""
|
|
1004
1062
|
mock_construct_prompt.return_value = "test prompt"
|
|
1005
|
-
|
|
1063
|
+
|
|
1006
1064
|
result = apply_non_deterministic_fix(
|
|
1007
|
-
True,
|
|
1008
|
-
|
|
1065
|
+
True,
|
|
1066
|
+
None,
|
|
1067
|
+
"corrected",
|
|
1068
|
+
MagicMock(),
|
|
1069
|
+
"reason",
|
|
1070
|
+
"file.txt",
|
|
1071
|
+
"original",
|
|
1072
|
+
MagicMock(),
|
|
1009
1073
|
)
|
|
1010
|
-
|
|
1074
|
+
|
|
1011
1075
|
assert result is None
|
|
1012
1076
|
assert "LLM agent failed" in capsys.readouterr().out
|
|
1013
1077
|
|
|
1014
|
-
|
|
1015
|
-
@patch(
|
|
1016
|
-
@patch(
|
|
1017
|
-
@patch(
|
|
1018
|
-
@patch(
|
|
1019
|
-
@patch(
|
|
1020
|
-
@patch(
|
|
1078
|
+
|
|
1079
|
+
@patch("ara_cli.artefact_autofix.check_file")
|
|
1080
|
+
@patch("ara_cli.artefact_autofix.read_artefact")
|
|
1081
|
+
@patch("ara_cli.artefact_autofix.write_corrected_artefact")
|
|
1082
|
+
@patch("ara_cli.artefact_autofix.populate_classified_artefact_info")
|
|
1083
|
+
@patch("ara_cli.artefact_autofix.should_skip_issue", return_value=False)
|
|
1084
|
+
@patch("ara_cli.artefact_autofix.apply_deterministic_fix")
|
|
1085
|
+
@patch("ara_cli.artefact_autofix.apply_non_deterministic_fix")
|
|
1021
1086
|
def test_attempt_autofix_loop_max_attempts_reached(
|
|
1022
|
-
mock_apply_non_det,
|
|
1023
|
-
|
|
1087
|
+
mock_apply_non_det,
|
|
1088
|
+
mock_apply_det,
|
|
1089
|
+
mock_should_skip,
|
|
1090
|
+
mock_populate,
|
|
1091
|
+
mock_write,
|
|
1092
|
+
mock_read,
|
|
1093
|
+
mock_check_file,
|
|
1094
|
+
capsys,
|
|
1024
1095
|
):
|
|
1025
1096
|
"""Test attempt_autofix_loop when max attempts are reached"""
|
|
1026
1097
|
mock_check_file.return_value = (False, "persistent error")
|
|
1027
1098
|
mock_read.return_value = "original text"
|
|
1028
1099
|
mock_apply_det.return_value = "modified text" # Ensure text is modified
|
|
1029
1100
|
mock_apply_non_det.return_value = "modified text" # Ensure text is modified
|
|
1030
|
-
|
|
1101
|
+
|
|
1031
1102
|
result = attempt_autofix_loop(
|
|
1032
1103
|
"file.txt", MagicMock(), MagicMock(), {}, 2, True, True, {}
|
|
1033
1104
|
)
|
|
1034
|
-
|
|
1105
|
+
|
|
1035
1106
|
assert result == False
|
|
1036
1107
|
assert "Failed to fix file.txt after 2 attempts" in capsys.readouterr().out
|
|
1037
1108
|
|
|
1038
|
-
|
|
1109
|
+
|
|
1110
|
+
@patch("ara_cli.artefact_autofix.check_file")
|
|
1039
1111
|
def test_attempt_autofix_loop_already_valid(mock_check_file, capsys):
|
|
1040
1112
|
"""Test attempt_autofix_loop when file is already valid"""
|
|
1041
1113
|
mock_check_file.return_value = (True, "")
|
|
1042
|
-
|
|
1114
|
+
|
|
1043
1115
|
result = attempt_autofix_loop(
|
|
1044
1116
|
"file.txt", MagicMock(), MagicMock(), {}, 3, True, True, {}
|
|
1045
1117
|
)
|
|
1046
|
-
|
|
1118
|
+
|
|
1047
1119
|
assert result == True
|
|
1048
1120
|
assert "is now valid" in capsys.readouterr().out
|
|
1049
1121
|
|
|
1050
|
-
|
|
1051
|
-
@patch(
|
|
1122
|
+
|
|
1123
|
+
@patch("ara_cli.artefact_autofix.check_file")
|
|
1124
|
+
@patch("ara_cli.artefact_autofix.read_artefact", return_value=None)
|
|
1052
1125
|
def test_attempt_autofix_loop_read_fails(mock_read, mock_check_file):
|
|
1053
1126
|
"""Test attempt_autofix_loop when reading artefact fails"""
|
|
1054
1127
|
mock_check_file.return_value = (False, "some error")
|
|
1055
|
-
|
|
1128
|
+
|
|
1056
1129
|
result = attempt_autofix_loop(
|
|
1057
1130
|
"file.txt", MagicMock(), MagicMock(), {}, 3, True, True, {}
|
|
1058
1131
|
)
|
|
1059
|
-
|
|
1060
|
-
assert result == False
|
|
1132
|
+
|
|
1133
|
+
assert result == False
|