zen-cli 0.1.4__tar.gz → 0.1.5__tar.gz

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 (215) hide show
  1. {zen_cli-0.1.4 → zen_cli-0.1.5}/PKG-INFO +2 -2
  2. {zen_cli-0.1.4 → zen_cli-0.1.5}/pyproject.toml +9 -3
  3. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/deployment/create.py +5 -1
  4. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/cmd_util.py +1 -1
  5. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/logger.py +1 -1
  6. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/csp.py +2 -2
  7. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/ez_client.py +1 -1
  8. zen_cli-0.1.5/src/zen_cli/support/test_utils.py +155 -0
  9. zen_cli-0.1.5/tests/blackbox/__init__.py +0 -0
  10. zen_cli-0.1.5/tests/blackbox/test_api.py +9 -0
  11. zen_cli-0.1.5/tests/blackbox/test_auth.py +20 -0
  12. zen_cli-0.1.5/tests/blackbox/test_completion.py +8 -0
  13. zen_cli-0.1.5/tests/blackbox/test_context.py +17 -0
  14. zen_cli-0.1.5/tests/blackbox/test_credential.py +11 -0
  15. zen_cli-0.1.5/tests/blackbox/test_deployment.py +11 -0
  16. zen_cli-0.1.5/tests/blackbox/test_entitlement.py +11 -0
  17. zen_cli-0.1.5/tests/blackbox/test_errors.py +88 -0
  18. zen_cli-0.1.5/tests/blackbox/test_group.py +11 -0
  19. zen_cli-0.1.5/tests/blackbox/test_io.py +88 -0
  20. zen_cli-0.1.5/tests/blackbox/test_login.py +27 -0
  21. zen_cli-0.1.5/tests/blackbox/test_logout.py +6 -0
  22. zen_cli-0.1.5/tests/blackbox/test_mcp.py +8 -0
  23. zen_cli-0.1.5/tests/blackbox/test_network.py +11 -0
  24. zen_cli-0.1.5/tests/blackbox/test_org.py +23 -0
  25. zen_cli-0.1.5/tests/blackbox/test_pool.py +33 -0
  26. zen_cli-0.1.5/tests/blackbox/test_profile.py +55 -0
  27. zen_cli-0.1.5/tests/blackbox/test_provider.py +11 -0
  28. zen_cli-0.1.5/tests/blackbox/test_root.py +14 -0
  29. zen_cli-0.1.5/tests/blackbox/test_sag.py +11 -0
  30. zen_cli-0.1.5/tests/blackbox/test_task.py +8 -0
  31. zen_cli-0.1.5/tests/blackbox/test_vm.py +8 -0
  32. zen_cli-0.1.5/tests/blackbox/util.py +31 -0
  33. {zen_cli-0.1.4 → zen_cli-0.1.5}/.gitignore +0 -0
  34. {zen_cli-0.1.4 → zen_cli-0.1.5}/LICENSE +0 -0
  35. {zen_cli-0.1.4 → zen_cli-0.1.5}/README.md +0 -0
  36. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/__init__.py +0 -0
  37. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/__main__.py +0 -0
  38. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cli.py +0 -0
  39. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/activity/__init__.py +0 -0
  40. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/activity/get.py +0 -0
  41. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/activity/list.py +0 -0
  42. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/api.py +0 -0
  43. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/auth/__init__.py +0 -0
  44. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/auth/details.py +0 -0
  45. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/auth/refresh.py +0 -0
  46. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/auth/status.py +0 -0
  47. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/auth/token.py +0 -0
  48. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/completion.py +0 -0
  49. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/credential/__init__.py +0 -0
  50. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/credential/create.py +0 -0
  51. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/credential/delete.py +0 -0
  52. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/credential/get.py +0 -0
  53. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/credential/list.py +0 -0
  54. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/deployment/__init__.py +0 -0
  55. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/deployment/delete.py +0 -0
  56. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/deployment/get.py +0 -0
  57. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/deployment/list.py +0 -0
  58. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/entitlement/__init__.py +0 -0
  59. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/entitlement/create.py +0 -0
  60. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/entitlement/delete.py +0 -0
  61. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/entitlement/get.py +0 -0
  62. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/entitlement/list.py +0 -0
  63. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/group/__init__.py +0 -0
  64. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/group/create.py +0 -0
  65. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/group/delete.py +0 -0
  66. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/group/get.py +0 -0
  67. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/group/list.py +0 -0
  68. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/login.py +0 -0
  69. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/logout.py +0 -0
  70. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/mcp.py +0 -0
  71. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/network/__init__.py +0 -0
  72. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/network/create.py +0 -0
  73. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/network/delete.py +0 -0
  74. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/network/get.py +0 -0
  75. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/network/list.py +0 -0
  76. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/notification/__init__.py +0 -0
  77. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/notification/get.py +0 -0
  78. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/notification/list.py +0 -0
  79. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/org/__init__.py +0 -0
  80. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/org/create.py +0 -0
  81. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/org/get.py +0 -0
  82. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/org/list.py +0 -0
  83. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/org/use.py +0 -0
  84. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/pool/__init__.py +0 -0
  85. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/pool/create.py +0 -0
  86. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/pool/delete.py +0 -0
  87. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/pool/get.py +0 -0
  88. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/pool/list.py +0 -0
  89. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/pool/use.py +0 -0
  90. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/profile/__init__.py +0 -0
  91. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/profile/init.py +0 -0
  92. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/provider/__init__.py +0 -0
  93. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/provider/create.py +0 -0
  94. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/provider/delete.py +0 -0
  95. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/provider/get.py +0 -0
  96. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/provider/list.py +0 -0
  97. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/sag/__init__.py +0 -0
  98. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/sag/create.py +0 -0
  99. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/sag/delete.py +0 -0
  100. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/sag/get.py +0 -0
  101. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/sag/list.py +0 -0
  102. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/task/__init__.py +0 -0
  103. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/task/cancel.py +0 -0
  104. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/task/delete.py +0 -0
  105. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/task/duplicate.py +0 -0
  106. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/task/get.py +0 -0
  107. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/task/list.py +0 -0
  108. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/task/log.py +0 -0
  109. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/task/namespaces.py +0 -0
  110. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/task/use.py +0 -0
  111. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/task/wait.py +0 -0
  112. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/upgrade.py +0 -0
  113. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/vm/__init__.py +0 -0
  114. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/vm/delete.py +0 -0
  115. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/vm/get.py +0 -0
  116. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/vm/list.py +0 -0
  117. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/vm/poweroff.py +0 -0
  118. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/vm/poweron.py +0 -0
  119. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/vm/restart.py +0 -0
  120. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/vm/shutdown.py +0 -0
  121. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/vm/use.py +0 -0
  122. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/cmds/vm/wait.py +0 -0
  123. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/config/default.yaml +0 -0
  124. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/config/int.yaml +0 -0
  125. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/config/prod.yaml +0 -0
  126. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/config/stg.yaml +0 -0
  127. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/__init__.py +0 -0
  128. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/_init.py +0 -0
  129. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/built_in_cmds/__init__.py +0 -0
  130. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/built_in_cmds/_ut.py +0 -0
  131. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/built_in_cmds/context.py +0 -0
  132. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/built_in_cmds/profile.py +0 -0
  133. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/cli_options.py +0 -0
  134. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/cli_processor.py +0 -0
  135. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/config.py +0 -0
  136. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/context.py +0 -0
  137. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/data_util.py +0 -0
  138. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/dispatcher.py +0 -0
  139. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/extension.py +0 -0
  140. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/fn_util.py +0 -0
  141. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/fstore.py +0 -0
  142. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/jsondot.py +0 -0
  143. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/profile.py +0 -0
  144. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/profile_store.py +0 -0
  145. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/recent.py +0 -0
  146. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/state.py +0 -0
  147. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/telemetry.py +0 -0
  148. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/template_util.py +0 -0
  149. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/timeutil.py +0 -0
  150. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/util.py +0 -0
  151. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/ctxp/var_template.py +0 -0
  152. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/__init__.py +0 -0
  153. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/_util.py +0 -0
  154. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/__init__.py +0 -0
  155. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/activity.py +0 -0
  156. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/api.py +0 -0
  157. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/credential.py +0 -0
  158. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/deployment.py +0 -0
  159. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/entitlement.py +0 -0
  160. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/group.py +0 -0
  161. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/network.py +0 -0
  162. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/notification.py +0 -0
  163. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/org.py +0 -0
  164. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/pool.py +0 -0
  165. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/provider.py +0 -0
  166. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/sag.py +0 -0
  167. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/task.py +0 -0
  168. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/resources/vm.py +0 -0
  169. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/mcp/server.py +0 -0
  170. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/__init__.py +0 -0
  171. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/activity.py +0 -0
  172. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/api.py +0 -0
  173. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/broker/__init__.py +0 -0
  174. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/broker/entitlement.py +0 -0
  175. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/broker/pool_group.py +0 -0
  176. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/cockpit/__init__.py +0 -0
  177. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/cockpit/network.py +0 -0
  178. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/cockpit/pool.py +0 -0
  179. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/cockpit/provider.py +0 -0
  180. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/cockpit/sag.py +0 -0
  181. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/cockpit/vm.py +0 -0
  182. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/credential.py +0 -0
  183. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/deployment.py +0 -0
  184. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/notification.py +0 -0
  185. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/org.py +0 -0
  186. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/service/task.py +0 -0
  187. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/__init__.py +0 -0
  188. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/auth.py +0 -0
  189. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/cli_options.py +0 -0
  190. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/client_util.py +0 -0
  191. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/init.py +0 -0
  192. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/login_support.py +0 -0
  193. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/mdc.py +0 -0
  194. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/payload_util.py +0 -0
  195. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/requtil.py +0 -0
  196. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/utils.py +0 -0
  197. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/sglib/zds_client.py +0 -0
  198. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/support/auth.py +0 -0
  199. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/support/payload.py +0 -0
  200. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/support/profile.py +0 -0
  201. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/support/task.py +0 -0
  202. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/support/use_util.py +0 -0
  203. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/support/vm.py +0 -0
  204. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/util/exec_util.py +0 -0
  205. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/util/exit.py +0 -0
  206. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/util/param_util.py +0 -0
  207. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/util/patch_util.py +0 -0
  208. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/util/query_util.py +0 -0
  209. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/util/readonly.py +0 -0
  210. {zen_cli-0.1.4 → zen_cli-0.1.5}/src/zen_cli/util/versions.py +0 -0
  211. {zen_cli-0.1.4 → zen_cli-0.1.5}/tests/test_auth_status.py +0 -0
  212. {zen_cli-0.1.4 → zen_cli-0.1.5}/tests/test_cli.py +0 -0
  213. {zen_cli-0.1.4 → zen_cli-0.1.5}/tests/test_device_login.py +0 -0
  214. {zen_cli-0.1.4 → zen_cli-0.1.5}/tests/test_mcp.py +0 -0
  215. {zen_cli-0.1.4 → zen_cli-0.1.5}/tests/test_payload.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: zen-cli
