octopize.deploy_tool 0.1.0__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.
@@ -0,0 +1,26 @@
1
+ """Octopize Avatar Deployment Tool."""
2
+
3
+ from .configure import DeploymentConfigurator, DeploymentRunner, main
4
+ from .printer import ConsolePrinter, Printer, SilentPrinter
5
+ from .version_compat import (
6
+ SCRIPT_VERSION,
7
+ VersionError,
8
+ check_version_compatibility,
9
+ validate_template_compatibility,
10
+ validate_template_version,
11
+ )
12
+
13
+ __version__ = "0.1.0"
14
+ __all__ = [
15
+ "main",
16
+ "DeploymentRunner",
17
+ "DeploymentConfigurator",
18
+ "Printer",
19
+ "ConsolePrinter",
20
+ "SilentPrinter",
21
+ "SCRIPT_VERSION",
22
+ "VersionError",
23
+ "check_version_compatibility",
24
+ "validate_template_compatibility",
25
+ "validate_template_version",
26
+ ]
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ CLI Test Harness for Avatar Deployment Tool
4
+
5
+ Allows testing the CLI with pre-configured responses instead of manual input.
6
+ This enables automated end-to-end testing of the entire CLI workflow.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ from octopize_avatar_deploy.configure import main
14
+ from octopize_avatar_deploy.input_gatherer import MockInputGatherer
15
+ from octopize_avatar_deploy.printer import FilePrinter, SilentPrinter
16
+
17
+
18
+ class CLITestHarness:
19
+ """
20
+ Test harness for running CLI with mocked input/output.
21
+
22
+ This allows automated testing of the complete CLI workflow by:
23
+ 1. Injecting pre-configured responses via MockInputGatherer
24
+ 2. Optionally capturing output via SilentPrinter or FilePrinter
25
+ 3. Setting command-line arguments programmatically
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ responses: list[str | bool],
31
+ args: list[str] | None = None,
32
+ silent: bool = False,
33
+ log_file: Path | str | None = None,
34
+ ):
35
+ """
36
+ Initialize CLI test harness.
37
+
38
+ Args:
39
+ responses: List of pre-configured responses for MockInputGatherer
40
+ args: Command-line arguments to pass (default: [])
41
+ silent: Whether to suppress output completely (default: False)
42
+ log_file: Optional path to log file for capturing output
43
+ If provided, overrides silent=True
44
+ """
45
+ self.responses = responses
46
+ self.args = args or []
47
+ self.silent = silent
48
+ self.log_file = log_file
49
+ self._original_argv = None
50
+ self._original_env: dict[str, str | None] = {}
51
+
52
+ def __enter__(self):
53
+ """Set up test environment."""
54
+ # Save original argv
55
+ self._original_argv = sys.argv.copy()
56
+
57
+ # Set new argv (program name + args)
58
+ sys.argv = ["octopize-avatar-deploy"] + self.args
59
+
60
+ # Save and set environment variables
61
+ env_vars = [
62
+ "AVATAR_DEPLOY_TEST_MODE",
63
+ "AVATAR_DEPLOY_TEST_RESPONSES",
64
+ "AVATAR_DEPLOY_TEST_SILENT",
65
+ "AVATAR_DEPLOY_TEST_LOG_FILE",
66
+ ]
67
+ for var in env_vars:
68
+ self._original_env[var] = os.environ.get(var)
69
+
70
+ os.environ["AVATAR_DEPLOY_TEST_MODE"] = "1"
71
+ os.environ["AVATAR_DEPLOY_TEST_RESPONSES"] = self.serialize_responses(self.responses)
72
+
73
+ if self.log_file:
74
+ os.environ["AVATAR_DEPLOY_TEST_LOG_FILE"] = str(self.log_file)
75
+ elif self.silent:
76
+ os.environ["AVATAR_DEPLOY_TEST_SILENT"] = "1"
77
+
78
+ return self
79
+
80
+ def __exit__(self, exc_type, exc_val, exc_tb):
81
+ """Clean up test environment."""
82
+ # Restore original argv
83
+ sys.argv = self._original_argv
84
+
85
+ # Restore environment
86
+ for var, value in self._original_env.items():
87
+ if value is not None:
88
+ os.environ[var] = value
89
+ else:
90
+ os.environ.pop(var, None)
91
+
92
+ def run(self) -> int:
93
+ """
94
+ Run the CLI with test configuration.
95
+
96
+ Returns:
97
+ Exit code (0 for success, non-zero for failure)
98
+ """
99
+
100
+ try:
101
+ with self:
102
+ main()
103
+ return 0
104
+ except SystemExit as e:
105
+ # e.code can be int, str, or None
106
+ if e.code is None:
107
+ return 0
108
+ if isinstance(e.code, int):
109
+ return e.code
110
+ # If it's a string or other type, return 1
111
+ return 1
112
+ except Exception as e:
113
+ if not self.silent and not self.log_file:
114
+ print(f"CLI test failed with exception: {e}", file=sys.stderr)
115
+ return 1
116
+
117
+ @staticmethod
118
+ def serialize_responses(responses: list[str | bool]) -> str:
119
+ """Serialize responses to string format."""
120
+ serialized = []
121
+ for r in responses:
122
+ if isinstance(r, bool):
123
+ serialized.append("__BOOL_TRUE__" if r else "__BOOL_FALSE__")
124
+ else:
125
+ # Escape delimiter
126
+ serialized.append(str(r).replace("|||", "\\|||"))
127
+ return "|||".join(serialized)
128
+
129
+ @staticmethod
130
+ def deserialize_responses(serialized: str) -> list[str | bool]:
131
+ """Deserialize responses from string format."""
132
+ if not serialized:
133
+ return []
134
+
135
+ responses: list[str | bool] = []
136
+ for r in serialized.split("|||"):
137
+ # Unescape delimiter
138
+ r = r.replace("\\|||", "|||")
139
+
140
+ # Convert bool markers back to bool
141
+ if r == "__BOOL_TRUE__":
142
+ responses.append(True)
143
+ elif r == "__BOOL_FALSE__":
144
+ responses.append(False)
145
+ else:
146
+ responses.append(r)
147
+ return responses
148
+
149
+
150
+ def get_test_input_gatherer():
151
+ """
152
+ Get MockInputGatherer if in test mode, otherwise None.
153
+
154
+ This function is called by configure.py to inject test responses.
155
+ """
156
+ if os.environ.get("AVATAR_DEPLOY_TEST_MODE") != "1":
157
+ return None
158
+
159
+ serialized = os.environ.get("AVATAR_DEPLOY_TEST_RESPONSES", "")
160
+ responses = CLITestHarness.deserialize_responses(serialized)
161
+
162
+ if not responses:
163
+ return None
164
+
165
+ return MockInputGatherer(responses)
166
+
167
+
168
+ def get_test_printer():
169
+ """
170
+ Get appropriate printer for test mode.
171
+
172
+ Returns FilePrinter if log file specified, SilentPrinter if silent mode,
173
+ otherwise None.
174
+ """
175
+ log_file = os.environ.get("AVATAR_DEPLOY_TEST_LOG_FILE")
176
+ if log_file:
177
+ return FilePrinter(log_file)
178
+
179
+ if os.environ.get("AVATAR_DEPLOY_TEST_SILENT") == "1":
180
+ return SilentPrinter()
181
+
182
+ return None
183
+
184
+
185
+ # Convenience function for quick testing
186
+ def run_cli_test(
187
+ responses: list[str | bool],
188
+ args: list[str] | None = None,
189
+ silent: bool = False,
190
+ log_file: Path | str | None = None,
191
+ ) -> int:
192
+ """
193
+ Convenience function to run a CLI test.
194
+
195
+ Args:
196
+ responses: Pre-configured responses
197
+ args: Command-line arguments
198
+ silent: Whether to suppress output completely
199
+ log_file: Optional path to log file (overrides silent)
200
+
201
+ Returns:
202
+ Exit code (0 for success)
203
+
204
+ Example:
205
+ >>> # Test with file logging
206
+ >>> exit_code = run_cli_test(
207
+ ... responses=["https://api.example.com", False, False],
208
+ ... args=["--output-dir", "/tmp/test"],
209
+ ... log_file="/tmp/test/deployment.log"
210
+ ... )
211
+ >>> # Check log file if test failed
212
+ >>> if exit_code != 0:
213
+ ... with open("/tmp/test/deployment.log") as f:
214
+ ... print(f.read())
215
+ """
216
+ harness = CLITestHarness(responses, args, silent, log_file)
217
+ return harness.run()