spaceforge 0.1.0.dev0__py3-none-any.whl → 1.0.1__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.
Files changed (35) hide show
  1. spaceforge/__init__.py +12 -4
  2. spaceforge/__main__.py +3 -3
  3. spaceforge/_version.py +0 -1
  4. spaceforge/_version_scm.py +34 -0
  5. spaceforge/cls.py +24 -14
  6. spaceforge/conftest.py +89 -0
  7. spaceforge/generator.py +129 -56
  8. spaceforge/plugin.py +199 -22
  9. spaceforge/runner.py +3 -15
  10. spaceforge/schema.json +45 -22
  11. spaceforge/templates/binary_install.sh.j2 +24 -0
  12. spaceforge/templates/ensure_spaceforge_and_run.sh.j2 +24 -0
  13. spaceforge/{generator_test.py → test_generator.py} +265 -53
  14. spaceforge/test_generator_binaries.py +194 -0
  15. spaceforge/test_generator_core.py +180 -0
  16. spaceforge/test_generator_hooks.py +90 -0
  17. spaceforge/test_generator_parameters.py +59 -0
  18. spaceforge/test_plugin.py +357 -0
  19. spaceforge/test_plugin_file_operations.py +118 -0
  20. spaceforge/test_plugin_hooks.py +100 -0
  21. spaceforge/test_plugin_inheritance.py +102 -0
  22. spaceforge/{runner_test.py → test_runner.py} +5 -68
  23. spaceforge/test_runner_cli.py +69 -0
  24. spaceforge/test_runner_core.py +124 -0
  25. spaceforge/test_runner_execution.py +169 -0
  26. spaceforge-1.0.1.dist-info/METADATA +606 -0
  27. spaceforge-1.0.1.dist-info/RECORD +33 -0
  28. spaceforge/plugin_test.py +0 -621
  29. spaceforge-0.1.0.dev0.dist-info/METADATA +0 -163
  30. spaceforge-0.1.0.dev0.dist-info/RECORD +0 -19
  31. /spaceforge/{cls_test.py → test_cls.py} +0 -0
  32. {spaceforge-0.1.0.dev0.dist-info → spaceforge-1.0.1.dist-info}/WHEEL +0 -0
  33. {spaceforge-0.1.0.dev0.dist-info → spaceforge-1.0.1.dist-info}/entry_points.txt +0 -0
  34. {spaceforge-0.1.0.dev0.dist-info → spaceforge-1.0.1.dist-info}/licenses/LICENSE +0 -0
  35. {spaceforge-0.1.0.dev0.dist-info → spaceforge-1.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,169 @@
1
+ """Tests for PluginRunner hook execution functionality."""
2
+
3
+ import os
4
+ from unittest.mock import Mock, patch
5
+
6
+ import pytest
7
+
8
+ from spaceforge.runner import PluginRunner
9
+
10
+
11
+ class TestPluginRunnerExecution:
12
+ """Test hook execution functionality."""
13
+
14
+ def test_should_load_plugin_automatically_when_not_already_loaded(
15
+ self, test_plugin_file: str
16
+ ) -> None:
17
+ """Should call load_plugin when plugin_instance is None."""
18
+ # Arrange
19
+ runner = PluginRunner(test_plugin_file)
20
+
21
+ # Act
22
+ with patch.object(runner, "load_plugin") as mock_load:
23
+ mock_instance = Mock()
24
+ mock_instance.after_plan = Mock()
25
+ runner.plugin_instance = None
26
+ runner.run_hook("after_plan")
27
+
28
+ # Assert
29
+ mock_load.assert_called_once()
30
+
31
+ def test_should_execute_hook_and_log_success_when_hook_exists(
32
+ self, temp_dir: str
33
+ ) -> None:
34
+ """Should successfully execute hook method and log completion."""
35
+ # Arrange
36
+ runner_plugin_path = os.path.join(temp_dir, "runner_plugin.py")
37
+ with open(runner_plugin_path, "w") as f:
38
+ f.write(
39
+ """
40
+ from spaceforge import SpaceforgePlugin
41
+
42
+ class TestRunnerPlugin(SpaceforgePlugin):
43
+ def __init__(self):
44
+ super().__init__()
45
+ self.executed_hooks = []
46
+
47
+ def after_plan(self):
48
+ self.executed_hooks.append('after_plan')
49
+ """
50
+ )
51
+
52
+ runner = PluginRunner(runner_plugin_path)
53
+ runner.load_plugin()
54
+
55
+ # Act
56
+ with patch("builtins.print") as mock_print:
57
+ runner.run_hook("after_plan")
58
+
59
+ # Assert
60
+ assert runner.plugin_instance is not None
61
+ assert hasattr(runner.plugin_instance, "executed_hooks")
62
+ assert "after_plan" in getattr(runner.plugin_instance, "executed_hooks")
63
+
64
+ mock_print.assert_any_call("[SpaceForge] Running hook: after_plan")
65
+ mock_print.assert_any_call("[SpaceForge] Hook completed: after_plan")
66
+
67
+ def test_should_print_error_when_hook_method_not_found(
68
+ self, test_plugin_file: str
69
+ ) -> None:
70
+ """Should print error message when hook method doesn't exist."""
71
+ # Arrange
72
+ runner = PluginRunner(test_plugin_file)
73
+ runner.load_plugin()
74
+
75
+ # Act
76
+ with patch("builtins.print") as mock_print:
77
+ runner.run_hook("nonexistent_hook")
78
+
79
+ # Assert
80
+ mock_print.assert_called_with(
81
+ "Hook method 'nonexistent_hook' not found in plugin"
82
+ )
83
+
84
+ def test_should_print_error_when_hook_exists_but_not_callable(
85
+ self, test_plugin_file: str
86
+ ) -> None:
87
+ """Should print error when hook attribute exists but is not callable."""
88
+ # Arrange
89
+ runner = PluginRunner(test_plugin_file)
90
+ runner.load_plugin()
91
+
92
+ assert runner.plugin_instance is not None
93
+ setattr(runner.plugin_instance, "not_callable", "not a method")
94
+
95
+ # Act
96
+ with patch("builtins.print") as mock_print:
97
+ runner.run_hook("not_callable")
98
+
99
+ # Assert
100
+ mock_print.assert_called_with("'not_callable' is not a callable method")
101
+
102
+ def test_should_print_error_and_reraise_when_hook_execution_fails(
103
+ self, temp_dir: str
104
+ ) -> None:
105
+ """Should print error and re-raise exception when hook execution fails."""
106
+ # Arrange
107
+ error_plugin_path = os.path.join(temp_dir, "error_plugin.py")
108
+ with open(error_plugin_path, "w") as f:
109
+ f.write(
110
+ """
111
+ from spaceforge import SpaceforgePlugin
112
+
113
+ class ErrorPlugin(SpaceforgePlugin):
114
+ def error_hook(self):
115
+ raise ValueError("Test error from hook")
116
+ """
117
+ )
118
+
119
+ runner = PluginRunner(error_plugin_path)
120
+ runner.load_plugin()
121
+
122
+ # Act & Assert
123
+ with patch("builtins.print") as mock_print:
124
+ with pytest.raises(ValueError, match="Test error from hook"):
125
+ runner.run_hook("error_hook")
126
+
127
+ mock_print.assert_any_call(
128
+ "[SpaceForge] Error running hook 'error_hook': Test error from hook"
129
+ )
130
+
131
+ def test_should_execute_multiple_hooks_maintaining_state(
132
+ self, temp_dir: str
133
+ ) -> None:
134
+ """Should execute multiple hooks while maintaining plugin instance state."""
135
+ # Arrange
136
+ multi_hook_path = os.path.join(temp_dir, "multi_hook.py")
137
+ with open(multi_hook_path, "w") as f:
138
+ f.write(
139
+ """
140
+ from spaceforge import SpaceforgePlugin
141
+
142
+ class MultiHookPlugin(SpaceforgePlugin):
143
+ def __init__(self):
144
+ super().__init__()
145
+ self.executed_hooks = []
146
+
147
+ def after_plan(self):
148
+ self.executed_hooks.append('after_plan')
149
+
150
+ def before_apply(self):
151
+ self.executed_hooks.append('before_apply')
152
+ """
153
+ )
154
+
155
+ runner = PluginRunner(multi_hook_path)
156
+ runner.load_plugin()
157
+
158
+ # Act
159
+ with patch("builtins.print"):
160
+ runner.run_hook("after_plan")
161
+ runner.run_hook("before_apply")
162
+
163
+ # Assert
164
+ assert runner.plugin_instance is not None
165
+ assert hasattr(runner.plugin_instance, "executed_hooks")
166
+ executed = getattr(runner.plugin_instance, "executed_hooks")
167
+ assert "after_plan" in executed
168
+ assert "before_apply" in executed
169
+ assert len(executed) == 2