3
- Version: 0.1.4
4
- Summary: Command-line toolbox for Zen Cloud PC. Operate orgs, pools, VMs, and tasks; call ZDS APIs; expose a stdio MCP server.
3
+ Version: 0.1.5
4
+ Summary: CLI for Zen Desktop Service. Designed for AI and Human.
5
5
  Project-URL: Homepage, https://pypi.org/project/zen-cli/
6
6
  Author: zen-cli authors
7
7
  License-Expression: MIT
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "zen-cli"
7
- version = "0.1.4"
8
- description = "Command-line toolbox for Zen Cloud PC. Operate orgs, pools, VMs, and tasks; call ZDS APIs; expose a stdio MCP server."
7
+ version = "0.1.5"
8
+ description = "CLI for Zen Desktop Service. Designed for AI and Human."
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  license-files = ["LICENSE"]
@@ -44,7 +44,7 @@ include = ["src/zen_cli", "tests", "README.md", "LICENSE"]
44
44
  target-version = "py312"
45
45
  line-length = 120
46
46
  src = ["src"]
47
- exclude = ["cli-ref"]
47
+ exclude = ["cli-ref", "src/zen_cli/support/test_utils2.py"]
48
48
 
49
49
  [tool.ruff.lint]
50
50
  select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
@@ -53,6 +53,7 @@ select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
53
53
  python_version = "3.12"
