hap-cli 0.7.0__tar.gz → 0.7.1__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.
- {hap_cli-0.7.0 → hap_cli-0.7.1}/PKG-INFO +1 -1
- {hap_cli-0.7.0 → hap_cli-0.7.1}/app_live_tests/__init__.py +1 -1
- {hap_cli-0.7.0 → hap_cli-0.7.1}/app_live_tests/config.py +1 -1
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/__init__.py +1 -1
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/instance_cmd.py +10 -4
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/instance.py +45 -32
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/workflow_node_dsl.py +10 -2
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_core.py +93 -28
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_integration_approval.py +83 -0
- hap_cli-0.7.1/hap_cli/tests/test_integration_approval_actions.py +237 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_integration_destructive.py +4 -4
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_worksheet_crud_cli.py +1 -1
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli.egg-info/PKG-INFO +1 -1
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli.egg-info/SOURCES.txt +1 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/seed/template.py +1 -1
- {hap_cli-0.7.0 → hap_cli-0.7.1}/MANIFEST.in +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/README.md +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/app_live_tests/__main__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/app_live_tests/harness.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/app_live_tests/smoke.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/app_live_tests/state.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/README.md +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/README_CN.md +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/__init__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/app_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/auth_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/calendar_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/chart_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/chat_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/chatbot_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/config_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/contact_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/department_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/group_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/icon_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/node_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/optionset_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/page_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/post_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/record_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/region_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/role_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/upload_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/v3_registry.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/workflow_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/commands/worksheet_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/context.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/__init__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/action_spec_adapter.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/app.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/auth.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/calendar_mod.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/chart.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/chat.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/chatbot.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/contact.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/control_type_codes.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/department.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/field_normalizer.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/field_spec_adapter.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/filter_translator.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/flow_node.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/global_meta.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/group.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/icon_index.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/logger.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/optionset.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/page.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/post.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/record.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/response_crypto.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/role.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/role_perm_builder.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/session.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/token_crypto.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/upload.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/v3/__init__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/v3/dispatcher.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/v3/render.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/v3/schema.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/view_spec_adapter.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/workflow.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/worksheet.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/core/worksheet_templates.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/hap_cli.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/i18n.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/locale/__init__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/locale/messages.json +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/locale/messages.schema.json +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/__init__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/conftest.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_auth_prerelease.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_chart.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_config_cmd.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_full_e2e.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_global_meta.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_i18n.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_integration.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_integration_calendar.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_integration_misc.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_integration_post.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_integration_social.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_integration_v3.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_integration_workflow.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_integration_worksheet_extra.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_org_id_cli.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_org_id_docs.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_parameter_conventions.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_parameter_mapping_registry.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_v3_api_schema_loader.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_v3_dispatcher.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_v3_registry_coverage.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_v3_session.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/tests/test_v3_yaml_translation.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/utils/__init__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/utils/formatting.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/utils/options.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli/utils/parameter_mapping.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli.egg-info/dependency_links.txt +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli.egg-info/entry_points.txt +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli.egg-info/requires.txt +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/hap_cli.egg-info/top_level.txt +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/__init__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/__main__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/_wftest.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/charts.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/cleanup.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/compiler.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/config.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/errors.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/executor.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/fields.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/hap.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/recording/__init__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/recording/console.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/recording/jsonl.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/recording/mirror.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/recording/report.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/schema.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/seed/__init__.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/seed/cli.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/seed/executor.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/selftest.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/smoke.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/step.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/steps.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/store.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/live_tests/workflow_dsl.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/setup.cfg +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/setup.py +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/INDEX.json +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/add_member_to_role.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/batch_delete_records.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/create_optionset.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/create_role.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/delete_optionset.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/delete_record.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/delete_role.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/delete_worksheet.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_app_info.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_app_knowledge_list.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_app_worksheets_list.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_optionset_list.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_record_details.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_record_discussions.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_record_list.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_record_logs.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_record_pivot_data.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_record_relations.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_record_share_link.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_regions.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_role_details.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/get_role_list.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/knowledge_search.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/leave_all_roles.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/remove_member_from_role.yaml +0 -0
- {hap_cli-0.7.0 → hap_cli-0.7.1}/sources/v3-api-schema/update_optionset.yaml +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Repeatable live smoke tests for the hap-app-editor skill.
|
|
1
|
+
"""Repeatable live smoke tests for the hap-cli-app-editor skill.
|
|
2
2
|
|
|
3
3
|
This is a top-level, stdlib-only package (no ``test_*.py`` files, so the
|
|
4
4
|
default pytest run never collects it — mirror of ``live_tests/``). It
|
|
@@ -12,7 +12,7 @@ PYTHON_BIN = os.environ.get("HAP_EDITOR_PYTHON", "") # "" -> sys.executable
|
|
|
12
12
|
|
|
13
13
|
_PKG_DIR = Path(__file__).resolve().parent
|
|
14
14
|
REPO_ROOT = _PKG_DIR.parent
|
|
15
|
-
SKILL_DIR = REPO_ROOT / "skills" / "hap-app-editor"
|
|
15
|
+
SKILL_DIR = REPO_ROOT / "skills" / "hap-cli-app-editor"
|
|
16
16
|
|
|
17
17
|
STATE_DIR = _PKG_DIR / "state"
|
|
18
18
|
STATE_FILE = STATE_DIR / "current_app.json"
|
|
@@ -294,18 +294,24 @@ def instance_history_detail(ctx, instance_id):
|
|
|
294
294
|
|
|
295
295
|
@instance.command("batch")
|
|
296
296
|
@click.option("--action", "-a", required=True, type=int, help="4=batch approve, 5=batch reject")
|
|
297
|
-
@click.option("--type", "type_", default
|
|
297
|
+
@click.option("--type", "type_", default=4, type=int, help="Task type the selection came from (4=pending approval)")
|
|
298
298
|
@click.option("--process-id", default="", help="Filter by process")
|
|
299
|
-
@click.option("--select", "-s", multiple=True, help="
|
|
299
|
+
@click.option("--select", "-s", multiple=True, help="Task to act on, as TASK_ID:WORK_ID (repeatable)")
|
|
300
|
+
@click.option("--opinion", default="", help="Comment applied to each task")
|
|
300
301
|
@pass_context
|
|
301
|
-
def instance_batch(ctx, action, type_, process_id, select):
|
|
302
|
-
"""Batch approve or reject tasks.
|
|
302
|
+
def instance_batch(ctx, action, type_, process_id, select, opinion):
|
|
303
|
+
"""Batch approve or reject tasks.
|
|
304
|
+
|
|
305
|
+
Pass each task as TASK_ID:WORK_ID (the IDs from the pending list); repeat
|
|
306
|
+
--select for more than one.
|
|
307
|
+
"""
|
|
303
308
|
try:
|
|
304
309
|
session = ctx.get_session()
|
|
305
310
|
data = inst_mod.batch_operation(
|
|
306
311
|
session, action,
|
|
307
312
|
type_=type_, process_id=process_id,
|
|
308
313
|
selects=list(select) if select else None,
|
|
314
|
+
opinion=opinion,
|
|
309
315
|
)
|
|
310
316
|
ctx.output(data, lambda d: click.echo(f"Batch operation completed: {d}"))
|
|
311
317
|
except Exception as e:
|
|
@@ -119,7 +119,8 @@ def get_instance_detail(
|
|
|
119
119
|
data: dict[str, Any] = {"id": instance_id}
|
|
120
120
|
if work_id:
|
|
121
121
|
data["workId"] = work_id
|
|
122
|
-
|
|
122
|
+
# pd-openweb instanceVersion.js:30-34 (get2) pins type='GET'; POST returns 404.
|
|
123
|
+
return session.workflow_call("v2/instance/get", data, method="GET")
|
|
123
124
|
|
|
124
125
|
|
|
125
126
|
def get_operation_detail(
|
|
@@ -140,7 +141,8 @@ def get_operation_detail(
|
|
|
140
141
|
data: dict[str, Any] = {"id": instance_id}
|
|
141
142
|
if work_id:
|
|
142
143
|
data["workId"] = work_id
|
|
143
|
-
|
|
144
|
+
# pd-openweb instance.js:101-105 pins type='GET'; POST returns 404.
|
|
145
|
+
return session.workflow_call("instance/getOperationDetail", data, method="GET")
|
|
144
146
|
|
|
145
147
|
|
|
146
148
|
def get_operation_history(
|
|
@@ -156,9 +158,11 @@ def get_operation_history(
|
|
|
156
158
|
Returns:
|
|
157
159
|
List of operation records
|
|
158
160
|
"""
|
|
161
|
+
# pd-openweb instance.js:114-118 pins type='GET'; POST returns 404.
|
|
159
162
|
result = session.workflow_call(
|
|
160
163
|
"instance/getOperationHistoryList",
|
|
161
164
|
{"instanceId": instance_id},
|
|
165
|
+
method="GET",
|
|
162
166
|
)
|
|
163
167
|
return result if isinstance(result, list) else result.get("list", [])
|
|
164
168
|
|
|
@@ -408,48 +412,52 @@ def urge(
|
|
|
408
412
|
def batch_operation(
|
|
409
413
|
session: Session,
|
|
410
414
|
batch_operation_type: int,
|
|
411
|
-
|
|
412
|
-
work_id: str = "",
|
|
413
|
-
page_index: int = 1,
|
|
414
|
-
page_size: int = 50,
|
|
415
|
-
type_: int = -1,
|
|
416
|
-
apk_id: str = "",
|
|
415
|
+
type_: int = 4,
|
|
417
416
|
process_id: str = "",
|
|
418
417
|
selects: Optional[list] = None,
|
|
418
|
+
opinion: str = "",
|
|
419
419
|
) -> dict[str, Any]:
|
|
420
|
-
"""Batch approve/reject
|
|
420
|
+
"""Batch approve/reject pending tasks.
|
|
421
421
|
|
|
422
422
|
Args:
|
|
423
423
|
session: Active session
|
|
424
|
-
batch_operation_type: 4=pass, 5=reject
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
424
|
+
batch_operation_type: 4=pass (approve), 5=overrule (reject)
|
|
425
|
+
type_: Task-type context the selection came from (4=pending approval).
|
|
426
|
+
process_id: Optional process filter (echoed back to the server).
|
|
427
|
+
selects: The work items to act on. Each entry is either a
|
|
428
|
+
``{"id": <instanceId>, "workId": <workId>, "opinion": <str>}``
|
|
429
|
+
dict or an ``"<instanceId>:<workId>"`` string. The wire requires
|
|
430
|
+
one object per item carrying ``id``/``workId``/``opinion`` and
|
|
431
|
+
``opinionType: 3`` (batch). A bare id list is rejected (HTTP 400).
|
|
432
|
+
opinion: Default opinion applied to items that don't carry their own.
|
|
433
433
|
|
|
434
434
|
Returns:
|
|
435
435
|
Batch result
|
|
436
436
|
"""
|
|
437
|
+
# pd-openweb instanceVersion.js batch2 (/v2/instance/batch, POST) — the
|
|
438
|
+
# payload is {type, batchOperationType, selects:[{id, workId, opinion,
|
|
439
|
+
# opinionType:3}]} (MyProcess/index.jsx). Paging fields are NOT part of
|
|
440
|
+
# the batch body; sending them (and bare-id selects) is what 400s.
|
|
441
|
+
sel: list[dict[str, Any]] = []
|
|
442
|
+
for s in selects or []:
|
|
443
|
+
if isinstance(s, dict):
|
|
444
|
+
item = {
|
|
445
|
+
"id": s.get("id", ""),
|
|
446
|
+
"workId": s.get("workId", ""),
|
|
447
|
+
"opinion": s.get("opinion", opinion),
|
|
448
|
+
"opinionType": 3,
|
|
449
|
+
}
|
|
450
|
+
else:
|
|
451
|
+
inst_id, _, w_id = str(s).partition(":")
|
|
452
|
+
item = {"id": inst_id, "workId": w_id, "opinion": opinion, "opinionType": 3}
|
|
453
|
+
sel.append(item)
|
|
437
454
|
data: dict[str, Any] = {
|
|
438
|
-
"batchOperationType": batch_operation_type,
|
|
439
|
-
"pageIndex": page_index,
|
|
440
|
-
"pageSize": page_size,
|
|
441
455
|
"type": type_,
|
|
456
|
+
"batchOperationType": batch_operation_type,
|
|
457
|
+
"selects": sel,
|
|
442
458
|
}
|
|
443
|
-
if instance_id:
|
|
444
|
-
data["id"] = instance_id
|
|
445
|
-
if work_id:
|
|
446
|
-
data["workId"] = work_id
|
|
447
|
-
if apk_id:
|
|
448
|
-
data["apkId"] = apk_id
|
|
449
459
|
if process_id:
|
|
450
460
|
data["processId"] = process_id
|
|
451
|
-
if selects:
|
|
452
|
-
data["selects"] = selects
|
|
453
461
|
return session.workflow_call("v2/instance/batch", data)
|
|
454
462
|
|
|
455
463
|
|
|
@@ -519,9 +527,11 @@ def get_history_detail(
|
|
|
519
527
|
Returns:
|
|
520
528
|
History detail with node execution info
|
|
521
529
|
"""
|
|
530
|
+
# pd-openweb instance.js:52-56 pins type='GET'; POST returns 404.
|
|
522
531
|
return session.workflow_call(
|
|
523
532
|
"instance/getHistoryDetail",
|
|
524
533
|
{"instanceId": instance_id},
|
|
534
|
+
method="GET",
|
|
525
535
|
)
|
|
526
536
|
|
|
527
537
|
|
|
@@ -538,8 +548,9 @@ def end_instance(session: Session, instance_id: str) -> dict[str, Any]:
|
|
|
538
548
|
Returns:
|
|
539
549
|
Termination result
|
|
540
550
|
"""
|
|
551
|
+
# pd-openweb instanceVersion.js:135-139 pins type='GET'; POST returns 404.
|
|
541
552
|
return session.workflow_call(
|
|
542
|
-
"v1/instance/endInstance", {"instanceId": instance_id}
|
|
553
|
+
"v1/instance/endInstance", {"instanceId": instance_id}, method="GET"
|
|
543
554
|
)
|
|
544
555
|
|
|
545
556
|
|
|
@@ -553,8 +564,9 @@ def reset_instance(session: Session, instance_id: str) -> dict[str, Any]:
|
|
|
553
564
|
Returns:
|
|
554
565
|
Reset result
|
|
555
566
|
"""
|
|
567
|
+
# pd-openweb instanceVersion.js:228-232 pins type='GET'; POST returns 404.
|
|
556
568
|
return session.workflow_call(
|
|
557
|
-
"v1/instance/resetInstance", {"instanceId": instance_id}
|
|
569
|
+
"v1/instance/resetInstance", {"instanceId": instance_id}, method="GET"
|
|
558
570
|
)
|
|
559
571
|
|
|
560
572
|
|
|
@@ -700,7 +712,8 @@ def get_work_item(
|
|
|
700
712
|
data: dict[str, Any] = {"id": instance_id}
|
|
701
713
|
if work_id:
|
|
702
714
|
data["workId"] = work_id
|
|
703
|
-
|
|
715
|
+
# pd-openweb instanceVersion.js:215-219 pins type='GET'; POST returns 404.
|
|
716
|
+
return session.workflow_call("v1/instance/getWorkItem", data, method="GET")
|
|
704
717
|
|
|
705
718
|
|
|
706
719
|
def get_archived_list(session: Session) -> list[dict[str, Any]]:
|
|
@@ -402,8 +402,16 @@ def translate_accounts(
|
|
|
402
402
|
"roleName": acc.get("roleName", ""),
|
|
403
403
|
"avatar": "", "count": 0})
|
|
404
404
|
elif kind == "user":
|
|
405
|
-
|
|
406
|
-
|
|
405
|
+
# A specific named member. Ground truth: pd-openweb's shared
|
|
406
|
+
# SelectUserDropDown (WorkflowSettings/.../SelectUserDropDown,
|
|
407
|
+
# USER_TYPE.USER) — a fixed user is **type 1** with the accountId
|
|
408
|
+
# in ``roleId`` (NOT type 2, and NOT in entityId). type 2 is a
|
|
409
|
+
# role; an accountId placed in entityId can't bind and the server
|
|
410
|
+
# renders the recipient "已删除", failing publish (warningType 105).
|
|
411
|
+
# Same shape for approve and cc/notice — the picker is shared.
|
|
412
|
+
out.append({"type": 1, "entityId": "", "entityName": "",
|
|
413
|
+
"roleId": acc.get("userId", ""),
|
|
414
|
+
"roleName": acc.get("userName", ""), "avatar": ""})
|
|
407
415
|
elif kind in ("owner", "triggerUser"):
|
|
408
416
|
# The record owner / trigger user: a type-6 ref keyed off a
|
|
409
417
|
# node (default the trigger) with the special "uaid" roleId.
|
|
@@ -1184,11 +1184,16 @@ class TestInstance:
|
|
|
1184
1184
|
result = inst_mod.get_todo_list(mock_session, type_=4)
|
|
1185
1185
|
assert result["count"] == 1
|
|
1186
1186
|
|
|
1187
|
+
@patch("hap_cli.core.session.requests.get")
|
|
1187
1188
|
@patch("hap_cli.core.session.requests.post")
|
|
1188
|
-
def test_get_instance_detail(self, mock_post, mock_session):
|
|
1189
|
-
|
|
1189
|
+
def test_get_instance_detail(self, mock_post, mock_get, mock_session):
|
|
1190
|
+
"""v2/instance/get is GET (instanceVersion.js:30-34)."""
|
|
1191
|
+
mock_get.return_value = _mock_response({"id": "inst1", "status": 1})
|
|
1190
1192
|
result = inst_mod.get_instance_detail(mock_session, "inst1")
|
|
1191
1193
|
assert result["status"] == 1
|
|
1194
|
+
assert mock_get.called
|
|
1195
|
+
assert not mock_post.called
|
|
1196
|
+
assert mock_get.call_args[0][0].endswith("/v2/instance/get")
|
|
1192
1197
|
|
|
1193
1198
|
@patch("hap_cli.core.session.requests.post")
|
|
1194
1199
|
def test_approve(self, mock_post, mock_session):
|
|
@@ -1243,19 +1248,8 @@ class TestInstance:
|
|
|
1243
1248
|
body = mock_post.call_args[1]["json"]
|
|
1244
1249
|
assert body["operationType"] == 18
|
|
1245
1250
|
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
mock_post.return_value = _mock_response(True)
|
|
1249
|
-
inst_mod.end_instance(mock_session, "inst1")
|
|
1250
|
-
body = mock_post.call_args[1]["json"]
|
|
1251
|
-
assert body["instanceId"] == "inst1"
|
|
1252
|
-
|
|
1253
|
-
@patch("hap_cli.core.session.requests.post")
|
|
1254
|
-
def test_reset_instance(self, mock_post, mock_session):
|
|
1255
|
-
mock_post.return_value = _mock_response(True)
|
|
1256
|
-
inst_mod.reset_instance(mock_session, "inst1")
|
|
1257
|
-
body = mock_post.call_args[1]["json"]
|
|
1258
|
-
assert body["instanceId"] == "inst1"
|
|
1251
|
+
# end_instance / reset_instance GET-verb guards live in
|
|
1252
|
+
# test_end_instance_is_get / test_reset_instance_is_get below.
|
|
1259
1253
|
|
|
1260
1254
|
@patch("hap_cli.core.session.requests.get")
|
|
1261
1255
|
@patch("hap_cli.core.session.requests.post")
|
|
@@ -1271,27 +1265,67 @@ class TestInstance:
|
|
|
1271
1265
|
assert not mock_post.called
|
|
1272
1266
|
assert mock_get.call_args[0][0].endswith("/instance/getHistoryList")
|
|
1273
1267
|
|
|
1268
|
+
@patch("hap_cli.core.session.requests.get")
|
|
1274
1269
|
@patch("hap_cli.core.session.requests.post")
|
|
1275
|
-
def test_get_history_detail(self, mock_post, mock_session):
|
|
1276
|
-
|
|
1270
|
+
def test_get_history_detail(self, mock_post, mock_get, mock_session):
|
|
1271
|
+
"""instance/getHistoryDetail is GET (instance.js:52-56)."""
|
|
1272
|
+
mock_get.return_value = _mock_response({"instanceId": "inst1", "nodes": []})
|
|
1277
1273
|
result = inst_mod.get_history_detail(mock_session, "inst1")
|
|
1278
1274
|
assert result["instanceId"] == "inst1"
|
|
1275
|
+
assert mock_get.called
|
|
1276
|
+
assert not mock_post.called
|
|
1277
|
+
assert mock_get.call_args[0][0].endswith("/instance/getHistoryDetail")
|
|
1279
1278
|
|
|
1279
|
+
@patch("hap_cli.core.session.requests.get")
|
|
1280
1280
|
@patch("hap_cli.core.session.requests.post")
|
|
1281
|
-
def test_get_operation_history(self, mock_post, mock_session):
|
|
1282
|
-
|
|
1281
|
+
def test_get_operation_history(self, mock_post, mock_get, mock_session):
|
|
1282
|
+
"""instance/getOperationHistoryList is GET (instance.js:114-118)."""
|
|
1283
|
+
mock_get.return_value = _mock_response([{"action": "pass", "time": "2024-01-01"}])
|
|
1283
1284
|
result = inst_mod.get_operation_history(mock_session, "inst1")
|
|
1284
1285
|
assert len(result) == 1
|
|
1286
|
+
assert mock_get.called
|
|
1287
|
+
assert not mock_post.called
|
|
1288
|
+
assert mock_get.call_args[0][0].endswith("/instance/getOperationHistoryList")
|
|
1285
1289
|
|
|
1286
1290
|
@patch("hap_cli.core.session.requests.post")
|
|
1287
1291
|
def test_batch_operation(self, mock_post, mock_session):
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1292
|
+
# v2/instance/batch wants {type, batchOperationType, selects:[{id,
|
|
1293
|
+
# workId, opinion, opinionType:3}]}. Bare-id selects / paging fields
|
|
1294
|
+
# are rejected (HTTP 400). "id:workId" strings normalise to objects.
|
|
1295
|
+
mock_post.return_value = _mock_response({"success": [], "fail": []})
|
|
1296
|
+
inst_mod.batch_operation(
|
|
1297
|
+
mock_session, 4, process_id="wf1",
|
|
1298
|
+
selects=["i1:w1", {"id": "i2", "workId": "w2", "opinion": "ok"}],
|
|
1291
1299
|
)
|
|
1292
1300
|
body = mock_post.call_args[1]["json"]
|
|
1301
|
+
assert mock_post.call_args[0][0].endswith("/v2/instance/batch")
|
|
1302
|
+
assert body["type"] == 4
|
|
1293
1303
|
assert body["batchOperationType"] == 4
|
|
1294
|
-
assert
|
|
1304
|
+
assert "pageIndex" not in body and "pageSize" not in body
|
|
1305
|
+
assert body["selects"] == [
|
|
1306
|
+
{"id": "i1", "workId": "w1", "opinion": "", "opinionType": 3},
|
|
1307
|
+
{"id": "i2", "workId": "w2", "opinion": "ok", "opinionType": 3},
|
|
1308
|
+
]
|
|
1309
|
+
|
|
1310
|
+
@patch("hap_cli.core.session.requests.get")
|
|
1311
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1312
|
+
def test_end_instance_is_get(self, mock_post, mock_get, mock_session):
|
|
1313
|
+
"""v1/instance/endInstance is GET (instanceVersion.js:135-139)."""
|
|
1314
|
+
mock_get.return_value = _mock_response({"status": 4})
|
|
1315
|
+
inst_mod.end_instance(mock_session, "i1")
|
|
1316
|
+
assert mock_get.called and not mock_post.called
|
|
1317
|
+
assert mock_get.call_args[0][0].endswith("/v1/instance/endInstance")
|
|
1318
|
+
assert mock_get.call_args[1]["params"] == {"instanceId": "i1"}
|
|
1319
|
+
|
|
1320
|
+
@patch("hap_cli.core.session.requests.get")
|
|
1321
|
+
@patch("hap_cli.core.session.requests.post")
|
|
1322
|
+
def test_reset_instance_is_get(self, mock_post, mock_get, mock_session):
|
|
1323
|
+
"""v1/instance/resetInstance is GET (instanceVersion.js:228-232)."""
|
|
1324
|
+
mock_get.return_value = _mock_response({"status": 1})
|
|
1325
|
+
inst_mod.reset_instance(mock_session, "i1")
|
|
1326
|
+
assert mock_get.called and not mock_post.called
|
|
1327
|
+
assert mock_get.call_args[0][0].endswith("/v1/instance/resetInstance")
|
|
1328
|
+
assert mock_get.call_args[1]["params"] == {"instanceId": "i1"}
|
|
1295
1329
|
|
|
1296
1330
|
|
|
1297
1331
|
# ── Role Tests ───────────────────────────────────────────────────────────
|
|
@@ -5610,14 +5644,29 @@ class TestApprovalBatchLifecycle:
|
|
|
5610
5644
|
class TestApprovalWorkItemAndArchive:
|
|
5611
5645
|
"""Wave 8 — single work-item + archived-service lookups."""
|
|
5612
5646
|
|
|
5647
|
+
@patch("hap_cli.core.session.requests.get")
|
|
5613
5648
|
@patch("hap_cli.core.session.requests.post")
|
|
5614
|
-
def test_get_work_item_sends_id_and_workId(self, mock_post, mock_session):
|
|
5615
|
-
|
|
5649
|
+
def test_get_work_item_sends_id_and_workId(self, mock_post, mock_get, mock_session):
|
|
5650
|
+
"""v1/instance/getWorkItem is GET (instanceVersion.js:215-219)."""
|
|
5651
|
+
mock_get.return_value = _mock_response({"id": "i1", "workId": "w1"})
|
|
5616
5652
|
inst_mod.get_work_item(mock_session, "i1", work_id="w1")
|
|
5617
|
-
|
|
5618
|
-
|
|
5653
|
+
assert mock_get.called
|
|
5654
|
+
assert not mock_post.called
|
|
5655
|
+
url = mock_get.call_args[0][0]
|
|
5619
5656
|
assert url.endswith("/v1/instance/getWorkItem")
|
|
5620
|
-
assert
|
|
5657
|
+
assert mock_get.call_args[1]["params"] == {"id": "i1", "workId": "w1"}
|
|
5658
|
+
|
|
5659
|
+
@patch("hap_cli.core.session.requests.get")
|
|
5660
|
+
@patch("hap_cli.core.session.requests.post")
|
|
5661
|
+
def test_get_operation_detail_is_get(self, mock_post, mock_get, mock_session):
|
|
5662
|
+
"""instance/getOperationDetail is GET (instance.js:101-105)."""
|
|
5663
|
+
mock_get.return_value = _mock_response({"workItems": [], "nextUserRange": []})
|
|
5664
|
+
inst_mod.get_operation_detail(mock_session, "i1", work_id="w1")
|
|
5665
|
+
assert mock_get.called
|
|
5666
|
+
assert not mock_post.called
|
|
5667
|
+
url = mock_get.call_args[0][0]
|
|
5668
|
+
assert url.endswith("/instance/getOperationDetail")
|
|
5669
|
+
assert mock_get.call_args[1]["params"] == {"id": "i1", "workId": "w1"}
|
|
5621
5670
|
|
|
5622
5671
|
@patch("hap_cli.core.session.requests.get")
|
|
5623
5672
|
@patch("hap_cli.core.session.requests.post")
|
|
@@ -6931,6 +6980,22 @@ class TestWorkflowNodeDslPrimitives:
|
|
|
6931
6980
|
assert out[0]["type"] == 2 and out[0]["entityId"] == "app1"
|
|
6932
6981
|
assert out[0]["roleId"] == "r1"
|
|
6933
6982
|
|
|
6983
|
+
def test_user_account_is_type1_with_account_in_roleid(self):
|
|
6984
|
+
# A specific named member: pd-openweb's shared SelectUserDropDown
|
|
6985
|
+
# (USER_TYPE.USER) encodes a fixed user as type 1 with the accountId
|
|
6986
|
+
# in roleId — NOT type 2, and NOT the accountId in entityId. type 2 is
|
|
6987
|
+
# a role; a misplaced accountId can't bind and publish fails 105.
|
|
6988
|
+
from hap_cli.core import workflow_node_dsl as dsl
|
|
6989
|
+
for for_approve in (False, True):
|
|
6990
|
+
out = dsl.translate_accounts(
|
|
6991
|
+
[{"kind": "user", "userId": "acc-123", "userName": "田然"}],
|
|
6992
|
+
for_approve=for_approve,
|
|
6993
|
+
)
|
|
6994
|
+
assert out[0]["type"] == 1, out[0]
|
|
6995
|
+
assert out[0]["roleId"] == "acc-123", out[0]
|
|
6996
|
+
assert out[0]["entityId"] == "", out[0]
|
|
6997
|
+
assert out[0]["roleName"] == "田然"
|
|
6998
|
+
|
|
6934
6999
|
def test_operator_to_condition_id_matches_enum(self):
|
|
6935
7000
|
from hap_cli.core import workflow_node_dsl as dsl
|
|
6936
7001
|
# Spot-check against pd-openweb CONDITION_TYPE.
|
|
@@ -44,6 +44,23 @@ def _sample_process_id(integration_session, app_id):
|
|
|
44
44
|
return items[0].get("id") or items[0].get("processId")
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
@pytest.fixture(scope="module")
|
|
48
|
+
def _sample_todo(integration_session):
|
|
49
|
+
"""A real (instance_id, work_id) pair drawn from the pending-task list.
|
|
50
|
+
|
|
51
|
+
The per-instance read endpoints (``get`` / ``getOperationDetail`` /
|
|
52
|
+
``getWorkItem`` …) only resolve for a live work item the current user can
|
|
53
|
+
see, so we pull one straight from ``getTodoList``. Skip the whole class
|
|
54
|
+
when the inbox is empty — there is nothing to read.
|
|
55
|
+
"""
|
|
56
|
+
result = inst.get_todo_list(integration_session, page_size=5)
|
|
57
|
+
items = (result or {}).get("data") or []
|
|
58
|
+
if not items:
|
|
59
|
+
pytest.skip("no pending approval tasks in inbox — cannot test per-instance reads")
|
|
60
|
+
item = items[0]
|
|
61
|
+
return item.get("id"), item.get("workId", "")
|
|
62
|
+
|
|
63
|
+
|
|
47
64
|
@pytest.fixture(scope="module")
|
|
48
65
|
def _dummy_account_id(integration_session, project_id):
|
|
49
66
|
"""Pick any real accountId from the project directory for the delegation
|
|
@@ -128,6 +145,72 @@ class TestApproval:
|
|
|
128
145
|
assert isinstance(result["count"], int)
|
|
129
146
|
|
|
130
147
|
|
|
148
|
+
# ── Per-instance read-side tests ─────────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class TestInstanceRead:
|
|
152
|
+
"""GET-vs-POST regression guards for the per-instance read endpoints.
|
|
153
|
+
|
|
154
|
+
All five 404'd in production because the wrappers issued POST while
|
|
155
|
+
pd-openweb pins ``base.ajaxOptions.type = 'GET'`` on each:
|
|
156
|
+
|
|
157
|
+
- ``v2/instance/get`` (instanceVersion.js:30-34)
|
|
158
|
+
- ``instance/getOperationDetail`` (instance.js:101-105)
|
|
159
|
+
- ``instance/getOperationHistoryList``(instance.js:114-118)
|
|
160
|
+
- ``v1/instance/getWorkItem`` (instanceVersion.js:215-219)
|
|
161
|
+
- ``instance/getHistoryDetail`` (instance.js:52-56)
|
|
162
|
+
|
|
163
|
+
Each test exercises the wrapper end-to-end against a live work item so a
|
|
164
|
+
verb regression surfaces immediately as a 404.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def test_01_get_detail(self, integration_session, _sample_todo):
|
|
168
|
+
"""v2/instance/get must be GET; returns the instance flow detail."""
|
|
169
|
+
instance_id, work_id = _sample_todo
|
|
170
|
+
result = inst.get_instance_detail(
|
|
171
|
+
integration_session, instance_id, work_id=work_id
|
|
172
|
+
)
|
|
173
|
+
assert_dict_like(result)
|
|
174
|
+
# Flow detail always carries the works array + a title.
|
|
175
|
+
assert "works" in result or "currentWork" in result, (
|
|
176
|
+
f"unexpected instance detail shape: {list(result)!r}"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def test_02_operations(self, integration_session, _sample_todo):
|
|
180
|
+
"""instance/getOperationDetail must be GET; returns available ops."""
|
|
181
|
+
instance_id, work_id = _sample_todo
|
|
182
|
+
result = inst.get_operation_detail(
|
|
183
|
+
integration_session, instance_id, work_id=work_id
|
|
184
|
+
)
|
|
185
|
+
assert_dict_like(result)
|
|
186
|
+
|
|
187
|
+
def test_03_operation_history(self, integration_session, _sample_todo):
|
|
188
|
+
"""instance/getOperationHistoryList must be GET; returns a list."""
|
|
189
|
+
instance_id, _ = _sample_todo
|
|
190
|
+
result = inst.get_operation_history(integration_session, instance_id)
|
|
191
|
+
assert_list_like(result, item_type=dict)
|
|
192
|
+
|
|
193
|
+
def test_04_work_item(self, integration_session, _sample_todo):
|
|
194
|
+
"""v1/instance/getWorkItem must be GET; returns the work-item entity."""
|
|
195
|
+
instance_id, work_id = _sample_todo
|
|
196
|
+
result = inst.get_work_item(
|
|
197
|
+
integration_session, instance_id, work_id=work_id
|
|
198
|
+
)
|
|
199
|
+
# Endpoint returns a dict entity (or null for already-completed work).
|
|
200
|
+
assert result is None or isinstance(result, dict), (
|
|
201
|
+
f"work-item should be dict/None, got {type(result).__name__}"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def test_05_history_detail(self, integration_session, _sample_todo):
|
|
205
|
+
"""instance/getHistoryDetail must be GET; returns execution detail."""
|
|
206
|
+
instance_id, _ = _sample_todo
|
|
207
|
+
result = inst.get_history_detail(integration_session, instance_id)
|
|
208
|
+
assert_dict_like(result)
|
|
209
|
+
assert "works" in result or "status" in result, (
|
|
210
|
+
f"unexpected history detail shape: {list(result)!r}"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
131
214
|
# ── Delegation CRUD ────────────────────────────────────────────────────────
|
|
132
215
|
|
|
133
216
|
|