hap-cli 0.9.2__tar.gz → 0.9.3__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 (413) hide show
  1. {hap_cli-0.9.2 → hap_cli-0.9.3}/PKG-INFO +1 -1
  2. {hap_cli-0.9.2 → hap_cli-0.9.3}/app_live_tests/smoke.py +84 -0
  3. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/__init__.py +1 -1
  4. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/context.py +5 -0
  5. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/field_spec_adapter.py +6 -0
  6. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/worksheet.py +131 -12
  7. hap_cli-0.9.3/hap_cli/tests/test_create_worksheet_partial.py +231 -0
  8. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli.egg-info/PKG-INFO +1 -1
  9. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli.egg-info/SOURCES.txt +1 -0
  10. {hap_cli-0.9.2 → hap_cli-0.9.3}/MANIFEST.in +0 -0
  11. {hap_cli-0.9.2 → hap_cli-0.9.3}/README.md +0 -0
  12. {hap_cli-0.9.2 → hap_cli-0.9.3}/app_live_tests/__init__.py +0 -0
  13. {hap_cli-0.9.2 → hap_cli-0.9.3}/app_live_tests/__main__.py +0 -0
  14. {hap_cli-0.9.2 → hap_cli-0.9.3}/app_live_tests/config.py +0 -0
  15. {hap_cli-0.9.2 → hap_cli-0.9.3}/app_live_tests/fixtures/__init__.py +0 -0
  16. {hap_cli-0.9.2 → hap_cli-0.9.3}/app_live_tests/fixtures/feedback_scenario.py +0 -0
  17. {hap_cli-0.9.2 → hap_cli-0.9.3}/app_live_tests/harness.py +0 -0
  18. {hap_cli-0.9.2 → hap_cli-0.9.3}/app_live_tests/state.py +0 -0
  19. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/README.md +0 -0
  20. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/README_CN.md +0 -0
  21. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/__main__.py +0 -0
  22. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/_entry.py +0 -0
  23. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/__init__.py +0 -0
  24. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/app_cmd.py +0 -0
  25. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/app_creator_cmd.py +0 -0
  26. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/app_editor_cmd.py +0 -0
  27. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/attachment_cmd.py +0 -0
  28. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/auth_cmd.py +0 -0
  29. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/calendar_cmd.py +0 -0
  30. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/chart_cmd.py +0 -0
  31. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/chat_cmd.py +0 -0
  32. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/chatbot_cmd.py +0 -0
  33. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/config_cmd.py +0 -0
  34. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/contact_cmd.py +0 -0
  35. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/department_cmd.py +0 -0
  36. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/group_cmd.py +0 -0
  37. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/guide_cmd.py +0 -0
  38. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/icon_cmd.py +0 -0
  39. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/instance_cmd.py +0 -0
  40. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/node_cmd.py +0 -0
  41. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/optionset_cmd.py +0 -0
  42. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/page_cmd.py +0 -0
  43. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/post_cmd.py +0 -0
  44. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/record_cmd.py +0 -0
  45. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/region_cmd.py +0 -0
  46. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/role_cmd.py +0 -0
  47. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/update_cmd.py +0 -0
  48. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/upload_cmd.py +0 -0
  49. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/v3_registry.py +0 -0
  50. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/workflow_cmd.py +0 -0
  51. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/commands/worksheet_cmd.py +0 -0
  52. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/__init__.py +0 -0
  53. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/action_spec_adapter.py +0 -0
  54. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app.py +0 -0
  55. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/__init__.py +0 -0
  56. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
  57. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
  58. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
  59. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/charts.py +0 -0
  60. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/cleanup.py +0 -0
  61. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/compiler.py +0 -0
  62. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/config.py +0 -0
  63. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/design/design.schema.json +0 -0
  64. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/errors.py +0 -0
  65. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
  66. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/executor.py +0 -0
  67. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/fields.py +0 -0
  68. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/hap.py +0 -0
  69. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/merge.py +0 -0
  70. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/recording/__init__.py +0 -0
  71. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/recording/console.py +0 -0
  72. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
  73. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/recording/mirror.py +0 -0
  74. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/recording/report.py +0 -0
  75. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/schema.py +0 -0
  76. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
  77. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/seed/__init__.py +0 -0
  78. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/seed/cli.py +0 -0
  79. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/seed/executor.py +0 -0
  80. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
  81. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
  82. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
  83. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
  84. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/seed/template.py +0 -0
  85. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/selftest.py +0 -0
  86. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/smoke.py +0 -0
  87. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/steps.py +0 -0
  88. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/store.py +0 -0
  89. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/validate.py +0 -0
  90. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
  91. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/attachment.py +0 -0
  92. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/auth.py +0 -0
  93. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/calendar_mod.py +0 -0
  94. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/chart.py +0 -0
  95. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/chat.py +0 -0
  96. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/chatbot.py +0 -0
  97. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/cli_access.py +0 -0
  98. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/condition_dsl.py +0 -0
  99. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/contact.py +0 -0
  100. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/control_type_codes.py +0 -0
  101. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/department.py +0 -0
  102. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/__init__.py +0 -0
  103. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
  104. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
  105. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/apply.py +0 -0
  106. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/componentlower.py +0 -0
  107. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/editspec/component.schema.json +0 -0
  108. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
  109. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
  110. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/editspec/field.schema.json +0 -0
  111. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/editspec.py +0 -0
  112. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/errors.py +0 -0
  113. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/fieldlower.py +0 -0
  114. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/jsonschema_mini.py +0 -0
  115. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/models.py +0 -0
  116. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/ops.py +0 -0
  117. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/planner.py +0 -0
  118. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/editor/reader.py +0 -0
  119. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/field_normalizer.py +0 -0
  120. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/filter_translator.py +0 -0
  121. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/filter_v3.py +0 -0
  122. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/flow_node.py +0 -0
  123. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/global_meta.py +0 -0
  124. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/group.py +0 -0
  125. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/guide.py +0 -0
  126. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/icon_index.py +0 -0
  127. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/instance.py +0 -0
  128. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/layout_check.py +0 -0
  129. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/logger.py +0 -0
  130. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/optionset.py +0 -0
  131. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/page.py +0 -0
  132. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/post.py +0 -0
  133. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/record.py +0 -0
  134. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/record_defaults.py +0 -0
  135. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/record_form_checks.py +0 -0
  136. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/record_lookups.py +0 -0
  137. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/record_rules.py +0 -0
  138. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/relation_scope.py +0 -0
  139. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/response_crypto.py +0 -0
  140. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/role.py +0 -0
  141. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/role_perm_builder.py +0 -0
  142. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/rule_spec_adapter.py +0 -0
  143. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/session.py +0 -0
  144. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/token_crypto.py +0 -0
  145. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/update.py +0 -0
  146. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/upload.py +0 -0
  147. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/v3/__init__.py +0 -0
  148. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/v3/dispatcher.py +0 -0
  149. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/v3/render.py +0 -0
  150. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/v3/schema.py +0 -0
  151. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/view_spec_adapter.py +0 -0
  152. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/workflow.py +0 -0
  153. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/workflow_node_dsl.py +0 -0
  154. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/worksheet_form.py +0 -0
  155. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/worksheet_reference.py +0 -0
  156. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/core/worksheet_templates.py +0 -0
  157. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/en/approval.md +0 -0
  158. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/en/chart.md +0 -0
  159. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/en/messages.md +0 -0
  160. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/en/overview.md +0 -0
  161. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/en/post.md +0 -0
  162. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/en/record.md +0 -0
  163. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/en/setup.md +0 -0
  164. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/en/skills.md +0 -0
  165. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/en/workflow.md +0 -0
  166. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/en/worksheet.md +0 -0
  167. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/index.json +0 -0
  168. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/zh-Hans/approval.md +0 -0
  169. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/zh-Hans/chart.md +0 -0
  170. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/zh-Hans/messages.md +0 -0
  171. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/zh-Hans/overview.md +0 -0
  172. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/zh-Hans/post.md +0 -0
  173. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/zh-Hans/record.md +0 -0
  174. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/zh-Hans/setup.md +0 -0
  175. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/zh-Hans/skills.md +0 -0
  176. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/zh-Hans/workflow.md +0 -0
  177. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/guide/zh-Hans/worksheet.md +0 -0
  178. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/i18n.py +0 -0
  179. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/locale/__init__.py +0 -0
  180. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/locale/messages.json +0 -0
  181. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/locale/messages.schema.json +0 -0
  182. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/__init__.py +0 -0
  183. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/conftest.py +0 -0
  184. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_action_mdpublic_parity.py +0 -0
  185. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_advanced_setting_guard.py +0 -0
  186. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_app_creator.py +0 -0
  187. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_app_editor.py +0 -0
  188. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_app_export_usage.py +0 -0
  189. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_app_light_color.py +0 -0
  190. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_app_section_name.py +0 -0
  191. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_approval_submit_fill_cli.py +0 -0
  192. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_approval_wire.py +0 -0
  193. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_atomic_write.py +0 -0
  194. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_attachment_share.py +0 -0
  195. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_auth_prerelease.py +0 -0
  196. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_batch_add_atomic.py +0 -0
  197. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_bug_036430.py +0 -0
  198. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_bug_036750.py +0 -0
  199. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_calendar_date_range.py +0 -0
  200. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_calendar_discussion_cli.py +0 -0
  201. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_chart.py +0 -0
  202. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_chart_filter_merge.py +0 -0
  203. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_chat_attachment_send.py +0 -0
  204. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_chat_messages_cli.py +0 -0
  205. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_chat_recall_cli.py +0 -0
  206. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_chat_send_socket.py +0 -0
  207. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_cli_access.py +0 -0
  208. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_coded_responses.py +0 -0
  209. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_condition_dsl.py +0 -0
  210. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_condition_dsl_entries.py +0 -0
  211. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_condition_dsl_readback.py +0 -0
  212. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_config_cmd.py +0 -0
  213. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_content_language.py +0 -0
  214. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_control_lookup_guard.py +0 -0
  215. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_core.py +0 -0
  216. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
  217. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_dsl_existing_node_refs.py +0 -0
  218. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_dsl_multi_record_guard.py +0 -0
  219. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_editor_inline_subtable.py +0 -0
  220. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_engine_core_parity.py +0 -0
  221. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_arg_consistency.py +0 -0
  222. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_bidirectional.py +0 -0
  223. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_discussion_mentions.py +0 -0
  224. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_entry_module.py +0 -0
  225. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_field_record_ref.py +0 -0
  226. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_http_error_body.py +0 -0
  227. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_json_anywhere.py +0 -0
  228. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_json_file_arg.py +0 -0
  229. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_layout_check.py +0 -0
  230. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_node_get_detail.py +0 -0
  231. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_node_summary.py +0 -0
  232. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_post_cursor.py +0 -0
  233. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_publish_reasons.py +0 -0
  234. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_replicate_workflow.py +0 -0
  235. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_save_fields_shape.py +0 -0
  236. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_subgroups.py +0 -0
  237. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_subtable_read.py +0 -0
  238. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_worksheet_references.py +0 -0
  239. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_worksheet_visibility.py +0 -0
  240. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fb_worksheet_workflows.py +0 -0
  241. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_fields_mdpublic_parity.py +0 -0
  242. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_full_e2e.py +0 -0
  243. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g1_chart_parity.py +0 -0
  244. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g1_record_parity.py +0 -0
  245. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g1_upload_parity.py +0 -0
  246. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g1_worksheet_parity.py +0 -0
  247. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g2_coverage_parity.py +0 -0
  248. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g3_workflow_parity.py +0 -0
  249. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g4_approval_parity.py +0 -0
  250. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g5_low_frequency_parity.py +0 -0
  251. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g6_advanced_fields.py +0 -0
  252. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g6_fields_roundtrip.py +0 -0
  253. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g7_misc_gaps.py +0 -0
  254. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g7b_permanent_delete.py +0 -0
  255. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g8_filter_operator.py +0 -0
  256. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g8b_pivot_operators.py +0 -0
  257. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g8c_empty_filter_and_help_truth.py +0 -0
  258. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_g9_appid_resolve.py +0 -0
  259. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_global_meta.py +0 -0
  260. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_guide.py +0 -0
  261. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_guide_sections.py +0 -0
  262. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_i18n.py +0 -0
  263. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_inner_flow_batch_add.py +0 -0
  264. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_instance_envelope.py +0 -0
  265. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_instance_history_cli.py +0 -0
  266. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration.py +0 -0
  267. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration_approval.py +0 -0
  268. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration_approval_actions.py +0 -0
  269. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration_calendar.py +0 -0
  270. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration_destructive.py +0 -0
  271. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration_misc.py +0 -0
  272. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration_post.py +0 -0
  273. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration_social.py +0 -0
  274. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration_v3.py +0 -0
  275. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration_workflow.py +0 -0
  276. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
  277. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_jsonschema_mini_copy.py +0 -0
  278. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
  279. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_multi_profile.py +0 -0
  280. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
  281. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_noninteractive_confirm.py +0 -0
  282. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
  283. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_org_id_cli.py +0 -0
  284. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_org_id_docs.py +0 -0
  285. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_page_filters_group_read.py +0 -0
  286. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_page_filters_group_update.py +0 -0
  287. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_page_save_page_id.py +0 -0
  288. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_parameter_conventions.py +0 -0
  289. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
  290. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_post_scope_cli.py +0 -0
  291. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
  292. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_record_business_rules.py +0 -0
  293. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_record_defaults.py +0 -0
  294. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_record_doc_id_hidden.py +0 -0
  295. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_record_field_alias.py +0 -0
  296. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_record_form_checks.py +0 -0
  297. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_record_run_action.py +0 -0
  298. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
  299. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_record_write_fixes.py +0 -0
  300. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_relation_scope.py +0 -0
  301. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_rmw_mdpublic_parity.py +0 -0
  302. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_role_app_scope.py +0 -0
  303. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_rule_filters_and_field_warning.py +0 -0
  304. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_save_action_silent_drop.py +0 -0
  305. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_save_rule_spec.py +0 -0
  306. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_select_add_option.py +0 -0
  307. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
  308. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_semantic_types_registry.py +0 -0
  309. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_subtable_mdpublic_parity.py +0 -0
  310. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_update.py +0 -0
  311. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_update_mode.py +0 -0
  312. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_upload_allow_down.py +0 -0
  313. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_upload_host_resolution.py +0 -0
  314. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_upload_mdpublic_parity.py +0 -0
  315. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
  316. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_v3_bool_flags.py +0 -0
  317. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_v3_dispatcher.py +0 -0
  318. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
  319. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_v3_response_format_json.py +0 -0
  320. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_v3_session.py +0 -0
  321. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
  322. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_view_mdpublic_parity.py +0 -0
  323. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_wf_config_keys.py +0 -0
  324. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_wf_inner_scope.py +0 -0
  325. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_wf_json_file_arg.py +0 -0
  326. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_wf_node_delete_missing.py +0 -0
  327. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_wf_save_action_dsl_fields.py +0 -0
  328. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_wf_structure_listing.py +0 -0
  329. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_wf_trigger_outcome.py +0 -0
  330. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_wf_trigger_preserve.py +0 -0
  331. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_wo4_result_handling.py +0 -0
  332. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_code_node.py +0 -0
  333. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_condition_field_types.py +0 -0
  334. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_condition_slots.py +0 -0
  335. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_delete_cascade.py +0 -0
  336. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
  337. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_lifecycle_wire.py +0 -0
  338. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_list_cli.py +0 -0
  339. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_node_wire.py +0 -0
  340. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_publish_inner.py +0 -0
  341. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
  342. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
  343. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_worksheet_create_section.py +0 -0
  344. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
  345. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_worksheet_filter_read.py +0 -0
  346. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_worksheet_switch.py +0 -0
  347. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
  348. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/utils/__init__.py +0 -0
  349. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/utils/atomic_write.py +0 -0
  350. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/utils/confirm.py +0 -0
  351. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/utils/formatting.py +0 -0
  352. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/utils/options.py +0 -0
  353. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli/utils/parameter_mapping.py +0 -0
  354. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli.egg-info/dependency_links.txt +0 -0
  355. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli.egg-info/entry_points.txt +0 -0
  356. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli.egg-info/requires.txt +0 -0
  357. {hap_cli-0.9.2 → hap_cli-0.9.3}/hap_cli.egg-info/top_level.txt +0 -0
  358. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/__init__.py +0 -0
  359. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/__main__.py +0 -0
  360. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/_wftest.py +0 -0
  361. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/charts.py +0 -0
  362. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/cleanup.py +0 -0
  363. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/compiler.py +0 -0
  364. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/config.py +0 -0
  365. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/errors.py +0 -0
  366. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/executor.py +0 -0
  367. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/fields.py +0 -0
  368. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/hap.py +0 -0
  369. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/recording/__init__.py +0 -0
  370. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/recording/console.py +0 -0
  371. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/recording/jsonl.py +0 -0
  372. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/recording/mirror.py +0 -0
  373. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/recording/report.py +0 -0
  374. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/schema.py +0 -0
  375. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/seed/__init__.py +0 -0
  376. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/seed/cli.py +0 -0
  377. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/seed/executor.py +0 -0
  378. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/seed/template.py +0 -0
  379. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/selftest.py +0 -0
  380. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/smoke.py +0 -0
  381. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/step.py +0 -0
  382. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/steps.py +0 -0
  383. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/store.py +0 -0
  384. {hap_cli-0.9.2 → hap_cli-0.9.3}/live_tests/workflow_dsl.py +0 -0
  385. {hap_cli-0.9.2 → hap_cli-0.9.3}/setup.cfg +0 -0
  386. {hap_cli-0.9.2 → hap_cli-0.9.3}/setup.py +0 -0
  387. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/INDEX.json +0 -0
  388. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
  389. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
  390. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/create_optionset.yaml +0 -0
  391. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/create_role.yaml +0 -0
  392. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/delete_optionset.yaml +0 -0
  393. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/delete_record.yaml +0 -0
  394. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/delete_role.yaml +0 -0
  395. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
  396. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_app_info.yaml +0 -0
  397. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
  398. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
  399. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
  400. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_record_details.yaml +0 -0
  401. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
  402. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_record_list.yaml +0 -0
  403. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_record_logs.yaml +0 -0
  404. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
  405. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_record_relations.yaml +0 -0
  406. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
  407. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_regions.yaml +0 -0
  408. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_role_details.yaml +0 -0
  409. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/get_role_list.yaml +0 -0
  410. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/knowledge_search.yaml +0 -0
  411. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
  412. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
  413. {hap_cli-0.9.2 → hap_cli-0.9.3}/sources/v3-api-schema/update_optionset.yaml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hap-cli