54
54
  strict = true
55
55
  files = ["src"]
56
+ exclude = ["test_utils2\\.py"]
56
57
 
57
58
  [[tool.mypy.overrides]]
58
59
  module = ["mcp", "mcp.*"]
@@ -67,9 +68,14 @@ module = [
67
68
  "yaml",
68
69
  "httpx2",
69
70
  "httpx2.*",
71
+ "coloredlogs",
70
72
  ]
71
73
  ignore_missing_imports = true
72
74
 
73
75
  [[tool.mypy.overrides]]
74
76
  module = ["zen_cli.cmds", "zen_cli.cmds.*", "zen_cli.ctxp", "zen_cli.ctxp.*"]
75
77
  disallow_untyped_decorators = false
78
+
79
+ [[tool.mypy.overrides]]
80
+ module = ["zen_cli.service.task"]
81
+ disallow_subclassing_any = false
@@ -12,4 +12,8 @@ from zen_cli.support.payload import payload_options, run_create
12
12
  @payload_options
13
13
  def create(org: str, data_arg: str | None, json_arg: str | None) -> Any:
14
14
  """Create an express deployment from a JSON payload."""
15
- return run_create("deployment", deployment.create, data_arg, json_arg, org_id=cli.get_org_id(org))
15
+
16
+ def _create(payload: Any) -> Any:
17
+ return deployment.create(payload, org_id=cli.get_org_id(org))
18
+
19
+ return run_create("deployment", _create, data_arg, json_arg)
@@ -40,7 +40,7 @@ def subprocess_args(include_stdout: bool = True) -> dict[str, Any]:
40
40
  # when run from Pyinstaller with the ``--noconsole`` option. Avoid this
