@shundoo-ai/dsh-cosmic 1.0.19 → 1.0.21
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/cordis.patch.yml +5 -5
- package/package.json +1 -1
- package/skills/cosmic-build/CHANGELOG.md +4 -0
- package/skills/cosmic-build/LOCAL-PATCHES.md +91 -40
- package/skills/cosmic-build/SKILL.md +46 -13
- package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +169 -0
- package/skills/cosmic-build/assets/platform-report-filters/platform.patch +98 -0
- package/skills/cosmic-build/assets/report-algo-benchmark/ReportAlgoBenchmark.java +48 -0
- package/skills/cosmic-build/assets/report-algo-benchmark/ReportBenchmarkLogFactory.java +7 -0
- package/skills/cosmic-build/examples/java-rules/CosmicEntryQuery.java +26 -0
- package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +35 -0
- package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +77 -0
- package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapter.java +25 -0
- package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapterCheck.java +26 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/api/algo/GroupbyDataSet.md +47 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterInfo.md +12 -12
- package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterItemInfo.md +2 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +2 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/java-type-contracts.md +11 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +18 -14
- package/skills/cosmic-build/knowledge/cosmic/platform/operation-plugin.md +4 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +17 -9
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/cross-entity-aggregate-writeback.md +2 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/pre-post-operation.md +15 -7
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +24 -68
- package/skills/cosmic-build/knowledge/cosmic/platform/workflow-plugin.md +2 -7
- package/skills/cosmic-build/references/application-context.md +35 -1
- package/skills/cosmic-build/references/common-reference.md +1 -1
- package/skills/cosmic-build/references/compile-guide.md +55 -68
- package/skills/cosmic-build/references/delivery.md +59 -7
- package/skills/cosmic-build/references/execution-contract.md +38 -0
- package/skills/cosmic-build/references/form-discovery.md +7 -1
- package/skills/cosmic-build/references/java-plugin-delivery.md +32 -162
- package/skills/cosmic-build/references/operation-dispatch.md +17 -0
- package/skills/cosmic-build/references/plugin-handoff-input.md +131 -0
- package/skills/cosmic-build/references/plugin-runtime-routing.md +1 -1
- package/skills/cosmic-build/references/report-filter-contract.md +78 -0
- package/skills/cosmic-build/references/sdk-inspection.md +23 -0
- package/skills/cosmic-build/schemas/data-model-authoring.schema.json +2 -3
- package/skills/cosmic-build/scripts/_http_client.py +55 -55
- package/skills/cosmic-build/scripts/appconfig_load.py +2 -2
- package/skills/cosmic-build/scripts/apply_report_filter_extension.py +39 -0
- package/skills/cosmic-build/scripts/bootstrap_cosmic_context.py +37 -2
- package/skills/cosmic-build/scripts/compile_plugin_source.py +279 -156
- package/skills/cosmic-build/scripts/execution_binding.py +40 -0
- package/skills/cosmic-build/scripts/inspect_platform_sdk.py +169 -0
- package/skills/cosmic-build/scripts/kd_api.py +23 -22
- package/skills/cosmic-build/scripts/kd_auth.py +1 -1
- package/skills/cosmic-build/scripts/kd_env_loader.py +19 -10
- package/skills/cosmic-build/scripts/kd_token_cache.py +2 -2
- package/skills/cosmic-build/scripts/kingscript_sdk_sync.py +350 -0
- package/skills/cosmic-build/scripts/kingscript_source_gate.py +11 -8
- package/skills/cosmic-build/scripts/platform_api_contract.py +246 -11
- package/skills/cosmic-build/scripts/plugin_corpus/build_index.py +403 -0
- package/skills/cosmic-build/scripts/plugin_corpus/jvm_classfile.py +281 -0
- package/skills/cosmic-build/scripts/plugin_corpus/measure_api_usage.py +446 -0
- package/skills/cosmic-build/scripts/plugin_source_gate.py +48 -7
- package/skills/cosmic-build/scripts/plugin_target_sdk.py +45 -0
- package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +493 -47
- package/skills/cosmic-build/scripts/preview_url.py +35 -6
- package/skills/cosmic-build/scripts/query_plugin_handoff.py +250 -0
- package/skills/cosmic-build/scripts/register_plugins.py +28 -2
- package/skills/cosmic-build/scripts/render_output_cards.py +42 -9
- package/skills/cosmic-build/scripts/report_algo_benchmark.py +46 -0
- package/skills/cosmic-build/scripts/run.py +2 -2
- package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +43 -0
- package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +64 -0
- package/skills/cosmic-build/scripts/test/fixtures/java_context_booking_native.json +541 -0
- package/skills/cosmic-build/scripts/test/test_add_rule_business_contract.py +85 -14
- package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +12 -3
- package/skills/cosmic-build/scripts/test/test_build_meta_order.py +0 -16
- package/skills/cosmic-build/scripts/test/test_cli_continuation.py +87 -0
- package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +224 -3
- package/skills/cosmic-build/scripts/test/test_control_authoring.py +123 -4
- package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +141 -0
- package/skills/cosmic-build/scripts/test/test_data_model_contract.py +30 -5
- package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +2 -10
- package/skills/cosmic-build/scripts/test/test_dym_sync_background.py +11 -0
- package/skills/cosmic-build/scripts/test/test_dynamic_form_metadata_only_contract.py +59 -1
- package/skills/cosmic-build/scripts/test/test_guide_skeleton_contract.py +87 -43
- package/skills/cosmic-build/scripts/test/test_inspect_platform_sdk.py +71 -0
- package/skills/cosmic-build/scripts/test/test_kd_api.py +67 -19
- package/skills/cosmic-build/scripts/test/test_kingscript_sdk_sync.py +221 -0
- package/skills/cosmic-build/scripts/test/test_kingscript_source_gate.py +10 -0
- package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +18 -0
- package/skills/cosmic-build/scripts/test/test_long_session_regressions.py +116 -0
- package/skills/cosmic-build/scripts/test/test_mcp_cli.py +524 -0
- package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +141 -0
- package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +36 -2
- package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +161 -1
- package/skills/cosmic-build/scripts/test/test_plugin_business_contract.py +91 -0
- package/skills/cosmic-build/scripts/test/test_plugin_corpus.py +195 -0
- package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +346 -1
- package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +70 -0
- package/skills/cosmic-build/scripts/test/test_preview_url.py +44 -1
- package/skills/cosmic-build/scripts/test/test_property_batch.py +22 -0
- package/skills/cosmic-build/scripts/test/test_property_input_discovery.py +13 -11
- package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +160 -0
- package/skills/cosmic-build/scripts/test/test_register_plugins.py +31 -1
- package/skills/cosmic-build/scripts/test/test_render_output_cards.py +50 -4
- package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -0
- package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +5 -4
- package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +36 -0
- package/skills/cosmic-build/scripts/test/test_rule_batch_evidence.py +1 -1
- package/skills/cosmic-build/scripts/test/test_rule_domain_contract.py +20 -0
- package/skills/cosmic-build/scripts/test/test_rule_function_formula.py +16 -6
- package/skills/cosmic-build/scripts/test/test_skill_entry_contract.py +1 -0
- package/skills/cosmic-build/scripts/test/test_test_environment.py +45 -0
- package/skills/cosmic-build/scripts/test/test_verify_plugin_loading.py +79 -0
- package/skills/cosmic-build/scripts/test_environment.py +49 -0
- package/skills/cosmic-build/scripts/verify_plugin_loading.py +71 -0
- package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +86 -162
- package/skills/cosmic-build/skills/cosmic-coding-script/ATTRIBUTION.md +1 -1
- package/skills/cosmic-build/skills/cosmic-coding-script/SKILL.md +45 -14
- package/skills/cosmic-build/skills/cosmic-coding-script/references/kwc-controller-authoring.md +6 -0
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/README.md +29 -19
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/docs//350/204/232/346/234/254SDK.md +3 -4
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/indexes/package-index.md +1 -1
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/strategy.md +27 -18
- package/skills/cosmic-build/skills/cosmic-meta-api/SKILL.md +11 -17
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/app_menu.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/meta_log.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/control-authoring.json +45 -4
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/form-control-traits.json +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/layout-ui.json +18 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/menu-publish.json +8 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-batch.json +7 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-input-index.json +3 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +1 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +37 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-event-contract.json +24 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/run-build-meta.json +61 -12
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/dropdown-menu.schema.json +69 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +2 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-set.schema.json +1 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-write.schema.json +14 -7
- package/skills/cosmic-build/skills/cosmic-meta-api/docs/api-summary.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/app-action/SKILL.md +6 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/develop/SKILL.md +1 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/export/SKILL.md +5 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/init/SKILL.md +13 -36
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/SKILL.md +8 -9
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/config.json.example +5 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/reset/SKILL.md +6 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/usage/SKILL.md +20 -148
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/build_receipts.py +171 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +25 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_controls.py +55 -21
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_fields.py +10 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +63 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_forms.py +59 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +8 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +36 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_plugins.py +115 -90
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +666 -4
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +1415 -87
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/commands.py +6 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +28 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +40 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +32 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_policy_contract.py +6 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +113 -17
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/reset.py +3 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/rule_contract.py +107 -26
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/build_meta_order.py +50 -49
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +856 -150
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +47 -14
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +374 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_cli_failure_contract.py +24 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_creation_identity_boundary.py +4 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_dropdown_menu_contract.py +382 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_existing_form_ui.py +0 -25
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_field_decorator.py +50 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_functional_group_resolution.py +83 -16
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_identity_template_resolution.py +103 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +28 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +22 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +65 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +1041 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +65 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +17 -0
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/api-param-checklist.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/rule-modeling-guide.md +7 -4
- package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +61 -13
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/app-menu/SKILL.md +8 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/SKILL.md +5 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/dynamic-form-artifact-schema.md +5 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +21 -9
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-entity-graphs.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-storage-constraints.md +5 -3
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-ui-composition.md +6 -3
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/creation-recovery.md +13 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/parse-core.md +3 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +6 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +135 -87
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/field_type_registry.py +11 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/isv_resolver.py +1 -3
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +29 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +4 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/context/SKILL.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/execute/SKILL.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/SKILL.md +5 -4
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/operation-write.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 +20 -5
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/complex-apis.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/policy-grammar.md +4 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +9 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +61 -4
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/raw-rule-authoring.md +3 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-actions.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-reference.md +6 -6
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/state.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/unit/SKILL.md +6 -3
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +4 -4
- package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +4 -1
- package/skills/cosmic-build/skills/cosmic-meta-ui/SKILL.md +9 -1
- package/skills/cosmic-build/skills/cosmic-meta-ui/modules/button/SKILL.md +3 -0
- package/skills/cosmic-build/skills/mcp-server/SKILL.md +210 -0
- package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +431 -0
- package/skills/cosmic-build/skills/openapi-gen/SKILL.md +3 -3
- package/skills/cosmic-build/skills/openapi-gen/scripts/api/gen_runner.py +2 -2
- package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +19 -9
- package/skills/cosmic-dev-best-practices/references/cosmic-meta-api.md +14 -11
- package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +1 -1
- package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/AssistantField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/BasedataField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/BasedataPropField.md +3 -3
- package/skills/cosmic-dev-best-practices/references/elements/CreaterField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/CurrencyField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/FlexField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/MaterielField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/OrgField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/UnitField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/UserField.md +3 -3
- package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +16 -11
- package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +5 -4
- package/skills/erp-analysis/references/delivery-contract.md +4 -2
- package/skills/erp-analysis/templates/AGENTS.template.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/.dsh/cosmic/.kd/config.json +0 -21
|
@@ -10,9 +10,150 @@ sys.path.insert(0, str(ROOT / "skills/cosmic-meta-modeling/scripts"))
|
|
|
10
10
|
sys.path.insert(0, str(ROOT / "skills/cosmic-meta-api/scripts"))
|
|
11
11
|
import data_model_emit as emitter
|
|
12
12
|
import cosmic_api as cli
|
|
13
|
+
from _core import cmd_forms
|
|
13
14
|
from _core.number_validator import validate_page_number
|
|
14
15
|
|
|
15
16
|
|
|
17
|
+
def test_emitter_resolves_template_name_before_preset_read(monkeypatch, tmp_path):
|
|
18
|
+
entity = {"type": "BillEntity", "bizName": "order", "templateKey": "单据带组织模版"}
|
|
19
|
+
section = {"artifact": {"entities": [entity]}}
|
|
20
|
+
calls = []
|
|
21
|
+
def run(_path, command, args, *, app_root):
|
|
22
|
+
calls.append((command, args))
|
|
23
|
+
rows = ([{"number": "vendor_org_template", "name": "单据带组织模板",
|
|
24
|
+
"modelType": "BillFormModel"}]
|
|
25
|
+
if command == "queryPublicTemplates" and args["modelType"] == "BillFormModel" else [])
|
|
26
|
+
return {"status": True, "data": {"success": True, "templates": rows}}
|
|
27
|
+
monkeypatch.setattr(emitter, "_run_cosmic_api", run)
|
|
28
|
+
class Logs:
|
|
29
|
+
def info(self, *args, **kwargs): pass
|
|
30
|
+
def warn(self, *args, **kwargs): pass
|
|
31
|
+
def err(self, *args, **kwargs): raise AssertionError(args)
|
|
32
|
+
assert emitter._validate_template_keys(section, Logs(), "cosmic_api.py", app_root=tmp_path)
|
|
33
|
+
assert entity["templateKey"] == "vendor_org_template"
|
|
34
|
+
assert calls == [("queryPublicTemplates", {"modelType": "BillFormModel"})]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_emitter_rejects_unknown_template_before_batch(monkeypatch, tmp_path):
|
|
38
|
+
section = {"artifact": {"entities": [{
|
|
39
|
+
"type": "BillEntity", "bizName": "order", "templateKey": "bill_template",
|
|
40
|
+
}]}}
|
|
41
|
+
class Logs:
|
|
42
|
+
errors = []
|
|
43
|
+
def info(self, *args, **kwargs): pass
|
|
44
|
+
def warn(self, *args, **kwargs): pass
|
|
45
|
+
def err(self, code, message, **kwargs): self.errors.append((code, message))
|
|
46
|
+
def run(_path, command, args, *, app_root):
|
|
47
|
+
if command == "getEntityFields":
|
|
48
|
+
return {"status": False, "data": None}
|
|
49
|
+
return {"status": True, "data": {"success": True, "templates": [
|
|
50
|
+
{"number": "bos_billtpl", "name": "单据模板", "modelType": "BillFormModel"},
|
|
51
|
+
{"number": "blankPage", "name": "空白页面", "modelType": "BillFormModel"},
|
|
52
|
+
]}}
|
|
53
|
+
monkeypatch.setattr(emitter, "_run_cosmic_api", run)
|
|
54
|
+
logs = Logs()
|
|
55
|
+
assert not emitter._validate_template_keys(section, logs, "cosmic_api.py", app_root=tmp_path)
|
|
56
|
+
assert logs.errors[0][0] == "INVALID-TEMPLATEKEY"
|
|
57
|
+
assert "bos_billtpl" in logs.errors[0][1]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_emitter_checks_exact_template_when_catalog_is_unavailable(monkeypatch, tmp_path):
|
|
61
|
+
section = {"artifact": {"entities": [{
|
|
62
|
+
"type": "BillEntity", "bizName": "order", "templateKey": "bos_billtpl",
|
|
63
|
+
}]}}
|
|
64
|
+
class Logs:
|
|
65
|
+
errors = []
|
|
66
|
+
warnings = []
|
|
67
|
+
def info(self, *args, **kwargs): pass
|
|
68
|
+
def warn(self, code, message, **kwargs): self.warnings.append(code)
|
|
69
|
+
def err(self, code, message, **kwargs): self.errors.append(code)
|
|
70
|
+
def run(_path, command, args, *, app_root):
|
|
71
|
+
if command == "getEntityFields":
|
|
72
|
+
return {"status": True, "data": {"headerFields": {
|
|
73
|
+
"headerKey": args["formNumber"], "fields": [],
|
|
74
|
+
}}}
|
|
75
|
+
return None
|
|
76
|
+
monkeypatch.setattr(emitter, "_run_cosmic_api", run)
|
|
77
|
+
logs = Logs()
|
|
78
|
+
assert emitter._validate_template_keys(section, logs, "cosmic_api.py", app_root=tmp_path)
|
|
79
|
+
assert "TEMPLATE-NOT-LISTED" in logs.warnings
|
|
80
|
+
assert logs.errors == []
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_catalog_failure_cannot_approve_unverified_template(monkeypatch, tmp_path):
|
|
84
|
+
section = {"artifact": {"entities": [{
|
|
85
|
+
"type": "BillEntity", "bizName": "order", "templateKey": "bill_template",
|
|
86
|
+
}]}}
|
|
87
|
+
class Logs:
|
|
88
|
+
errors = []
|
|
89
|
+
def info(self, *args, **kwargs): pass
|
|
90
|
+
def warn(self, *args, **kwargs): pass
|
|
91
|
+
def err(self, code, message, **kwargs): self.errors.append(code)
|
|
92
|
+
monkeypatch.setattr(emitter, "_run_cosmic_api", lambda *args, **kwargs: None)
|
|
93
|
+
logs = Logs()
|
|
94
|
+
assert not emitter._validate_template_keys(section, logs, "cosmic_api.py", app_root=tmp_path)
|
|
95
|
+
assert logs.errors == ["TEMPLATE-LOOKUP-UNAVAILABLE"]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_template_subprocess_keeps_app_binding_when_cwd_is_project_root(tmp_path, monkeypatch):
|
|
99
|
+
project = tmp_path / "project"
|
|
100
|
+
app_root = project / "app" / "demo"
|
|
101
|
+
runtime = app_root / "_runtime"
|
|
102
|
+
runtime.mkdir(parents=True)
|
|
103
|
+
(app_root / "archive.json").write_text(json.dumps({
|
|
104
|
+
"type": "cosmic", "appCode": "demo", "appId": "APP-ID",
|
|
105
|
+
}))
|
|
106
|
+
(runtime / "context.json").write_text(json.dumps({
|
|
107
|
+
"appCode": "demo", "appRoot": str(app_root), "projectRoot": str(project),
|
|
108
|
+
}))
|
|
109
|
+
# Real subprocess and path initialization, with transport isolated from the platform.
|
|
110
|
+
script = tmp_path / "template_cli.py"
|
|
111
|
+
script.write_text(
|
|
112
|
+
"import json, sys\n"
|
|
113
|
+
f"sys.path.insert(0, {str(ROOT / 'skills/cosmic-meta-api/scripts')!r})\n"
|
|
114
|
+
"from _core import config\n"
|
|
115
|
+
"config._init_paths(app_root=sys.argv[sys.argv.index('--app-root') + 1])\n"
|
|
116
|
+
"rows = [] if sys.argv[2] == 'queryBizTemplates' else "
|
|
117
|
+
"[{'number': 'bos_billtpl', 'name': 'Bill', 'modelType': 'BillFormModel'}]\n"
|
|
118
|
+
"print(json.dumps({'status': True, 'data': {'success': True, 'templates': rows}}))\n"
|
|
119
|
+
)
|
|
120
|
+
monkeypatch.chdir(project)
|
|
121
|
+
section = {"artifact": {"entities": [{
|
|
122
|
+
"type": "BillEntity", "bizName": "order", "templateKey": "bos_billtpl",
|
|
123
|
+
}]}}
|
|
124
|
+
logs = emitter._Logs(app_root)
|
|
125
|
+
assert "_section_io" not in section
|
|
126
|
+
assert emitter._validate_template_keys(section, logs, str(script), app_root=app_root)
|
|
127
|
+
assert not logs.errors
|
|
128
|
+
assert (runtime / "cosmic-metadata" / "workspace.json").is_file()
|
|
129
|
+
assert not (project / "_runtime").exists()
|
|
130
|
+
assert not (project / "metadata").exists()
|
|
131
|
+
assert not (project / "modify").exists()
|
|
132
|
+
assert not (project / "app" / "_runtime").exists()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_failed_child_query_preserves_underlying_error(tmp_path, capsys):
|
|
136
|
+
script = tmp_path / "failed_cli.py"
|
|
137
|
+
script.write_text(
|
|
138
|
+
"import json, sys\n"
|
|
139
|
+
"print(json.dumps({'status': False, 'errorCode': 'APP_ROOT_IDENTITY_INVALID'}))\n"
|
|
140
|
+
"sys.exit(2)\n"
|
|
141
|
+
)
|
|
142
|
+
assert emitter._run_cosmic_api(str(script), "queryPublicTemplates", {}, app_root=tmp_path) is None
|
|
143
|
+
assert "APP_ROOT_IDENTITY_INVALID" in capsys.readouterr().err
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_template_query_normalizes_legacy_model_type(monkeypatch):
|
|
147
|
+
calls = []
|
|
148
|
+
monkeypatch.setattr(cmd_forms, "_api_call", lambda *args, **kwargs: calls.append(args) or {
|
|
149
|
+
"status": True, "data": {"templates": []},
|
|
150
|
+
})
|
|
151
|
+
cmd_forms.do_query_public_templates({}, "token", "FormModel_Bill")
|
|
152
|
+
cmd_forms.do_query_biz_templates({}, "token", "FormModel_Base", biz_app_number="demo")
|
|
153
|
+
assert calls[0][3] == {"modelType": "BillFormModel"}
|
|
154
|
+
assert calls[1][3] == {"modelType": "BaseFormModel", "bizAppNumber": "demo"}
|
|
155
|
+
|
|
156
|
+
|
|
16
157
|
@pytest.mark.parametrize("kind,expected", [("BillEntity", "bos_billtpl"), ("BaseEntity", "bos_basetpl"), ("MainEntity", None), ("EntryEntity", None)])
|
|
17
158
|
def test_default_template_only_for_entity_roots(kind, expected):
|
|
18
159
|
data = {"artifact": {"entities": [{"type": kind}]}}
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
import builtins
|
|
5
5
|
import ast
|
|
6
6
|
import json
|
|
7
|
+
import os
|
|
7
8
|
import subprocess
|
|
8
9
|
import sys
|
|
9
10
|
from contextlib import redirect_stderr, redirect_stdout
|
|
@@ -69,7 +70,8 @@ def test_add_field_batch_validate_only_runs_without_platform_access(tmp_path):
|
|
|
69
70
|
source = tmp_path / "fields.json"
|
|
70
71
|
source.write_text(json.dumps([
|
|
71
72
|
{"name": "说明", "bizWord": "note"},
|
|
72
|
-
{"name": "来源", "bizWord": "source", "fieldType": "ComboField",
|
|
73
|
+
{"name": "来源", "bizWord": "source", "fieldType": "ComboField",
|
|
74
|
+
"comboOptions": [{"label": "线上", "value": "online"}, {"label": "电话", "value": "phone"}]},
|
|
73
75
|
]), encoding="utf-8")
|
|
74
76
|
result = subprocess.run([sys.executable, str(COSMIC_SCRIPTS / "cosmic_api.py"),
|
|
75
77
|
"--stdout", "addField", "--form-name", "missing_form", "--project-root", str(tmp_path),
|
|
@@ -561,6 +563,7 @@ def test_rule_domain_binding_rejects_two_form_selectors() -> None:
|
|
|
561
563
|
def test_rule_domain_catalog_exposes_form_name_and_optional_entity() -> None:
|
|
562
564
|
commands = (
|
|
563
565
|
"ensureFieldCalculation",
|
|
566
|
+
"ensureTimeDifferenceCalculation",
|
|
564
567
|
"ensureConditionalFieldState",
|
|
565
568
|
"ensureConditionalEntryStyle",
|
|
566
569
|
"ensureConditionalControlState",
|
|
@@ -1495,9 +1498,32 @@ def test_catalog_invoke_rejects_effects_and_runtime_override(tmp_path: Path) ->
|
|
|
1495
1498
|
raise AssertionError("unavailable action must not be invoked")
|
|
1496
1499
|
|
|
1497
1500
|
|
|
1501
|
+
@pytest.mark.parametrize("root_name", ["project", "app"])
|
|
1502
|
+
def test_project_or_app_container_cannot_be_used_as_app_root(tmp_path, root_name):
|
|
1503
|
+
root = tmp_path / root_name
|
|
1504
|
+
root.mkdir()
|
|
1505
|
+
previous = cosmic_config.RUNTIME_DIR
|
|
1506
|
+
with pytest.raises(ValueError, match="APP_ROOT_IDENTITY_INVALID"):
|
|
1507
|
+
cosmic_config._init_paths(app_root=root)
|
|
1508
|
+
assert cosmic_config.RUNTIME_DIR == previous
|
|
1509
|
+
assert list(root.iterdir()) == []
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
def test_unbound_session_does_not_create_application_artifact_directories(tmp_path, monkeypatch):
|
|
1513
|
+
monkeypatch.chdir(tmp_path)
|
|
1514
|
+
with mock.patch.dict(vars(cosmic_config)):
|
|
1515
|
+
cosmic_config._init_paths(temp_dir=str(tmp_path / "session"))
|
|
1516
|
+
assert Path(cosmic_config.WORKSPACE_JSON).exists()
|
|
1517
|
+
assert not Path(cosmic_config.ARTIFACTS_DIR).exists()
|
|
1518
|
+
assert not Path(cosmic_config.BATCH_MANIFESTS_DIR).exists()
|
|
1519
|
+
|
|
1520
|
+
|
|
1498
1521
|
def test_explicit_app_root_controls_every_runtime_path(tmp_path: Path) -> None:
|
|
1499
1522
|
app_root = tmp_path / "app" / "kdtest_demo"
|
|
1500
1523
|
app_root.mkdir(parents=True)
|
|
1524
|
+
(app_root / "archive.json").write_text(json.dumps({
|
|
1525
|
+
"type": "cosmic", "appCode": "kdtest_demo", "appId": "APP-ID",
|
|
1526
|
+
}))
|
|
1501
1527
|
names = (
|
|
1502
1528
|
"RUNTIME_DIR",
|
|
1503
1529
|
"TOKEN_CACHE",
|
|
@@ -1510,11 +1536,14 @@ def test_explicit_app_root_controls_every_runtime_path(tmp_path: Path) -> None:
|
|
|
1510
1536
|
"WORKSPACE_JSON",
|
|
1511
1537
|
)
|
|
1512
1538
|
previous = {name: getattr(cosmic_config, name) for name in names}
|
|
1539
|
+
previous_binding = getattr(cosmic_config, "_BOUND_APP_ROOT", None)
|
|
1540
|
+
previous_project_root = os.environ.get("PROJECT_ROOT")
|
|
1513
1541
|
try:
|
|
1514
|
-
|
|
1542
|
+
cosmic_api._retarget_runtime_dir(app_root)
|
|
1515
1543
|
assert Path(cosmic_config.RUNTIME_DIR) == (
|
|
1516
1544
|
app_root / "_runtime" / "cosmic-metadata"
|
|
1517
1545
|
)
|
|
1546
|
+
assert os.environ["PROJECT_ROOT"] == str(app_root.resolve())
|
|
1518
1547
|
assert Path(cosmic_config.ARTIFACTS_DIR) == app_root / "metadata"
|
|
1519
1548
|
assert Path(cosmic_config.BATCH_MANIFESTS_DIR) == app_root / "modify"
|
|
1520
1549
|
assert Path(cosmic_config._get_temp_dir()) == Path(
|
|
@@ -1522,6 +1551,11 @@ def test_explicit_app_root_controls_every_runtime_path(tmp_path: Path) -> None:
|
|
|
1522
1551
|
)
|
|
1523
1552
|
assert not (app_root / "app").exists()
|
|
1524
1553
|
finally:
|
|
1554
|
+
if previous_project_root is None:
|
|
1555
|
+
os.environ.pop("PROJECT_ROOT", None)
|
|
1556
|
+
else:
|
|
1557
|
+
os.environ["PROJECT_ROOT"] = previous_project_root
|
|
1558
|
+
cosmic_config._BOUND_APP_ROOT = previous_binding
|
|
1525
1559
|
for name, value in previous.items():
|
|
1526
1560
|
setattr(cosmic_config, name, value)
|
|
1527
1561
|
|
|
@@ -18,7 +18,79 @@ from platform_api_contract import ( # noqa: E402
|
|
|
18
18
|
_page, _qcp_constants, _scenes_verified_imports, base_class_issues,
|
|
19
19
|
filter_operator_issues, override_signature_issues, return_type_issues,
|
|
20
20
|
unverified_import_issues, unverified_member_issues,
|
|
21
|
+
describe_api, plugin_coding_evidence, scene_misuse_issues,
|
|
21
22
|
)
|
|
23
|
+
sys.path.append(str(SCRIPTS.parents[2] / "scripts" if SCRIPTS.parent.parent.name == ".locales" else SCRIPTS))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_api_lookup_distinguishes_same_named_members_by_receiver():
|
|
27
|
+
plugin = describe_api("AbstractOperationServicePlugIn", ["getDataEntities"])
|
|
28
|
+
validator = describe_api("AbstractValidator", ["getDataEntities"])
|
|
29
|
+
event = describe_api("BeforeOperationArgs", ["getDataEntities", "setCancel"])
|
|
30
|
+
assert plugin["members"][0]["deprecated"] is True
|
|
31
|
+
assert "DynamicObject[]" in plugin["members"][0]["signature"]
|
|
32
|
+
assert all(not item["deprecated"] for item in validator["members"])
|
|
33
|
+
assert any("ExtendedDataEntity[]" in item["signature"] for item in validator["members"])
|
|
34
|
+
assert {item["name"] for item in event["members"]} == {"getDataEntities", "setCancel"}
|
|
35
|
+
assert describe_api("BeforeOperationArgs", ["missingExport"])["undocumentedMembers"] == ["missingExport"]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_mistyped_platform_package_suggests_only_a_registered_candidate():
|
|
39
|
+
result = describe_api("kd.bos.service.BusinessDataServiceHelper", ["loadSingle"])
|
|
40
|
+
assert result["status"] is False
|
|
41
|
+
assert result["errorCode"] == "API_CLASS_NOT_DOCUMENTED"
|
|
42
|
+
assert result["candidateClass"] == "kd.bos.servicehelper.BusinessDataServiceHelper"
|
|
43
|
+
assert Path(result["candidateReference"]).is_file()
|
|
44
|
+
assert Path(result["sdkLookup"]["script"]).is_file()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_coding_evidence_uses_registered_type_and_exact_argument_references():
|
|
48
|
+
evidence = plugin_coding_evidence("operation")
|
|
49
|
+
assert evidence["baseContract"]["class"] == "kd.bos.entity.plugin.AbstractOperationServicePlugIn"
|
|
50
|
+
assert any("beforeExecuteOperationTransaction" in signature for signature in evidence["guideSignatures"])
|
|
51
|
+
assert any(item["class"] == "kd.bos.entity.plugin.args.BeforeOperationArgs" for item in evidence["parameterTypes"])
|
|
52
|
+
assert all(Path(item["reference"]).is_file() for item in evidence["parameterTypes"])
|
|
53
|
+
assert evidence["operationDispatch"]["transactionEvent"]["accessor"] == "event.getOperationKey()"
|
|
54
|
+
assert evidence["operationDispatch"]["validator"]["accessor"] == "this.getOperateKey()"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_validator_in_same_file_does_not_hide_operation_receiver_misuse():
|
|
58
|
+
source = '''
|
|
59
|
+
import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
|
|
60
|
+
import kd.bos.entity.validate.AbstractValidator;
|
|
61
|
+
public class SamplePlugin extends AbstractOperationServicePlugIn {
|
|
62
|
+
public void onAddValidators(AddValidatorsEventArgs e) {
|
|
63
|
+
e.addValidator(new AbstractValidator() {
|
|
64
|
+
public void validate() {
|
|
65
|
+
this.getDataEntities();
|
|
66
|
+
SamplePlugin.this.getDataEntities();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
private void inspect() { this.getDataEntities(); }
|
|
71
|
+
public void beginOperationTransaction(BeginOperationTransactionArgs e) { e.getDataEntities(); }
|
|
72
|
+
}
|
|
73
|
+
'''
|
|
74
|
+
issues = scene_misuse_issues(source)
|
|
75
|
+
assert len(issues) == 2
|
|
76
|
+
assert {source.splitlines()[issue["line"] - 1].strip() for issue in issues} == {
|
|
77
|
+
"SamplePlugin.this.getDataEntities();", "private void inspect() { this.getDataEntities(); }",
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_named_validator_and_an_unrelated_class_keep_their_own_receiver():
|
|
82
|
+
source = '''
|
|
83
|
+
import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
|
|
84
|
+
import kd.bos.entity.validate.AbstractValidator;
|
|
85
|
+
public class SamplePlugin extends kd.bos.entity.plugin.AbstractOperationServicePlugIn {
|
|
86
|
+
private void inspect() { getDataEntities(); }
|
|
87
|
+
}
|
|
88
|
+
class Validator extends AbstractValidator { public void validate() { this.getDataEntities(); } }
|
|
89
|
+
class Other { public void inspect() { this.getDataEntities(); } }
|
|
90
|
+
'''
|
|
91
|
+
issues = scene_misuse_issues(source)
|
|
92
|
+
assert len(issues) == 1
|
|
93
|
+
assert issues[0]["line"] == 5
|
|
22
94
|
|
|
23
95
|
|
|
24
96
|
def _source(body: str, imports: str = "") -> str:
|
|
@@ -207,6 +279,32 @@ def test_a_string_operator_names_the_qcp_constant_to_use():
|
|
|
207
279
|
assert "QCP.not_in" in issues[0]["message"]
|
|
208
280
|
|
|
209
281
|
|
|
282
|
+
def test_recorded_qcp_equal_typo_returns_the_verified_constant_before_javac():
|
|
283
|
+
source = _filter_source(' QFilter f = new QFilter("id", QCP.equal, id);\n').replace(
|
|
284
|
+
"import kd.bos.orm.query.QFilter;", "import kd.bos.orm.query.QFilter;\nimport kd.bos.orm.query.QCP;")
|
|
285
|
+
issues = filter_operator_issues(source)
|
|
286
|
+
assert len(issues) == 1
|
|
287
|
+
assert issues[0]["constant"] == "QCP.equals"
|
|
288
|
+
assert '"="' not in issues[0]["message"]
|
|
289
|
+
assert filter_operator_issues(source.replace("QCP.equal,", "QCP.equals,")) == []
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def test_a_project_qcp_class_is_not_judged_against_platform_constants():
|
|
293
|
+
source = _filter_source(' QFilter f = new QFilter("id", QCP.equal, id);\n').replace(
|
|
294
|
+
"import kd.bos.orm.query.QCP;\n", "")
|
|
295
|
+
assert filter_operator_issues(source) == []
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def test_pre_post_scene_provides_query_load_save_and_filter_evidence():
|
|
299
|
+
evidence = plugin_coding_evidence("operation", ["operation/custom-validator", "operation/pre-post-operation"])
|
|
300
|
+
assert Path(evidence["codingEntry"]).is_file()
|
|
301
|
+
classes = {item["class"]: item for item in evidence["apiContracts"]}
|
|
302
|
+
assert "kd.bos.servicehelper.QueryServiceHelper" in classes
|
|
303
|
+
assert any(item["name"] == "loadSingle" for item in classes["kd.bos.servicehelper.BusinessDataServiceHelper"]["members"])
|
|
304
|
+
assert any(item["name"] == "update" for item in classes["kd.bos.servicehelper.operation.SaveServiceHelper"]["members"])
|
|
305
|
+
assert next(item for item in evidence["filterOperators"] if item["literal"] == "=")["constant"] == "QCP.equals"
|
|
306
|
+
|
|
307
|
+
|
|
210
308
|
def test_an_operator_with_a_public_helper_is_told_to_use_the_helper():
|
|
211
309
|
"""`"is null"` used to be reported as QCP.is_null, an @SdkInternal constant the same page tells
|
|
212
310
|
the coding step to avoid in favour of QFilter.isNull."""
|
|
@@ -694,6 +792,38 @@ def test_a_jdk_root_member_on_a_platform_receiver_is_exempt():
|
|
|
694
792
|
)) == []
|
|
695
793
|
|
|
696
794
|
|
|
795
|
+
def test_generic_methods_on_the_pages_are_registered():
|
|
796
|
+
"""`public <T extends Control> T getControl(String key)` declares its type parameters before
|
|
797
|
+
the return type. The signature pattern used to skip such lines, so IFormView.getControl and
|
|
798
|
+
IDataModel.getContextVariable looked unregistered: describe-class sent the coding step to the
|
|
799
|
+
SDK for a documented member, and an ordinary call was reported as a remembered spelling."""
|
|
800
|
+
view = _page("kd.bos.form.IFormView")
|
|
801
|
+
model = _page("kd.bos.entity.datamodel.IDataModel")
|
|
802
|
+
|
|
803
|
+
assert ("String",) in view["signatures"]["getControl"]
|
|
804
|
+
assert "getControl" in view["genericReturns"]
|
|
805
|
+
assert ("String",) in model["signatures"]["getContextVariable"]
|
|
806
|
+
assert [item["name"] for item in describe_api("kd.bos.form.IFormView", ["getControl"])["members"]] == ["getControl"]
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
def test_ordinary_calls_to_generic_members_are_not_findings():
|
|
810
|
+
source = (
|
|
811
|
+
"package demo;\n"
|
|
812
|
+
"import kd.bos.form.control.Button;\n"
|
|
813
|
+
"import kd.bos.form.plugin.AbstractFormPlugin;\n"
|
|
814
|
+
"public class DemoPlugin extends AbstractFormPlugin {\n"
|
|
815
|
+
" @Override\n"
|
|
816
|
+
" public void registerListener(java.util.EventObject e) {\n"
|
|
817
|
+
" Button ok = this.getView().getControl(\"btnok\");\n"
|
|
818
|
+
" Object value = this.getModel().getContextVariable(\"key\");\n"
|
|
819
|
+
" }\n"
|
|
820
|
+
"}\n"
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
assert unverified_member_issues(source) == []
|
|
824
|
+
assert return_type_issues(source) == []
|
|
825
|
+
|
|
826
|
+
|
|
697
827
|
def test_a_real_service_lookup_is_not_a_truncated_name():
|
|
698
828
|
"""`getService(Class)` is IView's generic service lookup — the scenes call it — while the
|
|
699
829
|
page registers only `getServiceAppId(String)`. A pure truncation is deliberately not a
|
|
@@ -805,7 +935,7 @@ def test_the_generated_index_reports_no_drift():
|
|
|
805
935
|
import subprocess
|
|
806
936
|
|
|
807
937
|
result = subprocess.run(
|
|
808
|
-
[sys.executable, str(SCRIPTS / "gen_api_index.py"), "--check"],
|
|
938
|
+
[sys.executable, str((SCRIPTS.parents[2] / "scripts" if SCRIPTS.parent.parent.name == ".locales" else SCRIPTS) / "gen_api_index.py"), "--check"],
|
|
809
939
|
capture_output=True, text=True)
|
|
810
940
|
|
|
811
941
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
@@ -824,3 +954,33 @@ def test_the_scenes_verified_block_holds_only_pageless_imports():
|
|
|
824
954
|
assert pageless, "the exemption block is empty, so the contract is vacuous"
|
|
825
955
|
assert all(_page(fqcn) is None for fqcn in pageless)
|
|
826
956
|
assert _scenes_verified_imports() == _registered_fqcns() | pageless
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
def test_selected_scene_expands_data_service_signatures_and_real_paths():
|
|
960
|
+
evidence = plugin_coding_evidence("operation", ["operation/cross-entity-aggregate-writeback"])
|
|
961
|
+
contracts = {entry["class"]: entry for entry in evidence["apiContracts"]}
|
|
962
|
+
assert "kd.bos.servicehelper.operation.SaveServiceHelper" in contracts
|
|
963
|
+
query = contracts["kd.bos.servicehelper.QueryServiceHelper"]
|
|
964
|
+
assert any("DynamicObjectCollection" in item["signature"] for item in query["members"] if item["name"] == "query")
|
|
965
|
+
load = contracts["kd.bos.servicehelper.BusinessDataServiceHelper"]
|
|
966
|
+
assert any("Object pk, String entityName" in item["signature"] for item in load["members"] if item["name"] == "loadSingle")
|
|
967
|
+
assert all(Path(entry["reference"]).is_file() for entry in contracts.values())
|
|
968
|
+
assert all(Path(scene).is_file() for scene in evidence["scenes"])
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
def test_scene_return_types_provide_verified_imports_for_validator_entities():
|
|
972
|
+
evidence = plugin_coding_evidence("operation", ["operation/custom-validator"])
|
|
973
|
+
types = {entry["class"]: entry for entry in evidence["signatureTypes"]}
|
|
974
|
+
extended = types["kd.bos.entity.ExtendedDataEntity"]
|
|
975
|
+
assert extended["import"] == "import kd.bos.entity.ExtendedDataEntity;"
|
|
976
|
+
assert Path(extended["reference"]).is_file()
|
|
977
|
+
validator = next(entry for entry in evidence["apiContracts"] if entry["class"] == "kd.bos.entity.validate.AbstractValidator")
|
|
978
|
+
assert any("ExtendedDataEntity[]" in member["signature"] for member in validator["members"])
|
|
979
|
+
assert len(types) == len(evidence["signatureTypes"])
|
|
980
|
+
assert all(Path(entry["reference"]).is_file() for entry in types.values())
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
def test_scene_evidence_cannot_read_outside_registered_scene_tree():
|
|
984
|
+
import pytest
|
|
985
|
+
with pytest.raises(ValueError, match="unknown coding scene"):
|
|
986
|
+
plugin_coding_evidence("operation", ["../../../../references/type-registry.json"])
|
|
@@ -3,6 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import importlib
|
|
4
4
|
import json
|
|
5
5
|
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
import hashlib
|
|
6
8
|
import unittest
|
|
7
9
|
from pathlib import Path
|
|
8
10
|
|
|
@@ -12,6 +14,8 @@ API_SCRIPTS = APP_BUILD_ROOT / "skills" / "cosmic-meta-api" / "scripts"
|
|
|
12
14
|
if str(API_SCRIPTS) not in sys.path:
|
|
13
15
|
sys.path.insert(0, str(API_SCRIPTS))
|
|
14
16
|
|
|
17
|
+
sys.path.insert(0, str(APP_BUILD_ROOT / "scripts"))
|
|
18
|
+
|
|
15
19
|
cmd_plugins = importlib.import_module("_core.cmd_plugins")
|
|
16
20
|
|
|
17
21
|
|
|
@@ -100,5 +104,92 @@ class PluginBusinessContractTest(unittest.TestCase):
|
|
|
100
104
|
self.assertEqual("fpk-1", verification["fpk"])
|
|
101
105
|
|
|
102
106
|
|
|
107
|
+
class LocalJavaRegistrationTest(unittest.TestCase):
|
|
108
|
+
def check(self, source, archived_type, requested_target, *, declared_path=None):
|
|
109
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
110
|
+
file = Path(tmp, "Example.java")
|
|
111
|
+
file.write_text(source, encoding="utf-8")
|
|
112
|
+
Path(tmp, "archive.json").write_text(json.dumps({"javas": [{
|
|
113
|
+
"className": "example.Example", "type": archived_type,
|
|
114
|
+
"filePath": declared_path or str(file),
|
|
115
|
+
"sourceHash": hashlib.sha256(file.read_bytes()).hexdigest(), "compileStatus": "passed",
|
|
116
|
+
}]}), encoding="utf-8")
|
|
117
|
+
# Invalid inputs must fail before _api_call can access these absent credentials.
|
|
118
|
+
return cmd_plugins.validate_local_java_registration(tmp, {
|
|
119
|
+
"className": "example.Example", "targetType": requested_target,
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
def test_form_plugin_cannot_be_retargeted_to_save_to_bypass_registration_failure(self):
|
|
123
|
+
with self.assertRaisesRegex(ValueError, "PLUGIN_REGISTRATION_TYPE_MISMATCH"):
|
|
124
|
+
self.check("public class Example extends AbstractFormPlugin {}", "form", "OPERATION")
|
|
125
|
+
|
|
126
|
+
def test_removing_the_plugin_base_to_pass_javac_does_not_allow_registration(self):
|
|
127
|
+
with self.assertRaisesRegex(ValueError, "PLUGIN_SOURCE_BASE_CLASS_MISSING"):
|
|
128
|
+
self.check("public class Example { public void beginOperationTransaction() {} }",
|
|
129
|
+
"operation", "OPERATION")
|
|
130
|
+
|
|
131
|
+
def test_source_and_registration_target_must_agree_even_if_archive_type_was_changed(self):
|
|
132
|
+
with self.assertRaisesRegex(ValueError, "PLUGIN_SOURCE_TARGET_MISMATCH"):
|
|
133
|
+
self.check("public class Example extends AbstractFormPlugin {}", "operation", "OPERATION")
|
|
134
|
+
|
|
135
|
+
def test_direct_registration_does_not_bypass_a_missing_archive_source(self):
|
|
136
|
+
with self.assertRaisesRegex(ValueError, "PLUGIN_LOCAL_SOURCE_UNVERIFIED"):
|
|
137
|
+
self.check("public class Example extends AbstractFormPlugin {}", "form", "BILL_FORM",
|
|
138
|
+
declared_path="example/Example.java")
|
|
139
|
+
|
|
140
|
+
def test_valid_form_and_operation_sources_pass_the_local_consistency_check(self):
|
|
141
|
+
self.assertEqual(self.check("public class Example extends AbstractFormPlugin {}", "form", "BILL_FORM")["compileStatus"], "passed")
|
|
142
|
+
self.assertEqual(self.check("public class Example extends AbstractOperationServicePlugIn {}", "operation", "OPERATION")["compileStatus"], "passed")
|
|
143
|
+
|
|
144
|
+
def test_existing_deployed_class_without_local_declaration_remains_supported(self):
|
|
145
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
146
|
+
Path(tmp, "archive.json").write_text('{"javas": []}', encoding="utf-8")
|
|
147
|
+
self.assertIsNone(cmd_plugins.validate_local_java_registration(tmp, {
|
|
148
|
+
"className": "existing.Plugin", "targetType": "BILL_FORM",
|
|
149
|
+
}))
|
|
150
|
+
|
|
151
|
+
|
|
103
152
|
if __name__ == "__main__":
|
|
104
153
|
unittest.main()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_both_registration_entries_reject_manual_and_stale_archive(tmp_path, monkeypatch, capsys):
|
|
157
|
+
import hashlib
|
|
158
|
+
import register_plugins
|
|
159
|
+
source = tmp_path / "Example.java"
|
|
160
|
+
source.write_text("public class Example extends AbstractFormPlugin {}")
|
|
161
|
+
entry = {"className": "example.Example", "type": "form", "filePath": str(source),
|
|
162
|
+
"formNumber": "example", "description": "example", "operationKey": ""}
|
|
163
|
+
config = {"className": "example.Example", "targetType": "BILL_FORM"}
|
|
164
|
+
monkeypatch.setattr(sys, "argv", ["register_plugins.py", "--app-root", str(tmp_path)])
|
|
165
|
+
def no_auth(*a, **k):
|
|
166
|
+
raise AssertionError("invalid source must be rejected before credentials or platform calls")
|
|
167
|
+
monkeypatch.setattr(register_plugins, "_load_config", no_auth)
|
|
168
|
+
for expected in ["PLUGIN_SOURCE_NOT_READY", "PLUGIN_SOURCE_CHANGED"]:
|
|
169
|
+
(tmp_path / "archive.json").write_text(json.dumps({"javas": [entry]}))
|
|
170
|
+
with unittest.TestCase().assertRaisesRegex(ValueError, expected):
|
|
171
|
+
cmd_plugins.do_register_plugin({}, None, "example", config, app_root=str(tmp_path))
|
|
172
|
+
assert register_plugins.main() == 2
|
|
173
|
+
assert json.loads(capsys.readouterr().out)["errorCode"] == expected
|
|
174
|
+
entry.update(compileStatus="passed", sourceHash=hashlib.sha256(source.read_bytes()).hexdigest())
|
|
175
|
+
source.write_text(source.read_text() + "\n// changed after compilation")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_explicit_compile_skip_is_reported_as_skip_not_compile_pass(tmp_path):
|
|
179
|
+
import hashlib
|
|
180
|
+
source = tmp_path / "Example.java"
|
|
181
|
+
source.write_text("public class Example extends AbstractFormPlugin {}")
|
|
182
|
+
(tmp_path / "archive.json").write_text(json.dumps({"javas": [{
|
|
183
|
+
"className": "example.Example", "type": "form", "filePath": str(source),
|
|
184
|
+
"sourceHash": hashlib.sha256(source.read_bytes()).hexdigest(), "compileStatus": "skipped-by-config",
|
|
185
|
+
}]}))
|
|
186
|
+
result = cmd_plugins.validate_local_java_registration(str(tmp_path), {"className": "example.Example", "targetType": "BILL_FORM"})
|
|
187
|
+
assert result["compileStatus"] == "skipped-by-config"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def test_serve_registration_requires_application_context_before_platform_access():
|
|
191
|
+
import cosmic_api
|
|
192
|
+
with unittest.TestCase().assertRaisesRegex(ValueError, "SERVE_APP_ROOT_REQUIRED"):
|
|
193
|
+
cosmic_api._dispatch_serve({}, None, "registerPlugin", {
|
|
194
|
+
"formNumber": "example", "pluginConfig": {"className": "example.Plugin", "targetType": "BILL_FORM"},
|
|
195
|
+
})
|