3
- Version: 0.9.2
3
+ Version: 0.9.3
4
4
  Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
@@ -3602,6 +3602,89 @@ def case_editor_inline_subtable(app_id: str, section_id: str) -> None:
3602
3602
  hap(["worksheet", "delete", parent, "-a", app_id, "-y"], check=False)
3603
3603
 
3604
3604
 
3605
+ def case_create_partial(app_id: str, section_id: str) -> None:
3606
+ """Bug #455775: a failed `worksheet create --fields` leaves nothing."""
3607
+ print("\nscenario: worksheet create --fields failure (#455775)")
3608
+ import json as _json
3609
+ from hap_cli.core import app as app_mod
3610
+ from hap_cli.core.session import Session
3611
+ session = Session.load()
3612
+ project_id = app_mod.app_project_id(session, app_id)
3613
+ prefix = "QA455775"
3614
+ for leftover in [n for n in _worksheets(app_id) if n.startswith(prefix)]:
3615
+ _ensure_absent(app_id, leftover)
3616
+
3617
+ def _ours() -> list[str]:
3618
+ return [n for n in _worksheets(app_id) if n.startswith(prefix)]
3619
+
3620
+ def _purge(ws_id: str) -> None:
3621
+ try:
3622
+ app_mod.purge_app_item(session, app_id=app_id,
3623
+ project_id=project_id,
3624
+ section_id=section_id, item_id=ws_id,
3625
+ item_type=0)
3626
+ except Exception as exc: # noqa: BLE001 — reported, not fatal
3627
+ print(f" note: could not purge {ws_id}: {exc}")
3628
+
3629
+ single = {"source": "static", "value": "DEFAULT-QA"}
3630
+ ok, res = _try(["worksheet", "create", app_id, f"{prefix} Invalid",
3631
+ "--section-id", section_id, "--fields", _json.dumps([
3632
+ {"type": "Text", "name": "名称"},
3633
+ {"type": "Text", "name": "备注",
3634
+ "defaultValue": single}], ensure_ascii=False)])
3635
+ check("create-partial: a single-object default is refused", not ok,
3636
+ str(res)[:200])
3637
+ check("create-partial: the refusal names the field and the shape",
3638
+ "备注" in str(res) and "list of objects" in str(res),
3639
+ str(res)[:300])
3640
+ check("create-partial: nothing was created", _ours() == [], str(_ours()))
3641
+
3642
+ # Passes every offline check, refused by the server at save time:
3643
+ # an alias the worksheet's own record-id column already holds.
3644
+ ok, res = _try(["worksheet", "create", app_id, f"{prefix} PostFail",
3645
+ "--section-id", section_id, "--fields", _json.dumps([
3646
+ {"type": "Text", "name": "名称"},
3647
+ {"type": "Text", "name": "冲突",
3648
+ "extra": {"alias": "rowid"}}],
3649
+ ensure_ascii=False)])
3650
+ check("create-partial: a save rejected after the create fails", not ok,
3651
+ str(res)[:200])
3652
+ check("create-partial: the error says the create was undone",
3653
+ "undone" in str(res), str(res)[:300])
3654
+ check("create-partial: no half-built worksheet is left",
3655
+ _ours() == [], str(_ours()))
3656
+
3657
+ ws_id = ""
3658
+ try:
3659
+ made = hap(["worksheet", "create", app_id, f"{prefix} Array",
3660
+ "--section-id", section_id, "--fields", _json.dumps([
3661
+ {"type": "Text", "name": "名称"},
3662
+ {"type": "Text", "name": "备注",
3663
+ "defaultValue": [single]}], ensure_ascii=False)],
3664
+ check=False)
3665
+ ws_id = made.get("worksheetId", "") if isinstance(made, dict) else ""
3666
+ check("create-partial: the list form still creates", bool(ws_id),
3667
+ str(made)[:200])
3668
+ if not ws_id:
3669
+ return
3670
+ note = next((c for c in _fields(ws_id)
3671
+ if c.get("controlName") == "备注"), {})
3672
+ check("create-partial: its default is stored",
3673
+ "DEFAULT-QA" in str((note.get("advancedSetting") or {})
3674
+ .get("defsource")), str(note)[:200])
3675
+ ok, res = _try(["worksheet", "update-fields", ws_id, "--fields",
3676
+ _json.dumps([{"type": "Text", "name": "名称"},
3677
+ {"type": "Text", "name": "备注",
3678
+ "defaultValue": single}],
3679
+ ensure_ascii=False)])
3680
+ check("create-partial: update-fields refuses it clearly too",
3681
+ not ok and "defaultValue" in str(res)
3682
+ and "AttributeError" not in str(res), str(res)[:300])
3683
+ finally:
3684
+ if ws_id:
3685
+ _purge(ws_id)
3686
+
3687
+
3605
3688
  def case_fb_subgroups(app_id: str, section_id: str) -> None:
