Repository: DevOps-MBSE/AaC (language: python)

Issue:
📋 [TASK] - Improve Tests in Gen Plugin Unit Tests
### Parent Feature

#844

### Task Description

As an AaC Test Developer, I want to expand the test coverage in my unit tests, so that I can have more confidence in the robustness of my code.

### Acceptance Criteria

- [ ] Update test logic as necessary to expand test coverage. 
  - [ ] Happy path and sad path at a minimum
- [ ] (Stretch) Incorporate a behave feature file for testing if applicable.
- [ ] Run `flake8` on the targeted test directory and address findings.
- [ ] Document any identified gaps.

### Unresolved questions

_No response_


Discussion / hints:

Fix patch:
diff --git a/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff.aac b/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff.aac
index 5680002d0..1c805efc4 100644
--- a/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff.aac
+++ b/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff.aac
@@ -11,7 +11,7 @@ plugin:
         - name: Success test
           scenarios:
             - name: Test do-stuff command with valid input.
-              given: 
+              given:
                 - A path to an existing AaC file.
               when:
                 - The AaC do-stuff command is run with the path to the file.
@@ -20,9 +20,9 @@ plugin:
         - name: Failure test
           scenarios:
             - name: Test do-stuff command with invalid input.
-              given: 
+              given:
                 - An path that does not point to an existing file.
               when:
                 - The AaC do-stuff command is run on the file.
               then:
-                - The do-stuff command provides the output 'FAILURE'.
\ No newline at end of file
+                - The do-stuff command provides the output 'FAILURE'.
diff --git a/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff_bad.aac b/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff_bad.aac
new file mode 100644
index 000000000..3811517cb
--- /dev/null
+++ b/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff_bad.aac
@@ -0,0 +1,3 @@
+plugin:
+  name: My Plugin
+  package: aac_example.plugins.plugin_name
diff --git a/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff_overwrite.aac b/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff_overwrite.aac
new file mode 100644
index 000000000..8d7b70a2c
--- /dev/null
+++ b/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff_overwrite.aac
@@ -0,0 +1,28 @@
+plugin:
+  name: My Plugin
+  package: aac_example.plugins.plugin_name
+  commands:
+    - name: doing-stuff-overwrite
+      input:
+        - name: aac-file
+          type: file
+          description: The path to the architecture file with the plugin definition.
+      acceptance:
+        - name: Success test
+          scenarios:
+            - name: Test do-stuff command with valid input.
+              given:
+                - A path to an existing AaC file.
+              when:
+                - The AaC do-stuff command is run with the path to the file.
+              then:
+                - The do-stuff command provides the output 'SUCCESS'.
+        - name: Failure test
+          scenarios:
+            - name: Test do-stuff command with invalid input.
+              given:
+                - An path that does not point to an existing file.
+              when:
+                - The AaC do-stuff command is run on the file.
+              then:
+                - The do-stuff command provides the output 'FAILURE'.
diff --git a/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_project.aac b/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_project.aac
new file mode 100644
index 000000000..4e31c2a04
--- /dev/null
+++ b/python/features/docs_examples/dev_guide_plugin_dev_guide/docs_project.aac
@@ -0,0 +1,4 @@
+project:
+  name: My project
+  description: |
+    An AaC project definition used to test the gen-project command behavior.
diff --git a/python/features/plugins/gen_plugin.feature b/python/features/plugins/gen_plugin.feature
new file mode 100644
index 000000000..da829cb56
--- /dev/null
+++ b/python/features/plugins/gen_plugin.feature
@@ -0,0 +1,30 @@
+Feature:  Gen-Plugin Evaluation
+
+    Scenario: Generate From a Plugin File
+        Given I have a valid "./docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff.aac"
+        When gen-plugin is ran with the valid file
+        Then I should receive generated plugin files in a temporary directory
+
+    Scenario: Generate From a Plugin File Using overwrite
+        Given I have a valid "./docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff.aac"
+        When gen-plugin is ran with the valid file
+        And ran again with "./docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff_overwrite.aac" using overwrite flag
+        Then I should receive generated plugin files in a temporary directory
+
+    Scenario: Generate From a Plugin File Using evaluate
+        Given I have a valid "./docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff.aac"
+        When gen-plugin is ran with the valid file
+        And ran again with "./docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff_overwrite.aac" using evaluate flag
+        Then I should receive generated plugin files in a temporary directory
+
+
+    Scenario: Generate From a Project File
+        Given I have a valid "./docs_examples/dev_guide_plugin_dev_guide/docs_project.aac"
+        When gen-project is ran with the valid file
+        Then I should receive generated project files in a temporary directory
+
+    Scenario: Run Gen-Plugin on an incomplete plugin File
+        Given I have a valid "./docs_examples/dev_guide_plugin_dev_guide/docs_do_stuff_bad.aac"
+        When gen-plugin is ran with the valid file
+        Then I should receive a message that the command was not successful
+
diff --git a/python/features/steps/gen_plugin_steps.py b/python/features/steps/gen_plugin_steps.py
new file mode 100644
index 000000000..21c99b856
--- /dev/null
+++ b/python/features/steps/gen_plugin_steps.py
@@ -0,0 +1,223 @@
+"""General purpose steps for the behave gen-plugin tests."""""
+from behave import given, when, then, use_step_matcher
+import os
+from aac.execute.command_line import cli, initialize_cli
+from click.testing import CliRunner
+from shutil import rmtree
+from typing import Tuple
+
+use_step_matcher("cfparse")
+
+
+def run_cli_command_with_args(command_name: str, args: list[str]) -> Tuple[int, str]:
+    """
+    Utility function to invoke the CLI command with the given arguments.
+
+    Args:
+        command_name (str): CLI command to be executed.
+        args (list[str]): List of arguments associated with the CLI command.
+
+    Return:
+        Exit code and output message resulting from the execution of the CLI command.
+    """
+    initialize_cli()
+    runner = CliRunner()
+    result = runner.invoke(cli, [command_name] + args)
+    exit_code = result.exit_code
+    std_out = str(result.stdout)
+    output_message = std_out.strip().replace("\x1b[0m", "")
+    return exit_code, output_message
+
+
+def get_plugin_file(context, path: str) -> str:
+    """
+    Utility function to get the full path to the given model file.
+
+    Args:
+        context: Active context to check against.
+        path (str): Path to the model file being evaluated.
+
+    Return:
+        Valid path for the requested model file as tracked within the context.
+    """
+    return os.path.sep.join([context.config.paths[0], path])
+
+
+@given('I have a valid "{plugin_file}"')
+def gen_plugin_valid_check(context, plugin_file):
+    """
+    Run the check command on the given plugin file.
+
+    Args:
+        context: Active context to check against.
+        plugin_file (str): Path to plugin file being used for generation.
+    """
+    plugin_file_path = get_plugin_file(context, plugin_file)
+    exit_code, output_message = run_cli_command_with_args("check", [plugin_file_path])
+    if exit_code != 0:
+        raise AssertionError(f"Check cli command failed with message: {output_message}")
+    context.plugin_file_path = plugin_file_path
+    context.output_message = output_message
+
+
+@when("gen-plugin is ran with the valid file")
+def run_gen_plugin(context):
+    """
+    Run Gen-Plugin on a valid plugin file.
+
+    Args:
+        context: Active context to check against.
+    """
+    temp_dir = ("./temporary_directory")
+    if os.path.exists(temp_dir):
+        rmtree(temp_dir)
+    os.makedirs(temp_dir)
+    src_dir = os.path.join(temp_dir, "src")
+    os.makedirs(src_dir)
+    context.src_dir = src_dir
+    test_dir = os.path.join(temp_dir, "tests")
+    os.makedirs(test_dir)
+    context.test_dir = test_dir
+    context.temp_dir = temp_dir
+    plugin_args = [context.plugin_file_path, "--code-output", src_dir, "--test-output", test_dir, "--no-prompt"]
+    exit_code, output_message = run_cli_command_with_args(
+        "gen-plugin", plugin_args)
+
+    context.exit_code = exit_code
+    context.output_message = output_message
+
+
+@when('ran again with "{plugin_file_overwrite}" using overwrite flag')
+def run_gen_plugin_overwrite(context, plugin_file_overwrite):
+    """
+    Run Gen-Plugin again using the overwrite flag.
+
+    Args:
+        context: Active context to check against.
+        plugin_file_overwrite: The file used to overwrite the already existing plugin
+    """
+    src = os.path.join(context.src_dir, "aac_example/plugins/plugin_name")
+    file = open(os.path.join(src, "my_plugin_impl.py"), "r")
+    file_read = file.read()
+    assert "do_stuff(aac_file: str)" in file_read
+    assert "doing_stuff(aac_file: str)" not in file_read
+    file.close()
+
+    plugin_file_path = get_plugin_file(context, plugin_file_overwrite)
+    plugin_args = [plugin_file_path, "--code-output", context.src_dir, "--test-output", context.test_dir, "--no-prompt", "--force-overwrite"]
+    exit_code, output_message = run_cli_command_with_args(
+        "gen-plugin", plugin_args)
+    file = open(os.path.join(src, "my_plugin_impl.py"), "r")
+    file_read = file.read()
+    assert "do_stuff(aac_file: str)" not in file_read
+    assert "doing_stuff_overwrite(aac_file: str)" in file_read
+    file.close()
+
+
+@when('ran again with "{plugin_file_evaluate}" using evaluate flag')
+def run_gen_plugin_evaluate(context, plugin_file_evaluate):
+    """
+    Run Gen-Plugin again using the evaluate flag.
+
+    Args:
+        context: Active context to check against.
+        plugin_file_evaluate: The file used to check the evaluation flag
+    """
+    src = os.path.join(context.src_dir, "aac_example/plugins/plugin_name")
+    file = open(os.path.join(src, "my_plugin_impl.py"), "r")
+    file_read = file.read()
+    assert "do_stuff(aac_file: str)" in file_read
+    assert "doing_stuff(aac_file: str)" not in file_read
+    file.close()
+
+    plugin_file_path = get_plugin_file(context, plugin_file_evaluate)
+    plugin_args = [plugin_file_path, "--code-output", context.src_dir, "--test-output", context.test_dir, "--no-prompt", "--evaluate"]
+    exit_code, output_message = run_cli_command_with_args(
+        "gen-plugin", plugin_args)
+    file = open(os.path.join(src, "my_plugin_impl.py"), "r")
+    file_read = file.read()
+    assert "do_stuff(aac_file: str)" in file_read
+    assert "doing_stuff_overwrite(aac_file: str)" not in file_read
+    file.close()
+
+
+@when("gen-project is ran with the valid file")
+def run_gen_project(context):
+    """
+    Run Gen-Project on a valid plugin file.
+
+    Args:
+        context: Active context to check against.
+    """
+    temp_dir = ("./temporary_directory")
+    if os.path.exists(temp_dir):
+        rmtree(temp_dir)
+    os.makedirs(temp_dir)
+
+    context.temp_dir = temp_dir
+    plugin_args = [context.plugin_file_path, "--output", temp_dir, "--no-prompt"]
+    exit_code, output_message = run_cli_command_with_args(
+        "gen-project", plugin_args)
+
+    context.exit_code = exit_code
+    context.output_message = output_message
+
+
+@then("I should receive generated plugin files in a temporary directory")
+def gen_plugin_results(context):
+    """
+    Checks the output from the gen-plugin command.
+
+    Args:
+        context: Active context to check against.
+    """
+    assert "All AaC constraint checks were successful" in context.output_message
+    assert context.exit_code == 0
+    src = os.path.join(context.src_dir, "aac_example/plugins/plugin_name")
+    tests = os.path.join(context.test_dir, "test_aac_example/plugins/plugin_name")
+    # make sure the files were created correctly
+    assert (os.path.exists(os.path.join(src, "__init__.py")))
+    assert (os.path.exists(os.path.join(src, "my_plugin_impl.py")))
+    assert (os.path.exists(os.path.join(tests, "test_my_plugin.py")))
+    assert (os.path.exists(os.path.join(tests, "my_plugin_success_test.feature")))
+    assert (os.path.exists(os.path.join(tests, "my_plugin_failure_test.feature")))
+
+    if os.path.exists(context.temp_dir):
+        rmtree(context.temp_dir)
+
+
+@then("I should receive generated project files in a temporary directory")
+def gen_project_results(context):
+    """
+    Checks the output from the gen-project command.
+
+    Args:
+        context: Active context to check against.
+    """
+    assert ("All AaC constraint checks were successful" in context.output_message)
+    assert context.exit_code == 0
+    temp_dir = context.temp_dir
+    # make sure the files were created correctly
+    assert (os.path.exists(os.path.join(temp_dir, "pyproject.toml")))
+    assert (os.path.exists(os.path.join(temp_dir, "tox.ini")))
+    assert (os.path.exists(os.path.join(temp_dir, "README.md")))
+    assert (os.path.exists(os.path.join(temp_dir, "src")))
+    assert (os.path.exists(os.path.join(temp_dir, "tests")))
+    assert (os.path.exists(os.path.join(temp_dir, "docs")))
+
+    if os.path.exists(context.temp_dir):
+        rmtree(context.temp_dir)
+
+
+@then("I should receive a message that the command was not successful")
+def command_not_successful(context):
+    """
+    Checks the output from the gen-project command.
+
+    Args:
+        context: Active context to check against.
+    """
+    assert "All AaC constraint checks were successful" not in context.output_message
+    assert context.exit_code != 0
+    if os.path.exists(context.temp_dir):
+        rmtree(context.temp_dir)
diff --git a/python/features/steps/general_purpose_steps.py b/python/features/steps/general_purpose_steps.py
index 4d7b4a559..ccbd3a3dd 100644
--- a/python/features/steps/general_purpose_steps.py
+++ b/python/features/steps/general_purpose_steps.py
@@ -141,6 +141,6 @@ def and_check(context):
     Ensure the check command with verbose ran returned the list of models.
 
     Args:
-        context: Active context to check against
+        context: Active context to check against.
     """
     assert ("was successful." in context.output_message)  # only appears when using verbose
diff --git a/python/src/aac/__init__.py b/python/src/aac/__init__.py
index f9d507395..32a605e43 100644
--- a/python/src/aac/__init__.py
+++ b/python/src/aac/__init__.py
@@ -12,7 +12,7 @@
 import logging
 import os
 
-__version__ = "0.4.36"
+__version__ = "0.4.37"
 
 __log_file_name__ = os.path.join(os.path.dirname(__file__), "aac.log")
 


Test patch:
diff --git a/python/tests/test_aac/plugins/gen_plugin/my_plugin_bad.aac b/python/tests/test_aac/plugins/gen_plugin/my_plugin_bad.aac
new file mode 100644
index 000000000..344d3d7c4
--- /dev/null
+++ b/python/tests/test_aac/plugins/gen_plugin/my_plugin_bad.aac
@@ -0,0 +1,115 @@
+plugin:
+  package: happy
+  description: |
+    An AaC plugin definition used to test the gen-plugin command behavior.
+  commands:
+    - name: test-command-one
+      help_text: |
+        A test command used to test the gen-plugin command behavior.
+      run_before:
+        - plugin: Check AaC
+          command: check
+      run_after:
+        - plugin: Generate
+          command: generate
+      input:
+        - name: aac-plugin-file
+          type: file
+          description: The path to the architecture file with the plugin definition.
+      acceptance:
+        - name: Command test
+          scenarios:
+            - name: Simple Command Test
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+    - name: test-command-two
+      help_text: |
+        A second test command used to test the gen-plugin command behavior.
+      run_before:
+        - plugin: Check AaC
+          command: check
+      run_after:
+        - plugin: Generate
+          command: generate
+      input:
+        - name: aac-plugin-file
+          type: file
+          description: The path to the architecture file with the plugin definition.
+      acceptance:
+        - name: Command test two
+          scenarios:
+            - name: Simple Command Test Two
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+    - name: test-command-three
+      help_text: |
+        A third test command used to test the gen-plugin command behavior.
+      run_before:
+        - plugin: Check AaC
+          command: check
+      run_after:
+        - plugin: Generate
+          command: generate
+      input:
+        - name: aac-plugin-file
+          type: file
+          description: The path to the architecture file with the plugin definition.
+      acceptance:
+        - name: Command test three
+          scenarios:
+            - name: Simple Command Test Three
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+  context_constraints:
+    - name: The sky is blue
+      description: Ensures the sky is blue.
+      acceptance:
+        - name: Context test
+          scenarios:
+            - name: Simple Constraint Test
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+  schema_constraints:
+    - name: Schemas have happy names
+      description: Check every schema declared provides positive vibes.
+      universal: true
+      acceptance:
+        - name: Schema test
+          scenarios:
+            - name: Simple Schema Test
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+  primitive_constraints:
+    - name: Primitives have happy names
+      description: Check every primitive declared provides positive vibes.
+      acceptance:
+        - name: Primitive test
+          scenarios:
+            - name: Simple Primitive Test
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+
diff --git a/python/tests/test_aac/plugins/gen_plugin/my_plugin_eval.aac b/python/tests/test_aac/plugins/gen_plugin/my_plugin_eval.aac
new file mode 100644
index 000000000..7d2492162
--- /dev/null
+++ b/python/tests/test_aac/plugins/gen_plugin/my_plugin_eval.aac
@@ -0,0 +1,115 @@
+plugin:
+  name: My plugin
+  package: happy
+  description: |
+    An AaC plugin definition used to test the gen-plugin command behavior.
+  commands:
+    - name: test-command-1-evaluation
+      help_text: |
+        A test command used to test the gen-plugin command behavior.
+      run_before:
+        - plugin: Check AaC
+          command: check
+      run_after:
+        - plugin: Generate
+          command: generate
+      input:
+        - name: aac-plugin-file
+          type: file
+          description: The path to the architecture file with the plugin definition.
+      acceptance:
+        - name: Command test
+          scenarios:
+            - name: Simple Command Test
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+    - name: test-command-two
+      help_text: |
+        A second test command used to test the gen-plugin command behavior.
+      run_before:
+        - plugin: Check AaC
+          command: check
+      run_after:
+        - plugin: Generate
+          command: generate
+      input:
+        - name: aac-plugin-file
+          type: file
+          description: The path to the architecture file with the plugin definition.
+      acceptance:
+        - name: Command test two
+          scenarios:
+            - name: Simple Command Test Two
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+    - name: test-command-three
+      help_text: |
+        A third test command used to test the gen-plugin command behavior.
+      run_before:
+        - plugin: Check AaC
+          command: check
+      run_after:
+        - plugin: Generate
+          command: generate
+      input:
+        - name: aac-plugin-file
+          type: file
+          description: The path to the architecture file with the plugin definition.
+      acceptance:
+        - name: Command test three
+          scenarios:
+            - name: Simple Command Test Three
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+  context_constraints:
+    - name: The sky is blue
+      description: Ensures the sky is blue.
+      acceptance:
+        - name: Context test
+          scenarios:
+            - name: Simple Constraint Test
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+  schema_constraints:
+    - name: Schemas have happy names
+      description: Check every schema declared provides positive vibes.
+      universal: true
+      acceptance:
+        - name: Schema test
+          scenarios:
+            - name: Simple Schema Test
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
+  primitive_constraints:
+    - name: Primitives have happy names
+      description: Check every primitive declared provides positive vibes.
+      acceptance:
+        - name: Primitive test
+          scenarios:
+            - name: Simple Primitive Test
+              given:
+                - An AaC file containing schemas with no extra fields.
+              when:
+                - The AaC check command is run on the schema.
+              then:
+                - The check commands provides the output 'All AaC constraint checks were successful.'
diff --git a/python/tests/test_aac/plugins/gen_plugin/my_project_bad.aac b/python/tests/test_aac/plugins/gen_plugin/my_project_bad.aac
new file mode 100644
index 000000000..c1a814f46
--- /dev/null
+++ b/python/tests/test_aac/plugins/gen_plugin/my_project_bad.aac
@@ -0,0 +1,3 @@
+project:
+  description: |
+    An AaC project definition used to test the gen-project command behavior.
diff --git a/python/tests/test_aac/plugins/gen_plugin/test_gen_plugin.py b/python/tests/test_aac/plugins/gen_plugin/test_gen_plugin.py
index c2bf3bd21..c8af70746 100644
--- a/python/tests/test_aac/plugins/gen_plugin/test_gen_plugin.py
+++ b/python/tests/test_aac/plugins/gen_plugin/test_gen_plugin.py
@@ -1,24 +1,14 @@
+from io import StringIO
 from unittest import TestCase
+from unittest.mock import patch
 from typing import Tuple
 from click.testing import CliRunner
-from aac.execute.command_line import cli, initialize_cli
-from aac.execute.aac_execution_result import ExecutionStatus
-
-import os
-import shutil
-import tempfile
+from os import path, mkdir, listdir
+from shutil import copy
+from tempfile import TemporaryDirectory
+from traceback import print_exception
 
-import traceback
-
-from aac.plugins.gen_plugin.gen_plugin_impl import (
-    plugin_name,
-    gen_plugin,
-    before_gen_plugin_check,
-    after_gen_plugin_generate,
-    gen_project,
-    before_gen_project_check,
-    after_gen_project_generate,
-)
+from aac.execute.command_line import cli, initialize_cli
 
 
 class TestGenPlugin(TestCase):
@@ -39,10 +29,10 @@ def run_gen_plugin_cli_command_with_args(self, args: list[str]) -> Tuple[int, st
 
     def test_cli_gen_plugin(self):
         # first we need a project to work in, so generate a temporary one
-        with tempfile.TemporaryDirectory() as temp_dir:
-            aac_file_path = os.path.join(os.path.dirname(__file__), "my_project.aac")
-            temp_aac_file_path = os.path.join(temp_dir, "my_project.aac")
-            shutil.copy(aac_file_path, temp_aac_file_path)
+        with TemporaryDirectory() as temp_dir:
+            aac_file_path = path.join(path.dirname(__file__), "my_project.aac")
+            temp_aac_file_path = path.join(temp_dir, "my_project.aac")
+            copy(aac_file_path, temp_aac_file_path)
 
             proj_args = [temp_aac_file_path, "--output", temp_dir, "--no-prompt"]
 
@@ -51,36 +41,112 @@ def test_cli_gen_plugin(self):
             self.assertEqual(0, exit_code, f"Expected success but failed with message: {output_message}")  # asserts the command ran successfully
 
             # now create an AaC plugin file in the project src directory
-            package_src_path = os.path.join(temp_dir, "src", "happy")
-            os.mkdir(package_src_path)
-            package_tests_path = os.path.join(temp_dir, "tests", "test_happy")
-            os.mkdir(package_tests_path)
-            plugin_file_path = os.path.join(package_src_path, "my_plugin.aac")
+            package_src_path = path.join(temp_dir, "src", "happy")
+            mkdir(package_src_path)
+            package_tests_path = path.join(temp_dir, "tests", "test_happy")
+            mkdir(package_tests_path)
+            plugin_file_path = path.join(package_src_path, "my_plugin.aac")
 
-            aac_plugin_path = os.path.join(os.path.dirname(__file__), "my_plugin.aac")
-            shutil.copy(aac_plugin_path, plugin_file_path)
+            aac_plugin_path = path.join(path.dirname(__file__), "my_plugin.aac")
+            copy(aac_plugin_path, plugin_file_path)
 
-            plugin_args = [plugin_file_path, "--code-output", os.path.join(temp_dir, "src"), "--test-output", os.path.join(temp_dir, "tests"), "--no-prompt"]
+            plugin_args = [plugin_file_path, "--code-output", path.join(temp_dir, "src"), "--test-output", path.join(temp_dir, "tests"), "--no-prompt"]
 
             exit_code, output_message = self.run_gen_plugin_cli_command_with_args(plugin_args)
             self.assertEqual(0, exit_code)  # asserts the command ran successfully
             self.assertIn("All AaC constraint checks were successful", output_message)  # asserts the command ran check successfully
 
             # make sure the files were created correctly
-            self.assertTrue(os.path.exists(os.path.join(package_src_path, "__init__.py")))
-            self.assertTrue(os.path.exists(os.path.join(package_src_path, "my_plugin_impl.py")))
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "test_my_plugin.py")))
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "my_plugin_command_test.feature")))
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "my_plugin_context_test.feature")))
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "my_plugin_schema_test.feature")))
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "my_plugin_primitive_test.feature")))
+            self.assertTrue(path.exists(path.join(package_src_path, "__init__.py")))
+            self.assertTrue(path.exists(path.join(package_src_path, "my_plugin_impl.py")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "test_my_plugin.py")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "my_plugin_command_test.feature")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "my_plugin_context_test.feature")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "my_plugin_schema_test.feature")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "my_plugin_primitive_test.feature")))
+
+    def test_cli_gen_plugin_overwrite(self):
+        # first we need a project to work in, so generate a temporary one
+        with TemporaryDirectory() as temp_dir:
+            # now create an AaC plugin file in the project src directory
+            aac_file_path = path.join(path.dirname(__file__), "my_project.aac")
+            temp_aac_file_path = path.join(temp_dir, "my_project.aac")
+            copy(aac_file_path, temp_aac_file_path)
+
+            proj_args = [temp_aac_file_path, "--output", temp_dir, "--no-prompt"]
+            exit_code, output_message = self.run_gen_project_cli_command_with_args(proj_args)
+
+            package_src_path = path.join(temp_dir, "src", "happy")
+            mkdir(package_src_path)
+            package_tests_path = path.join(temp_dir, "tests", "test_happy")
+            mkdir(package_tests_path)
+            plugin_file_path = path.join(package_src_path, "my_plugin.aac")
+
+            aac_plugin_path = path.join(path.dirname(__file__), "my_plugin.aac")
+            copy(aac_plugin_path, plugin_file_path)
+            plugin_args = [plugin_file_path, "--code-output", path.join(temp_dir, "src"), "--test-output", path.join(temp_dir, "tests"), "--no-prompt"]
+            exit_code, output_message = self.run_gen_plugin_cli_command_with_args(plugin_args)
+            file = open(path.join(package_src_path, "my_plugin_impl.py"), "r")
+            file_read = file.read()
+            self.assertIn("test_command_one(aac_plugin_file: str)", file_read)
+            self.assertNotIn("test_command_1_evaluation(aac_plugin_file: str)", file_read)
+            file.close()
+
+            aac_plugin_path = path.join(path.dirname(__file__), "my_plugin_eval.aac")
+            copy(aac_plugin_path, plugin_file_path)
+            plugin_args = [plugin_file_path, "--code-output", path.join(temp_dir, "src"), "--test-output", path.join(temp_dir, "tests"), "--no-prompt", "--force-overwrite"]
+            exit_code, output_message = self.run_gen_plugin_cli_command_with_args(plugin_args)
+            file = open(path.join(package_src_path, "my_plugin_impl.py"), "r")
+            file_read = file.read()
+            self.assertIn("test_command_1_evaluation(aac_plugin_file: str)", file_read)
+            self.assertNotIn("test_command_one(aac_plugin_file: str)", file_read)
+            file.close()
+
+
+    def test_cli_gen_plugin_evaluate(self):
+        # first we need a project to work in, so generate a temporary one
+        with TemporaryDirectory() as temp_dir:
+            # now create an AaC plugin file in the project src directory
+            aac_file_path = path.join(path.dirname(__file__), "my_project.aac")
+            temp_aac_file_path = path.join(temp_dir, "my_project.aac")
+            copy(aac_file_path, temp_aac_file_path)
+
+            proj_args = [temp_aac_file_path, "--output", temp_dir, "--no-prompt"]
+            exit_code, output_message = self.run_gen_project_cli_command_with_args(proj_args)
+
+            package_src_path = path.join(temp_dir, "src", "happy")
+            mkdir(package_src_path)
+            package_tests_path = path.join(temp_dir, "tests", "test_happy")
+            mkdir(package_tests_path)
+            plugin_file_path = path.join(package_src_path, "my_plugin.aac")
+
+            aac_plugin_path = path.join(path.dirname(__file__), "my_plugin.aac")
+            copy(aac_plugin_path, plugin_file_path)
+            plugin_args = [plugin_file_path, "--code-output", path.join(temp_dir, "src"), "--test-output", path.join(temp_dir, "tests"), "--no-prompt"]
+            exit_code, output_message = self.run_gen_plugin_cli_command_with_args(plugin_args)
+            file = open(path.join(package_src_path, "my_plugin_impl.py"), "r")
+            file_read = file.read()
+            self.assertIn("test_command_one(aac_plugin_file: str)", file_read)
+            self.assertNotIn("test_command_one_evaluation(aac_plugin_file: str)", file_read)
+            file.close()
+
+            aac_plugin_path = path.join(path.dirname(__file__), "my_plugin_eval.aac")
+            copy(aac_plugin_path, plugin_file_path)
+            plugin_args = [plugin_file_path, "--code-output", path.join(temp_dir, "src"), "--test-output", path.join(temp_dir, "tests"), "--no-prompt", "--evaluate"]
+            exit_code, output_message = self.run_gen_plugin_cli_command_with_args(plugin_args)
+            file = open(path.join(package_src_path, "my_plugin_impl.py"), "r")
+            file_read = file.read()
+            self.assertNotIn("test_command_1_evaluation(aac_plugin_file: str)", file_read)
+            self.assertIn("test_command_one(aac_plugin_file: str)", file_read)
+            file.close()
+
 
     def test_cli_gen_plugin_with_incomplete_dirs(self):
         # first we need a project to work in, so generate a temporary one
-         with tempfile.TemporaryDirectory() as temp_dir:
-            aac_file_path = os.path.join(os.path.dirname(__file__), "my_project.aac")
-            temp_aac_file_path = os.path.join(temp_dir, "my_project.aac")
-            shutil.copy(aac_file_path, temp_aac_file_path)
+        with TemporaryDirectory() as temp_dir:
+            aac_file_path = path.join(path.dirname(__file__), "my_project.aac")
+            temp_aac_file_path = path.join(temp_dir, "my_project.aac")
+            copy(aac_file_path, temp_aac_file_path)
 
             proj_args = [temp_aac_file_path, "--output", temp_dir, "--no-prompt"]
 
@@ -89,32 +155,32 @@ def test_cli_gen_plugin_with_incomplete_dirs(self):
             self.assertEqual(0, exit_code, f"Expected success but failed with message: {output_message}")  # asserts the command ran successfully
 
             # now create an AaC plugin file in the project src directory
-            package_src_path = os.path.join(temp_dir, "src", "happy")
-            os.mkdir(package_src_path)
-            plugin_file_path = os.path.join(temp_dir, "my_plugin.aac")
+            package_src_path = path.join(temp_dir, "src", "happy")
+            mkdir(package_src_path)
+            plugin_file_path = path.join(temp_dir, "my_plugin.aac")
 
-            aac_plugin_path = os.path.join(os.path.dirname(__file__), "my_plugin.aac")
-            shutil.copy(aac_plugin_path, plugin_file_path)
+            aac_plugin_path = path.join(path.dirname(__file__), "my_plugin.aac")
+            copy(aac_plugin_path, plugin_file_path)
 
-            plugin_args = [plugin_file_path, "--code-output", os.path.join(temp_dir, "src"), "--no-prompt"]
+            plugin_args = [plugin_file_path, "--code-output", path.join(temp_dir, "src"), "--no-prompt"]
 
             exit_code, output_message = self.run_gen_plugin_cli_command_with_args(plugin_args)
             self.assertEqual(0, exit_code)  # asserts the command ran successfully
             self.assertIn("All AaC constraint checks were successful", output_message)  # asserts the command ran check successfully
 
-            self.assertTrue(os.path.exists(os.path.join(package_src_path, "my_plugin_impl.py")))
+            self.assertTrue(path.exists(path.join(package_src_path, "my_plugin_impl.py")))
 
-            package_tests_path = os.path.join(temp_dir, "test_happy") # making sure that tests get output to the correct location
-            self.assertTrue(os.path.exists(os.path.join(package_src_path, "__init__.py")))
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "my_plugin_command_test.feature")))
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "my_plugin_command_test_two.feature")))
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "my_plugin_command_test_three.feature")))
+            package_tests_path = path.join(temp_dir, "test_happy")  # making sure that tests get output to the correct location
+            self.assertTrue(path.exists(path.join(package_src_path, "__init__.py")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "my_plugin_command_test.feature")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "my_plugin_command_test_two.feature")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "my_plugin_command_test_three.feature")))
 
     def test_cli_gen_plugin_multiple_commands(self):
-        with tempfile.TemporaryDirectory() as temp_dir:
-            aac_file_path = os.path.join(os.path.dirname(__file__), "my_project.aac")
-            temp_aac_file_path = os.path.join(temp_dir, "my_project.aac")
-            shutil.copy(aac_file_path, temp_aac_file_path)
+        with TemporaryDirectory() as temp_dir:
+            aac_file_path = path.join(path.dirname(__file__), "my_project.aac")
+            temp_aac_file_path = path.join(temp_dir, "my_project.aac")
+            copy(aac_file_path, temp_aac_file_path)
 
             proj_args = [temp_aac_file_path, "--output", temp_dir, "--no-prompt"]
 
@@ -123,23 +189,23 @@ def test_cli_gen_plugin_multiple_commands(self):
             self.assertEqual(0, exit_code, f"Expected success but failed with message: {output_message}")  # asserts the command ran successfully
 
             # now create an AaC plugin file in the project src directory
-            package_src_path = os.path.join(temp_dir, "src", "happy")
-            os.mkdir(package_src_path)
-            package_tests_path = os.path.join(temp_dir, "tests", "test_happy")
-            os.mkdir(package_tests_path)
-            plugin_file_path = os.path.join(package_src_path, "my_plugin.aac")
+            package_src_path = path.join(temp_dir, "src", "happy")
+            mkdir(package_src_path)
+            package_tests_path = path.join(temp_dir, "tests", "test_happy")
+            mkdir(package_tests_path)
+            plugin_file_path = path.join(package_src_path, "my_plugin.aac")
 
-            aac_plugin_path = os.path.join(os.path.dirname(__file__), "my_plugin.aac")
-            shutil.copy(aac_plugin_path, plugin_file_path)
+            aac_plugin_path = path.join(path.dirname(__file__), "my_plugin.aac")
+            copy(aac_plugin_path, plugin_file_path)
 
-            plugin_args = [plugin_file_path, "--code-output", os.path.join(temp_dir, "src"), "--test-output", os.path.join(temp_dir, "tests"), "--no-prompt"]
+            plugin_args = [plugin_file_path, "--code-output", path.join(temp_dir, "src"), "--test-output", path.join(temp_dir, "tests"), "--no-prompt"]
 
             exit_code, output_message = self.run_gen_plugin_cli_command_with_args(plugin_args)
             self.assertEqual(0, exit_code)  # asserts the command ran successfully
             self.assertIn("All AaC constraint checks were successful", output_message)  # asserts the command ran check successfully
 
-            self.assertTrue(os.path.exists(os.path.join(package_src_path, "my_plugin_impl.py")))
-            with open(os.path.join(package_src_path, "my_plugin_impl.py")) as impl_file:
+            self.assertTrue(path.exists(path.join(package_src_path, "my_plugin_impl.py")))
+            with open(path.join(package_src_path, "my_plugin_impl.py")) as impl_file:
                 impl_file_read = impl_file.read()
                 self.assertIn("def before_test_command_one_check", impl_file_read)
                 self.assertIn("def test_command_one", impl_file_read)
@@ -153,18 +219,32 @@ def test_cli_gen_plugin_multiple_commands(self):
                 self.assertIn("def test_command_three", impl_file_read)
                 self.assertIn("def after_test_command_three_generate", impl_file_read)
 
-            self.assertTrue(os.path.exists(os.path.join(package_src_path, "__init__.py")))
-            with open(os.path.join(package_src_path, "__init__.py")) as init_file:
+            self.assertTrue(path.exists(path.join(package_src_path, "__init__.py")))
+            with open(path.join(package_src_path, "__init__.py")) as init_file:
                 init_file_read = init_file.read()
                 self.assertIn("def run_test_command_one", init_file_read)
 
                 self.assertIn("def run_test_command_two", init_file_read)
 
                 self.assertIn("def run_test_command_three", init_file_read)
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "my_plugin_command_test.feature")))
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "my_plugin_command_test_two.feature")))
-            self.assertTrue(os.path.exists(os.path.join(package_tests_path, "my_plugin_command_test_three.feature")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "my_plugin_command_test.feature")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "my_plugin_command_test_two.feature")))
+            self.assertTrue(path.exists(path.join(package_tests_path, "my_plugin_command_test_three.feature")))
+
+    # The following decorators are to stop std_out and std_err from clogging up the terminal with System errors.
+    @patch('sys.stdout', new_callable=StringIO)  # Suppress stdout
+    @patch('sys.stderr', new_callable=StringIO)  # Suppress stderr
+    def test_cli_gen_plugin_failure(self, mock_stderr, mock_stdout):
+        with TemporaryDirectory() as temp_dir:
+            aac_file_path = path.join(path.dirname(__file__), "my_plugin_bad.aac")
+            temp_aac_file_path = path.join(temp_dir, "my_plugin_bad.aac")
+            copy(aac_file_path, temp_aac_file_path)
 
+            proj_args = [temp_aac_file_path, "--code-output", temp_dir, "--no-prompt"]
+
+            exit_code, output_message = self.run_gen_plugin_cli_command_with_args(proj_args)
+            self.assertNotEqual(0, exit_code)
+            self.assertIn("Definition is missing field 'name'", output_message)
 
     def test_gen_project(self):
         # I'm going to rely on the CLI testing for this one, but will leave there here in case we need it later
@@ -177,17 +257,17 @@ def run_gen_project_cli_command_with_args(self, args: list[str]) -> Tuple[int, s
         result = runner.invoke(cli, ["gen-project"] + args)
         if result.exception:
             exc_type, exc_value, exc_traceback = result.exc_info
-            traceback.print_exception(exc_type, exc_value, exc_traceback)
+            print_exception(exc_type, exc_value, exc_traceback)
         exit_code = result.exit_code
         std_out = str(result.stdout)
         output_message = std_out.strip().replace("\x1b[0m", "")
         return exit_code, output_message
 
     def test_cli_gen_project(self):
-        with tempfile.TemporaryDirectory() as temp_dir:
-            aac_file_path = os.path.join(os.path.dirname(__file__), "my_project.aac")
-            temp_aac_file_path = os.path.join(temp_dir, "my_project.aac")
-            shutil.copy(aac_file_path, temp_aac_file_path)
+        with TemporaryDirectory() as temp_dir:
+            aac_file_path = path.join(path.dirname(__file__), "my_project.aac")
+            temp_aac_file_path = path.join(temp_dir, "my_project.aac")
+            copy(aac_file_path, temp_aac_file_path)
 
             args = [temp_aac_file_path, "--output", temp_dir, "--no-prompt"]
 
@@ -197,9 +277,24 @@ def test_cli_gen_project(self):
             self.assertIn("All AaC constraint checks were successful", output_message)  # asserts the command ran check successfully
 
             # make sure the files and directories were created
-            self.assertTrue(os.path.exists(os.path.join(temp_dir, "pyproject.toml")))
-            self.assertTrue(os.path.exists(os.path.join(temp_dir, "tox.ini")))
-            self.assertTrue(os.path.exists(os.path.join(temp_dir, "README.md")))
-            self.assertTrue(os.path.exists(os.path.join(temp_dir, "src")))
-            self.assertTrue(os.path.exists(os.path.join(temp_dir, "tests")))
-            self.assertTrue(os.path.exists(os.path.join(temp_dir, "docs")))
+            self.assertTrue(path.exists(path.join(temp_dir, "pyproject.toml")))
+            self.assertTrue(path.exists(path.join(temp_dir, "tox.ini")))
+            self.assertTrue(path.exists(path.join(temp_dir, "README.md")))
+            self.assertTrue(path.exists(path.join(temp_dir, "src")))
+            self.assertTrue(path.exists(path.join(temp_dir, "tests")))
+            self.assertTrue(path.exists(path.join(temp_dir, "docs")))
+
+    # The following decorators are to stop std_out and std_err from clogging up the terminal with System errors.
+    @patch('sys.stdout', new_callable=StringIO)  # Suppress stdout
+    @patch('sys.stderr', new_callable=StringIO)  # Suppress stderr
+    def test_cli_gen_project_failure(self, mock_stderr, mock_stdout):
+        with TemporaryDirectory() as temp_dir:
+            aac_file_path = path.join(path.dirname(__file__), "my_project_bad.aac")
+            temp_aac_file_path = path.join(temp_dir, "my_project_bad.aac")
+            copy(aac_file_path, temp_aac_file_path)
+
+            proj_args = [temp_aac_file_path, "--output", temp_dir, "--no-prompt"]
+
+            exit_code, output_message = self.run_gen_project_cli_command_with_args(proj_args)
+            self.assertNotEqual(0, exit_code)
+            self.assertIn("Definition is missing field 'name'", output_message)