@shundoo-ai/dsh-cosmic 1.0.21 → 1.0.22
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.
- package/package.json +1 -1
- package/skills/cosmic-build/LOCAL-PATCHES.md +43 -14
- package/skills/cosmic-build/SKILL.md +24 -9
- package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +26 -17
- package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +96 -2
- package/skills/cosmic-build/knowledge/cosmic/arch/security/security_standard.md +4 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/allimport.md +2 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/core/dynamic-object-collection.md +6 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/core/model.md +9 -9
- package/skills/cosmic-build/knowledge/cosmic/platform/core/view.md +5 -5
- package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +8 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +5 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/print-plugin-guide.md +1 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/report-plugin.md +269 -57
- package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +130 -23
- package/skills/cosmic-build/knowledge/cosmic/platform/reporttreedata-plugin.md +48 -28
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/_index.md +12 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/common/attachment-name-process.md +17 -13
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/common/dynamic-update-field.md +4 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/filter-grid/filter-grid-set-value.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-basedatafield.md +2 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-container-init.md +0 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-popup.md +0 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-value-parse.md +5 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/list-show-form.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/query-field-dynamic.md +0 -7
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/business-data/delete-service.md +24 -29
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/ksql/db-batch.md +4 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/ksql/db-direct-access.md +4 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/qfilter/entity-object-access.md +8 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/qfilter/qfilter-compose.md +6 -5
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/after-bind-data-set-visible.md +5 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/click-return-data.md +2 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/closed-call-back-set-value.md +2 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/register-listener.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/set-filter-tab-org.md +3 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/dispatch-service/microservice-dev.md +18 -11
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/dist-lock/dist-lock-basic.md +3 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/mq/mq-subscribe.md +5 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/async-operation.md +10 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/big-task-async.md +4 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/schedule-task.md +9 -7
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/operation-interactive.md +33 -34
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/operation-plugin-events.md +30 -22
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-async-query.md +133 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-batch-query.md +26 -22
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-chart.md +112 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-dataset-assembly.md +200 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-head-filter.md +177 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-hyperlink-drill.md +216 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-list-data-query.md +12 -12
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-merge-column.md +44 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-print-data.md +163 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-row-data-toolbar.md +188 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-subtotal-total.md +139 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-tree-data-query.md +108 -12
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-tree-list.md +181 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/system-service/report-plugin-interface.md +3 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-basedata-cascade-confirm.md +14 -11
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-before-item-validate.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-build-filter.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-multi-level-filter.md +11 -11
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-refresh-node.md +22 -15
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +2 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-entity-model.md +3 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-exception.md +9 -5
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-orm-access.md +4 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-request-context.md +7 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/standards.md +28 -3
- package/skills/cosmic-build/references/compile-guide.md +36 -1
- package/skills/cosmic-build/references/dcs-code-rules.md +180 -0
- package/skills/cosmic-build/references/delivery.md +16 -2
- package/skills/cosmic-build/references/execution-contract.md +15 -3
- package/skills/cosmic-build/references/java-plugin-delivery.md +21 -11
- package/skills/cosmic-build/references/plugin-handoff-input.md +82 -24
- package/skills/cosmic-build/references/plugin-runtime-routing.md +23 -6
- package/skills/cosmic-build/references/report-filter-contract.md +53 -15
- package/skills/cosmic-build/references/sdk-inspection.md +4 -1
- package/skills/cosmic-build/references/type-registry.json +26 -17
- package/skills/cosmic-build/schemas/data-model-authoring.schema.json +1 -1
- package/skills/cosmic-build/scripts/_http_client.py +3 -0
- package/skills/cosmic-build/scripts/compile_plugin_source.py +251 -42
- package/skills/cosmic-build/scripts/configure_list.py +112 -0
- package/skills/cosmic-build/scripts/dcs-rule-catalog.json +799 -0
- package/skills/cosmic-build/scripts/dcs_code_gate.py +1976 -0
- package/skills/cosmic-build/scripts/execution_binding.py +25 -0
- package/skills/cosmic-build/scripts/platform_api_contract.py +352 -7
- package/skills/cosmic-build/scripts/plugin_source_gate.py +9 -1
- package/skills/cosmic-build/scripts/plugin_target_sdk.py +12 -8
- package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +488 -88
- package/skills/cosmic-build/scripts/query_plugin_handoff.py +207 -20
- package/skills/cosmic-build/scripts/register_plugins.py +27 -10
- package/skills/cosmic-build/scripts/render_output_cards.py +57 -20
- package/skills/cosmic-build/scripts/resolve_output_path.py +60 -26
- package/skills/cosmic-build/scripts/run_java_tests.py +162 -0
- package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +28 -0
- package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +1 -22
- package/skills/cosmic-build/scripts/test/fixtures/list_column_property_declarations.json +887 -0
- package/skills/cosmic-build/scripts/test/fixtures/operation-entry-field-prepare.java +78 -0
- package/skills/cosmic-build/scripts/test/test_add_field_required_contract.py +12 -0
- package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +23 -2
- package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +287 -4
- package/skills/cosmic-build/scripts/test/test_configure_list_delivery.py +132 -0
- package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +58 -1
- package/skills/cosmic-build/scripts/test/test_data_model_contract.py +19 -0
- package/skills/cosmic-build/scripts/test/test_dcs_code_gate.py +901 -0
- package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +1 -1
- package/skills/cosmic-build/scripts/test/test_direct_cli_architecture.py +2 -0
- package/skills/cosmic-build/scripts/test/test_http_client.py +6 -0
- package/skills/cosmic-build/scripts/test/test_java_toolchain_feedback.py +206 -0
- package/skills/cosmic-build/scripts/test/test_kd_api.py +21 -0
- package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +184 -0
- package/skills/cosmic-build/scripts/test/test_mcp_cli.py +139 -2
- package/skills/cosmic-build/scripts/test/test_menu_creation_closure.py +16 -0
- package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +55 -1
- package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +1 -1
- package/skills/cosmic-build/scripts/test/test_operation_field_preparation.py +84 -0
- package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +322 -1
- package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +951 -4
- package/skills/cosmic-build/scripts/test/test_plugin_source_gate.py +30 -2
- package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +21 -0
- package/skills/cosmic-build/scripts/test/test_property_reroute.py +165 -0
- package/skills/cosmic-build/scripts/test/test_property_schema_first.py +35 -0
- package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +221 -0
- package/skills/cosmic-build/scripts/test/test_register_plugins.py +76 -4
- package/skills/cosmic-build/scripts/test/test_render_output_cards.py +75 -3
- package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -5
- package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +103 -11
- package/skills/cosmic-build/scripts/test/test_report_plugin_source_gate.py +46 -16
- package/skills/cosmic-build/scripts/test/test_report_properties.py +74 -15
- package/skills/cosmic-build/scripts/test/test_report_query_data_source.py +3 -1
- package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +1 -2
- package/skills/cosmic-build/scripts/test/test_test_environment.py +76 -1
- package/skills/cosmic-build/scripts/test_environment.py +67 -1
- package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +52 -10
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +16 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +59 -26
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +75 -18
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/pdm_auto.py +39 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/pdm_client.py +97 -10
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +10 -12
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +3 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +38 -29
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +400 -233
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +87 -45
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +42 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +135 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +17 -11
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_reroute.py +15 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +1429 -238
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/validators.py +56 -28
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +380 -134
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/meta_export.py +12 -11
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/meta_verifier.py +42 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_add_field_placement_closure.py +8 -8
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_configuration_readback.py +94 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +47 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_metadata_unchanged_fail_closed.py +87 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_dym_evidence.py +23 -23
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_entity_integrity.py +279 -4
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_field_guard.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +509 -32
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +178 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_recreate_plan.py +125 -10
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_serve_contract.py +208 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +259 -50
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_inventory_perf.py +29 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +20 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +18 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +35 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_identity_guard.py +25 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_time_range_rule.py +98 -0
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/domain-model/report-model.md +20 -9
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/report-modeling-guide.md +55 -25
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/property-guides/report-column-properties.md +15 -39
- package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +19 -17
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +82 -38
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-batch.md +64 -19
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-plugin.md +32 -20
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-tree.md +78 -31
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/report-artifact-schema.json +2 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-companion-fields.md +3 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-list-configuration.md +2 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +3 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +27 -4
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +99 -44
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/test_report_examples.py +31 -4
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/test_report_validator.py +129 -29
- package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +6 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/validation.md +2 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/SKILL.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +104 -25
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +9 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +39 -12
- package/skills/cosmic-build/skills/mcp-server/SKILL.md +39 -24
- package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +20 -6
- package/skills/cosmic-dev-best-practices/SKILL.md +1 -1
- package/skills/cosmic-dev-best-practices/references/README.md +2 -2
- package/skills/cosmic-dev-best-practices/references/basedata-dev-convention.md +2 -2
- package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +4 -4
- package/skills/cosmic-dev-best-practices/references/design-document-convention.md +2 -2
- package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +4 -4
- package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +13 -13
- package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/README.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +109 -13
- package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +1 -1
- package/skills/erp-analysis/references/delivery-contract.md +2 -1
- package/skills/erp-analysis/templates/AGENTS.template.md +3 -1
- package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +0 -169
- package/skills/cosmic-build/assets/platform-report-filters/platform.patch +0 -98
- package/skills/cosmic-build/scripts/apply_report_filter_extension.py +0 -39
package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_entity_integrity.py
CHANGED
|
@@ -12,9 +12,15 @@ as setReportFilters.
|
|
|
12
12
|
These tests pin the fail-closed guard: the CLI refuses before touching the platform.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
import copy
|
|
16
|
+
import io
|
|
17
|
+
import json
|
|
15
18
|
import os
|
|
16
19
|
import sys
|
|
20
|
+
import tempfile
|
|
21
|
+
import urllib.error
|
|
17
22
|
import unittest
|
|
23
|
+
from pathlib import Path
|
|
18
24
|
from unittest import mock
|
|
19
25
|
|
|
20
26
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
@@ -74,8 +80,6 @@ class ReportEntityGuardTest(unittest.TestCase):
|
|
|
74
80
|
for label, action in (
|
|
75
81
|
("filters", lambda: report_ops.set_report_filters(
|
|
76
82
|
{}, None, "FID", FILTERS, form_number="kdtest_rpt")),
|
|
77
|
-
("sorts", lambda: report_ops.set_report_sorts(
|
|
78
|
-
{}, None, "FID", SORTS, form_number="kdtest_rpt")),
|
|
79
83
|
("plugin", lambda: report_ops.register_report_plugin(
|
|
80
84
|
{}, None, "FID", PLUGIN, form_number="kdtest_rpt")),
|
|
81
85
|
):
|
|
@@ -83,6 +87,13 @@ class ReportEntityGuardTest(unittest.TestCase):
|
|
|
83
87
|
self.assertIsNone(error, f"{label}: {error}")
|
|
84
88
|
self.assertEqual(1, writes, label)
|
|
85
89
|
|
|
90
|
+
def test_sorts_are_refused_even_when_the_entity_is_healthy(self):
|
|
91
|
+
for fields in (["sup_number", "sup_name"], ["sup_number", "sup_number"]):
|
|
92
|
+
error, writes = self._run(fields, lambda: report_ops.set_report_sorts(
|
|
93
|
+
{}, None, "FID", SORTS, form_number="kdtest_rpt"))
|
|
94
|
+
self.assertIn("REPORT_SORTS_API_UNSAFE", str(error))
|
|
95
|
+
self.assertEqual(writes, 0)
|
|
96
|
+
|
|
86
97
|
# ── second column write: refuse before corrupting ───────────────────────
|
|
87
98
|
def test_second_column_write_is_refused_without_touching_the_platform(self):
|
|
88
99
|
error, writes = self._run(
|
|
@@ -107,6 +118,7 @@ class ReportEntityGuardTest(unittest.TestCase):
|
|
|
107
118
|
{}, None, "FID", COLUMNS, form_number="kdtest_rpt"))
|
|
108
119
|
message = str(error)
|
|
109
120
|
self.assertIn("addReportColumns", message)
|
|
121
|
+
self.assertIn("removeReportColumn", message)
|
|
110
122
|
self.assertIn("planReportRecreate", message)
|
|
111
123
|
self.assertIn("Do not add the column in the designer", message)
|
|
112
124
|
|
|
@@ -132,8 +144,6 @@ class ReportEntityGuardTest(unittest.TestCase):
|
|
|
132
144
|
{}, None, "FID", COLUMNS, form_number="kdtest_rpt")),
|
|
133
145
|
("filters", lambda: report_ops.set_report_filters(
|
|
134
146
|
{}, None, "FID", FILTERS, form_number="kdtest_rpt")),
|
|
135
|
-
("sorts", lambda: report_ops.set_report_sorts(
|
|
136
|
-
{}, None, "FID", SORTS, form_number="kdtest_rpt")),
|
|
137
147
|
("plugin", lambda: report_ops.register_report_plugin(
|
|
138
148
|
{}, None, "FID", PLUGIN, form_number="kdtest_rpt")),
|
|
139
149
|
):
|
|
@@ -282,5 +292,270 @@ class ReportWriteFailureHonestyTest(unittest.TestCase):
|
|
|
282
292
|
self.assertIn("setReportColumns failed: boom", str(ctx.exception))
|
|
283
293
|
|
|
284
294
|
|
|
295
|
+
class ReportColumnRemovalTest(unittest.TestCase):
|
|
296
|
+
"""真实删除契约和旧后端失败边界共用同一实现,不调用线上接口。"""
|
|
297
|
+
|
|
298
|
+
def column(self, key):
|
|
299
|
+
return {"kind": "column", "fieldKey": key, "dateIndex": key,
|
|
300
|
+
"fieldType": "text", "caption": key}
|
|
301
|
+
|
|
302
|
+
def schema(self, columns):
|
|
303
|
+
return {"formNumber": "kdtest_rpt", "reportColumns": columns,
|
|
304
|
+
"reportFilters": [], "reportSorts": [{"fieldKey": "keep", "direction": "asc"}],
|
|
305
|
+
"reportPlugins": [], "readBackAvailable": True, "readBackSource": "canonical"}
|
|
306
|
+
|
|
307
|
+
def receipt(self, key="drop"):
|
|
308
|
+
return {"status": True, "errorCode": "0", "data": {"success": True, "errorCode": "0",
|
|
309
|
+
"data": {"fieldKey": key, "fieldId": "FIELD", "action": "removed"}}}
|
|
310
|
+
|
|
311
|
+
def failed(self, message, code="META_SAVE_FAIL"):
|
|
312
|
+
return {"status": False, "errorCode": "META_SAVE_FAIL", "message": message,
|
|
313
|
+
"data": {"success": False, "errorCode": code, "message": message}}
|
|
314
|
+
|
|
315
|
+
def run_removal(self, root, response=None, after=None):
|
|
316
|
+
import cosmic_api
|
|
317
|
+
before = self.schema([self.column("drop"), self.column("keep")])
|
|
318
|
+
projection = after if after is not None else self.schema([self.column("keep")])
|
|
319
|
+
calls = []
|
|
320
|
+
def api(cfg, token, command, payload, method="GET"):
|
|
321
|
+
calls.append((command, payload, method))
|
|
322
|
+
if method == "POST":
|
|
323
|
+
if isinstance(response, Exception):
|
|
324
|
+
raise response
|
|
325
|
+
return response if response is not None else self.receipt()
|
|
326
|
+
if any(call[2] == "POST" for call in calls):
|
|
327
|
+
if isinstance(projection, Exception):
|
|
328
|
+
raise projection
|
|
329
|
+
return {"data": {"formNumber": "kdtest_rpt", "reportSchema": projection}}
|
|
330
|
+
return {"data": {"formNumber": "kdtest_rpt", "reportSchema": before}}
|
|
331
|
+
with mock.patch.object(report_ops, "_api_call", side_effect=api), \
|
|
332
|
+
mock.patch.object(cosmic_api, "_resolve_owned_report_target", return_value={"formId": "FID"}):
|
|
333
|
+
result = cosmic_api._dispatch_serve({}, "fixture", "removeReportColumn",
|
|
334
|
+
{"appRoot": root, "formNumber": "kdtest_rpt", "fieldKey": "drop"})
|
|
335
|
+
return result, calls
|
|
336
|
+
|
|
337
|
+
def baseline(self, root):
|
|
338
|
+
before = self.schema([self.column("drop"), self.column("keep")])
|
|
339
|
+
artifact = {"reportColumns": copy.deepcopy(before["reportColumns"]),
|
|
340
|
+
"reportSorts": [{"fieldKey": "drop", "direction": "desc"}, *before["reportSorts"]],
|
|
341
|
+
"reportFilters": [{"fieldPath": "flt_drop", "displayName": "Drop", "compare": "="}],
|
|
342
|
+
"reportPlugins": [{"className": "kd.example.report.Query", "type": "Java"}],
|
|
343
|
+
"reportProperties": {"columns": {"drop": {"Width": "80"}, "keep": {"Width": "100"}}}}
|
|
344
|
+
artifact["reportColumns"][1]["presentation"] = {"kind": "basedata", "entityId": "bos_user", "displayProp": "name"}
|
|
345
|
+
report_ops.write_report_authoring_state(root, "kdtest_rpt", "FID", artifact, before)
|
|
346
|
+
return artifact, before
|
|
347
|
+
|
|
348
|
+
def test_success_is_one_real_remove_post_with_independent_readback(self):
|
|
349
|
+
with tempfile.TemporaryDirectory() as root:
|
|
350
|
+
result, calls = self.run_removal(root)
|
|
351
|
+
self.assertTrue(result["status"])
|
|
352
|
+
self.assertTrue(result["verification"]["verified"])
|
|
353
|
+
self.assertTrue(result["data"]["platformMutationApplied"])
|
|
354
|
+
self.assertFalse(result["data"]["runtimeVerified"])
|
|
355
|
+
self.assertFalse(result["verification"]["entityFieldCleanup"]["verified"])
|
|
356
|
+
self.assertEqual(result["data"]["reportAuthoringState"]["status"], "incomplete")
|
|
357
|
+
writes = [call for call in calls if call[2] == "POST"]
|
|
358
|
+
self.assertEqual(writes, [("setComplexProperty", {"formNumber": "kdtest_rpt",
|
|
359
|
+
"complexType": "ReportColumns.remove", "data": {"fieldKey": "drop"}}, "POST")])
|
|
360
|
+
self.assertEqual(calls[-1][0], "getFormSchema")
|
|
361
|
+
|
|
362
|
+
def test_old_branch_and_http_route_preserve_complete_baseline(self):
|
|
363
|
+
from _core.execution_errors import attach_execution_error
|
|
364
|
+
responses = [self.failed("不支持的复杂属性类型: ReportColumns.remove。"),
|
|
365
|
+
self.failed("Unsupported complex property type: ReportColumns.remove."),
|
|
366
|
+
{"status": False, "errorCode": "404", "message": "cannot found openapi"},
|
|
367
|
+
*[urllib.error.HTTPError("https://example.invalid/setComplexProperty", code,
|
|
368
|
+
"Missing route", {}, io.BytesIO(b"not found")) for code in (404, 405, 501)]]
|
|
369
|
+
for response in responses:
|
|
370
|
+
with self.subTest(response=response), tempfile.TemporaryDirectory() as root:
|
|
371
|
+
self.baseline(root)
|
|
372
|
+
path = report_ops.report_authoring_path(root, "kdtest_rpt")
|
|
373
|
+
original = path.read_bytes()
|
|
374
|
+
result, calls = self.run_removal(root, response)
|
|
375
|
+
self.assertEqual(path.read_bytes(), original)
|
|
376
|
+
self.assertFalse(result["status"])
|
|
377
|
+
self.assertEqual(result["errorCode"], "REPORT_COLUMN_DELETE_API_UNAVAILABLE")
|
|
378
|
+
categorized = attach_execution_error(result)
|
|
379
|
+
self.assertEqual(categorized["errorCategory"], "capability_limit")
|
|
380
|
+
self.assertTrue(categorized["capabilityLimitConfirmed"])
|
|
381
|
+
self.assertFalse(result["data"]["platformMutationApplied"])
|
|
382
|
+
self.assertNotIn("platformMutationPossible", result["data"])
|
|
383
|
+
self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
|
|
384
|
+
self.assertNotIn("reportAuthoringState", result["data"])
|
|
385
|
+
|
|
386
|
+
def test_save_permission_and_transient_failures_are_not_capability_missing(self):
|
|
387
|
+
responses = [self.failed("save rejected"), self.failed("permission denied", "403"),
|
|
388
|
+
self.failed("NullPointerException"), {"status": False, "errorCode": "404", "message": "column not found"},
|
|
389
|
+
TimeoutError("gateway"), urllib.error.HTTPError("https://example.invalid", 503, "Unavailable", {}, None)]
|
|
390
|
+
for response in responses:
|
|
391
|
+
with self.subTest(response=response), tempfile.TemporaryDirectory() as root:
|
|
392
|
+
self.baseline(root)
|
|
393
|
+
result, calls = self.run_removal(root, response)
|
|
394
|
+
self.assertFalse(result["status"])
|
|
395
|
+
self.assertNotEqual(result["errorCode"], "REPORT_COLUMN_DELETE_API_UNAVAILABLE")
|
|
396
|
+
self.assertTrue(result["data"]["platformMutationPossible"])
|
|
397
|
+
self.assertNotIn("platformMutationApplied", result["data"])
|
|
398
|
+
state = json.loads(report_ops.report_authoring_path(root, "kdtest_rpt").read_text())
|
|
399
|
+
self.assertEqual(state["status"], "incomplete")
|
|
400
|
+
self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
|
|
401
|
+
|
|
402
|
+
def test_conflicting_wrappers_and_http_bodies_do_not_mask_failures(self):
|
|
403
|
+
unsupported = "不支持的复杂属性类型: ReportColumns.remove。"
|
|
404
|
+
conflict = {**self.failed("save rejected"), "message": unsupported}
|
|
405
|
+
success_error = self.receipt()
|
|
406
|
+
success_error["data"]["errorCode"] = "META_SAVE_FAIL"
|
|
407
|
+
broken_body = mock.Mock()
|
|
408
|
+
broken_body.read.side_effect = OSError("body unavailable")
|
|
409
|
+
responses = [conflict, success_error,
|
|
410
|
+
{**self.failed(unsupported, "403"), "message": unsupported},
|
|
411
|
+
urllib.error.HTTPError("https://example.invalid", 404, "Not Found", {},
|
|
412
|
+
io.BytesIO(json.dumps(self.failed("save rejected")).encode())),
|
|
413
|
+
urllib.error.HTTPError("https://example.invalid", 404, "Not Found", {},
|
|
414
|
+
io.BytesIO(json.dumps(self.receipt()).encode())),
|
|
415
|
+
urllib.error.HTTPError("https://example.invalid", 404, "Not Found", {}, broken_body)]
|
|
416
|
+
for response in responses:
|
|
417
|
+
with self.subTest(response=response), tempfile.TemporaryDirectory() as root:
|
|
418
|
+
result, calls = self.run_removal(root, response)
|
|
419
|
+
self.assertFalse(result["status"])
|
|
420
|
+
self.assertNotEqual(result["errorCode"], "REPORT_COLUMN_DELETE_API_UNAVAILABLE")
|
|
421
|
+
self.assertTrue(result["data"]["platformMutationPossible"])
|
|
422
|
+
self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
|
|
423
|
+
|
|
424
|
+
def test_explicit_business_rejection_is_not_capability_missing_or_mutation(self):
|
|
425
|
+
for code in ("PARAM_INVALID", "PROPERTY_ELEMENT_NOT_FOUND", "ELEMENT_NOT_FOUND"):
|
|
426
|
+
with self.subTest(code=code), tempfile.TemporaryDirectory() as root:
|
|
427
|
+
self.baseline(root)
|
|
428
|
+
with self.assertRaisesRegex(RuntimeError, "REPORT_COLUMN_DELETE_REJECTED") as error:
|
|
429
|
+
self.run_removal(root, self.failed("target rejected", code))
|
|
430
|
+
self.assertNotIsInstance(error.exception, report_ops.ReportMutationError)
|
|
431
|
+
self.assertEqual(json.loads(report_ops.report_authoring_path(root, "kdtest_rpt").read_text())["status"], "current")
|
|
432
|
+
|
|
433
|
+
def test_success_receipt_followed_by_bad_readback_invalidates_without_replay(self):
|
|
434
|
+
for after in (TimeoutError("read failed"), {}, self.schema([self.column("drop"), self.column("keep")]),
|
|
435
|
+
self.schema([self.column("other")])):
|
|
436
|
+
with self.subTest(after=after), tempfile.TemporaryDirectory() as root:
|
|
437
|
+
self.baseline(root)
|
|
438
|
+
result, calls = self.run_removal(root, after=after)
|
|
439
|
+
self.assertFalse(result["status"])
|
|
440
|
+
self.assertTrue(result["data"]["platformMutationApplied"])
|
|
441
|
+
self.assertFalse(result["verification"]["verified"])
|
|
442
|
+
self.assertEqual(result["data"]["reportAuthoringState"]["status"], "incomplete")
|
|
443
|
+
self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
|
|
444
|
+
|
|
445
|
+
def test_canonical_missing_sections_are_not_empty_state_evidence(self):
|
|
446
|
+
before = self.schema([self.column("drop"), self.column("keep")])
|
|
447
|
+
expected = report_ops.plan_report_column_removal(before, "drop")
|
|
448
|
+
for missing in ("reportColumns", "reportFilters", "reportSorts", "reportPlugins"):
|
|
449
|
+
with self.subTest(missing=missing):
|
|
450
|
+
canonical = self.schema([self.column("keep")])
|
|
451
|
+
canonical.pop(missing)
|
|
452
|
+
with mock.patch.object(report_ops, "_get_schema", return_value={
|
|
453
|
+
"data": {"formNumber": "kdtest_rpt", "reportSchema": canonical}}):
|
|
454
|
+
after = report_ops.get_report_schema({}, None, "FID")
|
|
455
|
+
self.assertIn(missing, after["unverifiableSections"])
|
|
456
|
+
result = report_ops.report_readback_verification("columnsRemove", expected, after)
|
|
457
|
+
if missing == "reportColumns":
|
|
458
|
+
self.assertFalse(result["verified"])
|
|
459
|
+
with self.assertRaisesRegex(ValueError, "REPORT_READBACK_UNAVAILABLE"):
|
|
460
|
+
report_ops.plan_report_column_removal(after, "drop")
|
|
461
|
+
else:
|
|
462
|
+
self.assertTrue(result["verified"])
|
|
463
|
+
self.assertIn(missing, result["unverifiedProperties"])
|
|
464
|
+
with tempfile.TemporaryDirectory() as root:
|
|
465
|
+
result, calls = self.run_removal(root, after={"readBackAvailable": True})
|
|
466
|
+
self.assertFalse(result["status"])
|
|
467
|
+
self.assertTrue(result["data"]["platformMutationApplied"])
|
|
468
|
+
self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
|
|
469
|
+
|
|
470
|
+
def test_wrong_receipt_is_applied_evidence_not_verified_deletion(self):
|
|
471
|
+
with tempfile.TemporaryDirectory() as root:
|
|
472
|
+
result, calls = self.run_removal(root, self.receipt("other"))
|
|
473
|
+
self.assertFalse(result["status"])
|
|
474
|
+
self.assertEqual(result["errorCode"], "REPORT_COLUMN_DELETE_RECEIPT_INVALID")
|
|
475
|
+
self.assertTrue(result["data"]["platformMutationApplied"])
|
|
476
|
+
self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
|
|
477
|
+
|
|
478
|
+
def test_nested_leaf_preserves_parent_sibling_order_and_other_properties(self):
|
|
479
|
+
before = self.schema([{"kind": "group", "fieldKey": "group", "caption": "Group", "children": [
|
|
480
|
+
{**self.column("drop"), "index": 0}, {**self.column("keep"), "index": 1, "width": "120"}]}])
|
|
481
|
+
after = copy.deepcopy(before)
|
|
482
|
+
after["reportColumns"][0]["children"].pop(0)
|
|
483
|
+
after["reportColumns"][0]["children"][0]["index"] = 0
|
|
484
|
+
expected = report_ops.plan_report_column_removal(before, "drop")
|
|
485
|
+
self.assertTrue(report_ops.report_readback_verification("columnsRemove", expected, after)["verified"])
|
|
486
|
+
after["reportColumns"][0]["children"][0]["width"] = "80"
|
|
487
|
+
self.assertFalse(report_ops.report_readback_verification("columnsRemove", expected, after)["verified"])
|
|
488
|
+
|
|
489
|
+
def test_sort_cleanup_is_verified_when_observable_and_not_invented_when_missing(self):
|
|
490
|
+
before = self.schema([self.column("drop"), self.column("keep")])
|
|
491
|
+
before["reportSorts"].append({"fieldKey": "drop", "direction": "desc"})
|
|
492
|
+
expected = report_ops.plan_report_column_removal(before, "drop")
|
|
493
|
+
after = self.schema([self.column("keep")])
|
|
494
|
+
self.assertTrue(report_ops.report_readback_verification("columnsRemove", expected, after)["verified"])
|
|
495
|
+
after["reportSorts"] = before["reportSorts"]
|
|
496
|
+
self.assertFalse(report_ops.report_readback_verification("columnsRemove", expected, after)["verified"])
|
|
497
|
+
after["unverifiableSections"] = ["reportSorts"]
|
|
498
|
+
result = report_ops.report_readback_verification("columnsRemove", expected, after)
|
|
499
|
+
self.assertTrue(result["verified"])
|
|
500
|
+
self.assertIn("reportSorts", result["unverifiedProperties"])
|
|
501
|
+
|
|
502
|
+
def test_absent_group_ambiguous_and_malformed_targets_fail_before_write(self):
|
|
503
|
+
schemas = [self.schema([self.column("keep")]), self.schema([{"kind": "group", "fieldKey": "drop", "children": []}]),
|
|
504
|
+
self.schema([self.column("drop"), self.column("drop")]), self.schema([None]), {"readBackAvailable": False},
|
|
505
|
+
{**self.schema([self.column("drop")]), "formNumber": "another_report"}]
|
|
506
|
+
for schema in schemas:
|
|
507
|
+
with self.subTest(schema=schema), mock.patch.object(report_ops, "_api_call") as api:
|
|
508
|
+
with self.assertRaises(ValueError):
|
|
509
|
+
report_ops.plan_report_column_removal(schema, "drop", form_number="kdtest_rpt")
|
|
510
|
+
api.assert_not_called()
|
|
511
|
+
|
|
512
|
+
def test_authoring_withdraws_only_deleted_column_sort_and_properties(self):
|
|
513
|
+
with tempfile.TemporaryDirectory() as root:
|
|
514
|
+
artifact, before = self.baseline(root)
|
|
515
|
+
after = self.schema([self.column("keep")])
|
|
516
|
+
result = report_ops.sync_report_authoring_state(root, "kdtest_rpt", "FID", "columnsRemove",
|
|
517
|
+
{"fieldKey": "drop", "before": before}, before, after, {"verified": True})
|
|
518
|
+
compiled = json.loads(Path(result["contractPath"]).read_text(encoding="utf-8"))
|
|
519
|
+
current = json.loads(Path(result["artifactPath"]).read_text(encoding="utf-8"))
|
|
520
|
+
self.assertEqual(compiled["dataSetColumns"], ["keep"])
|
|
521
|
+
self.assertEqual(compiled["defaultSorts"], [{"fieldKey": "keep", "dateIndex": "keep", "direction": "asc"}])
|
|
522
|
+
self.assertFalse(compiled["defaultSortPolicy"]["runtimeVerified"])
|
|
523
|
+
self.assertEqual(current["reportSorts"], [{"fieldKey": "keep", "direction": "asc"}])
|
|
524
|
+
self.assertEqual(current["reportProperties"]["columns"], {"keep": {"Width": "100"}})
|
|
525
|
+
self.assertEqual(current["reportColumns"][0]["presentation"], artifact["reportColumns"][1]["presentation"])
|
|
526
|
+
self.assertEqual(current["reportFilters"], artifact["reportFilters"])
|
|
527
|
+
self.assertEqual(current["reportPlugins"], artifact["reportPlugins"])
|
|
528
|
+
|
|
529
|
+
def test_historical_precision_dependency_still_blocks_deletion(self):
|
|
530
|
+
with tempfile.TemporaryDirectory() as root:
|
|
531
|
+
artifact, before = self.baseline(root)
|
|
532
|
+
artifact["reportColumns"][0]["fieldType"] = "bigint"
|
|
533
|
+
artifact["reportColumns"][1]["fieldType"] = "decimal"
|
|
534
|
+
artifact["reportColumns"][1]["presentation"] = {"kind": "amount", "currencyField": "drop"}
|
|
535
|
+
with self.assertRaisesRegex(ValueError, "REPORT_PRECISION_REFERENCE_UNVERIFIED"):
|
|
536
|
+
report_ops.normalize_report_columns_input(artifact["reportColumns"])
|
|
537
|
+
# 模拟门禁上线前已存在的历史夹具;不调用当前 writer 将危险输入标成新成功状态。
|
|
538
|
+
path = report_ops.report_authoring_path(root, "kdtest_rpt")
|
|
539
|
+
historical = json.loads(path.read_text(encoding="utf-8"))
|
|
540
|
+
historical["artifact"] = artifact
|
|
541
|
+
path.write_text(json.dumps(historical), encoding="utf-8")
|
|
542
|
+
original = path.read_bytes()
|
|
543
|
+
with mock.patch.object(report_ops, "_api_call") as http:
|
|
544
|
+
with self.assertRaisesRegex(ValueError, "REPORT_COLUMN_IN_USE"):
|
|
545
|
+
report_ops.plan_report_column_removal(before, "drop", root, "kdtest_rpt", "FID")
|
|
546
|
+
http.assert_not_called()
|
|
547
|
+
self.assertEqual(path.read_bytes(), original)
|
|
548
|
+
|
|
549
|
+
def test_empty_remaining_tree_or_group_cannot_become_current_artifact(self):
|
|
550
|
+
for columns in ([self.column("drop")], [{"kind": "group", "fieldKey": "g", "caption": "Group", "children": [self.column("drop")]}]):
|
|
551
|
+
with self.subTest(columns=columns), tempfile.TemporaryDirectory() as root:
|
|
552
|
+
before = self.schema(columns)
|
|
553
|
+
report_ops.write_report_authoring_state(root, "kdtest_rpt", "FID", {"reportColumns": columns}, before)
|
|
554
|
+
after = {**before, "reportColumns": report_ops._without_report_column(columns, "drop")}
|
|
555
|
+
result = report_ops.sync_report_authoring_state(root, "kdtest_rpt", "FID", "columnsRemove",
|
|
556
|
+
{"fieldKey": "drop"}, before, after, {"verified": True})
|
|
557
|
+
self.assertEqual(result["status"], "incomplete")
|
|
558
|
+
|
|
559
|
+
|
|
285
560
|
if __name__ == "__main__":
|
|
286
561
|
unittest.main(verbosity=2)
|
|
@@ -66,7 +66,7 @@ def _add_field(target, quick=None):
|
|
|
66
66
|
"fieldType": "TextField"},
|
|
67
67
|
}
|
|
68
68
|
result = cmd_formactions.do_add_field(
|
|
69
|
-
{}, None, "kdtest_ljbb_rpt", "Creator Email", "creator_email",
|
|
69
|
+
{}, None, "kdtest_ljbb_rpt", "Creator Email", "creator_email", "TextField",
|
|
70
70
|
app_root="/tmp/kdtest_guaner",
|
|
71
71
|
)
|
|
72
72
|
return result, quickfield.call_count
|