41
41
  # distraction.
42
42
  si = subprocess.STARTUPINFO()
43
- si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
43
+ si.dwFlags |= getattr(subprocess, "STARTF_USESHOWWINDOW", 1)
44
44
  # Windows doesn't search the path by default. Pass it an environment so
45
45
  # it will.
46
46
  env = os.environ
@@ -5,7 +5,7 @@ import sys
5
5
  from logging.handlers import RotatingFileHandler
6
6
  from typing import Any, ClassVar, TextIO
7
7
 
8
- import coloredlogs # type: ignore[import-untyped]
8
+ import coloredlogs
9
9
 
10
10
  LOG_FORMAT_SIMPLE = "%(levelname).4s %(asctime)s %(name)-16s %(message)s"
11
11
  LOG_FORMAT_LONG = (
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  import logging
3
- from typing import Any
3
+ from typing import Any, cast
4
4
 
5
5
  import httpx2
6
6
 
@@ -103,7 +103,7 @@ class CspClient:
103
103
  "Accept": "application/json",
104
104
  }
105
105
  # <no org id for this API>
106
- resp = self._client.post("/oauth/v2/token", headers=headers, data=f"api_token={api_token}") # type: ignore[arg-type]
106
+ resp = self._client.post("/oauth/v2/token", headers=headers, data=cast(Any, f"api_token={api_token}"))
107
107
  self._oauth_token = resp.json()
108
108
  return self._oauth_token
109
109
 
@@ -148,7 +148,7 @@ def _parse_resp(resp: httpx2.Response) -> Any:
148
148
 
149
149
 
150
150
  def _is_404(e: httpx2.HTTPStatusError) -> bool:
151
- return e.response.status_code == 404
151
+ return bool(e.response.status_code == 404)
152
152
 
153
153
 
154
154
  def on404ReturnNone(func: Callable[..., Any]) -> Any:
@@ -0,0 +1,155 @@
1
+ import atexit
2
+ import json
3
+ import os
4
+ import subprocess
5
+ import sys
6
+ from collections.abc import Callable
7
+ from pathlib import Path
8
+ from typing import Any
9
+ from unittest import TestCase
10
+
11
+ _SRC_DIR = str(Path(__file__).resolve().parents[2])
12
+
13
+
14
+ def _zen_prefix() -> str:
15
+ exe_dir = Path(sys.executable).parent
16
+ for candidate in (
17
+ exe_dir / "Scripts" / "zen.exe",
18
+ exe_dir / "Scripts" / "zen",
19
+ exe_dir / "zen.exe",
20
+ exe_dir / "zen",
21
+ ):
22
+ if candidate.is_file():
23
+ return f'"{candidate}"'
24
+ return f'"{sys.executable}" -m zen_cli'
25
+
26
+
27
+ def _zen_cmd(cmd: str) -> str:
28
+ """Rewrite a `zen ...` command so tests work when `zen` is not on PATH."""
29
+ if cmd == "zen" or cmd.startswith("zen "):
30
+ return _zen_prefix() + cmd[3:]
31
+ return cmd
32
+
33
+
34
+ def _test_env() -> dict[str, str]:
35
+ env = os.environ.copy()
36
+ env["ZEN_CLI_TELEMETRY"] = "false"
37
+ env["HCS_CLI_CHECK_UPGRADE"] = "false"
38
+ existing = env.get("PYTHONPATH", "")
39
+ env["PYTHONPATH"] = _SRC_DIR if not existing else _SRC_DIR + os.pathsep + existing
40
+ return env
41
+
42
+
43
+ def _run(cmd: str, stdin_payload: str | None = None) -> tuple[str, str, int]:
44
+ env = _test_env()
45
+ p = subprocess.run(
46
+ _zen_cmd(cmd),
47
+ input="" if stdin_payload is None else stdin_payload,
48
+ shell=True,
49
+ text=True,
50
+ check=False,
51
+ capture_output=True,
52
+ env=env,
53
+ )
54
+ return p.stdout, p.stderr, p.returncode
55
+
56
+
57
+ _test_profile_name = "__ctxp_ut"
58
+
59
+
60
+ _TEST_PROFILE = {
61
+ "description": "unit-test",
62
+ "readonly": False,
63
+ "csp": {"url": "https://csp.example.invalid"},
64
+ "zds": {"mdcs": {"ut": {"url": "https://zds.example.invalid"}}},
65
+ }
66
+
67
+
68
+ def _create_test_profile() -> None:
69
+ p = subprocess.Popen(
70
+ _zen_cmd(f"zen profile create {_test_profile_name}"),
71
+ shell=True,
72
+ text=True,
73
+ stdin=subprocess.PIPE,
74
+ stdout=subprocess.PIPE,
75
+ stderr=subprocess.PIPE,
76
+ env=_test_env(),
77
+ )
78
+ p.communicate(input=json.dumps(_TEST_PROFILE))
79
+ p.wait()
80
+
81
+
82
+ def _delete_test_profile() -> None:
83
+ _run(f"zen profile delete {_test_profile_name} -y")
84
+
85
+
86
+ _profile_ready = False
87
+ _prev_profile: str | None = None
88
+
89
+
90
+ def _restore_profiles() -> None:
91
+ _delete_test_profile()
92
+ if _prev_profile and _prev_profile != _test_profile_name:
93
+ _run(f"zen profile use {_prev_profile}")
94
+
95
+
96
+ def _ensure_test_profile() -> None:
97
+ global _profile_ready, _prev_profile
98
+ if _profile_ready:
99
+ return
100
+ stdout, _, code = _run("zen profile name")
101
+ _prev_profile = stdout.strip().strip('"') if code == 0 else None
102
+ _delete_test_profile()
103
+ _create_test_profile()
104
+ atexit.register(_restore_profiles)
105
+ _profile_ready = True
106
+
107
+
108
+ class CliTest(TestCase):
109
+ @classmethod
110
+ def setUpClass(cls) -> None:
111
+ _ensure_test_profile()
112
+
113
+ @classmethod
114
+ def NON_EMPTY_STRING(cls, data: Any) -> None:
115
+ if data is None or len(data) == 0:
116
+ raise Exception("Expect non-empty output, but got empty.")
117
+
118
+ @classmethod
119
+ def NON_EMPTY_JSON(cls, data: Any) -> None:
120
+ if data is None or len(data) == 0:
121
+ raise Exception("Expect non-empty json, but got empty.")
122
+ d = json.loads(data)
123
+ if len(d) == 0:
124
+ raise Exception("Expect non-empty json, but got empty dict.")
125
+
126
+ def verify(
127
+ self,
128
+ cmd: str,
129
+ verify_output: Callable[[str], None] | str | None = None,
130
+ expected_return_codes: list[int] | int | None = None,
131
+ stdin_payload: str | None = None,
132
+ ) -> None:
133
+ stdout, stderr, returncode = _run(cmd, stdin_payload)
134
+ try:
135
+ if expected_return_codes is None:
136
+ codes = [0]
137
+ elif isinstance(expected_return_codes, int):
138
+ codes = [expected_return_codes]
139
+ else:
140
+ codes = expected_return_codes
141
+ self.assertIn(returncode, codes, "Invalid return code.")
142
+
143
+ combined_output = stdout or ""
144
+ if stderr:
145
+ combined_output = f"{combined_output}\n{stderr}" if combined_output else stderr
146
+
147
+ if verify_output is not None:
148
+ if callable(verify_output):
149
+ verify_output(combined_output)
150
+ else:
151
+ self.assertEqual(combined_output, verify_output)
152
+ except Exception:
153
+ print("<DUMP STDOUT>", stdout)
154
+ print("<DUMP STDERR>", stderr)
155
+ raise
File without changes
@@ -0,0 +1,9 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+
4
+ class TestApi(CliTest):
5
+ def test_requires_path(self) -> None:
6
+ self.verify("zen api", expected_return_codes=[2])
7
+
8
+ def test_path_must_start_with_slash(self) -> None:
9
+ self.verify("zen api org/v1/org-details", expected_return_codes=[2])
@@ -0,0 +1,20 @@
1
+ from zen_cli.support.test_utils import CliTest, _test_profile_name
2
+
3
+ from .util import contains, json_has
4
+
5
+
6
+ class TestAuth(CliTest):
7
+ def test_help(self) -> None:
8
+ self.verify("zen auth --help", contains("status", "details", "token", "refresh"))
9
+
10
+ def test_status_logged_out(self) -> None:
11
+ self.verify(
12
+ f"zen -p {_test_profile_name} auth status",
13
+ json_has(logged_in=False, ttl=0),
14
+ )
15
+
16
+ def test_details_when_logged_out(self) -> None:
17
+ self.verify(f"zen -p {_test_profile_name} auth details", expected_return_codes=[1])
18
+
19
+ def test_token_when_logged_out(self) -> None:
20
+ self.verify(f"zen -p {_test_profile_name} auth token", expected_return_codes=[1])
@@ -0,0 +1,8 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains
4
+
5
+
6
+ class TestCompletion(CliTest):
7
+ def test_bash(self) -> None:
8
+ self.verify("zen completion bash", contains("_ZEN_COMPLETE", "_zen_completion"))
@@ -0,0 +1,17 @@
1
+ from zen_cli.support.test_utils import CliTest, _test_profile_name
2
+
3
+ from .util import contains, json_has
4
+
5
+
6
+ class TestContext(CliTest):
7
+ def test_list(self) -> None:
8
+ self.verify(f"zen -p {_test_profile_name} context list", CliTest.NON_EMPTY_STRING)
9
+
10
+ def test_roundtrip(self) -> None:
11
+ name = "bb_ctx"
12
+ try:
13
+ self.verify(f"zen -p {_test_profile_name} context set {name} k=v", None)
14
+ self.verify(f"zen -p {_test_profile_name} context get {name}", json_has(k="v"))
15
+ self.verify(f"zen -p {_test_profile_name} context list", contains(name))
16
+ finally:
17
+ self.verify(f"zen -p {_test_profile_name} context delete {name}", None)
@@ -0,0 +1,11 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains
4
+
5
+
6
+ class TestCredential(CliTest):
7
+ def test_help(self) -> None:
8
+ self.verify("zen credential --help", contains("create", "delete", "list", "get"))
9
+
10
+ def test_create_requires_payload(self) -> None:
11
+ self.verify("zen credential create", expected_return_codes=[2])
@@ -0,0 +1,11 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains
4
+
5
+
6
+ class TestDeployment(CliTest):
7
+ def test_help(self) -> None:
8
+ self.verify("zen deployment --help", contains("create", "delete", "list", "get"))
9
+
10
+ def test_create_requires_payload(self) -> None:
11
+ self.verify("zen deployment create", expected_return_codes=[2])
@@ -0,0 +1,11 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains
4
+
5
+
6
+ class TestEntitlement(CliTest):
7
+ def test_help(self) -> None:
8
+ self.verify("zen entitlement --help", contains("create", "delete", "list", "get"))
9
+
10
+ def test_create_requires_payload(self) -> None:
11
+ self.verify("zen entitlement create", expected_return_codes=[2])
@@ -0,0 +1,88 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains
4
+
5
+
6
+ class TestFrameworkErrors(CliTest):
7
+ """Generic error handling from ctxp (_process_result, print_error, panic, error_details)."""
8
+
9
+ def test_click_missing_argument(self) -> None:
10
+ self.verify(
11
+ "zen cli-test echo-obj",
12
+ contains("Missing argument 'ID'"),
13
+ expected_return_codes=[2],
14
+ )
15
+
16
+ def test_click_unknown_option(self) -> None:
17
+ self.verify(
18
+ "zen cli-test echo-obj foo --not-a-flag",
19
+ contains("No such option"),
20
+ expected_return_codes=[2],
21
+ )
22
+
23
+ def test_click_unknown_command(self) -> None:
24
+ self.verify("zen nosuchcommand", contains("No such command"), expected_return_codes=[2])
25
+
26
+ def test_error_tuple_prints_reason_and_exit_code(self) -> None:
27
+ self.verify(
28
+ "zen cli-test echo-error --reason boom --code 3",
29
+ contains("boom"),
30
+ expected_return_codes=[3],
31
+ )
32
+
33
+ def test_ctxp_error_tuple_prints_exception_and_exit_code(self) -> None:
34
+ self.verify(
35
+ "zen cli-test echo-error --reason boom --code 4 --ctxp-error",
36
+ contains("CtxpException: boom"),
37
+ expected_return_codes=[4],
38
+ )
39
+
40
+ def test_error_return_code_zero_is_rejected(self) -> None:
41
+ self.verify(
42
+ "zen cli-test echo-error --reason boom --code 0",
43
+ contains("return_code must not be 0"),
44
+ expected_return_codes=[1],
45
+ )
46
+
47
+ def test_ctxp_exception_prints_cause_chain_without_traceback(self) -> None:
48
+ def check(data: str) -> None:
49
+ if "CtxpException: pseudo-error" not in data:
50
+ raise AssertionError(data)
51
+ if "Caused by: KeyError: 'missing'" not in data:
52
+ raise AssertionError(data)
53
+ if "Traceback (most recent call last)" in data:
54
+ raise AssertionError(f"user error should not dump a traceback:\n{data}")
55
+
56
+ self.verify(
57
+ "zen cli-test echo-exception --reason missing --type CtxpException",
58
+ check,
59
+ expected_return_codes=[1],
60
+ )
61
+
62
+ def test_generic_exception_prints_cause_chain(self) -> None:
63
+ self.verify(
64
+ "zen cli-test echo-exception --reason x --type Exception",
65
+ contains("Exception: pseudo-error", "Caused by: KeyError: 'x'"),
66
+ expected_return_codes=[1],
67
+ )
68
+
69
+ def test_program_error_prints_traceback(self) -> None:
70
+ self.verify(
71
+ "zen cli-test echo-exception --reason missing --type TypeError",
72
+ contains("Traceback (most recent call last)", "TypeError", "pseudo-error"),
73
+ expected_return_codes=[1],
74
+ )
75
+
76
+ def test_http_status_error_includes_body(self) -> None:
77
+ self.verify(
78
+ "zen cli-test echo-exception --reason body --type httpx2.HTTPStatusError",
79
+ contains("HTTPStatusError", "400", "body"),
80
+ expected_return_codes=[1],
81
+ )
82
+
83
+ def test_panic_from_missing_profile(self) -> None:
84
+ self.verify(
85
+ "zen profile get no-such-profile-xyz",
86
+ contains("Profile not found"),
87
+ expected_return_codes=[1],
88
+ )
@@ -0,0 +1,11 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains
4
+
5
+
6
+ class TestGroup(CliTest):
7
+ def test_help(self) -> None:
8
+ self.verify("zen group --help", contains("create", "delete", "list", "get"))
9
+
10
+ def test_create_requires_payload(self) -> None:
11
+ self.verify("zen group create", expected_return_codes=[2])
@@ -0,0 +1,88 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains, json_equals, json_has
4
+
5
+
6
+ class TestCommonIo(CliTest):
7
+ """Shared I/O flags from ctxp.cli_options, applied by cli_processor._default_io."""
8
+
9
+ def test_help_lists_io_flags_and_examples(self) -> None:
10
+ self.verify(
11
+ "zen cli-test echo-obj --help",
12
+ contains(
13
+ "--output",
14
+ "--field",
15
+ "--exclude-field",
16
+ "--ids",
17
+ "--first",
18
+ "Examples:",
19
+ "-o table",
20
+ "--field id,name",
21
+ "--ids",
22
+ ),
23
+ )
24
+
25
+ def test_default_output_is_json(self) -> None:
26
+ self.verify("zen cli-test echo-obj foo --opt bar", json_has(id="foo", opt="bar"))
27
+
28
+ def test_output_json_compact(self) -> None:
29
+ self.verify(
30
+ "zen cli-test echo-obj foo --opt bar -o json-compact",
31
+ json_equals({"id": "foo", "opt": "bar"}),
32
+ )
33
+
34
+ def test_output_yaml_and_yml_alias(self) -> None:
35
+ self.verify("zen cli-test echo-obj foo --opt bar -o yaml", contains("id: foo", "opt: bar"))
36
+ self.verify("zen cli-test echo-obj foo --opt bar -o yml", contains("id: foo", "opt: bar"))
37
+
38
+ def test_output_table_and_t_alias(self) -> None:
39
+ self.verify("zen cli-test echo-obj-list a b --opt x -o table", contains("Id", "Opt", "a", "b", "x"))
40
+ self.verify("zen cli-test echo-obj-list a --opt x -o t", contains("Id", "Opt", "a", "x"))
41
+
42
+ def test_output_text_object_and_list(self) -> None:
43
+ self.verify("zen cli-test echo-obj foo --opt bar -o text", json_has(id="foo", opt="bar"))
44
+ self.verify("zen cli-test echo-str-list a b -o text", contains("a", "b"))
45
+
46
+ def test_output_case_insensitive(self) -> None:
47
+ self.verify("zen cli-test echo-obj foo --opt bar -o JSON", json_has(id="foo", opt="bar"))
48
+
49
+ def test_invalid_output_is_usage_error(self) -> None:
50
+ self.verify(
51
+ "zen cli-test echo-obj foo --opt bar -o not-a-format",
52
+ contains("Invalid value for '--output'"),
53
+ expected_return_codes=[2],
54
+ )
55
+
56
+ def test_field_keeps_only_named_keys(self) -> None:
57
+ self.verify("zen cli-test echo-obj foo --opt bar --field id", json_equals({"id": "foo"}))
58
+ self.verify(
59
+ "zen cli-test echo-obj-list a b --opt x --field id",
60
+ json_equals([{"id": "a"}, {"id": "b"}]),
61
+ )
62
+
63
+ def test_exclude_field_drops_named_keys(self) -> None:
64
+ self.verify("zen cli-test echo-obj foo --opt bar --exclude-field opt", json_equals({"id": "foo"}))
65
+
66
+ def test_ids_prints_id_only(self) -> None:
67
+ self.verify("zen cli-test echo-obj foo --opt bar --ids", json_equals("foo"))
68
+ self.verify("zen cli-test echo-obj-list a b --opt x --ids", json_equals(["a", "b"]))
69
+
70
+ def test_ids_and_field_are_rejected(self) -> None:
71
+ self.verify(
72
+ "zen cli-test echo-obj foo --opt bar --ids --field id",
73
+ contains("--ids and --fields should not be used together."),
74
+ )
75
+
76
+ def test_first_returns_first_list_item(self) -> None:
77
+ self.verify("zen cli-test echo-obj-list a b c --opt x --first", json_has(id="a", opt="x"))
78
+
79
+ def test_first_on_empty_list_prints_nothing(self) -> None:
80
+ self.verify("zen cli-test echo-obj-list --opt x --first", None)
81
+
82
+ def test_none_result_prints_nothing(self) -> None:
83
+ self.verify("zen cli-test echo-none", None)
84
+
85
+ def test_scalar_and_list_json(self) -> None:
86
+ self.verify("zen cli-test echo-int 7", contains("7"))
87
+ self.verify("zen cli-test echo-str hello", contains("hello"))
88
+ self.verify("zen cli-test echo-int-list 1 2 3 -o json-compact", json_equals([1, 2, 3]))
@@ -0,0 +1,27 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains
4
+
5
+
6
+ class TestLogin(CliTest):
7
+ def test_help(self) -> None:
8
+ self.verify("zen login --help", contains("--device-code", "--browser"))
9
+
10
+ def test_help_has_no_details(self) -> None:
11
+ def check(data: str) -> None:
12
+ if "--details" in data or "-d," in data:
13
+ raise AssertionError(f"login help should not list --details:\n{data}")
14
+ if "--device-code" not in data:
15
+ raise AssertionError(data)
16
+
17
+ self.verify("zen login --help", check)
18
+
19
+ def test_exclusive_methods(self) -> None:
20
+ self.verify(
21
+ "zen login --device-code --browser",
22
+ contains("Specify only one"),
23
+ expected_return_codes=[1],
24
+ )
25
+
26
+ def test_client_id_needs_secret(self) -> None:
27
+ self.verify("zen login --client-id demo", expected_return_codes=[1])
@@ -0,0 +1,6 @@
1
+ from zen_cli.support.test_utils import CliTest, _test_profile_name
2
+
3
+
4
+ class TestLogout(CliTest):
5
+ def test_logout(self) -> None:
6
+ self.verify(f"zen -p {_test_profile_name} logout", None)
@@ -0,0 +1,8 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains
4
+
5
+
6
+ class TestMcp(CliTest):
7
+ def test_help(self) -> None:
8
+ self.verify("zen mcp --help", contains("stdio"))
@@ -0,0 +1,11 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains
4
+
5
+
6
+ class TestNetwork(CliTest):
7
+ def test_help(self) -> None:
8
+ self.verify("zen network --help", contains("create", "delete", "list", "get"))
9
+
10
+ def test_create_requires_payload(self) -> None:
11
+ self.verify("zen network create", expected_return_codes=[2])
@@ -0,0 +1,23 @@
1
+ from zen_cli.support.test_utils import CliTest
2
+
3
+ from .util import contains
4
+
5
+
6
+ class TestOrg(CliTest):
7
+ def test_help_has_create_not_delete(self) -> None:
8
+ def check(data: str) -> None:
9
+ if "create" not in data:
10
+ raise AssertionError(data)
11
+ if "delete" in data:
12
+ raise AssertionError(f"org help should not list delete:\n{data}")
13
+
14
+ self.verify("zen org --help", check)
15
+
16
+ def test_list_io_flags(self) -> None:
17
+ self.verify(
18
+ "zen org list --help",
19
+ contains("--output", "--field", "--exclude-field", "--ids", "--first"),
20
+ )
21
+
22
+ def test_create_requires_payload(self) -> None:
23
+ self.verify("zen org create", expected_return_codes=[2])