hap-cli 0.9.2__tar.gz → 0.9.4__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.4}/PKG-INFO +1 -1
  2. {hap_cli-0.9.2 → hap_cli-0.9.4}/app_live_tests/smoke.py +111 -0
  3. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/__init__.py +1 -1
  4. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/context.py +5 -0
  5. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/field_spec_adapter.py +6 -0
  6. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/worksheet.py +147 -14
  7. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_core.py +72 -0
  8. hap_cli-0.9.4/hap_cli/tests/test_create_worksheet_partial.py +231 -0
  9. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli.egg-info/PKG-INFO +1 -1
  10. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli.egg-info/SOURCES.txt +1 -0
  11. {hap_cli-0.9.2 → hap_cli-0.9.4}/MANIFEST.in +0 -0
  12. {hap_cli-0.9.2 → hap_cli-0.9.4}/README.md +0 -0
  13. {hap_cli-0.9.2 → hap_cli-0.9.4}/app_live_tests/__init__.py +0 -0
  14. {hap_cli-0.9.2 → hap_cli-0.9.4}/app_live_tests/__main__.py +0 -0
  15. {hap_cli-0.9.2 → hap_cli-0.9.4}/app_live_tests/config.py +0 -0
  16. {hap_cli-0.9.2 → hap_cli-0.9.4}/app_live_tests/fixtures/__init__.py +0 -0
  17. {hap_cli-0.9.2 → hap_cli-0.9.4}/app_live_tests/fixtures/feedback_scenario.py +0 -0
  18. {hap_cli-0.9.2 → hap_cli-0.9.4}/app_live_tests/harness.py +0 -0
  19. {hap_cli-0.9.2 → hap_cli-0.9.4}/app_live_tests/state.py +0 -0
  20. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/README.md +0 -0
  21. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/README_CN.md +0 -0
  22. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/__main__.py +0 -0
  23. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/_entry.py +0 -0
  24. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/__init__.py +0 -0
  25. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/app_cmd.py +0 -0
  26. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/app_creator_cmd.py +0 -0
  27. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/app_editor_cmd.py +0 -0
  28. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/attachment_cmd.py +0 -0
  29. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/auth_cmd.py +0 -0
  30. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/calendar_cmd.py +0 -0
  31. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/chart_cmd.py +0 -0
  32. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/chat_cmd.py +0 -0
  33. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/chatbot_cmd.py +0 -0
  34. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/config_cmd.py +0 -0
  35. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/contact_cmd.py +0 -0
  36. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/department_cmd.py +0 -0
  37. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/group_cmd.py +0 -0
  38. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/guide_cmd.py +0 -0
  39. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/icon_cmd.py +0 -0
  40. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/instance_cmd.py +0 -0
  41. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/node_cmd.py +0 -0
  42. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/optionset_cmd.py +0 -0
  43. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/page_cmd.py +0 -0
  44. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/post_cmd.py +0 -0
  45. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/record_cmd.py +0 -0
  46. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/region_cmd.py +0 -0
  47. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/role_cmd.py +0 -0
  48. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/update_cmd.py +0 -0
  49. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/upload_cmd.py +0 -0
  50. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/v3_registry.py +0 -0
  51. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/workflow_cmd.py +0 -0
  52. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/commands/worksheet_cmd.py +0 -0
  53. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/__init__.py +0 -0
  54. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/action_spec_adapter.py +0 -0
  55. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app.py +0 -0
  56. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/__init__.py +0 -0
  57. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/_hapmeta/__init__.py +0 -0
  58. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/_hapmeta/control_type_codes.py +0 -0
  59. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/_hapmeta/worksheet_templates.py +0 -0
  60. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/charts.py +0 -0
  61. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/cleanup.py +0 -0
  62. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/compiler.py +0 -0
  63. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/config.py +0 -0
  64. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/design/design.schema.json +0 -0
  65. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/errors.py +0 -0
  66. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/examples/minimal.design.json +0 -0
  67. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/executor.py +0 -0
  68. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/fields.py +0 -0
  69. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/hap.py +0 -0
  70. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/merge.py +0 -0
  71. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/recording/__init__.py +0 -0
  72. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/recording/console.py +0 -0
  73. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/recording/jsonl.py +0 -0
  74. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/recording/mirror.py +0 -0
  75. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/recording/report.py +0 -0
  76. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/schema.py +0 -0
  77. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/seed/INSTRUCTIONS.md +0 -0
  78. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/seed/__init__.py +0 -0
  79. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/seed/cli.py +0 -0
  80. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/seed/executor.py +0 -0
  81. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/seed/resources/attachments.json +0 -0
  82. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/seed/resources/sample.docx +0 -0
  83. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/seed/resources/sample.pdf +0 -0
  84. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/seed/resources/sample_images.json +0 -0
  85. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/seed/template.py +0 -0
  86. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/selftest.py +0 -0
  87. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/smoke.py +0 -0
  88. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/steps.py +0 -0
  89. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/store.py +0 -0
  90. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/validate.py +0 -0
  91. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/app_creator/workflow_dsl.py +0 -0
  92. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/attachment.py +0 -0
  93. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/auth.py +0 -0
  94. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/calendar_mod.py +0 -0
  95. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/chart.py +0 -0
  96. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/chat.py +0 -0
  97. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/chatbot.py +0 -0
  98. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/cli_access.py +0 -0
  99. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/condition_dsl.py +0 -0
  100. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/contact.py +0 -0
  101. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/control_type_codes.py +0 -0
  102. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/department.py +0 -0
  103. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/__init__.py +0 -0
  104. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/_hapmeta/__init__.py +0 -0
  105. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/_hapmeta/control_type_codes.py +0 -0
  106. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/apply.py +0 -0
  107. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/componentlower.py +0 -0
  108. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/editspec/component.schema.json +0 -0
  109. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/editspec/custom-action.schema.json +0 -0
  110. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/editspec/envelope.schema.json +0 -0
  111. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/editspec/field.schema.json +0 -0
  112. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/editspec.py +0 -0
  113. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/errors.py +0 -0
  114. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/fieldlower.py +0 -0
  115. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/jsonschema_mini.py +0 -0
  116. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/models.py +0 -0
  117. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/ops.py +0 -0
  118. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/planner.py +0 -0
  119. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/editor/reader.py +0 -0
  120. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/field_normalizer.py +0 -0
  121. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/filter_translator.py +0 -0
  122. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/filter_v3.py +0 -0
  123. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/flow_node.py +0 -0
  124. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/global_meta.py +0 -0
  125. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/group.py +0 -0
  126. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/guide.py +0 -0
  127. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/icon_index.py +0 -0
  128. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/instance.py +0 -0
  129. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/layout_check.py +0 -0
  130. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/logger.py +0 -0
  131. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/optionset.py +0 -0
  132. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/page.py +0 -0
  133. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/post.py +0 -0
  134. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/record.py +0 -0
  135. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/record_defaults.py +0 -0
  136. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/record_form_checks.py +0 -0
  137. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/record_lookups.py +0 -0
  138. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/record_rules.py +0 -0
  139. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/relation_scope.py +0 -0
  140. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/response_crypto.py +0 -0
  141. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/role.py +0 -0
  142. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/role_perm_builder.py +0 -0
  143. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/rule_spec_adapter.py +0 -0
  144. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/session.py +0 -0
  145. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/token_crypto.py +0 -0
  146. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/update.py +0 -0
  147. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/upload.py +0 -0
  148. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/v3/__init__.py +0 -0
  149. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/v3/dispatcher.py +0 -0
  150. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/v3/render.py +0 -0
  151. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/v3/schema.py +0 -0
  152. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/view_spec_adapter.py +0 -0
  153. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/workflow.py +0 -0
  154. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/workflow_node_dsl.py +0 -0
  155. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/worksheet_form.py +0 -0
  156. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/worksheet_reference.py +0 -0
  157. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/core/worksheet_templates.py +0 -0
  158. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/en/approval.md +0 -0
  159. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/en/chart.md +0 -0
  160. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/en/messages.md +0 -0
  161. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/en/overview.md +0 -0
  162. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/en/post.md +0 -0
  163. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/en/record.md +0 -0
  164. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/en/setup.md +0 -0
  165. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/en/skills.md +0 -0
  166. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/en/workflow.md +0 -0
  167. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/en/worksheet.md +0 -0
  168. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/index.json +0 -0
  169. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/zh-Hans/approval.md +0 -0
  170. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/zh-Hans/chart.md +0 -0
  171. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/zh-Hans/messages.md +0 -0
  172. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/zh-Hans/overview.md +0 -0
  173. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/zh-Hans/post.md +0 -0
  174. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/zh-Hans/record.md +0 -0
  175. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/zh-Hans/setup.md +0 -0
  176. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/zh-Hans/skills.md +0 -0
  177. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/zh-Hans/workflow.md +0 -0
  178. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/guide/zh-Hans/worksheet.md +0 -0
  179. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/i18n.py +0 -0
  180. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/locale/__init__.py +0 -0
  181. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/locale/messages.json +0 -0
  182. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/locale/messages.schema.json +0 -0
  183. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/__init__.py +0 -0
  184. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/conftest.py +0 -0
  185. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_action_mdpublic_parity.py +0 -0
  186. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_advanced_setting_guard.py +0 -0
  187. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_app_creator.py +0 -0
  188. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_app_editor.py +0 -0
  189. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_app_export_usage.py +0 -0
  190. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_app_light_color.py +0 -0
  191. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_app_section_name.py +0 -0
  192. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_approval_submit_fill_cli.py +0 -0
  193. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_approval_wire.py +0 -0
  194. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_atomic_write.py +0 -0
  195. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_attachment_share.py +0 -0
  196. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_auth_prerelease.py +0 -0
  197. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_batch_add_atomic.py +0 -0
  198. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_bug_036430.py +0 -0
  199. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_bug_036750.py +0 -0
  200. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_calendar_date_range.py +0 -0
  201. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_calendar_discussion_cli.py +0 -0
  202. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_chart.py +0 -0
  203. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_chart_filter_merge.py +0 -0
  204. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_chat_attachment_send.py +0 -0
  205. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_chat_messages_cli.py +0 -0
  206. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_chat_recall_cli.py +0 -0
  207. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_chat_send_socket.py +0 -0
  208. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_cli_access.py +0 -0
  209. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_coded_responses.py +0 -0
  210. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_condition_dsl.py +0 -0
  211. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_condition_dsl_entries.py +0 -0
  212. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_condition_dsl_readback.py +0 -0
  213. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_config_cmd.py +0 -0
  214. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_content_language.py +0 -0
  215. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_control_lookup_guard.py +0 -0
  216. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_destructive_confirm_cli.py +0 -0
  217. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_dsl_existing_node_refs.py +0 -0
  218. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_dsl_multi_record_guard.py +0 -0
  219. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_editor_inline_subtable.py +0 -0
  220. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_engine_core_parity.py +0 -0
  221. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_arg_consistency.py +0 -0
  222. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_bidirectional.py +0 -0
  223. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_discussion_mentions.py +0 -0
  224. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_entry_module.py +0 -0
  225. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_field_record_ref.py +0 -0
  226. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_http_error_body.py +0 -0
  227. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_json_anywhere.py +0 -0
  228. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_json_file_arg.py +0 -0
  229. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_layout_check.py +0 -0
  230. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_node_get_detail.py +0 -0
  231. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_node_summary.py +0 -0
  232. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_post_cursor.py +0 -0
  233. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_publish_reasons.py +0 -0
  234. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_replicate_workflow.py +0 -0
  235. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_save_fields_shape.py +0 -0
  236. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_subgroups.py +0 -0
  237. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_subtable_read.py +0 -0
  238. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_worksheet_references.py +0 -0
  239. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_worksheet_visibility.py +0 -0
  240. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fb_worksheet_workflows.py +0 -0
  241. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_fields_mdpublic_parity.py +0 -0
  242. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_full_e2e.py +0 -0
  243. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g1_chart_parity.py +0 -0
  244. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g1_record_parity.py +0 -0
  245. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g1_upload_parity.py +0 -0
  246. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g1_worksheet_parity.py +0 -0
  247. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g2_coverage_parity.py +0 -0
  248. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g3_workflow_parity.py +0 -0
  249. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g4_approval_parity.py +0 -0
  250. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g5_low_frequency_parity.py +0 -0
  251. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g6_advanced_fields.py +0 -0
  252. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g6_fields_roundtrip.py +0 -0
  253. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g7_misc_gaps.py +0 -0
  254. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g7b_permanent_delete.py +0 -0
  255. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g8_filter_operator.py +0 -0
  256. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g8b_pivot_operators.py +0 -0
  257. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g8c_empty_filter_and_help_truth.py +0 -0
  258. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_g9_appid_resolve.py +0 -0
  259. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_global_meta.py +0 -0
  260. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_guide.py +0 -0
  261. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_guide_sections.py +0 -0
  262. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_i18n.py +0 -0
  263. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_inner_flow_batch_add.py +0 -0
  264. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_instance_envelope.py +0 -0
  265. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_instance_history_cli.py +0 -0
  266. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration.py +0 -0
  267. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration_approval.py +0 -0
  268. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration_approval_actions.py +0 -0
  269. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration_calendar.py +0 -0
  270. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration_destructive.py +0 -0
  271. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration_misc.py +0 -0
  272. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration_post.py +0 -0
  273. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration_social.py +0 -0
  274. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration_v3.py +0 -0
  275. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration_workflow.py +0 -0
  276. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
  277. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_jsonschema_mini_copy.py +0 -0
  278. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_member_relation_array_wrap.py +0 -0
  279. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_multi_profile.py +0 -0
  280. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_no_workflow_unsupported.py +0 -0
  281. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_noninteractive_confirm.py +0 -0
  282. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_org_approval_v3_semantic_cli.py +0 -0
  283. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_org_id_cli.py +0 -0
  284. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_org_id_docs.py +0 -0
  285. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_page_filters_group_read.py +0 -0
  286. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_page_filters_group_update.py +0 -0
  287. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_page_save_page_id.py +0 -0
  288. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_parameter_conventions.py +0 -0
  289. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
  290. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_post_scope_cli.py +0 -0
  291. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_record_batch_v3_semantic_cli.py +0 -0
  292. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_record_business_rules.py +0 -0
  293. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_record_defaults.py +0 -0
  294. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_record_doc_id_hidden.py +0 -0
  295. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_record_field_alias.py +0 -0
  296. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_record_form_checks.py +0 -0
  297. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_record_run_action.py +0 -0
  298. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_record_v3_semantic_cli.py +0 -0
  299. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_record_write_fixes.py +0 -0
  300. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_relation_scope.py +0 -0
  301. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_rmw_mdpublic_parity.py +0 -0
  302. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_role_app_scope.py +0 -0
  303. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_rule_filters_and_field_warning.py +0 -0
  304. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_save_action_silent_drop.py +0 -0
  305. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_save_rule_spec.py +0 -0
  306. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_select_add_option.py +0 -0
  307. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_select_rejects_relation_value.py +0 -0
  308. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_semantic_types_registry.py +0 -0
  309. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_subtable_mdpublic_parity.py +0 -0
  310. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_update.py +0 -0
  311. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_update_mode.py +0 -0
  312. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_upload_allow_down.py +0 -0
  313. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_upload_host_resolution.py +0 -0
  314. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_upload_mdpublic_parity.py +0 -0
  315. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
  316. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_v3_bool_flags.py +0 -0
  317. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_v3_dispatcher.py +0 -0
  318. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
  319. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_v3_response_format_json.py +0 -0
  320. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_v3_session.py +0 -0
  321. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
  322. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_view_mdpublic_parity.py +0 -0
  323. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_wf_config_keys.py +0 -0
  324. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_wf_inner_scope.py +0 -0
  325. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_wf_json_file_arg.py +0 -0
  326. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_wf_node_delete_missing.py +0 -0
  327. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_wf_save_action_dsl_fields.py +0 -0
  328. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_wf_structure_listing.py +0 -0
  329. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_wf_trigger_outcome.py +0 -0
  330. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_wf_trigger_preserve.py +0 -0
  331. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_wo4_result_handling.py +0 -0
  332. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_code_node.py +0 -0
  333. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_condition_field_types.py +0 -0
  334. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_condition_slots.py +0 -0
  335. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_delete_cascade.py +0 -0
  336. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_envelope_errors.py +0 -0
  337. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_lifecycle_wire.py +0 -0
  338. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_list_cli.py +0 -0
  339. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_node_wire.py +0 -0
  340. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_publish_inner.py +0 -0
  341. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_read_write_keys.py +0 -0
  342. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_workflow_v3_semantic_cli.py +0 -0
  343. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_worksheet_create_section.py +0 -0
  344. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_worksheet_crud_cli.py +0 -0
  345. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_worksheet_filter_read.py +0 -0
  346. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_worksheet_switch.py +0 -0
  347. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/tests/test_worksheet_v3_semantic_cli.py +0 -0
  348. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/utils/__init__.py +0 -0
  349. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/utils/atomic_write.py +0 -0
  350. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/utils/confirm.py +0 -0
  351. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/utils/formatting.py +0 -0
  352. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/utils/options.py +0 -0
  353. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli/utils/parameter_mapping.py +0 -0
  354. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli.egg-info/dependency_links.txt +0 -0
  355. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli.egg-info/entry_points.txt +0 -0
  356. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli.egg-info/requires.txt +0 -0
  357. {hap_cli-0.9.2 → hap_cli-0.9.4}/hap_cli.egg-info/top_level.txt +0 -0
  358. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/__init__.py +0 -0
  359. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/__main__.py +0 -0
  360. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/_wftest.py +0 -0
  361. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/charts.py +0 -0
  362. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/cleanup.py +0 -0
  363. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/compiler.py +0 -0
  364. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/config.py +0 -0
  365. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/errors.py +0 -0
  366. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/executor.py +0 -0
  367. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/fields.py +0 -0
  368. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/hap.py +0 -0
  369. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/recording/__init__.py +0 -0
  370. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/recording/console.py +0 -0
  371. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/recording/jsonl.py +0 -0
  372. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/recording/mirror.py +0 -0
  373. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/recording/report.py +0 -0
  374. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/schema.py +0 -0
  375. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/seed/__init__.py +0 -0
  376. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/seed/cli.py +0 -0
  377. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/seed/executor.py +0 -0
  378. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/seed/template.py +0 -0
  379. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/selftest.py +0 -0
  380. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/smoke.py +0 -0
  381. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/step.py +0 -0
  382. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/steps.py +0 -0
  383. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/store.py +0 -0
  384. {hap_cli-0.9.2 → hap_cli-0.9.4}/live_tests/workflow_dsl.py +0 -0
  385. {hap_cli-0.9.2 → hap_cli-0.9.4}/setup.cfg +0 -0
  386. {hap_cli-0.9.2 → hap_cli-0.9.4}/setup.py +0 -0
  387. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/INDEX.json +0 -0
  388. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
  389. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
  390. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/create_optionset.yaml +0 -0
  391. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/create_role.yaml +0 -0
  392. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/delete_optionset.yaml +0 -0
  393. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/delete_record.yaml +0 -0
  394. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/delete_role.yaml +0 -0
  395. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
  396. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_app_info.yaml +0 -0
  397. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
  398. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
  399. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
  400. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_record_details.yaml +0 -0
  401. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
  402. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_record_list.yaml +0 -0
  403. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_record_logs.yaml +0 -0
  404. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
  405. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_record_relations.yaml +0 -0
  406. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
  407. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_regions.yaml +0 -0
  408. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_role_details.yaml +0 -0
  409. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/get_role_list.yaml +0 -0
  410. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/knowledge_search.yaml +0 -0
  411. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
  412. {hap_cli-0.9.2 → hap_cli-0.9.4}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
  413. {hap_cli-0.9.2 → hap_cli-0.9.4}/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.4
4
4
  Summary: CLI harness for Mingdao HAP - Enterprise no-code platform
5
5
  Author: hap-cli
6
6
  License: Apache-2.0
@@ -155,6 +155,32 @@ def case_view(app_id: str, section_id: str) -> None:
155
155
  hap(["worksheet", "delete", ws_id, "-a", app_id, "-y"], check=False)
156
156
 
157
157
 
158
+ def case_view_spec_partial(app_id: str, section_id: str) -> None:
159
+ """Bug #455744: a --view-spec naming only the card title must leave the
160
+ calendar's name, type and start-date field alone."""
161
+ print("scenario: view update --view-spec keeps unexpressed settings")
162
+ ws_id = _make_worksheet(app_id, section_id, "LT-view-spec-partial")
163
+ title_id = next((c["controlId"] for c in _fields(ws_id)
164
+ if c.get("attribute") == 1), "")
165
+ res = hap(["worksheet", "view", "create", ws_id, "日历",
166
+ "--view-type", "calendar", "--begin-date", "ctime",
167
+ "-a", app_id])
168
+ vid = res.get("viewId") if isinstance(res, dict) else ""
169
+ check("calendar view created", bool(vid), str(res))
170
+ ok, res = _try(["worksheet", "view", "update", ws_id, vid, "-a", app_id,
171
+ "--view-spec",
172
+ json.dumps({"card": {"titleField": title_id}})])
173
+ check("title-only view-spec update succeeds", ok, str(res))
174
+ view = hap(["worksheet", "view", "info", ws_id, vid]) or {}
175
+ adv = view.get("advancedSetting") or {}
176
+ check("name kept", view.get("name") == "日历", str(view.get("name")))
177
+ check("still a calendar", view.get("viewType") == 4,
178
+ str(view.get("viewType")))
179
+ check("start date kept", adv.get("begindate") == "ctime", str(adv))
180
+ check("title field written", adv.get("viewtitle") == title_id, str(adv))
181
+ hap(["worksheet", "delete", ws_id, "-a", app_id, "-y"], check=False)
182
+
183
+
158
184
  def _roles(app_id: str) -> dict[str, str]:
159
185
  data = hap(["app", "role", "list", "-a", app_id], check=False)
160
186
  roles = data.get("roles", []) if isinstance(data, dict) else []
@@ -3602,6 +3628,89 @@ def case_editor_inline_subtable(app_id: str, section_id: str) -> None:
3602
3628
  hap(["worksheet", "delete", parent, "-a", app_id, "-y"], check=False)
3603
3629
 
3604
3630
 
3631
+ def case_create_partial(app_id: str, section_id: str) -> None:
3632
+ """Bug #455775: a failed `worksheet create --fields` leaves nothing."""
3633
+ print("\nscenario: worksheet create --fields failure (#455775)")
3634
+ import json as _json
3635
+ from hap_cli.core import app as app_mod
3636
+ from hap_cli.core.session import Session
3637
+ session = Session.load()
3638
+ project_id = app_mod.app_project_id(session, app_id)
3639
+ prefix = "QA455775"
3640
+ for leftover in [n for n in _worksheets(app_id) if n.startswith(prefix)]:
3641
+ _ensure_absent(app_id, leftover)
3642
+
3643
+ def _ours() -> list[str]:
3644
+ return [n for n in _worksheets(app_id) if n.startswith(prefix)]
3645
+
3646
+ def _purge(ws_id: str) -> None:
3647
+ try:
3648
+ app_mod.purge_app_item(session, app_id=app_id,
3649
+ project_id=project_id,
3650
+ section_id=section_id, item_id=ws_id,
3651
+ item_type=0)
3652
+ except Exception as exc: # noqa: BLE001 — reported, not fatal
3653
+ print(f" note: could not purge {ws_id}: {exc}")
3654
+
3655
+ single = {"source": "static", "value": "DEFAULT-QA"}
3656
+ ok, res = _try(["worksheet", "create", app_id, f"{prefix} Invalid",
3657
+ "--section-id", section_id, "--fields", _json.dumps([
3658
+ {"type": "Text", "name": "名称"},
3659
+ {"type": "Text", "name": "备注",
3660
+ "defaultValue": single}], ensure_ascii=False)])
3661
+ check("create-partial: a single-object default is refused", not ok,
3662
+ str(res)[:200])
3663
+ check("create-partial: the refusal names the field and the shape",
3664
+ "备注" in str(res) and "list of objects" in str(res),
3665
+ str(res)[:300])
3666
+ check("create-partial: nothing was created", _ours() == [], str(_ours()))
3667
+
3668
+ # Passes every offline check, refused by the server at save time:
3669
+ # an alias the worksheet's own record-id column already holds.
3670
+ ok, res = _try(["worksheet", "create", app_id, f"{prefix} PostFail",
3671
+ "--section-id", section_id, "--fields", _json.dumps([
3672
+ {"type": "Text", "name": "名称"},
3673
+ {"type": "Text", "name": "冲突",
3674
+ "extra": {"alias": "rowid"}}],
3675
+ ensure_ascii=False)])
3676
+ check("create-partial: a save rejected after the create fails", not ok,
3677
+ str(res)[:200])
3678
+ check("create-partial: the error says the create was undone",
3679
+ "undone" in str(res), str(res)[:300])
3680
+ check("create-partial: no half-built worksheet is left",
3681
+ _ours() == [], str(_ours()))
3682
+
3683
+ ws_id = ""
3684
+ try:
3685
+ made = hap(["worksheet", "create", app_id, f"{prefix} Array",
3686
+ "--section-id", section_id, "--fields", _json.dumps([
3687
+ {"type": "Text", "name": "名称"},
3688
+ {"type": "Text", "name": "备注",
3689
+ "defaultValue": [single]}], ensure_ascii=False)],
3690
+ check=False)
3691
+ ws_id = made.get("worksheetId", "") if isinstance(made, dict) else ""
3692
+ check("create-partial: the list form still creates", bool(ws_id),
3693
+ str(made)[:200])
3694
+ if not ws_id:
3695
+ return
3696
+ note = next((c for c in _fields(ws_id)
3697
+ if c.get("controlName") == "备注"), {})
3698
+ check("create-partial: its default is stored",
3699
+ "DEFAULT-QA" in str((note.get("advancedSetting") or {})
3700
+ .get("defsource")), str(note)[:200])
3701
+ ok, res = _try(["worksheet", "update-fields", ws_id, "--fields",
3702
+ _json.dumps([{"type": "Text", "name": "名称"},
3703
+ {"type": "Text", "name": "备注",
3704
+ "defaultValue": single}],
3705
+ ensure_ascii=False)])
3706
+ check("create-partial: update-fields refuses it clearly too",
3707
+ not ok and "defaultValue" in str(res)
3708
+ and "AttributeError" not in str(res), str(res)[:300])
3709
+ finally:
3710
+ if ws_id:
3711
+ _purge(ws_id)
3712
+
3713
+
3605
3714
  def case_fb_subgroups(app_id: str, section_id: str) -> None:
3606
3715
  """WO-FB 3.1: the editor can see worksheets in second-level groups."""
3607
3716
  print("\nscenario: sub-group visibility (WO-FB 3.1)")
@@ -4749,6 +4858,7 @@ _CASES = {
4749
4858
  "rs-business-rules": case_rs_business_rules,
4750
4859
  "field": case_field,
4751
4860
  "view": case_view,
4861
+ "view-spec-partial": case_view_spec_partial,
4752
4862
  "role": case_role,
4753
4863
  "custom-action": case_custom_action,
4754
4864
  "custom-action-update": case_custom_action_update,
@@ -4792,6 +4902,7 @@ _CASES = {
4792
4902
  "fb-layout-check": case_fb_layout_check,
4793
4903
  "fb-pair-relation": case_fb_pair_relation,
4794
4904
  "fb-subtable-read": case_fb_subtable_read,
4905
+ "create-partial": case_create_partial,
4795
4906
  "editor-inline-subtable": case_editor_inline_subtable,
4796
4907
  "fb-subgroups": case_fb_subgroups,
4797
4908
  "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.4"
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
  *,
@@ -2103,20 +2222,34 @@ def update_view_spec(
2103
2222
  from hap_cli.core import view_spec_adapter as _vsa
2104
2223
  field_resolver, control_types = _view_field_maps(
2105
2224
  session, worksheet_id, app_id)
2225
+ current = get_view_detail(
2226
+ session, worksheet_id, view_id, app_id=app_id) or {}
2227
+ # The adapter is built for create, where a missing type means a table
2228
+ # view and a missing name means "". On an update the spec only says
2229
+ # what changes, so an absent type is the view's current type (it also
2230
+ # drives type-specific translation such as card/cover defaults) and an
2231
+ # absent name must not be written at all — otherwise a title-only spec
2232
+ # turns a calendar into a nameless table view (bug #455744).
2233
+ spec = dict(spec)
2234
+ if "viewType" not in spec and "type" not in spec \
2235
+ and current.get("viewType") is not None:
2236
+ spec["viewType"] = current["viewType"]
2106
2237
  payload = _vsa.build_view_payload(
2107
2238
  spec, worksheet_id=worksheet_id, app_id=app_id, view_id=view_id,
2108
2239
  field_resolver=field_resolver or None,
2109
2240
  control_types=control_types or None,
2110
2241
  )
2242
+ if "name" not in spec:
2243
+ payload.pop("name", None)
2111
2244
  # Changing the view TYPE is not a partial edit. The product handles it
2112
2245
  # by posting the whole view object — read back, merged, re-defaulted for
2113
2246
  # the new type — with no editAttrs (ViewControl.jsx:256-259). Naming
2114
2247
  # viewType in editAttrs is a path the product never takes.
2115
2248
  new_type = payload.get("viewType")
2116
2249
  if new_type is not None:
2117
- current = get_view_detail(
2118
- session, worksheet_id, view_id, app_id=app_id) or {}
2119
2250
  if current and current.get("viewType") != new_type:
2251
+ logger.info("view spec: view %s type %s -> %s, saving whole view",
2252
+ view_id, current.get("viewType"), new_type)
2120
2253
  merged = {**current, **payload}
2121
2254
  merged.pop("editAttrs", None)
2122
2255
  merged.pop("editAdKeys", None)
@@ -10566,6 +10566,78 @@ class TestUpdateViewSpec:
10566
10566
  assert identity not in body["editAttrs"]
10567
10567
  assert "navshow" in body["editAdKeys"]
10568
10568
 
10569
+ @staticmethod
10570
+ def _route_current(mock_post, current):
10571
+ def _route(url, json=None, **kw):
10572
+ if url.endswith("/Worksheet/GetWorksheetControls"):
10573
+ return _mock_response({"controls": [
10574
+ {"controlId": "c1", "controlName": "标题", "type": 2},
10575
+ ]})
10576
+ if url.endswith("/Worksheet/GetWorksheetViewById"):
10577
+ return _mock_response(current)
10578
+ return _mock_response({"viewId": "vw1"})
10579
+ mock_post.side_effect = _route
10580
+
10581
+ @staticmethod
10582
+ def _save_body(mock_post):
10583
+ saves = [c for c in mock_post.call_args_list
10584
+ if c.args[0].endswith("/Worksheet/SaveWorksheetView")]
10585
+ assert len(saves) == 1
10586
+ return saves[0].kwargs["json"]
10587
+
10588
+ @patch("hap_cli.core.session.requests.post")
10589
+ def test_title_only_spec_keeps_calendar_type_and_name(
10590
+ self, mock_post, mock_session):
10591
+ """Bug #455744: a spec naming only card.titleField must not turn a
10592
+ calendar into a table view or blank its name — the spec says
10593
+ nothing about either, so both are left as they are."""
10594
+ self._route_current(mock_post, {
10595
+ "viewId": "vw1", "viewType": 4, "name": "QA Calendar",
10596
+ "advancedSetting": {"begindate": "cd1"},
10597
+ })
10598
+ ws_mod.update_view_spec(
10599
+ mock_session, "ws1", "vw1", {"card": {"titleField": "c1"}},
10600
+ app_id="app1",
10601
+ )
10602
+ body = self._save_body(mock_post)
10603
+ # Still a partial edit — not the whole-object type-change save.
10604
+ assert "editAttrs" in body
10605
+ assert body.get("viewType", 4) == 4
10606
+ assert "name" not in body
10607
+ assert "name" not in body["editAttrs"]
10608
+ assert "viewType" not in body["editAttrs"]
10609
+ assert body["advancedSetting"]["viewtitle"] == "c1"
10610
+ assert "begindate" not in body["editAdKeys"]
10611
+ assert body["editAdKeys"] == list(body["advancedSetting"])
10612
+
10613
+ @patch("hap_cli.core.session.requests.post")
10614
+ def test_spec_without_name_keeps_table_view_name(
10615
+ self, mock_post, mock_session):
10616
+ self._route_current(mock_post, {
10617
+ "viewId": "vw1", "viewType": 0, "name": "全部"})
10618
+ ws_mod.update_view_spec(
10619
+ mock_session, "ws1", "vw1", {"card": {"titleField": "c1"}},
10620
+ app_id="app1",
10621
+ )
10622
+ body = self._save_body(mock_post)
10623
+ assert "name" not in body
10624
+ assert "name" not in body["editAttrs"]
10625
+
10626
+ @patch("hap_cli.core.session.requests.post")
10627
+ def test_explicit_type_change_still_saves_whole_view(
10628
+ self, mock_post, mock_session):
10629
+ self._route_current(mock_post, {
10630
+ "viewId": "vw1", "viewType": 0, "name": "全部"})
10631
+ ws_mod.update_view_spec(
10632
+ mock_session, "ws1", "vw1", {"viewType": "gallery"},
10633
+ app_id="app1",
10634
+ )
10635
+ body = self._save_body(mock_post)
10636
+ assert body["viewType"] == 3
10637
+ assert "editAttrs" not in body
10638
+ # The spec did not rename it, so the current name rides along.
10639
+ assert body["name"] == "全部"
10640
+
10569
10641
 
10570
10642
  class TestFieldNormalizerFixtureCoverage:
10571
10643
  """Loads the user's captured get_worksheet_structure fixtures from