hecks 3.14.0 → 4.0.0
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.
- checksums.yaml +4 -4
- data/examples/pizzas/adapters/fake_processor.rb +34 -0
- data/examples/pizzas/adapters/payment_signature.rb +66 -0
- data/examples/pizzas/adapters/payment_webhook.rb +87 -0
- data/examples/pizzas/bluebook/pizzas.behaviors +33 -0
- data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
- data/examples/pizzas/config.ru +22 -0
- data/lib/hecks/adapters/driven/prism.rb +0 -2
- data/lib/hecks/adapters/driven.rb +0 -2
- data/lib/hecks/adapters/driving/json.rb +1 -7
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
- data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
- data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
- data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
- data/lib/hecks/bluebook/hecksagon.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
- data/lib/hecks/bluebook/meta_validator.rb +0 -11
- data/lib/hecks/bluebook/translation.rb +18 -10
- data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
- data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
- data/lib/hecks/bluebook/translation_expression.rb +39 -0
- data/lib/hecks/bluebook/translation_plan.rb +70 -0
- data/lib/hecks/bluebook.rb +2 -0
- data/lib/hecks/chapters.rb +3 -3
- data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
- data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
- data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
- data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
- data/lib/hecks/composer/adapters/jev.adapter +3 -0
- data/lib/hecks/composer/adapters/jev.rb +66 -0
- data/lib/hecks/composer/agent.rb +113 -0
- data/lib/hecks/composer/assistant.rb +110 -0
- data/lib/hecks/composer/behavior_run.rb +53 -0
- data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
- data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
- data/lib/hecks/composer/board.rb +65 -0
- data/lib/hecks/composer/board_edit/command_text.rb +39 -0
- data/lib/hecks/composer/board_edit/source.rb +84 -0
- data/lib/hecks/composer/board_edit.rb +144 -0
- data/lib/hecks/composer/check.rb +89 -0
- data/lib/hecks/composer/domains.rb +81 -0
- data/lib/hecks/composer/jobs.rb +134 -0
- data/lib/hecks/composer/layout.rb +37 -0
- data/lib/hecks/composer/live.rb +150 -0
- data/lib/hecks/composer/outline.rb +48 -0
- data/lib/hecks/composer/page.html +989 -0
- data/lib/hecks/composer/panels.rb +142 -0
- data/lib/hecks/composer/parse.rb +83 -0
- data/lib/hecks/composer/plain.rb +28 -0
- data/lib/hecks/composer/ports/composer_agent.port +7 -0
- data/lib/hecks/composer/session.rb +141 -0
- data/lib/hecks/composer/toolbox.rb +166 -0
- data/lib/hecks/composer/web.rb +156 -0
- data/lib/hecks/composer/workbench.rb +51 -0
- data/lib/hecks/composer/workspace.rb +81 -0
- data/lib/hecks/composer.rb +17 -0
- data/lib/hecks/corpus/adapters.rb +22 -0
- data/lib/hecks/corpus/rust.rb +1 -1
- data/lib/hecks/corpus/worlds.rb +19 -0
- data/lib/hecks/corpus.rb +5 -0
- data/lib/hecks/forms/app/commands.rb +2 -3
- data/lib/hecks/forms/app/queries.rb +2 -3
- data/lib/hecks/forms/app/records.rb +17 -5
- data/lib/hecks/forms/command_form_renderer.rb +42 -16
- data/lib/hecks/forms/field_controls.rb +2 -2
- data/lib/hecks/forms/field_renderer.rb +1 -1
- data/lib/hecks/forms/field_shape.rb +36 -17
- data/lib/hecks/forms/index_renderer.rb +2 -1
- data/lib/hecks/forms/page.rb +1 -1
- data/lib/hecks/forms/params.rb +0 -13
- data/lib/hecks/forms/query_form_renderer.rb +7 -6
- data/lib/hecks/forms/record_renderer.rb +11 -7
- data/lib/hecks/forms/rule_words.rb +100 -0
- data/lib/hecks/forms/value_object_shape.rb +2 -1
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
- data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
- data/lib/hecks/fuzzing/replay/filters.rb +2 -6
- data/lib/hecks/gate/stages.yml +25 -0
- data/lib/hecks/grammar/evolve/seeds.rb +0 -6
- data/lib/hecks/grammar/evolve.rb +0 -3
- data/lib/hecks/grammar/translation.bluebook +1 -1
- data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
- data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
- data/lib/hecks/hecks/codebase.behaviors +6 -0
- data/lib/hecks/hecks/codebase.bluebook +21 -0
- data/lib/hecks/hecks/composer.behaviors +68 -0
- data/lib/hecks/hecks/context_map.hecksagon +1 -1
- data/lib/hecks/hecks/custodian.behaviors +0 -6
- data/lib/hecks/hecks/custodian.bluebook +3 -80
- data/lib/hecks/hecks/hecks.hecksagon +2 -2
- data/lib/hecks/hecks/hecks.world +1 -1
- data/lib/hecks/hecks/rehearsal.bluebook +7 -13
- data/lib/hecks/kernel_capabilities/templates.rb +4 -4
- data/lib/hecks/kernel_capabilities.rb +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
- data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
- data/lib/hecks/ports/access_control.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
- data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
- data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
- data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
- data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
- data/lib/hecks/ports.rb +0 -1
- data/lib/hecks/projection_files.rb +3 -1
- data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
- data/lib/hecks/projections/glossary/entries.rb +10 -4
- data/lib/hecks/projections/glossary/groups.rb +42 -0
- data/lib/hecks/projections/glossary/html/page.rb +7 -2
- data/lib/hecks/projections/glossary/html/parser.rb +1 -1
- data/lib/hecks/projections/glossary/html/sections.rb +31 -11
- data/lib/hecks/projections/glossary/html.rb +1 -2
- data/lib/hecks/projections/glossary/markdown.rb +31 -28
- data/lib/hecks/projections/glossary/mermaid.rb +3 -0
- data/lib/hecks/projections/glossary/page.css +143 -153
- data/lib/hecks/projections/glossary/page.js +63 -3
- data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
- data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
- data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
- data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
- data/lib/hecks/projections/glossary/sentences.rb +13 -3
- data/lib/hecks/projections/glossary/slugs.rb +4 -1
- data/lib/hecks/projections/glossary.rb +2 -1
- data/lib/hecks/projections/plain_english.rb +97 -0
- data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
- data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary.rb +14 -6
- data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
- data/lib/hecks/projections/statements/actions.rb +99 -0
- data/lib/hecks/projections/statements/relationships.rb +86 -0
- data/lib/hecks/projections/statements.rb +15 -24
- data/lib/hecks/projector/exporter/adapters.rb +112 -0
- data/lib/hecks/projector/exporter/translations.rb +18 -35
- data/lib/hecks/projector/exporter/worlds.rb +103 -0
- data/lib/hecks/projector/exporter.rb +5 -1
- data/lib/hecks/quality_control/adapters/agent.rb +6 -7
- data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
- data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
- data/lib/hecks/runtime/outbox.rb +2 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
- data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
- data/lib/hecks/runtime/value.rb +13 -0
- data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
- data/lib/hecks/rust_build/wasm.rb +0 -12
- data/lib/hecks/rust_build/write_if_changed.rb +12 -12
- data/lib/hecks/rust_format.rb +77 -0
- data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
- data/lib/hecks/tools/fuzz_sweep.rb +1 -3
- data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
- data/lib/hecks/tools/translation_audit.rb +8 -10
- data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +2 -1
- data/lib/hecks.rb +3 -0
- data/prebuilt/{a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
- data/prebuilt/{1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
- data/prebuilt/{verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json → verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json} +1 -1
- data/rust/Cargo.toml +40 -0
- data/rust/build/Cargo.toml +36 -0
- data/rust/build/clippy.toml +4 -0
- data/rust/build/src/adapters_pass.rs +196 -0
- data/rust/build/src/binding_pass.rs +54 -13
- data/rust/build/src/build_artifact.rs +23 -5
- data/rust/build/src/cargo_sync.rs +69 -26
- data/rust/build/src/lineage_pass.rs +101 -87
- data/rust/build/src/main.rs +11 -2
- data/rust/build/src/pipeline.rs +80 -19
- data/rust/build/src/reserved_names.rs +6 -50
- data/rust/build/src/resolve.rs +82 -35
- data/rust/build/src/rustfmt.rs +100 -0
- data/rust/build/src/seams_pass.rs +85 -16
- data/rust/build/src/subprocess.rs +30 -6
- data/rust/build/src/tmp.rs +5 -2
- data/rust/build/src/translation_pass.rs +72 -22
- data/rust/build/src/world_pass.rs +109 -0
- data/rust/clippy.toml +4 -0
- data/rust/codegen/Cargo.toml +36 -0
- data/rust/codegen/clippy.toml +4 -0
- data/rust/codegen/src/asks.rs +40 -14
- data/rust/codegen/src/attr.rs +2 -2
- data/rust/codegen/src/bridging.rs +676 -117
- data/rust/codegen/src/commands.rs +979 -373
- data/rust/codegen/src/constraints.rs +88 -25
- data/rust/codegen/src/dependency_planning.rs +128 -34
- data/rust/codegen/src/domain_generator.rs +300 -170
- data/rust/codegen/src/exemplar.rs +87 -32
- data/rust/codegen/src/expr_emitter.rs +54 -6
- data/rust/codegen/src/fielded.rs +193 -41
- data/rust/codegen/src/hecks_naming.rs +124 -16
- data/rust/codegen/src/hecks_naming_tests.rs +81 -0
- data/rust/codegen/src/json.rs +80 -44
- data/rust/codegen/src/json_codec.rs +585 -149
- data/rust/codegen/src/literal.rs +104 -138
- data/rust/codegen/src/main.rs +145 -52
- data/rust/codegen/src/manifest.rs +76 -14
- data/rust/codegen/src/mutations.rs +1212 -239
- data/rust/codegen/src/naming.rs +111 -60
- data/rust/codegen/src/optional_pass.rs +90 -28
- data/rust/codegen/src/ports.rs +5 -4
- data/rust/codegen/src/prelude.rs +139 -49
- data/rust/codegen/src/queries.rs +601 -226
- data/rust/codegen/src/reactions.rs +268 -74
- data/rust/codegen/src/read_models.rs +606 -205
- data/rust/codegen/src/reference_specs.rs +16 -3
- data/rust/codegen/src/registry.rs +128 -79
- data/rust/codegen/src/reserved_names.rs +6 -50
- data/rust/codegen/src/shared.rs +71 -22
- data/rust/codegen/src/sidecars.rs +15 -5
- data/rust/codegen/src/skip_reason.rs +8 -2
- data/rust/codegen/src/types.rs +306 -108
- data/rust/host/Cargo.lock +1 -0
- data/rust/host/Cargo.toml +40 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/clippy.toml +4 -0
- data/rust/host/src/api.rs +698 -218
- data/rust/host/src/auth.rs +491 -214
- data/rust/host/src/b64.rs +91 -0
- data/rust/host/src/bin/expr_harness.rs +6 -2
- data/rust/host/src/bin/lineage_harness.rs +47 -11
- data/rust/host/src/bin/mint_harness.rs +70 -37
- data/rust/host/src/bin/validate_harness.rs +16 -8
- data/rust/host/src/boot.rs +144 -68
- data/rust/host/src/boundary_fuzz/api.rs +130 -32
- data/rust/host/src/boundary_fuzz/auth.rs +108 -23
- data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
- data/rust/host/src/boundary_fuzz/ir.rs +164 -12
- data/rust/host/src/boundary_fuzz/server.rs +283 -36
- data/rust/host/src/boundary_fuzz/web.rs +56 -8
- data/rust/host/src/checkout.rs +78 -32
- data/rust/host/src/clock.rs +8 -0
- data/rust/host/src/commerce_ir.rs +333 -95
- data/rust/host/src/crypto.rs +33 -0
- data/rust/host/src/dispatch.rs +990 -331
- data/rust/host/src/expr_json.rs +1021 -273
- data/rust/host/src/extension.rs +24 -4
- data/rust/host/src/field_hints.rs +11 -7
- data/rust/host/src/fuzz_support.rs +85 -13
- data/rust/host/src/host.rs +100 -0
- data/rust/host/src/http.rs +22 -0
- data/rust/host/src/ir.rs +326 -16
- data/rust/host/src/journal.rs +277 -176
- data/rust/host/src/lambda_client.rs +219 -53
- data/rust/host/src/lib.rs +16 -4
- data/rust/host/src/log.rs +62 -11
- data/rust/host/src/mint.rs +1153 -326
- data/rust/host/src/needs.rs +185 -44
- data/rust/host/src/payments/keystore.rs +197 -45
- data/rust/host/src/payments/roles.rs +39 -7
- data/rust/host/src/payments/tests.rs +1427 -282
- data/rust/host/src/payments.rs +391 -112
- data/rust/host/src/platform_signin/attack_tests.rs +5 -5
- data/rust/host/src/platform_signin/config.rs +11 -11
- data/rust/host/src/platform_signin/cookie.rs +2 -2
- data/rust/host/src/platform_signin/extension.rs +2 -1
- data/rust/host/src/platform_signin/handler.rs +9 -8
- data/rust/host/src/platform_signin/jti.rs +4 -2
- data/rust/host/src/platform_signin/jwks.rs +2 -1
- data/rust/host/src/platform_signin/jws.rs +2 -37
- data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
- data/rust/host/src/platform_signin/mode_tests.rs +5 -3
- data/rust/host/src/platform_signin/pages.rs +20 -20
- data/rust/host/src/platform_signin/session.rs +8 -1
- data/rust/host/src/platform_signin/settings.rs +6 -6
- data/rust/host/src/platform_signin/strict_json.rs +12 -9
- data/rust/host/src/platform_signin/verify.rs +41 -41
- data/rust/host/src/platform_signin/wiring_support.rs +13 -18
- data/rust/host/src/presentation.rs +207 -63
- data/rust/host/src/presentation_write.rs +502 -187
- data/rust/host/src/query_step.rs +69 -18
- data/rust/host/src/rate_limit.rs +541 -142
- data/rust/host/src/reference_transform.rs +77 -25
- data/rust/host/src/reference_validate.rs +443 -104
- data/rust/host/src/resend.rs +190 -55
- data/rust/host/src/secrets.rs +116 -21
- data/rust/host/src/server.rs +483 -104
- data/rust/host/src/storage_shape.rs +88 -21
- data/rust/host/src/test_pg.rs +17 -7
- data/rust/host/src/ui_schema.rs +495 -207
- data/rust/host/src/wasm_runner.rs +21 -6
- data/rust/host/src/web/commerce.rs +101 -23
- data/rust/host/src/web/newsletter/cooldown.rs +10 -3
- data/rust/host/src/web/newsletter/template.rs +26 -7
- data/rust/host/src/web/newsletter/tests.rs +112 -23
- data/rust/host/src/web/newsletter.rs +209 -59
- data/rust/host/src/web/newsletter_send.rs +251 -79
- data/rust/host/src/web/registration_receipt/tests.rs +16 -4
- data/rust/host/src/web/registration_receipt.rs +100 -24
- data/rust/host/src/web/registrations.rs +1384 -332
- data/rust/host/src/web/resend_webhook.rs +138 -80
- data/rust/host/src/web.rs +2416 -508
- data/rust/lsp/Cargo.toml +36 -0
- data/rust/lsp/clippy.toml +4 -0
- data/rust/lsp/src/json.rs +29 -22
- data/rust/lsp/src/main.rs +81 -31
- data/rust/lsp/src/outline.rs +23 -7
- data/rust/lsp/src/rpc.rs +17 -4
- data/rust/parser/Cargo.toml +36 -0
- data/rust/parser/clippy.toml +4 -0
- data/rust/parser/src/build/closed_sets.rs +6 -1
- data/rust/parser/src/build/identity.rs +4 -3
- data/rust/parser/src/build/mod.rs +1 -1
- data/rust/parser/src/build/named_pattern.rs +4 -1
- data/rust/parser/src/build/naming.rs +2 -80
- data/rust/parser/src/build/pattern_subset.rs +6 -8
- data/rust/parser/src/build/query_derive.rs +3 -1
- data/rust/parser/src/build/query_inference.rs +14 -5
- data/rust/parser/src/build/read_model.rs +4 -1
- data/rust/parser/src/build/references.rs +4 -3
- data/rust/parser/src/canonical.rs +34 -15
- data/rust/parser/src/emit.rs +187 -105
- data/rust/parser/src/expr/ast_json.rs +134 -42
- data/rust/parser/src/expr/evaluator.rs +27 -9
- data/rust/parser/src/expr/mod.rs +54 -13
- data/rust/parser/src/expr/resolver.rs +299 -92
- data/rust/parser/src/ir.rs +35 -3
- data/rust/parser/src/keywords.rs +7168 -516
- data/rust/parser/src/lex.rs +11 -25
- data/rust/parser/src/lib.rs +1 -0
- data/rust/parser/src/main.rs +65 -6
- data/rust/parser/src/parse/adapter.rs +157 -0
- data/rust/parser/src/parse/aggregate.rs +19 -10
- data/rust/parser/src/parse/chapter.rs +16 -17
- data/rust/parser/src/parse/command.rs +35 -16
- data/rust/parser/src/parse/domain_port.rs +4 -7
- data/rust/parser/src/parse/entity.rs +21 -11
- data/rust/parser/src/parse/hecksagon.rs +10 -4
- data/rust/parser/src/parse/lifecycle.rs +9 -5
- data/rust/parser/src/parse/mod.rs +22 -91
- data/rust/parser/src/parse/needs.rs +21 -12
- data/rust/parser/src/parse/policy.rs +2 -2
- data/rust/parser/src/parse/process_manager.rs +9 -11
- data/rust/parser/src/parse/query.rs +8 -4
- data/rust/parser/src/parse/read_model.rs +57 -16
- data/rust/parser/src/parse/value_object.rs +7 -2
- data/rust/parser/src/parse/world.rs +322 -0
- data/rust/parser/src/ruby_value.rs +7 -6
- data/rust/src/exemplar/json.rs +4 -4
- data/rust/src/exemplar/mutations.rs +18 -0
- data/rust/src/exemplar/read_models.rs +1 -10
- data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
- data/rust/src/kernel/attribute_shapes/list.rs +2 -2
- data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
- data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
- data/rust/src/kernel/cli.rs +229 -112
- data/rust/src/kernel/dispatch.rs +323 -115
- data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
- data/rust/src/kernel/expr.rs +203 -86
- data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
- data/rust/src/kernel/expression_operators/comparison.rs +87 -2
- data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
- data/rust/src/kernel/expression_operators/logical.rs +9 -3
- data/rust/src/kernel/expression_operators/membership.rs +3 -1
- data/rust/src/kernel/expression_operators/mod.rs +34 -21
- data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
- data/rust/src/kernel/expression_operators/positional.rs +84 -30
- data/rust/src/kernel/expression_operators/presence.rs +52 -13
- data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
- data/rust/src/kernel/expression_operators/sized.rs +24 -8
- data/rust/src/kernel/expression_operators/text.rs +120 -27
- data/rust/src/kernel/expression_operators/to_string.rs +10 -4
- data/rust/src/kernel/json.rs +230 -148
- data/rust/src/kernel/mod.rs +46 -33
- data/rust/src/kernel/named_query.rs +63 -15
- data/rust/src/kernel/needs.rs +112 -25
- data/rust/src/kernel/orchestrate.rs +980 -581
- data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
- data/rust/src/kernel/pattern.rs +107 -34
- data/rust/src/kernel/query_comparators.rs +118 -41
- data/rust/src/kernel/query_ordering.rs +13 -5
- data/rust/src/kernel/read_model.rs +479 -181
- data/rust/src/kernel/reference_lookup.rs +122 -26
- data/rust/src/kernel/repository.rs +293 -66
- data/rust/src/kernel/routing.rs +58 -42
- data/rust/src/kernel/ruby_float.rs +139 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
- data/rust/src/kernel/vocab/field_hint.rs +19 -25
- data/rust/src/kernel/vocab/mod.rs +6 -6
- data/rust/src/kernel/vocab/query_comparator.rs +23 -24
- data/rust/src/kernel/vocab/refusal_template.rs +501 -297
- data/rust/src/lib.rs +50 -0
- data/rust/src/main.rs +4 -1
- data/rust/web/Cargo.toml +36 -0
- data/rust/web/clippy.toml +4 -0
- metadata +82 -15
- data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
- data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
- data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
- data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
- data/lib/hecks/ports/authentication.port +0 -6
- data/lib/hecks/ports/authentication.rb +0 -67
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
- data/rust/host/src/approval.rs +0 -652
|
@@ -10,12 +10,24 @@ struct Bus {
|
|
|
10
10
|
|
|
11
11
|
impl AggregateScan for Bus {
|
|
12
12
|
fn scan(&self, aggregate: &str) -> Option<Vec<(String, Json)>> {
|
|
13
|
-
(aggregate == "D::Order").then(||
|
|
13
|
+
(aggregate == "D::Order").then(|| {
|
|
14
|
+
vec![
|
|
15
|
+
("o1".to_string(), Json::obj(vec![])),
|
|
16
|
+
("o2".to_string(), Json::obj(vec![])),
|
|
17
|
+
]
|
|
18
|
+
})
|
|
14
19
|
}
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
fn event(name: &str, aggregate: &str, id: &str, payload: Json) -> Event {
|
|
18
|
-
Event {
|
|
23
|
+
Event {
|
|
24
|
+
name: name.to_string(),
|
|
25
|
+
aggregate: aggregate.to_string(),
|
|
26
|
+
id: id.to_string(),
|
|
27
|
+
payload,
|
|
28
|
+
occurred_at: None,
|
|
29
|
+
correlation: None,
|
|
30
|
+
}
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
// `D::Xfer.<Name>` emits an event called `<Name>` correlated on `ref: x1`; a verb ending in
|
|
@@ -33,10 +45,20 @@ fn script(
|
|
|
33
45
|
return Err(Refusal::GivenNotMet("refused".to_string()));
|
|
34
46
|
}
|
|
35
47
|
if let Some(name) = verb.strip_prefix("D::Xfer.") {
|
|
36
|
-
return Ok(vec![event(
|
|
48
|
+
return Ok(vec![event(
|
|
49
|
+
name,
|
|
50
|
+
"D::Xfer",
|
|
51
|
+
"x1",
|
|
52
|
+
Json::obj(vec![("ref", Json::str("x1"))]),
|
|
53
|
+
)]);
|
|
37
54
|
}
|
|
38
55
|
match verb {
|
|
39
|
-
"D::Order.Place" => Ok(vec![event(
|
|
56
|
+
"D::Order.Place" => Ok(vec![event(
|
|
57
|
+
"Placed",
|
|
58
|
+
"D::Order",
|
|
59
|
+
"o1",
|
|
60
|
+
Json::obj(vec![("amount", Json::int(5))]),
|
|
61
|
+
)]),
|
|
40
62
|
"D::Order.Ping" => Ok(vec![event("Ping", "D::Order", "o1", Json::obj(vec![]))]),
|
|
41
63
|
_ => Ok(vec![]),
|
|
42
64
|
}
|
|
@@ -65,26 +87,64 @@ const fn policy(name: &'static str, event_name: &'static str, target: &'static s
|
|
|
65
87
|
}
|
|
66
88
|
|
|
67
89
|
const fn cross(name: &'static str, event_name: &'static str) -> CrossDomainPolicyRule {
|
|
68
|
-
CrossDomainPolicyRule {
|
|
90
|
+
CrossDomainPolicyRule {
|
|
91
|
+
policy_name: name,
|
|
92
|
+
event_name,
|
|
93
|
+
event_qualifier: None,
|
|
94
|
+
where_expr: None,
|
|
95
|
+
target_domain: "Other",
|
|
96
|
+
target_verb: "Other::Do",
|
|
97
|
+
}
|
|
69
98
|
}
|
|
70
99
|
|
|
71
100
|
static POLICIES: [PolicyRule; 8] = [
|
|
72
101
|
policy("NotifyOnPlaced", "Placed", "D::Order.Notify"),
|
|
73
|
-
PolicyRule {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
102
|
+
PolicyRule {
|
|
103
|
+
event_qualifier: Some("Other"),
|
|
104
|
+
..policy("QualWrong", "Qual", "D::Order.Wrong")
|
|
105
|
+
},
|
|
106
|
+
PolicyRule {
|
|
107
|
+
event_qualifier: Some("Order"),
|
|
108
|
+
..policy("QualRight", "Qual", "D::Order.Right")
|
|
109
|
+
},
|
|
110
|
+
PolicyRule {
|
|
111
|
+
where_expr: Some(off),
|
|
112
|
+
..policy("GuardedOff", "Guard", "D::Order.Off")
|
|
113
|
+
},
|
|
114
|
+
PolicyRule {
|
|
115
|
+
where_expr: Some(on),
|
|
116
|
+
..policy("GuardedOn", "Guard", "D::Order.On")
|
|
117
|
+
},
|
|
77
118
|
policy("Pinger", "Ping", "D::Order.Ping"),
|
|
78
|
-
PolicyRule {
|
|
79
|
-
|
|
119
|
+
PolicyRule {
|
|
120
|
+
for_each: Some("D::Order.Open"),
|
|
121
|
+
for_each_key: Some("order_id"),
|
|
122
|
+
..policy("FanPing", "Fan", "D::Order.Ping")
|
|
123
|
+
},
|
|
124
|
+
PolicyRule {
|
|
125
|
+
for_each: Some("D::Order.Missing"),
|
|
126
|
+
..policy("FanMissing", "FanMissing", "D::Order.Ping")
|
|
127
|
+
},
|
|
80
128
|
];
|
|
81
129
|
|
|
82
130
|
static CROSS_POLICIES: [CrossDomainPolicyRule; 5] = [
|
|
83
131
|
cross("XPlain", "XEvt"),
|
|
84
|
-
CrossDomainPolicyRule {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
132
|
+
CrossDomainPolicyRule {
|
|
133
|
+
event_qualifier: Some("Nope"),
|
|
134
|
+
..cross("XQualWrong", "XQual")
|
|
135
|
+
},
|
|
136
|
+
CrossDomainPolicyRule {
|
|
137
|
+
event_qualifier: Some("Order"),
|
|
138
|
+
..cross("XQualRight", "XQual")
|
|
139
|
+
},
|
|
140
|
+
CrossDomainPolicyRule {
|
|
141
|
+
where_expr: Some(off),
|
|
142
|
+
..cross("XWhereOff", "XGuard")
|
|
143
|
+
},
|
|
144
|
+
CrossDomainPolicyRule {
|
|
145
|
+
where_expr: Some(on),
|
|
146
|
+
..cross("XWhereOn", "XGuard")
|
|
147
|
+
},
|
|
88
148
|
];
|
|
89
149
|
|
|
90
150
|
static QUERIES: [crate::kernel::QueryDef; 1] = [crate::kernel::QueryDef {
|
|
@@ -98,9 +158,17 @@ static QUERIES: [crate::kernel::QueryDef; 1] = [crate::kernel::QueryDef {
|
|
|
98
158
|
authorization: None,
|
|
99
159
|
}];
|
|
100
160
|
|
|
101
|
-
static UNDO: DispatchSpec = DispatchSpec {
|
|
161
|
+
static UNDO: DispatchSpec = DispatchSpec {
|
|
162
|
+
command_name: "Ship.Undo",
|
|
163
|
+
with: &[("ref", WithValue::Ref("ref"))],
|
|
164
|
+
compensates: None,
|
|
165
|
+
};
|
|
102
166
|
|
|
103
|
-
static PING_SPEC: DispatchSpec = DispatchSpec {
|
|
167
|
+
static PING_SPEC: DispatchSpec = DispatchSpec {
|
|
168
|
+
command_name: "Order.Ping",
|
|
169
|
+
with: &[],
|
|
170
|
+
compensates: None,
|
|
171
|
+
};
|
|
104
172
|
|
|
105
173
|
static PROCESS_MANAGERS: [ProcessManagerDef; 3] = [
|
|
106
174
|
ProcessManagerDef {
|
|
@@ -113,7 +181,11 @@ static PROCESS_MANAGERS: [ProcessManagerDef; 3] = [
|
|
|
113
181
|
event_type: "Funded",
|
|
114
182
|
from_state: "start",
|
|
115
183
|
to_state: "mid",
|
|
116
|
-
dispatches: &[DispatchSpec {
|
|
184
|
+
dispatches: &[DispatchSpec {
|
|
185
|
+
command_name: "Ship.Go",
|
|
186
|
+
with: &[("ref", WithValue::Ref("ref"))],
|
|
187
|
+
compensates: Some(&UNDO),
|
|
188
|
+
}],
|
|
117
189
|
}],
|
|
118
190
|
},
|
|
119
191
|
ProcessManagerDef {
|
|
@@ -128,11 +200,24 @@ static PROCESS_MANAGERS: [ProcessManagerDef; 3] = [
|
|
|
128
200
|
from_state: "start",
|
|
129
201
|
to_state: "mid",
|
|
130
202
|
dispatches: &[
|
|
131
|
-
DispatchSpec {
|
|
132
|
-
|
|
203
|
+
DispatchSpec {
|
|
204
|
+
command_name: "Ship.Go",
|
|
205
|
+
with: &[("ref", WithValue::Ref("ref"))],
|
|
206
|
+
compensates: Some(&UNDO),
|
|
207
|
+
},
|
|
208
|
+
DispatchSpec {
|
|
209
|
+
command_name: "Ship.Refuse",
|
|
210
|
+
with: &[("ref", WithValue::Ref("ref"))],
|
|
211
|
+
compensates: None,
|
|
212
|
+
},
|
|
133
213
|
],
|
|
134
214
|
},
|
|
135
|
-
Handler {
|
|
215
|
+
Handler {
|
|
216
|
+
event_type: REFUSED,
|
|
217
|
+
from_state: "mid",
|
|
218
|
+
to_state: "undone",
|
|
219
|
+
dispatches: &[],
|
|
220
|
+
},
|
|
136
221
|
],
|
|
137
222
|
},
|
|
138
223
|
ProcessManagerDef {
|
|
@@ -145,7 +230,11 @@ static PROCESS_MANAGERS: [ProcessManagerDef; 3] = [
|
|
|
145
230
|
event_type: "PlainFunded",
|
|
146
231
|
from_state: "start",
|
|
147
232
|
to_state: "mid",
|
|
148
|
-
dispatches: &[DispatchSpec {
|
|
233
|
+
dispatches: &[DispatchSpec {
|
|
234
|
+
command_name: "Ship.Refuse",
|
|
235
|
+
with: &[("ref", WithValue::Ref("ref"))],
|
|
236
|
+
compensates: None,
|
|
237
|
+
}],
|
|
149
238
|
}],
|
|
150
239
|
},
|
|
151
240
|
];
|
|
@@ -160,7 +249,9 @@ fn identity_head(aggregate: &str) -> Option<&'static str> {
|
|
|
160
249
|
(aggregate == "D::Order").then_some("order_id")
|
|
161
250
|
}
|
|
162
251
|
fn declared(_verb: &str) -> &'static [&'static str] {
|
|
163
|
-
&[
|
|
252
|
+
&[
|
|
253
|
+
"amount", "note", "tag", "order_id", "reason", "ref", "line_id", "to", "with",
|
|
254
|
+
]
|
|
164
255
|
}
|
|
165
256
|
fn entity_head(qualified: &str) -> Option<&'static str> {
|
|
166
257
|
(qualified == "D::Order.Line").then_some("line_id")
|
|
@@ -180,6 +271,14 @@ fn tables() -> Tables<'static> {
|
|
|
180
271
|
}
|
|
181
272
|
}
|
|
182
273
|
|
|
274
|
+
fn ping_leg() -> SagaLeg<'static> {
|
|
275
|
+
SagaLeg {
|
|
276
|
+
pm: &PROCESS_MANAGERS[0],
|
|
277
|
+
correlation: "x1",
|
|
278
|
+
domain_name: "D",
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
183
282
|
struct World {
|
|
184
283
|
bus: Bus,
|
|
185
284
|
sagas: HashMap<(String, String), SagaInstance>,
|
|
@@ -224,74 +323,62 @@ impl World {
|
|
|
224
323
|
)
|
|
225
324
|
}
|
|
226
325
|
|
|
326
|
+
fn cascade(&mut self) -> Cascade<'_, Bus> {
|
|
327
|
+
Cascade {
|
|
328
|
+
store: &mut self.bus,
|
|
329
|
+
dispatch_fn: script,
|
|
330
|
+
tables: tables(),
|
|
331
|
+
sagas: &mut self.sagas,
|
|
332
|
+
occurred_at: None,
|
|
333
|
+
all_events: &mut self.all_events,
|
|
334
|
+
mutations: &mut self.mutations,
|
|
335
|
+
cross_domain: &mut self.cross_domain,
|
|
336
|
+
reaction_log: &mut self.reaction_log,
|
|
337
|
+
saga_log: &mut self.saga_log,
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
227
341
|
fn react(&mut self, event: &Event, depth: usize) {
|
|
228
|
-
react_policies(
|
|
229
|
-
&mut self.bus,
|
|
230
|
-
script,
|
|
231
|
-
tables(),
|
|
232
|
-
&mut self.sagas,
|
|
233
|
-
event,
|
|
234
|
-
None,
|
|
235
|
-
depth,
|
|
236
|
-
&mut self.all_events,
|
|
237
|
-
&mut self.mutations,
|
|
238
|
-
&mut self.cross_domain,
|
|
239
|
-
&mut self.reaction_log,
|
|
240
|
-
&mut self.saga_log,
|
|
241
|
-
);
|
|
342
|
+
self.cascade().react_policies(event, depth);
|
|
242
343
|
}
|
|
243
344
|
|
|
244
345
|
fn verbs(&self) -> Vec<&str> {
|
|
245
|
-
self.bus
|
|
346
|
+
self.bus
|
|
347
|
+
.calls
|
|
348
|
+
.iter()
|
|
349
|
+
.map(|(verb, _)| verb.as_str())
|
|
350
|
+
.collect()
|
|
246
351
|
}
|
|
247
352
|
|
|
248
353
|
fn deliver_saga(&mut self, depth: usize) -> Option<bool> {
|
|
249
|
-
deliver_saga_dispatch(
|
|
250
|
-
&
|
|
251
|
-
script,
|
|
252
|
-
tables(),
|
|
253
|
-
&mut self.sagas,
|
|
254
|
-
&PROCESS_MANAGERS[0],
|
|
354
|
+
self.cascade().deliver_saga_dispatch(
|
|
355
|
+
&ping_leg(),
|
|
255
356
|
&PING_SPEC,
|
|
256
|
-
"D",
|
|
257
357
|
&Json::obj(vec![]),
|
|
258
|
-
"x1",
|
|
259
|
-
None,
|
|
260
358
|
depth,
|
|
261
|
-
&mut self.all_events,
|
|
262
|
-
&mut self.mutations,
|
|
263
|
-
&mut self.cross_domain,
|
|
264
|
-
&mut self.reaction_log,
|
|
265
|
-
&mut self.saga_log,
|
|
266
|
-
&HashMap::new(),
|
|
267
359
|
None,
|
|
268
360
|
)
|
|
269
361
|
}
|
|
270
362
|
|
|
271
363
|
fn deliver_compensation(&mut self, depth: usize) {
|
|
272
|
-
let entry = CompletedCompensation {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
&
|
|
278
|
-
&PROCESS_MANAGERS[0],
|
|
279
|
-
&entry,
|
|
280
|
-
"D",
|
|
281
|
-
"x1",
|
|
282
|
-
None,
|
|
283
|
-
depth,
|
|
284
|
-
&mut self.all_events,
|
|
285
|
-
&mut self.mutations,
|
|
286
|
-
&mut self.cross_domain,
|
|
287
|
-
&mut self.reaction_log,
|
|
288
|
-
&mut self.saga_log,
|
|
289
|
-
);
|
|
364
|
+
let entry = CompletedCompensation {
|
|
365
|
+
command_name: "Order.Ping".to_string(),
|
|
366
|
+
args: Json::obj(vec![]),
|
|
367
|
+
};
|
|
368
|
+
self.cascade()
|
|
369
|
+
.deliver_derived_compensation(&ping_leg(), &entry, depth);
|
|
290
370
|
}
|
|
291
371
|
}
|
|
292
372
|
|
|
293
373
|
fn reaction(entry: &Json) -> (String, bool) {
|
|
294
|
-
(
|
|
374
|
+
(
|
|
375
|
+
entry
|
|
376
|
+
.get("policy")
|
|
377
|
+
.and_then(Json::as_str)
|
|
378
|
+
.unwrap()
|
|
379
|
+
.to_string(),
|
|
380
|
+
matches!(entry.get("delivered"), Some(Json::Bool(true))),
|
|
381
|
+
)
|
|
295
382
|
}
|
|
296
383
|
|
|
297
384
|
fn reason(entry: &Json) -> Option<&str> {
|
|
@@ -310,7 +397,10 @@ fn a_policy_answers_only_the_event_it_names() {
|
|
|
310
397
|
w.react(&event("Unrelated", "D::Order", "o1", Json::obj(vec![])), 0);
|
|
311
398
|
|
|
312
399
|
assert_eq!(w.verbs(), ["D::Order.Notify"]);
|
|
313
|
-
assert_eq!(
|
|
400
|
+
assert_eq!(
|
|
401
|
+
w.reaction_log.iter().map(reaction).collect::<Vec<_>>(),
|
|
402
|
+
[("NotifyOnPlaced".to_string(), true)]
|
|
403
|
+
);
|
|
314
404
|
}
|
|
315
405
|
|
|
316
406
|
#[test]
|
|
@@ -319,7 +409,11 @@ fn a_policy_with_an_event_qualifier_fires_only_for_that_emitting_aggregate() {
|
|
|
319
409
|
|
|
320
410
|
w.react(&event("Qual", "D::Order", "o1", Json::obj(vec![])), 0);
|
|
321
411
|
|
|
322
|
-
assert_eq!(
|
|
412
|
+
assert_eq!(
|
|
413
|
+
w.verbs(),
|
|
414
|
+
["D::Order.Right"],
|
|
415
|
+
"the qualifier `Other` skips, `Order` fires"
|
|
416
|
+
);
|
|
323
417
|
}
|
|
324
418
|
|
|
325
419
|
#[test]
|
|
@@ -336,8 +430,18 @@ fn where_holds_reads_the_event_payload_and_defaults_to_true_with_no_clause() {
|
|
|
336
430
|
fn go() -> Expr {
|
|
337
431
|
Expr::Lookup("go")
|
|
338
432
|
}
|
|
339
|
-
let yes = event(
|
|
340
|
-
|
|
433
|
+
let yes = event(
|
|
434
|
+
"E",
|
|
435
|
+
"D::Order",
|
|
436
|
+
"o1",
|
|
437
|
+
Json::obj(vec![("go", Json::Bool(true))]),
|
|
438
|
+
);
|
|
439
|
+
let no = event(
|
|
440
|
+
"E",
|
|
441
|
+
"D::Order",
|
|
442
|
+
"o1",
|
|
443
|
+
Json::obj(vec![("go", Json::Bool(false))]),
|
|
444
|
+
);
|
|
341
445
|
|
|
342
446
|
assert!(where_holds(None, &no));
|
|
343
447
|
assert!(where_holds(Some(go), &yes));
|
|
@@ -349,14 +453,28 @@ fn where_holds_reads_the_event_payload_and_defaults_to_true_with_no_clause() {
|
|
|
349
453
|
fn a_cross_domain_policy_is_recorded_for_the_host_never_dispatched() {
|
|
350
454
|
let mut w = world();
|
|
351
455
|
|
|
352
|
-
w.react(
|
|
456
|
+
w.react(
|
|
457
|
+
&event(
|
|
458
|
+
"XEvt",
|
|
459
|
+
"D::Order",
|
|
460
|
+
"o1",
|
|
461
|
+
Json::obj(vec![("k", Json::int(1))]),
|
|
462
|
+
),
|
|
463
|
+
0,
|
|
464
|
+
);
|
|
353
465
|
|
|
354
466
|
assert!(w.bus.calls.is_empty());
|
|
355
467
|
assert!(w.reaction_log.is_empty());
|
|
356
468
|
assert_eq!(w.cross_domain.len(), 1);
|
|
357
469
|
let pending = &w.cross_domain[0];
|
|
358
|
-
assert_eq!(
|
|
359
|
-
|
|
470
|
+
assert_eq!(
|
|
471
|
+
(pending.policy_name.as_str(), pending.event_name.as_str()),
|
|
472
|
+
("XPlain", "XEvt")
|
|
473
|
+
);
|
|
474
|
+
assert_eq!(
|
|
475
|
+
(pending.target_domain.as_str(), pending.target_verb.as_str()),
|
|
476
|
+
("Other", "Other::Do")
|
|
477
|
+
);
|
|
360
478
|
assert_eq!(pending.payload, Json::obj(vec![("k", Json::int(1))]));
|
|
361
479
|
}
|
|
362
480
|
|
|
@@ -364,11 +482,23 @@ fn a_cross_domain_policy_is_recorded_for_the_host_never_dispatched() {
|
|
|
364
482
|
fn a_cross_domain_policy_honours_its_event_qualifier_and_where_clause() {
|
|
365
483
|
let mut w = world();
|
|
366
484
|
w.react(&event("XQual", "D::Order", "o1", Json::obj(vec![])), 0);
|
|
367
|
-
assert_eq!(
|
|
485
|
+
assert_eq!(
|
|
486
|
+
w.cross_domain
|
|
487
|
+
.iter()
|
|
488
|
+
.map(|p| p.policy_name.as_str())
|
|
489
|
+
.collect::<Vec<_>>(),
|
|
490
|
+
["XQualRight"]
|
|
491
|
+
);
|
|
368
492
|
|
|
369
493
|
let mut w = world();
|
|
370
494
|
w.react(&event("XGuard", "D::Order", "o1", Json::obj(vec![])), 0);
|
|
371
|
-
assert_eq!(
|
|
495
|
+
assert_eq!(
|
|
496
|
+
w.cross_domain
|
|
497
|
+
.iter()
|
|
498
|
+
.map(|p| p.policy_name.as_str())
|
|
499
|
+
.collect::<Vec<_>>(),
|
|
500
|
+
["XWhereOn"]
|
|
501
|
+
);
|
|
372
502
|
|
|
373
503
|
let mut w = world();
|
|
374
504
|
w.react(&event("Nothing", "D::Order", "o1", Json::obj(vec![])), 0);
|
|
@@ -385,7 +515,11 @@ fn a_reaction_below_the_depth_ceiling_is_delivered_and_one_at_it_is_logged_undel
|
|
|
385
515
|
w.react(&event("Placed", "D::Order", "o1", Json::obj(vec![])), depth);
|
|
386
516
|
|
|
387
517
|
assert_eq!(w.verbs().len(), usize::from(delivers), "depth {depth}");
|
|
388
|
-
assert_eq!(
|
|
518
|
+
assert_eq!(
|
|
519
|
+
w.reaction_log.iter().map(reaction).collect::<Vec<_>>(),
|
|
520
|
+
[("NotifyOnPlaced".to_string(), delivers)],
|
|
521
|
+
"depth {depth}"
|
|
522
|
+
);
|
|
389
523
|
if !delivers {
|
|
390
524
|
assert_eq!(reason(&w.reaction_log[0]), Some(DEPTH_REACHED));
|
|
391
525
|
}
|
|
@@ -399,7 +533,11 @@ fn a_policy_that_re_triggers_itself_stops_after_five_dispatches() {
|
|
|
399
533
|
w.orchestrate("D::Order.Ping", 0).unwrap();
|
|
400
534
|
|
|
401
535
|
assert_eq!(w.verbs(), ["D::Order.Ping"; 5]);
|
|
402
|
-
let delivered: Vec<bool> = w
|
|
536
|
+
let delivered: Vec<bool> = w
|
|
537
|
+
.reaction_log
|
|
538
|
+
.iter()
|
|
539
|
+
.map(|entry| reaction(entry).1)
|
|
540
|
+
.collect();
|
|
403
541
|
// The innermost reaction is logged first: the ceiling, then the four that delivered.
|
|
404
542
|
assert_eq!(delivered, [false, true, true, true, true]);
|
|
405
543
|
assert_eq!(reason(&w.reaction_log[0]), Some(DEPTH_REACHED));
|
|
@@ -413,11 +551,20 @@ fn a_fan_out_dispatches_once_per_row_with_the_row_id_under_the_declared_key_one_
|
|
|
413
551
|
|
|
414
552
|
// Each row dispatches `Ping` at depth 4, whose own `Pinger` reaction is stopped there.
|
|
415
553
|
assert_eq!(w.verbs(), ["D::Order.Ping", "D::Order.Ping"]);
|
|
416
|
-
assert_eq!(
|
|
417
|
-
|
|
554
|
+
assert_eq!(
|
|
555
|
+
w.bus.calls[0].1,
|
|
556
|
+
Json::obj(vec![("order_id", Json::str("o1"))])
|
|
557
|
+
);
|
|
558
|
+
assert_eq!(
|
|
559
|
+
w.bus.calls[1].1,
|
|
560
|
+
Json::obj(vec![("order_id", Json::str("o2"))])
|
|
561
|
+
);
|
|
418
562
|
let log: Vec<(String, bool)> = w.reaction_log.iter().map(reaction).collect();
|
|
419
563
|
assert_eq!(log.iter().filter(|(name, _)| name == "Pinger").count(), 2);
|
|
420
|
-
assert!(w
|
|
564
|
+
assert!(w
|
|
565
|
+
.reaction_log
|
|
566
|
+
.iter()
|
|
567
|
+
.any(|entry| entry.get("for_row").and_then(Json::as_str) == Some("o2")));
|
|
421
568
|
}
|
|
422
569
|
|
|
423
570
|
#[test]
|
|
@@ -427,7 +574,10 @@ fn a_fan_out_over_a_query_nothing_declares_logs_the_missing_query() {
|
|
|
427
574
|
w.react(&event("FanMissing", "D::Order", "o1", Json::obj(vec![])), 0);
|
|
428
575
|
|
|
429
576
|
assert!(w.bus.calls.is_empty());
|
|
430
|
-
assert_eq!(
|
|
577
|
+
assert_eq!(
|
|
578
|
+
reason(&w.reaction_log[0]),
|
|
579
|
+
Some("no query D::Order.Missing")
|
|
580
|
+
);
|
|
431
581
|
}
|
|
432
582
|
|
|
433
583
|
#[test]
|
|
@@ -459,7 +609,10 @@ fn a_derived_compensation_stopped_at_the_ceiling_is_logged_failed_and_never_disp
|
|
|
459
609
|
|
|
460
610
|
assert!(w.bus.calls.is_empty());
|
|
461
611
|
assert_eq!(reason(&w.saga_log[0]), Some(DEPTH_REACHED));
|
|
462
|
-
assert_eq!(
|
|
612
|
+
assert_eq!(
|
|
613
|
+
w.saga_log[0].get("compensation_failed"),
|
|
614
|
+
Some(&Json::Bool(true))
|
|
615
|
+
);
|
|
463
616
|
}
|
|
464
617
|
|
|
465
618
|
#[test]
|
|
@@ -480,7 +633,11 @@ fn dispatch_log(w: &World) -> Vec<(String, bool, bool)> {
|
|
|
480
633
|
.iter()
|
|
481
634
|
.filter_map(|entry| {
|
|
482
635
|
let dispatch = entry.get("dispatch")?.as_str()?.to_string();
|
|
483
|
-
Some((
|
|
636
|
+
Some((
|
|
637
|
+
dispatch,
|
|
638
|
+
matches!(entry.get("delivered"), Some(Json::Bool(true))),
|
|
639
|
+
matches!(entry.get("compensation"), Some(Json::Bool(true))),
|
|
640
|
+
))
|
|
484
641
|
})
|
|
485
642
|
.collect()
|
|
486
643
|
}
|
|
@@ -496,15 +653,24 @@ fn a_saga_is_born_advances_and_ends_on_its_declared_events() {
|
|
|
496
653
|
|
|
497
654
|
w.orchestrate("D::Xfer.Funded", 0).unwrap();
|
|
498
655
|
assert_eq!(w.sagas[&key].state, "mid");
|
|
499
|
-
assert_eq!(
|
|
656
|
+
assert_eq!(
|
|
657
|
+
w.verbs(),
|
|
658
|
+
["D::Xfer.Started", "D::Xfer.Funded", "D::Ship.Go"]
|
|
659
|
+
);
|
|
500
660
|
assert_eq!(w.bus.calls[2].1, Json::obj(vec![("ref", Json::str("x1"))]));
|
|
501
661
|
let ledger = &w.sagas[&key].completed_compensations;
|
|
502
662
|
assert_eq!(ledger.len(), 1);
|
|
503
|
-
assert_eq!(
|
|
663
|
+
assert_eq!(
|
|
664
|
+
(ledger[0].command_name.as_str(), &ledger[0].args),
|
|
665
|
+
("Ship.Undo", &Json::obj(vec![("ref", Json::str("x1"))]))
|
|
666
|
+
);
|
|
504
667
|
|
|
505
668
|
w.orchestrate("D::Xfer.Ended", 0).unwrap();
|
|
506
669
|
assert!(w.sagas.is_empty());
|
|
507
|
-
assert_eq!(
|
|
670
|
+
assert_eq!(
|
|
671
|
+
w.saga_log.last().unwrap().get("ended"),
|
|
672
|
+
Some(&Json::Bool(true))
|
|
673
|
+
);
|
|
508
674
|
}
|
|
509
675
|
|
|
510
676
|
#[test]
|
|
@@ -515,14 +681,31 @@ fn a_leg_that_refuses_runs_the_refused_leg_and_fires_earlier_compensations() {
|
|
|
515
681
|
w.orchestrate("D::Xfer.RiskyStarted", 0).unwrap();
|
|
516
682
|
w.orchestrate("D::Xfer.RiskyFunded", 0).unwrap();
|
|
517
683
|
|
|
518
|
-
assert_eq!(
|
|
684
|
+
assert_eq!(
|
|
685
|
+
w.verbs(),
|
|
686
|
+
[
|
|
687
|
+
"D::Xfer.RiskyStarted",
|
|
688
|
+
"D::Xfer.RiskyFunded",
|
|
689
|
+
"D::Ship.Go",
|
|
690
|
+
"D::Ship.Refuse",
|
|
691
|
+
"D::Ship.Undo"
|
|
692
|
+
]
|
|
693
|
+
);
|
|
519
694
|
assert_eq!(w.sagas[&key].state, "undone");
|
|
520
695
|
assert!(w.sagas[&key].completed_compensations.is_empty());
|
|
521
696
|
assert_eq!(
|
|
522
697
|
dispatch_log(&w),
|
|
523
|
-
[
|
|
698
|
+
[
|
|
699
|
+
("Ship.Go".to_string(), true, false),
|
|
700
|
+
("Ship.Refuse".to_string(), false, false),
|
|
701
|
+
("Ship.Undo".to_string(), true, true)
|
|
702
|
+
]
|
|
524
703
|
);
|
|
525
|
-
assert!(w
|
|
704
|
+
assert!(w
|
|
705
|
+
.saga_log
|
|
706
|
+
.iter()
|
|
707
|
+
.any(|e| e.get("on").and_then(Json::as_str) == Some("refused")
|
|
708
|
+
&& e.get("advanced") == Some(&Json::Bool(true))));
|
|
526
709
|
}
|
|
527
710
|
|
|
528
711
|
#[test]
|
|
@@ -533,9 +716,22 @@ fn a_refusal_in_a_saga_with_no_refused_leg_compensates_nothing_and_logs_nothing(
|
|
|
533
716
|
w.orchestrate("D::Xfer.PlainStarted", 0).unwrap();
|
|
534
717
|
w.orchestrate("D::Xfer.PlainFunded", 0).unwrap();
|
|
535
718
|
|
|
536
|
-
assert_eq!(
|
|
719
|
+
assert_eq!(
|
|
720
|
+
w.verbs(),
|
|
721
|
+
[
|
|
722
|
+
"D::Xfer.PlainStarted",
|
|
723
|
+
"D::Xfer.PlainFunded",
|
|
724
|
+
"D::Ship.Refuse"
|
|
725
|
+
]
|
|
726
|
+
);
|
|
537
727
|
assert_eq!(w.sagas[&key].state, "mid");
|
|
538
|
-
assert!(
|
|
728
|
+
assert!(
|
|
729
|
+
!w.saga_log
|
|
730
|
+
.iter()
|
|
731
|
+
.any(|e| e.get("on").and_then(Json::as_str) == Some("refused")),
|
|
732
|
+
"{:?}",
|
|
733
|
+
w.saga_log
|
|
734
|
+
);
|
|
539
735
|
}
|
|
540
736
|
|
|
541
737
|
#[test]
|
|
@@ -546,13 +742,18 @@ fn a_leg_naming_an_unremembered_conversation_is_logged_not_advanced() {
|
|
|
546
742
|
|
|
547
743
|
assert_eq!(w.verbs(), ["D::Xfer.Funded"]);
|
|
548
744
|
assert_eq!(w.saga_log[0].get("advanced"), Some(&Json::Bool(false)));
|
|
549
|
-
assert!(reason(&w.saga_log[0])
|
|
745
|
+
assert!(reason(&w.saga_log[0])
|
|
746
|
+
.unwrap()
|
|
747
|
+
.contains("no conversation remembers"));
|
|
550
748
|
}
|
|
551
749
|
|
|
552
750
|
// ---- argument assembly ----
|
|
553
751
|
|
|
554
752
|
fn rule(target: &'static str, with_spec: &'static [(&'static str, &'static str)]) -> PolicyRule {
|
|
555
|
-
PolicyRule {
|
|
753
|
+
PolicyRule {
|
|
754
|
+
with_spec,
|
|
755
|
+
..policy("P", "E", target)
|
|
756
|
+
}
|
|
556
757
|
}
|
|
557
758
|
|
|
558
759
|
fn order_event(payload: Json) -> Event {
|
|
@@ -564,11 +765,24 @@ fn payload() -> Json {
|
|
|
564
765
|
}
|
|
565
766
|
|
|
566
767
|
#[test]
|
|
567
|
-
fn an_empty_with_forwards_the_payload_and_lifts_the_event_id_to_the_receiver_of_a_non_creating_same_aggregate_target(
|
|
568
|
-
|
|
768
|
+
fn an_empty_with_forwards_the_payload_and_lifts_the_event_id_to_the_receiver_of_a_non_creating_same_aggregate_target(
|
|
769
|
+
) {
|
|
770
|
+
let args = trigger_args(
|
|
771
|
+
&rule("D::Order.Close", NO_WITH),
|
|
772
|
+
&order_event(payload()),
|
|
773
|
+
None,
|
|
774
|
+
"D::Order.Close",
|
|
775
|
+
&tables(),
|
|
776
|
+
);
|
|
569
777
|
|
|
570
778
|
// The payload's own `to` is dropped; the lifted receiver replaces it.
|
|
571
|
-
assert_eq!(
|
|
779
|
+
assert_eq!(
|
|
780
|
+
args,
|
|
781
|
+
Json::obj(vec![
|
|
782
|
+
("to", Json::str("o1")),
|
|
783
|
+
("with", Json::obj(vec![("amount", Json::int(5))]))
|
|
784
|
+
])
|
|
785
|
+
);
|
|
572
786
|
}
|
|
573
787
|
|
|
574
788
|
#[test]
|
|
@@ -576,24 +790,55 @@ fn an_empty_with_forwards_the_payload_verbatim_when_nothing_is_lifted() {
|
|
|
576
790
|
let t = tables();
|
|
577
791
|
// A creating target, a different aggregate, a fan-out row's own key, and a verb with no
|
|
578
792
|
// aggregate each leave the payload as it came.
|
|
579
|
-
for (verb, extra) in [
|
|
580
|
-
|
|
793
|
+
for (verb, extra) in [
|
|
794
|
+
("D::Order.Place", None),
|
|
795
|
+
("D::Ship.Close", None),
|
|
796
|
+
("Close", None),
|
|
797
|
+
] {
|
|
798
|
+
let args = trigger_args(
|
|
799
|
+
&rule(verb, NO_WITH),
|
|
800
|
+
&order_event(payload()),
|
|
801
|
+
extra,
|
|
802
|
+
verb,
|
|
803
|
+
&t,
|
|
804
|
+
);
|
|
581
805
|
assert_eq!(args, payload(), "{verb}");
|
|
582
806
|
}
|
|
583
807
|
}
|
|
584
808
|
|
|
585
809
|
#[test]
|
|
586
810
|
fn a_fan_out_row_id_replaces_any_payload_field_of_the_same_name_and_is_never_lifted() {
|
|
587
|
-
let with_old = Json::obj(vec![
|
|
588
|
-
|
|
589
|
-
|
|
811
|
+
let with_old = Json::obj(vec![
|
|
812
|
+
("order_id", Json::str("old")),
|
|
813
|
+
("amount", Json::int(5)),
|
|
814
|
+
]);
|
|
815
|
+
|
|
816
|
+
let args = trigger_args(
|
|
817
|
+
&rule("D::Order.Close", NO_WITH),
|
|
818
|
+
&order_event(with_old),
|
|
819
|
+
Some(("order_id", "row-1")),
|
|
820
|
+
"D::Order.Close",
|
|
821
|
+
&tables(),
|
|
822
|
+
);
|
|
590
823
|
|
|
591
|
-
assert_eq!(
|
|
824
|
+
assert_eq!(
|
|
825
|
+
args,
|
|
826
|
+
Json::obj(vec![
|
|
827
|
+
("amount", Json::int(5)),
|
|
828
|
+
("order_id", Json::str("row-1"))
|
|
829
|
+
])
|
|
830
|
+
);
|
|
592
831
|
}
|
|
593
832
|
|
|
594
833
|
#[test]
|
|
595
834
|
fn a_payload_that_is_not_an_object_forwards_as_an_empty_object() {
|
|
596
|
-
let args = trigger_args(
|
|
835
|
+
let args = trigger_args(
|
|
836
|
+
&rule("D::Order.Place", NO_WITH),
|
|
837
|
+
&order_event(Json::Null),
|
|
838
|
+
None,
|
|
839
|
+
"D::Order.Place",
|
|
840
|
+
&tables(),
|
|
841
|
+
);
|
|
597
842
|
|
|
598
843
|
assert_eq!(args, Json::Object(vec![]));
|
|
599
844
|
}
|
|
@@ -604,22 +849,51 @@ fn row_record() -> Json {
|
|
|
604
849
|
|
|
605
850
|
#[test]
|
|
606
851
|
fn an_explicit_with_may_read_a_fan_out_rows_fields_but_the_payload_wins() {
|
|
607
|
-
const READ_ROW: &[(&str, &str)] =
|
|
852
|
+
const READ_ROW: &[(&str, &str)] =
|
|
853
|
+
&[("note", ":charged"), ("amount", ":amount"), ("tag", ":id")];
|
|
608
854
|
let record = row_record();
|
|
609
855
|
|
|
610
|
-
let args = trigger_args_with_row(
|
|
856
|
+
let args = trigger_args_with_row(
|
|
857
|
+
&rule("D::Order.Place", READ_ROW),
|
|
858
|
+
&order_event(payload()),
|
|
859
|
+
None,
|
|
860
|
+
Some(("r1", &record)),
|
|
861
|
+
"D::Order.Place",
|
|
862
|
+
&tables(),
|
|
863
|
+
);
|
|
611
864
|
|
|
612
|
-
assert_eq!(
|
|
865
|
+
assert_eq!(
|
|
866
|
+
args,
|
|
867
|
+
Json::obj(vec![
|
|
868
|
+
("note", Json::int(40)),
|
|
869
|
+
("amount", Json::int(5)),
|
|
870
|
+
("tag", Json::str("r1"))
|
|
871
|
+
])
|
|
872
|
+
);
|
|
613
873
|
}
|
|
614
874
|
|
|
615
875
|
#[test]
|
|
616
876
|
fn an_undeclared_with_never_offers_a_fan_out_rows_fields() {
|
|
617
877
|
let record = row_record();
|
|
618
|
-
let extra = Some(("order_id", "r1"
|
|
619
|
-
|
|
620
|
-
let args = trigger_args_with_row(
|
|
878
|
+
let extra = Some(("order_id", "r1"));
|
|
879
|
+
|
|
880
|
+
let args = trigger_args_with_row(
|
|
881
|
+
&rule("D::Order.Place", NO_WITH),
|
|
882
|
+
&order_event(payload()),
|
|
883
|
+
extra,
|
|
884
|
+
Some(("r1", &record)),
|
|
885
|
+
"D::Order.Place",
|
|
886
|
+
&tables(),
|
|
887
|
+
);
|
|
621
888
|
|
|
622
|
-
assert_eq!(
|
|
889
|
+
assert_eq!(
|
|
890
|
+
args,
|
|
891
|
+
Json::obj(vec![
|
|
892
|
+
("amount", Json::int(5)),
|
|
893
|
+
("to", Json::str("zzz")),
|
|
894
|
+
("order_id", Json::str("r1"))
|
|
895
|
+
])
|
|
896
|
+
);
|
|
623
897
|
}
|
|
624
898
|
|
|
625
899
|
const READS_ORDER_ID: crate::kernel::QueryDef = crate::kernel::QueryDef {
|
|
@@ -646,7 +920,11 @@ fn a_for_each_query_is_lent_the_emitting_records_id_under_its_identity_head() {
|
|
|
646
920
|
|
|
647
921
|
#[test]
|
|
648
922
|
fn a_for_each_query_keeps_a_payload_value_over_the_lent_identity() {
|
|
649
|
-
let args = for_each_query_args(
|
|
923
|
+
let args = for_each_query_args(
|
|
924
|
+
&READS_ORDER_ID,
|
|
925
|
+
&order_event(Json::obj(vec![("order_id", Json::str("mine"))])),
|
|
926
|
+
&tables(),
|
|
927
|
+
);
|
|
650
928
|
|
|
651
929
|
assert_eq!(args, Json::obj(vec![("order_id", Json::str("mine"))]));
|
|
652
930
|
}
|
|
@@ -660,18 +938,39 @@ fn a_for_each_query_that_does_not_read_the_head_is_lent_nothing() {
|
|
|
660
938
|
|
|
661
939
|
#[test]
|
|
662
940
|
fn a_for_each_query_is_lent_nothing_for_an_aggregate_without_a_single_head() {
|
|
663
|
-
let args = for_each_query_args(
|
|
941
|
+
let args = for_each_query_args(
|
|
942
|
+
&READS_ORDER_ID,
|
|
943
|
+
&event("E", "D::Other", "x1", Json::obj(vec![])),
|
|
944
|
+
&tables(),
|
|
945
|
+
);
|
|
664
946
|
|
|
665
947
|
assert_eq!(args, Json::obj(vec![]));
|
|
666
948
|
}
|
|
667
949
|
|
|
668
|
-
const PROJECT: &[(&str, &str)] = &[
|
|
950
|
+
const PROJECT: &[(&str, &str)] = &[
|
|
951
|
+
("amount", ":amount"),
|
|
952
|
+
("note", "\"hi\""),
|
|
953
|
+
("tag", ":absent"),
|
|
954
|
+
];
|
|
669
955
|
|
|
670
956
|
#[test]
|
|
671
957
|
fn an_explicit_with_projects_source_fields_decodes_literals_and_nulls_what_is_absent() {
|
|
672
|
-
let args = trigger_args(
|
|
958
|
+
let args = trigger_args(
|
|
959
|
+
&rule("D::Order.Place", PROJECT),
|
|
960
|
+
&order_event(payload()),
|
|
961
|
+
None,
|
|
962
|
+
"D::Order.Place",
|
|
963
|
+
&tables(),
|
|
964
|
+
);
|
|
673
965
|
|
|
674
|
-
assert_eq!(
|
|
966
|
+
assert_eq!(
|
|
967
|
+
args,
|
|
968
|
+
Json::obj(vec![
|
|
969
|
+
("amount", Json::int(5)),
|
|
970
|
+
("note", Json::str("hi")),
|
|
971
|
+
("tag", Json::Null)
|
|
972
|
+
])
|
|
973
|
+
);
|
|
675
974
|
}
|
|
676
975
|
|
|
677
976
|
#[test]
|
|
@@ -679,39 +978,97 @@ fn an_explicit_with_may_read_the_emitting_records_identity_unless_the_payload_ca
|
|
|
679
978
|
const READ_ID: &[(&str, &str)] = &[("order_id", ":order_id")];
|
|
680
979
|
let t = tables();
|
|
681
980
|
|
|
682
|
-
let from_event = trigger_args(
|
|
981
|
+
let from_event = trigger_args(
|
|
982
|
+
&rule("D::Order.Place", READ_ID),
|
|
983
|
+
&order_event(payload()),
|
|
984
|
+
None,
|
|
985
|
+
"D::Order.Place",
|
|
986
|
+
&t,
|
|
987
|
+
);
|
|
683
988
|
assert_eq!(from_event, Json::obj(vec![("order_id", Json::str("o1"))]));
|
|
684
989
|
|
|
685
990
|
let carried = Json::obj(vec![("order_id", Json::str("p9"))]);
|
|
686
|
-
let from_payload = trigger_args(
|
|
991
|
+
let from_payload = trigger_args(
|
|
992
|
+
&rule("D::Order.Place", READ_ID),
|
|
993
|
+
&order_event(carried),
|
|
994
|
+
None,
|
|
995
|
+
"D::Order.Place",
|
|
996
|
+
&t,
|
|
997
|
+
);
|
|
687
998
|
assert_eq!(from_payload, Json::obj(vec![("order_id", Json::str("p9"))]));
|
|
688
999
|
|
|
689
1000
|
let blank = event("E", "D::Order", "", payload());
|
|
690
|
-
let none = trigger_args(
|
|
1001
|
+
let none = trigger_args(
|
|
1002
|
+
&rule("D::Order.Place", READ_ID),
|
|
1003
|
+
&blank,
|
|
1004
|
+
None,
|
|
1005
|
+
"D::Order.Place",
|
|
1006
|
+
&t,
|
|
1007
|
+
);
|
|
691
1008
|
assert_eq!(none, Json::obj(vec![("order_id", Json::Null)]));
|
|
692
1009
|
}
|
|
693
1010
|
|
|
694
1011
|
#[test]
|
|
695
|
-
fn an_explicit_with_for_a_non_creating_target_names_the_receiver_from_the_projection_or_the_event()
|
|
1012
|
+
fn an_explicit_with_for_a_non_creating_target_names_the_receiver_from_the_projection_or_the_event()
|
|
1013
|
+
{
|
|
696
1014
|
const REASON: &[(&str, &str)] = &[("reason", "\"x\"")];
|
|
697
1015
|
const BOTH: &[(&str, &str)] = &[("order_id", ":order_id"), ("reason", "\"x\"")];
|
|
698
1016
|
let t = tables();
|
|
699
1017
|
|
|
700
1018
|
// Nothing projected names the identity: the same-aggregate event's own id is the receiver.
|
|
701
|
-
let fallback = trigger_args(
|
|
702
|
-
|
|
1019
|
+
let fallback = trigger_args(
|
|
1020
|
+
&rule("D::Order.Close", REASON),
|
|
1021
|
+
&order_event(payload()),
|
|
1022
|
+
None,
|
|
1023
|
+
"D::Order.Close",
|
|
1024
|
+
&t,
|
|
1025
|
+
);
|
|
1026
|
+
assert_eq!(
|
|
1027
|
+
fallback,
|
|
1028
|
+
Json::obj(vec![
|
|
1029
|
+
("to", Json::str("o1")),
|
|
1030
|
+
("with", Json::obj(vec![("reason", Json::str("x"))]))
|
|
1031
|
+
])
|
|
1032
|
+
);
|
|
703
1033
|
|
|
704
1034
|
// The projection names it: that wins and is not re-routed.
|
|
705
|
-
let named = trigger_args(
|
|
1035
|
+
let named = trigger_args(
|
|
1036
|
+
&rule("D::Order.Close", BOTH),
|
|
1037
|
+
&order_event(payload()),
|
|
1038
|
+
None,
|
|
1039
|
+
"D::Order.Close",
|
|
1040
|
+
&t,
|
|
1041
|
+
);
|
|
706
1042
|
assert_eq!(
|
|
707
1043
|
named,
|
|
708
|
-
Json::obj(vec![
|
|
1044
|
+
Json::obj(vec![
|
|
1045
|
+
("to", Json::str("o1")),
|
|
1046
|
+
(
|
|
1047
|
+
"with",
|
|
1048
|
+
Json::obj(vec![
|
|
1049
|
+
("order_id", Json::str("o1")),
|
|
1050
|
+
("reason", Json::str("x"))
|
|
1051
|
+
])
|
|
1052
|
+
)
|
|
1053
|
+
])
|
|
709
1054
|
);
|
|
710
1055
|
|
|
711
1056
|
// A different aggregate has no event identity to offer, and a fan-out row never falls back.
|
|
712
|
-
let other = trigger_args(
|
|
1057
|
+
let other = trigger_args(
|
|
1058
|
+
&rule("D::Ship.Close", REASON),
|
|
1059
|
+
&order_event(payload()),
|
|
1060
|
+
None,
|
|
1061
|
+
"D::Ship.Close",
|
|
1062
|
+
&t,
|
|
1063
|
+
);
|
|
713
1064
|
assert_eq!(other, Json::obj(vec![("reason", Json::str("x"))]));
|
|
714
|
-
let fan = trigger_args(
|
|
1065
|
+
let fan = trigger_args(
|
|
1066
|
+
&rule("D::Order.Close", REASON),
|
|
1067
|
+
&order_event(payload()),
|
|
1068
|
+
Some(("order_id", "r1")),
|
|
1069
|
+
"D::Order.Close",
|
|
1070
|
+
&t,
|
|
1071
|
+
);
|
|
715
1072
|
assert_eq!(fan, Json::obj(vec![("reason", Json::str("x"))]));
|
|
716
1073
|
}
|
|
717
1074
|
|
|
@@ -723,23 +1080,59 @@ fn a_projected_fact_named_to_or_with_alone_is_not_mistaken_for_an_already_routed
|
|
|
723
1080
|
const JUST_WITH: &[(&str, &str)] = &[("with", "\"w\"")];
|
|
724
1081
|
let t = tables();
|
|
725
1082
|
|
|
726
|
-
let to = trigger_args(
|
|
727
|
-
|
|
1083
|
+
let to = trigger_args(
|
|
1084
|
+
&rule("D::Order.Close", JUST_TO),
|
|
1085
|
+
&order_event(payload()),
|
|
1086
|
+
None,
|
|
1087
|
+
"D::Order.Close",
|
|
1088
|
+
&t,
|
|
1089
|
+
);
|
|
1090
|
+
assert_eq!(
|
|
1091
|
+
to,
|
|
1092
|
+
Json::obj(vec![
|
|
1093
|
+
("to", Json::str("o1")),
|
|
1094
|
+
("with", Json::obj(vec![("to", Json::str("somewhere"))]))
|
|
1095
|
+
])
|
|
1096
|
+
);
|
|
728
1097
|
|
|
729
|
-
let with = trigger_args(
|
|
730
|
-
|
|
1098
|
+
let with = trigger_args(
|
|
1099
|
+
&rule("D::Order.Close", JUST_WITH),
|
|
1100
|
+
&order_event(payload()),
|
|
1101
|
+
None,
|
|
1102
|
+
"D::Order.Close",
|
|
1103
|
+
&t,
|
|
1104
|
+
);
|
|
1105
|
+
assert_eq!(
|
|
1106
|
+
with,
|
|
1107
|
+
Json::obj(vec![
|
|
1108
|
+
("to", Json::str("o1")),
|
|
1109
|
+
("with", Json::obj(vec![("with", Json::str("w"))]))
|
|
1110
|
+
])
|
|
1111
|
+
);
|
|
731
1112
|
}
|
|
732
1113
|
|
|
733
1114
|
#[test]
|
|
734
|
-
fn an_entity_command_is_routed_to_the_parent_named_by_the_event_when_the_projection_does_not_name_it(
|
|
1115
|
+
fn an_entity_command_is_routed_to_the_parent_named_by_the_event_when_the_projection_does_not_name_it(
|
|
1116
|
+
) {
|
|
735
1117
|
let t = tables();
|
|
736
1118
|
let projected = || Json::obj(vec![("line_id", Json::str("l1")), ("note", Json::str("n"))]);
|
|
737
1119
|
|
|
738
|
-
let got = route_dispatch_args(
|
|
1120
|
+
let got = route_dispatch_args(
|
|
1121
|
+
&projected(),
|
|
1122
|
+
"D::Order.Line.Add",
|
|
1123
|
+
&t,
|
|
1124
|
+
&order_event(payload()),
|
|
1125
|
+
);
|
|
739
1126
|
assert_eq!(
|
|
740
1127
|
got,
|
|
741
1128
|
Json::obj(vec![
|
|
742
|
-
(
|
|
1129
|
+
(
|
|
1130
|
+
"to",
|
|
1131
|
+
Json::obj(vec![
|
|
1132
|
+
("aggregate", Json::str("o1")),
|
|
1133
|
+
("entities", Json::Array(vec![Json::str("l1")]))
|
|
1134
|
+
])
|
|
1135
|
+
),
|
|
743
1136
|
("with", projected()),
|
|
744
1137
|
])
|
|
745
1138
|
);
|
|
@@ -748,7 +1141,7 @@ fn an_entity_command_is_routed_to_the_parent_named_by_the_event_when_the_project
|
|
|
748
1141
|
let other = event("E", "D::Other", "o1", payload());
|
|
749
1142
|
let blank = event("E", "D::Order", "", payload());
|
|
750
1143
|
for e in [other, blank] {
|
|
751
|
-
let flat = route_dispatch_args(projected(), "D::Order.Line.Add", &t, &e);
|
|
1144
|
+
let flat = route_dispatch_args(&projected(), "D::Order.Line.Add", &t, &e);
|
|
752
1145
|
assert!(flat.get("to").is_none(), "{flat:?}");
|
|
753
1146
|
}
|
|
754
1147
|
}
|
|
@@ -756,7 +1149,12 @@ fn an_entity_command_is_routed_to_the_parent_named_by_the_event_when_the_project
|
|
|
756
1149
|
#[test]
|
|
757
1150
|
fn resolve_with_reads_the_correlation_then_the_payload_then_the_memory_then_null() {
|
|
758
1151
|
let pm = &PROCESS_MANAGERS[0];
|
|
759
|
-
let e = event(
|
|
1152
|
+
let e = event(
|
|
1153
|
+
"E",
|
|
1154
|
+
"D::Xfer",
|
|
1155
|
+
"x1",
|
|
1156
|
+
Json::obj(vec![("from_event", Json::str("p"))]),
|
|
1157
|
+
);
|
|
760
1158
|
let memory = Json::obj(vec![("from_memory", Json::str("m"))]);
|
|
761
1159
|
let resolve = |name: &'static str| resolve_with(pm, &WithValue::Ref(name), &e, "corr", &memory);
|
|
762
1160
|
|