3606
3689
  """WO-FB 3.1: the editor can see worksheets in second-level groups."""
3607
3690
  print("\nscenario: sub-group visibility (WO-FB 3.1)")
@@ -4792,6 +4875,7 @@ _CASES = {
4792
4875
  "fb-layout-check": case_fb_layout_check,
4793
4876
  "fb-pair-relation": case_fb_pair_relation,
4794
4877
  "fb-subtable-read": case_fb_subtable_read,
4878
+ "create-partial": case_create_partial,
4795
4879
  "editor-inline-subtable": case_editor_inline_subtable,
4796
4880
  "fb-subgroups": case_fb_subgroups,
4797
4881
  "fb-button-target": case_fb_button_target,
@@ -3,7 +3,7 @@
3
3
  import sys as _sys
4
4
  import types as _types
5
5
 
6
- __version__ = "0.9.2"
6
+ __version__ = "0.9.3"
7
7
 
8
8
  # The command tree used to live in hap_cli/hap_cli.py. A module named
9
9
  # after the package it sits in shadows that package: run python with the
@@ -84,6 +84,11 @@ class Context:
84
84
  is_auth = isinstance(e, (AuthenticationError, SessionError))
85
85
  if self.json_mode:
86
86
  payload = {"error": msg, "type": type(e).__name__}
87
+ # An error may carry state the caller needs to act on, e.g.
88
+ # the id of a worksheet a failed create left behind.
89
+ details = getattr(e, "details", None)
90
+ if isinstance(details, dict):
91
+ payload.update(details)
87
92
  if is_auth:
88
93
  payload["hint"] = NOT_LOGGED_IN_HINT
89
94
  payload["not_logged_in"] = True
@@ -156,6 +156,12 @@ def adapt_relation_config(spec: dict[str, Any]) -> None:
156
156
  spec["advanced_setting"] = adv
157
157
 
158
158
 
159
+ # The ``source`` values build_defsource below can encode. Anything else
160
+ # was skipped there without a word; the field-spec check refuses it
161
+ # up front instead (bug #455775).
162
+ DEFAULT_VALUE_SOURCES = ("system", "static", "field", "relation")
163
+
164
+
159
165
  def build_defsource(
160
166
  default_value: list[dict[str, Any]],
161
167
  *,
@@ -490,9 +490,24 @@ def create_worksheet_with_fields(
490
490
  ``{"worksheetId": "...", "saveResult": <SaveWorksheetControls
491
491
  response>}``
492
492
  """
493
- # Checked here and not only inside update_fields below: that runs
494
- # after the worksheet exists, so a bad spec used to leave one behind.
495
- check_field_specs(fields)
493
+ # Shape the whole field payload before the worksheet exists. Every
494
+ # step of it is offline here (no default columns are kept, so nothing
495
+ # is read back), and it is the same shaping update_fields and
496
+ # save_controls run below — so any spec they would refuse is refused
497
+ # now, instead of after AddWorkSheet left a worksheet behind with
498
+ # only its auto-created columns (bug #455775). The host id only
499
+ # feeds a two-way relation's reverse half; a stand-in builds it the
500
+ # same way the real one will.
501
+ preview = build_fields_payload(
502
+ fields, worksheet_id=_PREFLIGHT_WORKSHEET_ID, title_name=title_name,
503
+ keep_default_controls=False)
504
+ _normalize_defsource(_checked_values(preview))
505
+ # Resolved here rather than left to create_worksheet (which would do
506
+ # the same lookup): undoing a failed create needs the group too, and
507
+ # without it the removal does not reach the recycle bin (seen live
508
+ # 2026-10-08).
509
+ if not section_id:
510
+ section_id = _first_app_section_id(session, app_id)
496
511
  created = create_worksheet(
497
512
  session, app_id, name,
498
513
  project_id=project_id, section_id=section_id,
@@ -505,18 +520,86 @@ def create_worksheet_with_fields(
505
520
  raise ValueError(
506
521
  f"AddWorkSheet did not return a worksheetId; got {created!r}"
507
522
  )
508
- save_result = update_fields(
509
- session, worksheet_id, fields,
510
- title_name=title_name,
511
- # Match `worksheet update-fields` CLI: drop the auto-created
512
- # 名称/描述/附件 columns so the caller's `fields[]` is the full
513
- # layout. Callers wanting to preserve them can issue an extra
514
- # update-fields call with keep_default_controls=True.
515
- keep_default_controls=False,
516
- )
523
+ try:
524
+ save_result = update_fields(
525
+ session, worksheet_id, fields,
526
+ title_name=title_name,
527
+ # Match `worksheet update-fields` CLI: drop the auto-created
528
+ # 名称/描述/附件 columns so the caller's `fields[]` is the full
529
+ # layout. Callers wanting to preserve them can issue an extra
530
+ # update-fields call with keep_default_controls=True.
531
+ keep_default_controls=False,
532
+ )
533
+ # The save reports a rejection in its body rather than raising.
534
+ if isinstance(save_result, dict) and "code" in save_result \
535
+ and save_result.get("code") != 1:
536
+ raise APIError(
537
+ f"The fields were not saved: "
538
+ f"{save_result.get('msg') or save_result.get('code')}",
539
+ code=save_result.get("code"))
540
+ except Exception as exc:
541
+ raise _undo_partial_create(
542
+ session, exc, app_id=app_id, project_id=project_id,
543
+ section_id=section_id, worksheet_id=worksheet_id) from exc
517
544
  return {"worksheetId": worksheet_id, "saveResult": save_result}
518
545
 
519
546
 
547
+ _PREFLIGHT_WORKSHEET_ID = "0" * 24
548
+
549
+
550
+ class WorksheetCreateError(APIError):
551
+ """A create that failed after the worksheet already existed.
552
+
553
+ ``details`` is added to the CLI's JSON error output, so a caller
554
+ reading it can tell whether the worksheet is gone or still there.
555
+ """
556
+
557
+ def __init__(self, message: str, *, worksheet_id: str,
558
+ rolled_back: bool, code: Optional[int] = None):
559
+ super().__init__(message, code=code)
560
+ self.worksheet_id = worksheet_id
561
+ self.rolled_back = rolled_back
562
+ self.details = {"worksheetId": worksheet_id,
563
+ "rolledBack": rolled_back}
564
+
565
+
566
+ def _undo_partial_create(session: Session, exc: Exception, *, app_id: str,
567
+ project_id: str, section_id: str,
568
+ worksheet_id: str) -> WorksheetCreateError:
569
+ """Remove the worksheet a failed create left behind.
570
+
571
+ It is removed the way the product removes one — to the recycle bin,
572
+ then out of it (``app.purge_app_item``) — since it holds nothing but
573
+ the auto-created columns and keeping it in the bin would only leave
574
+ a stray to clean up. When that fails as well, the error names the
575
+ worksheet so the caller can finish the job. Either way the original
576
+ reason leads the message.
577
+ """
578
+ from hap_cli.core import app as app_mod
579
+ reason = str(exc).rstrip(".") or type(exc).__name__
580
+ code = getattr(exc, "code", None)
581
+ logger.warning(
582
+ "worksheet create: fields failed on new worksheet %s (%s); "
583
+ "removing it", worksheet_id, reason)
584
+ try:
585
+ app_mod.purge_app_item(
586
+ session, app_id=app_id, project_id=project_id,
587
+ section_id=section_id, item_id=worksheet_id, item_type=0)
588
+ except Exception as undo_exc: # noqa: BLE001 — reported below
589
+ logger.warning("worksheet create: could not remove %s: %s",
590
+ worksheet_id, undo_exc, exc_info=True)
591
+ return WorksheetCreateError(
592
+ f"{reason}. Worksheet {worksheet_id} was created but its "
593
+ f"fields were not written — delete it, or retry with "
594
+ f"'hap worksheet update-fields {worksheet_id}'.",
595
+ worksheet_id=worksheet_id, rolled_back=False, code=code)
596
+ logger.warning("worksheet create: removed %s", worksheet_id)
597
+ return WorksheetCreateError(
598
+ f"{reason}. The worksheet it had just created was undone; "
599
+ f"nothing was left in the app.",
600
+ worksheet_id=worksheet_id, rolled_back=True, code=code)
601
+
602
+
520
603
  def copy_worksheet(
521
604
  session: Session,
522
605
  worksheet_id: str,
@@ -1026,6 +1109,7 @@ def check_field_specs(fields: Any) -> None:
1026
1109
  f'"Text" / "Number" / "Date" / "Dropdown".')
1027
1110
  name = spec.get("name")
1028
1111
  if isinstance(name, str) and name.strip():
1112
+ _check_default_value(spec, f'{where} ("{name}")')
1029
1113
  continue
1030
1114
  alias = next((k for k in _FIELD_SPEC_NAME_ALIASES if k in spec), "")
1031
1115
  if alias:
@@ -1041,6 +1125,41 @@ def check_field_specs(fields: Any) -> None:
1041
1125
  f'name it will show under.')
1042
1126
 
1043
1127
 
1128
+ _DEFAULT_VALUE_EXAMPLE = '"defaultValue": [{"source": "static", "value": "..."}]'
1129
+
1130
+
1131
+ def _check_default_value(spec: dict[str, Any], where: str) -> None:
1132
+ """Refuse a ``defaultValue`` the field shaping cannot read.
1133
+
1134
+ It is a list of objects, each naming a ``source`` that
1135
+ ``field_spec_adapter.build_defsource`` encodes. A single object or a
1136
+ plain string used to pass this check and crash the shaping halfway —
1137
+ on a create, after the worksheet already existed (bug #455775). An
1138
+ unknown ``source`` was dropped there silently.
1139
+ """
1140
+ from hap_cli.core.field_spec_adapter import DEFAULT_VALUE_SOURCES
1141
+ value = spec.get("defaultValue")
1142
+ if value in (None, []):
1143
+ return
1144
+ sources = ", ".join(DEFAULT_VALUE_SOURCES)
1145
+ rule = (f"Write it as a list of objects, e.g. {_DEFAULT_VALUE_EXAMPLE}; "
1146
+ f'"source" is one of {sources}.')
1147
+ if not isinstance(value, list):
1148
+ kind = "a single object" if isinstance(value, dict) \
1149
+ else type(value).__name__
1150
+ raise ValueError(f'{where} has "defaultValue" as {kind}. {rule}')
1151
+ for n, entry in enumerate(value, start=1):
1152
+ if not isinstance(entry, dict):
1153
+ raise ValueError(
1154
+ f'{where}: "defaultValue" entry {n} is '
1155
+ f"{type(entry).__name__}, not an object. {rule}")
1156
+ if entry.get("source") not in DEFAULT_VALUE_SOURCES:
1157
+ got = entry.get("source")
1158
+ said = f'"source" {got!r}' if got is not None else 'no "source"'
1159
+ raise ValueError(
1160
+ f'{where}: "defaultValue" entry {n} has {said}. {rule}')
1161
+
1162
+
1044
1163
  def build_fields_payload(
1045
1164
  fields: list[dict[str, Any]],
1046
1165
  *,
@@ -0,0 +1,231 @@
1
+ """Bug #455775: `worksheet create --fields` left a half-built worksheet.
2
+
3
+ A Text field with ``"defaultValue": {"source": "static", ...}`` (one
4
+ object, not a list) passed the pre-create check, the worksheet was
5
+ created, and shaping the fields then crashed with ``AttributeError`` —
6
+ the app kept a worksheet holding only the auto-created columns and the
7
+ caller never learned its id.
8
+
9
+ Two layers: every field-shaping error now surfaces before the worksheet
10
+ exists, and a failure after it exists undoes the create (or, when that
11
+ fails too, names the worksheet that was left behind).
12
+ """
13
+ import json
14
+
15
+ import pytest
16
+ from click.testing import CliRunner
17
+
18
+ from hap_cli.core import app as app_mod
19
+ from hap_cli.core import worksheet as ws_mod
20
+ from hap_cli.core.session import APIError, Session
21
+
22
+
23
+ GOOD = {"type": "Text", "name": "名称"}
24
+
25
+
26
+ @pytest.fixture
27
+ def tmp_config(tmp_path, monkeypatch):
28
+ cfg = tmp_path / ".hap-cli"
29
+ monkeypatch.setattr("hap_cli.core.session.CONFIG_DIR", cfg)
30
+ monkeypatch.setattr("hap_cli.core.session.CONFIG_FILE",
31
+ cfg / "config.json")
32
+ monkeypatch.setattr("hap_cli.core.token_crypto._KEY_FILE", cfg / ".key")
33
+ return cfg
34
+
35
+
36
+ def _with_default(value):
37
+ return [GOOD, {"type": "Text", "name": "备注", "defaultValue": value}]
38
+
39
+
40
+ # ── defaultValue shape is checked before anything is sent ────────────────
41
+ class TestDefaultValueShape:
42
+ @pytest.mark.parametrize("value", [
43
+ {"source": "static", "value": "DEFAULT-QA"}, # the report
44
+ "DEFAULT-QA",
45
+ ["x"],
46
+ [{"value": "x"}], # no source
47
+ [{"source": "constant", "value": "x"}], # unknown source
48
+ 7,
49
+ ])
50
+ def test_refused_with_field_and_right_shape(self, value):
51
+ with pytest.raises(ValueError) as exc:
52
+ ws_mod.check_field_specs(_with_default(value))
53
+ msg = str(exc.value)
54
+ assert "Field 2" in msg and "备注" in msg
55
+ assert '"defaultValue": [{"source": "static"' in msg
56
+ for name in ("system", "static", "field", "relation"):
57
+ assert name in msg
58
+
59
+ @pytest.mark.parametrize("value", [
60
+ None, [],
61
+ [{"source": "static", "value": "x"}],
62
+ [{"source": "system", "value": "currentUser"}],
63
+ [{"source": "field", "field": "abc"}],
64
+ [{"source": "relation", "relationField": "r", "field": "f"}],
65
+ ])
66
+ def test_accepted(self, value):
67
+ ws_mod.check_field_specs(_with_default(value))
68
+
69
+ def test_update_fields_refuses_cleanly_not_attribute_error(self):
70
+ with pytest.raises(ValueError, match="defaultValue"):
71
+ ws_mod.build_fields_payload(
72
+ _with_default({"source": "static", "value": "x"}),
73
+ keep_default_controls=False)
74
+
75
+
76
+ # ── create: preflight builds the whole payload offline ───────────────────
77
+ class _Recorder:
78
+ def __init__(self, monkeypatch, *, save=None, save_raises=None,
79
+ purge_raises=None):
80
+ self.calls = []
81
+ rec = self
82
+
83
+ def fake_create(session, app_id, name, **kw):
84
+ rec.calls.append(("create", app_id, name, kw.get("section_id")))
85
+ return {"worksheetId": "ws_new"}
86
+
87
+ def fake_update(session, worksheet_id, fields, **kw):
88
+ rec.calls.append(("update", worksheet_id))
89
+ if save_raises:
90
+ raise save_raises
91
+ return save if save is not None else {"code": 1, "data": {}}
92
+
93
+ def fake_purge(session, **kw):
94
+ rec.calls.append(("purge", kw.get("item_id"), kw.get("item_type"),
95
+ kw.get("app_id")))
96
+ if purge_raises:
97
+ raise purge_raises
98
+ return {"deleted": kw.get("item_id"), "permanently": True}
99
+
100
+ monkeypatch.setattr(ws_mod, "create_worksheet", fake_create)
101
+ monkeypatch.setattr(ws_mod, "update_fields", fake_update)
102
+ monkeypatch.setattr(app_mod, "purge_app_item", fake_purge)
103
+
104
+ def kinds(self):
105
+ return [c[0] for c in self.calls]
106
+
107
+
108
+ def _create(fields, **kw):
109
+ return ws_mod.create_worksheet_with_fields(
110
+ object(), "app1", "QA", fields, project_id="org1",
111
+ section_id="sec1", **kw)
112
+
113
+
114
+ class TestCreatePreflight:
115
+ def test_bad_default_never_creates(self, monkeypatch):
116
+ rec = _Recorder(monkeypatch)
117
+ with pytest.raises(ValueError, match="defaultValue"):
118
+ _create(_with_default({"source": "static", "value": "x"}))
119
+ assert rec.calls == []
120
+
121
+ def test_any_shaping_error_surfaces_before_create(self, monkeypatch):
122
+ # A field-settings value the save path refuses (an object where
123
+ # text belongs) used to be found only after the create.
124
+ rec = _Recorder(monkeypatch)
125
+ bad = [GOOD, {"type": "Text", "name": "x",
126
+ "advancedSetting": {"sorttype": {"a": 1}}}]
127
+ with pytest.raises(ValueError):
128
+ _create(bad)
129
+ assert rec.calls == []
130
+
131
+ def test_good_spec_creates_and_saves(self, monkeypatch):
132
+ rec = _Recorder(monkeypatch)
133
+ out = _create([GOOD, {"type": "Text", "name": "备注", "defaultValue":
134
+ [{"source": "static", "value": "D"}]}])
135
+ assert out["worksheetId"] == "ws_new"
136
+ assert rec.kinds() == ["create", "update"]
137
+
138
+
139
+ # ── create: a failure after the worksheet exists ─────────────────────────
140
+ class TestCreateRollback:
141
+ def test_rollback_knows_the_group_when_none_was_given(self, monkeypatch):
142
+ rec = _Recorder(monkeypatch, save_raises=APIError("x"))
143
+ monkeypatch.setattr(ws_mod, "_first_app_section_id",
144
+ lambda session, app_id: "sec-first")
145
+ seen = {}
146
+ orig = app_mod.purge_app_item
147
+
148
+ def spy(session, **kw):
149
+ seen.update(kw)
150
+ return orig(session, **kw)
151
+
152
+ monkeypatch.setattr(app_mod, "purge_app_item", spy)
153
+ with pytest.raises(ws_mod.WorksheetCreateError):
154
+ ws_mod.create_worksheet_with_fields(
155
+ object(), "app1", "QA", [GOOD], project_id="org1")
156
+ assert seen["section_id"] == "sec-first"
157
+ assert rec.calls[0][3] == "sec-first"
158
+
159
+ def test_save_error_rolls_back_and_keeps_original_reason(
160
+ self, monkeypatch):
161
+ rec = _Recorder(monkeypatch, save_raises=APIError("字段保存失败"))
162
+ with pytest.raises(ws_mod.WorksheetCreateError) as exc:
163
+ _create([GOOD])
164
+ assert rec.kinds() == ["create", "update", "purge"]
165
+ assert rec.calls[2][1:] == ("ws_new", 0, "app1")
166
+ msg = str(exc.value)
167
+ assert "字段保存失败" in msg
168
+ assert "undone" in msg
169
+ assert exc.value.details == {"worksheetId": "ws_new",
170
+ "rolledBack": True}
171
+
172
+ def test_rejected_save_code_rolls_back(self, monkeypatch):
173
+ rec = _Recorder(monkeypatch, save={"code": 16, "msg": "别名重复"})
174
+ with pytest.raises(ws_mod.WorksheetCreateError, match="别名重复"):
175
+ _create([GOOD])
176
+ assert "purge" in rec.kinds()
177
+
178
+ def test_failed_rollback_names_the_worksheet(self, monkeypatch):
179
+ rec = _Recorder(monkeypatch, save_raises=RuntimeError("boom"),
180
+ purge_raises=APIError("no"))
181
+ with pytest.raises(ws_mod.WorksheetCreateError) as exc:
182
+ _create([GOOD])
183
+ msg = str(exc.value)
184
+ assert "boom" in msg and "ws_new" in msg
185
+ assert "update-fields" in msg
186
+ assert exc.value.details == {"worksheetId": "ws_new",
187
+ "rolledBack": False}
188
+ assert rec.kinds() == ["create", "update", "purge"]
189
+
190
+ def test_rollback_is_logged(self, monkeypatch):
191
+ import logging
192
+ seen = []
193
+
194
+ class Grab(logging.Handler):
195
+ def emit(self, record):
196
+ seen.append(record)
197
+
198
+ log = logging.getLogger("hap_cli.worksheet")
199
+ handler = Grab(level=logging.WARNING)
200
+ old_level = log.level
201
+ log.addHandler(handler)
202
+ log.setLevel(logging.WARNING)
203
+ try:
204
+ _Recorder(monkeypatch, save_raises=APIError("x"))
205
+ with pytest.raises(ws_mod.WorksheetCreateError):
206
+ _create([GOOD])
207
+ finally:
208
+ log.removeHandler(handler)
209
+ log.setLevel(old_level)
210
+ assert any("ws_new" in r.getMessage() for r in seen
211
+ if r.levelno == logging.WARNING)
212
+
213
+
214
+ def test_cli_json_error_carries_worksheet_id(tmp_config, monkeypatch):
215
+ from hap_cli._entry import cli
216
+ sess = Session(server_url="https://t.com", auth_token="tok")
217
+ sess.default_project_id = "proj1"
218
+ sess.save()
219
+
220
+ def boom(*a, **kw):
221
+ raise ws_mod.WorksheetCreateError(
222
+ "x. Worksheet ws_new was created but its fields were not "
223
+ "written", worksheet_id="ws_new", rolled_back=False)
224
+
225
+ monkeypatch.setattr(ws_mod, "create_worksheet_with_fields", boom)
226
+ res = CliRunner().invoke(cli, ["--json", "worksheet", "create", "app1",
227
+ "QA", "--fields", json.dumps([GOOD])])
228
+ assert res.exit_code == 1
229
+ payload = json.loads(res.output)
230
+ assert payload["worksheetId"] == "ws_new"
231
+ assert payload["rolledBack"] is False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hap-cli
3
- Version: 0.9.2
3
+ Version: 0.9.3
4
4
  Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
@@ -216,6 +216,7 @@ hap_cli/tests/test_config_cmd.py
216
216
  hap_cli/tests/test_content_language.py
217
217
  hap_cli/tests/test_control_lookup_guard.py
218
218
  hap_cli/tests/test_core.py
219
+ hap_cli/tests/test_create_worksheet_partial.py
219
220
  hap_cli/tests/test_destructive_confirm_cli.py
220
221
  hap_cli/tests/test_dsl_existing_node_refs.py
221
222
  hap_cli/tests/test_dsl_multi_record_guard.py
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes