hecks 1.3.0 → 1.5.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/lib/hecks/adapters/driven/claude_code.rb +72 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +83 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
- data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
- data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +106 -9
- data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
- data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
- data/lib/hecks/adapters/driven/lambda.rb +82 -35
- data/lib/hecks/adapters/driven/local_storage.rb +83 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -1
- data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
- data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
- data/lib/hecks/adapters/driven/postgres.rb +175 -28
- data/lib/hecks/adapters/driven/prism.rb +50 -11
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
- data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
- data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
- data/lib/hecks/adapters/driven/sqlite.rb +181 -21
- data/lib/hecks/adapters/driven/system_clock.rb +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
- data/lib/hecks/adapters/driven.rb +6 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
- data/lib/hecks/behaviors/dsl.rb +60 -2
- data/lib/hecks/behaviors/expectations.rb +190 -29
- data/lib/hecks/behaviors/ir.rb +12 -1
- data/lib/hecks/behaviors/rspec.rb +9 -1
- data/lib/hecks/behaviors/runner.rb +21 -2
- data/lib/hecks/behaviors.rb +9 -1
- data/lib/hecks/bluebook/aggregate.rb +43 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
- data/lib/hecks/bluebook/assembly/build.rb +26 -5
- data/lib/hecks/bluebook/assembly/contract.rb +98 -23
- data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
- data/lib/hecks/bluebook/assembly/marks.rb +159 -30
- data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
- data/lib/hecks/bluebook/assembly.rb +32 -14
- data/lib/hecks/bluebook/attribute.rb +26 -12
- data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
- data/lib/hecks/bluebook/behaviour/command.rb +55 -25
- data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
- data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
- data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
- data/lib/hecks/bluebook/behaviour/query.rb +6 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +51 -9
- data/lib/hecks/bluebook/command.rb +62 -17
- data/lib/hecks/bluebook/domain_port.rb +34 -9
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +282 -123
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
- data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
- data/lib/hecks/bluebook/dsl/entity_builder.rb +191 -61
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
- data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +111 -47
- data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
- data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
- data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
- data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
- data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
- data/lib/hecks/bluebook/entity.rb +40 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
- data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
- data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
- data/lib/hecks/bluebook/expression/resolver.rb +369 -128
- data/lib/hecks/bluebook/hexagon.rb +35 -1
- data/lib/hecks/bluebook/lifecycle.rb +12 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
- data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
- data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
- data/lib/hecks/bluebook/meta_validator.rb +235 -139
- data/lib/hecks/bluebook/model_check.rb +434 -104
- data/lib/hecks/bluebook/pattern_subset.rb +32 -10
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +27 -14
- data/lib/hecks/bluebook/project_discovery.rb +5 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +50 -6
- data/lib/hecks/bluebook/query.rb +31 -4
- data/lib/hecks/bluebook/read_model.rb +35 -15
- data/lib/hecks/bluebook/reference.rb +26 -13
- data/lib/hecks/bluebook/smoke_test.rb +46 -23
- data/lib/hecks/bluebook/synthesizer.rb +46 -12
- data/lib/hecks/bluebook/translation.rb +34 -5
- data/lib/hecks/bluebook/value_object.rb +29 -11
- data/lib/hecks/bluebook.rb +5 -6
- data/lib/hecks/codemod.rb +138 -50
- data/lib/hecks/construct.rb +21 -7
- data/lib/hecks/corpus.rb +438 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/doc/reference.rb +200 -31
- data/lib/hecks/embryonaut_bluebook.rb +38 -15
- data/lib/hecks/facade/cli_door.rb +69 -10
- data/lib/hecks/facade/cli_runner.rb +105 -24
- data/lib/hecks/facade/command_request.rb +23 -0
- data/lib/hecks/facade/handle.rb +155 -35
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +50 -27
- data/lib/hecks/facade/surface/chapter.rb +26 -17
- data/lib/hecks/facade/surface.rb +16 -3
- data/lib/hecks/facade.rb +15 -4
- data/lib/hecks/forms/app.rb +46 -30
- data/lib/hecks/forms/command_form_renderer.rb +70 -9
- data/lib/hecks/forms/field_renderer.rb +142 -6
- data/lib/hecks/forms/field_shape.rb +183 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +14 -2
- data/lib/hecks/forms/query_form_renderer.rb +65 -2
- data/lib/hecks/forms/record_renderer.rb +60 -2
- data/lib/hecks/forms/record_table.rb +28 -1
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +13 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +59 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +79 -25
- data/lib/hecks/freezer.rb +27 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
- data/lib/hecks/fuzzing/combination_miner.rb +178 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
- data/lib/hecks/fuzzing/differential.rb +192 -0
- data/lib/hecks/fuzzing/domain_generator.rb +871 -0
- data/lib/hecks/fuzzing/era_boundary.rb +68 -19
- data/lib/hecks/fuzzing/form_census.rb +121 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
- data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
- data/lib/hecks/fuzzing/properties/guards.rb +86 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
- data/lib/hecks/fuzzing/properties/querying.rb +82 -28
- data/lib/hecks/fuzzing/properties.rb +84 -51
- data/lib/hecks/fuzzing/qa_settings.rb +164 -0
- data/lib/hecks/fuzzing/replay.rb +241 -133
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
- data/lib/hecks/fuzzing/self_consistency.rb +315 -137
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
- data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
- data/lib/hecks/fuzzing/shrinker.rb +309 -0
- data/lib/hecks/fuzzing/structural_skips.rb +37 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
- data/lib/hecks/fuzzing/value_generator.rb +110 -22
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +188 -12
- data/lib/hecks/grammar.rb +53 -7
- data/lib/hecks/ir.rb +51 -20
- data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
- data/lib/hecks/language/bluebook/policy.bluebook +11 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +41 -9
- data/lib/hecks/naming.rb +112 -31
- data/lib/hecks/ports/access_control.rb +53 -2
- data/lib/hecks/ports/agent/answers.rb +83 -6
- data/lib/hecks/ports/agent.rb +119 -35
- data/lib/hecks/ports/authentication.rb +44 -4
- data/lib/hecks/ports/authorization.rb +50 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +23 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +17 -1
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +58 -0
- data/lib/hecks/ports/loading.rb +4 -0
- data/lib/hecks/ports/persistence/append_only.rb +172 -8
- data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
- data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
- data/lib/hecks/ports/persistence/execution.rb +4 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
- data/lib/hecks/ports/persistence/plugin.rb +42 -4
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +34 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +95 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +48 -8
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +58 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
- data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
- data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
- data/lib/hecks/ports/persistence/state_codec.rb +319 -0
- data/lib/hecks/ports/persistence.rb +36 -1
- data/lib/hecks/ports/projection.rb +61 -7
- data/lib/hecks/ports/query/in_memory.rb +41 -8
- data/lib/hecks/ports/query/ordering.rb +21 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +147 -0
- data/lib/hecks/projections/diagrams.rb +315 -79
- data/lib/hecks/projections/glossary/html.rb +91 -3
- data/lib/hecks/projections/glossary/markdown.rb +50 -3
- data/lib/hecks/projections/glossary/mermaid.rb +49 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +110 -5
- data/lib/hecks/projections/glossary.rb +173 -31
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +62 -17
- data/lib/hecks/projections/model.rb +75 -21
- data/lib/hecks/projections/oidc.rb +45 -8
- data/lib/hecks/projections/parser_table.rb +53 -12
- data/lib/hecks/projections/reference.rb +15 -3
- data/lib/hecks/projections/rust_vocabulary.rb +646 -0
- data/lib/hecks/projections/shape.rb +10 -2
- data/lib/hecks/projections/statements.rb +69 -22
- data/lib/hecks/projections/vocabulary.rb +26 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +245 -37
- data/lib/hecks/projector/docs_projector.rb +154 -28
- data/lib/hecks/projector/exporter.rb +104 -29
- data/lib/hecks/projector/ir_projector.rb +7 -1
- data/lib/hecks/projector/narrate_projector.rb +144 -23
- data/lib/hecks/projector/target.rb +42 -18
- data/lib/hecks/projector.rb +86 -18
- data/lib/hecks/query_ir.rb +94 -47
- data/lib/hecks/query_specification/common/comparators.rb +19 -3
- data/lib/hecks/query_specification/common/comparison.rb +109 -27
- data/lib/hecks/query_specification/common/dsl.rb +65 -9
- data/lib/hecks/query_specification/common/null_policy.rb +61 -15
- data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
- data/lib/hecks/query_specification/common/options.rb +25 -0
- data/lib/hecks/query_specification/field_path.rb +69 -15
- data/lib/hecks/query_specification/hop_path.rb +57 -20
- data/lib/hecks/query_specification/read_model/specification.rb +4 -0
- data/lib/hecks/rendering.rb +9 -3
- data/lib/hecks/router/namespace_installer.rb +16 -3
- data/lib/hecks/router.rb +56 -1
- data/lib/hecks/runtime/aggregate_lock.rb +20 -11
- data/lib/hecks/runtime/boot_gates.rb +21 -3
- data/lib/hecks/runtime/caller.rb +40 -8
- data/lib/hecks/runtime/capability_graph.rb +13 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
- data/lib/hecks/runtime/command_interpreter.rb +195 -113
- data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
- data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
- data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
- data/lib/hecks/runtime/command_rules/emission.rb +18 -1
- data/lib/hecks/runtime/command_rules/references.rb +145 -55
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +56 -11
- data/lib/hecks/runtime/dispatcher.rb +263 -92
- data/lib/hecks/runtime/entity_element.rb +304 -67
- data/lib/hecks/runtime/entity_interpreter.rb +149 -88
- data/lib/hecks/runtime/errors.rb +37 -23
- data/lib/hecks/runtime/event.rb +15 -10
- data/lib/hecks/runtime/identity.rb +91 -23
- data/lib/hecks/runtime/instance.rb +103 -18
- data/lib/hecks/runtime/interpreting.rb +24 -16
- data/lib/hecks/runtime/invocation.rb +358 -0
- data/lib/hecks/runtime/loader.rb +105 -19
- data/lib/hecks/runtime/outbox.rb +164 -26
- data/lib/hecks/runtime/policy_interpreter.rb +73 -60
- data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
- data/lib/hecks/runtime/query_interpreter.rb +93 -74
- data/lib/hecks/runtime/reaction_invocation.rb +73 -28
- data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
- data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
- data/lib/hecks/runtime/reference_hop.rb +48 -6
- data/lib/hecks/runtime/refusal_wording.rb +142 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
- data/lib/hecks/runtime/registry/verification.rb +153 -28
- data/lib/hecks/runtime/registry.rb +202 -32
- data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
- data/lib/hecks/runtime/routing.rb +36 -154
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
- data/lib/hecks/runtime/saga_interpreter.rb +90 -76
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +33 -13
- data/lib/hecks/runtime/tenant_scope.rb +23 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +555 -142
- data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
- data/lib/hecks/runtime/value.rb +71 -21
- data/lib/hecks/runtime.rb +39 -7
- data/lib/hecks/storehouse.rb +368 -72
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +91 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
- metadata +28 -2
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Fuzzing
|
|
3
|
+
# What one sweep has already reached, and where the next seed starts.
|
|
4
|
+
#
|
|
5
|
+
# `SequenceGenerator` steers within one sequence (an unexercised verb is
|
|
6
|
+
# weighted up — picker.rb's `steer`), but without this, every seed of a
|
|
7
|
+
# sweep would start from nothing and forget everything the seeds before
|
|
8
|
+
# it reached. Roughly half of the QA ledger's bugs are refusal-kind splits (BUG#2–4,
|
|
9
|
+
# 7, 8, 14–16, 19–21, 23, 27, 28, 36–38, 41, 54, 56): a particular verb,
|
|
10
|
+
# in a particular state, with a particular malformed argument, refused
|
|
11
|
+
# one way on Ruby and another on Rust. BUG#11 was found by hand because
|
|
12
|
+
# a 25-step random walk never reached `Annotate` at all. Uniform seeds
|
|
13
|
+
# reach those corners by luck; this makes the sweep remember.
|
|
14
|
+
#
|
|
15
|
+
# ## The coverage unit
|
|
16
|
+
#
|
|
17
|
+
# A tuple, not a verb — `verb | step kind | lifecycle state before |
|
|
18
|
+
# adversarial mutation | outcome` (see `SequenceGenerator#coverage_tuple`).
|
|
19
|
+
# "Renew refused LifecycleRefused from free" and "Renew ok from held" are
|
|
20
|
+
# different places a runtime can be wrong, where a verb-level count
|
|
21
|
+
# calls them the same.
|
|
22
|
+
#
|
|
23
|
+
# ## Two levers
|
|
24
|
+
#
|
|
25
|
+
# Both expressed as plain generator arguments so a seed stays
|
|
26
|
+
# reproducible from one call:
|
|
27
|
+
#
|
|
28
|
+
# 1. Splicing (`prefix:`). A sequence that reached a new tuple joins
|
|
29
|
+
# the corpus, cut at its last new tuple. A later seed may start by
|
|
30
|
+
# re-generating a random-length prefix of a corpus entry — the same
|
|
31
|
+
# seed, favor and nested prefix, so the same steps, dispatched for
|
|
32
|
+
# real, reaching the same state — and then carries on with its own
|
|
33
|
+
# seed's randomness from there. Deep state gets reached once and
|
|
34
|
+
# explored many times, instead of re-rolled per seed.
|
|
35
|
+
# 2. Favor (`favor:`). The verbs this sweep has hit least often —
|
|
36
|
+
# including declared verbs it has never hit at all — are weighted
|
|
37
|
+
# up for the next seed.
|
|
38
|
+
#
|
|
39
|
+
# ## Reproducibility
|
|
40
|
+
#
|
|
41
|
+
# Nothing here is random at the campaign's own level beyond
|
|
42
|
+
# `Random.new(seed)` per plan, and a plan is printed with every finding
|
|
43
|
+
# (`bin/qa_sweep`'s `reproduce:` line), so no finding depends on
|
|
44
|
+
# re-running the whole campaign to get back to it.
|
|
45
|
+
class CoverageCampaign
|
|
46
|
+
# One seed's generation instructions, printable and reproducible.
|
|
47
|
+
Plan = Struct.new(:prefix, :favor, keyword_init: true) do
|
|
48
|
+
# Renders this plan as the keyword arguments a generator call accepts.
|
|
49
|
+
#
|
|
50
|
+
# @return [Hash{Symbol => Object}] `{ prefix:, favor: }`, the keyword arguments
|
|
51
|
+
# `SequenceGenerator.generate` and `.trace` accept for this plan
|
|
52
|
+
def generator_options = { prefix: prefix, favor: favor }
|
|
53
|
+
|
|
54
|
+
# Answers whether this plan starts from a spliced corpus prefix.
|
|
55
|
+
#
|
|
56
|
+
# @return [Boolean] whether this plan starts from a spliced corpus prefix
|
|
57
|
+
def spliced? = !prefix.nil?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
attr_reader :corpus
|
|
61
|
+
|
|
62
|
+
# @param splice_probability [Float, Integer] chance, per seed with a non-empty
|
|
63
|
+
# corpus, of splicing a corpus entry's prefix instead of generating fresh
|
|
64
|
+
# @param favor_count [Integer, Float] how many rare verbs to favor per plan; 0
|
|
65
|
+
# or non-positive disables favoring entirely
|
|
66
|
+
# @param corpus_limit [Integer] maximum corpus entries kept; the oldest is
|
|
67
|
+
# dropped once this is exceeded
|
|
68
|
+
# @param max_prefix_depth [Integer] maximum nested-prefix depth a new corpus
|
|
69
|
+
# entry may reach before splicing stops extending it further
|
|
70
|
+
def initialize(splice_probability:, favor_count:, corpus_limit: 64, max_prefix_depth: 6)
|
|
71
|
+
@splice_probability = splice_probability.to_f
|
|
72
|
+
@favor_count = favor_count.to_i
|
|
73
|
+
@corpus_limit = corpus_limit
|
|
74
|
+
@max_prefix_depth = max_prefix_depth
|
|
75
|
+
@corpus = []
|
|
76
|
+
@seen = Set.new
|
|
77
|
+
@verb_hits = Hash.new(0)
|
|
78
|
+
@declared_verbs = Set.new
|
|
79
|
+
@seeds = 0
|
|
80
|
+
@spliced = 0
|
|
81
|
+
@seeds_with_new = 0
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Builds this seed's generation plan — a possible splice, plus which
|
|
85
|
+
# verbs to favor.
|
|
86
|
+
#
|
|
87
|
+
# @param seed [Integer] the seed this plan is for; drives the splice-or-not
|
|
88
|
+
# coin flip and, when splicing, which corpus entry and prefix length are drawn
|
|
89
|
+
# @return [Fuzzing::CoverageCampaign::Plan] the plan `SequenceGenerator.generate`
|
|
90
|
+
# or `.trace` should be run with for this seed
|
|
91
|
+
def plan(seed)
|
|
92
|
+
random = Random.new(seed)
|
|
93
|
+
prefix = nil
|
|
94
|
+
if !@corpus.empty? && random.rand < @splice_probability
|
|
95
|
+
entry = @corpus[random.rand(@corpus.size)]
|
|
96
|
+
prefix = entry[:spec].merge("steps" => random.rand(1..entry[:attempts]))
|
|
97
|
+
end
|
|
98
|
+
Plan.new(prefix: prefix, favor: rare_verbs)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Folds one seed's generation results into the campaign's running
|
|
102
|
+
# coverage, and admits its corpus entry if it reached anything new.
|
|
103
|
+
#
|
|
104
|
+
# @param seed [Integer] the seed this trace was generated from
|
|
105
|
+
# @param plan [Fuzzing::CoverageCampaign::Plan] the plan that seed was generated with
|
|
106
|
+
# @param trace [Fuzzing::SequenceGenerator::Trace] `SequenceGenerator.trace`'s answer:
|
|
107
|
+
# `coverage` is `[[attempt_index, tuple], ...]`, `verbs` every verb the booted
|
|
108
|
+
# catalog offered
|
|
109
|
+
# @return [void]
|
|
110
|
+
def record(seed, plan, trace)
|
|
111
|
+
@seeds += 1
|
|
112
|
+
@spliced += 1 if plan.spliced?
|
|
113
|
+
@declared_verbs.merge(trace.verbs)
|
|
114
|
+
|
|
115
|
+
new_at = trace.coverage.filter_map do |index, tuple|
|
|
116
|
+
@verb_hits[tuple.split(" | ").first] += 1
|
|
117
|
+
index if @seen.add?(tuple)
|
|
118
|
+
end
|
|
119
|
+
return if new_at.empty?
|
|
120
|
+
|
|
121
|
+
@seeds_with_new += 1
|
|
122
|
+
admit(seed, plan, new_at.max + 1)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Counts the distinct coverage tuples this campaign has reached so far.
|
|
126
|
+
#
|
|
127
|
+
# @return [Integer] the number of distinct coverage tuples seen
|
|
128
|
+
def tuples_seen = @seen.size
|
|
129
|
+
|
|
130
|
+
# Renders the campaign's running totals as one human-readable line.
|
|
131
|
+
#
|
|
132
|
+
# @return [String] a one-line summary of tuples seen, seeds run, seeds that
|
|
133
|
+
# reached something new, and how many were spliced from the corpus
|
|
134
|
+
def summary
|
|
135
|
+
"coverage: #{@seen.size} distinct (verb, kind, state, mutation, outcome) tuple(s) over #{@seeds} seed(s); " \
|
|
136
|
+
"#{@seeds_with_new} seed(s) reached something new; #{@spliced} spliced from a corpus of #{@corpus.size}"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def admit(seed, plan, attempts)
|
|
142
|
+
return if depth(plan.prefix) + 1 > @max_prefix_depth
|
|
143
|
+
|
|
144
|
+
spec = { "seed" => seed, "favor" => plan.favor }
|
|
145
|
+
spec["prefix"] = plan.prefix if plan.prefix
|
|
146
|
+
@corpus << { spec: spec, attempts: attempts }
|
|
147
|
+
@corpus.shift while @corpus.size > @corpus_limit
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def depth(prefix) = prefix.nil? ? 0 : 1 + depth(prefix["prefix"])
|
|
151
|
+
|
|
152
|
+
# Never-hit declared verbs first (hits 0), then the least-hit, ties
|
|
153
|
+
# broken by name so a plan is a pure function of what was recorded.
|
|
154
|
+
def rare_verbs
|
|
155
|
+
return [] unless @favor_count.positive?
|
|
156
|
+
|
|
157
|
+
(@declared_verbs | @verb_hits.keys).min_by(@favor_count) { |verb| [@verb_hits[verb], verb] }
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "open3"
|
|
3
|
+
require_relative "replay"
|
|
4
|
+
require_relative "properties"
|
|
5
|
+
require_relative "self_consistency"
|
|
6
|
+
require_relative "rust_gap_manifest"
|
|
7
|
+
require_relative "nondeterministic"
|
|
8
|
+
|
|
9
|
+
module Hecks
|
|
10
|
+
module Fuzzing
|
|
11
|
+
# The Ruby-vs-Rust comparison of one generated sequence — moved here,
|
|
12
|
+
# unchanged, out of `bin/qa_sweep`'s own `diff_ruby_vs_rust` so a second
|
|
13
|
+
# caller (`bin/qa_generated_domains --rust`, which compares domains the
|
|
14
|
+
# generator wrote rather than ones on the rotation) runs the same
|
|
15
|
+
# comparison rather than a re-derived one. `bin/qa_sweep` delegates to
|
|
16
|
+
# it; its own comments on the known-gap filtering, the wire-precision
|
|
17
|
+
# reduction and the deep copy taken before `strip_emitted_flags!` apply
|
|
18
|
+
# here word for word.
|
|
19
|
+
#
|
|
20
|
+
# `differ` is anything answering `RustConformanceHelpers`' comparison
|
|
21
|
+
# helpers (spec/support/rust_conformance_helpers.rb) plus a
|
|
22
|
+
# `structural_skips` set — duck-typed, so lib never requires spec/.
|
|
23
|
+
# Which query verbs may diverge is not the differ's call: it is read
|
|
24
|
+
# off the binary's own manifest.json (`manifest_partition`, below).
|
|
25
|
+
#
|
|
26
|
+
# Returns one divergence list per active mode — `{ differential: [...],
|
|
27
|
+
# self_consistency: [...], properties_in_differential: [...],
|
|
28
|
+
# adapter_parity_sqlite: [...] }`, keys present only for the modes in
|
|
29
|
+
# `modes:` (`differential` always). `adapter_parity_sqlite:` is the
|
|
30
|
+
# caller's own callable, because which two adapters that mode pairs is
|
|
31
|
+
# the caller's dial, not this module's.
|
|
32
|
+
module Differential
|
|
33
|
+
module_function
|
|
34
|
+
|
|
35
|
+
# The one place a Ruby/Rust query divergence may be tolerated — and
|
|
36
|
+
# only for a verb `gaps` (a `RustGapManifest`) declares not generated.
|
|
37
|
+
# Rust refuses such a verb outright while Ruby answers it for real
|
|
38
|
+
# (or refuses it for its own business reason), so both sides' rows for
|
|
39
|
+
# that verb leave the comparison. Everything else stays in: a refusal
|
|
40
|
+
# the manifest doesn't account for — however its message is worded —
|
|
41
|
+
# is a real divergence.
|
|
42
|
+
#
|
|
43
|
+
# `skipped` is every tolerated verb this history actually reached (for
|
|
44
|
+
# the sweep's structural_skip_report). `stale` is a divergence per
|
|
45
|
+
# tolerated verb Rust nonetheless answered with a query row: the
|
|
46
|
+
# manifest says "not generated" but the binary disagrees, so the
|
|
47
|
+
# tolerance itself is wrong and must not silently hold.
|
|
48
|
+
# @param gaps [Hecks::Fuzzing::RustGapManifest] the compiled binary's manifest
|
|
49
|
+
# @param ruby_refusals [Array<Hash>] Ruby's own refusal rows for this history
|
|
50
|
+
# @param rust_refusals [Array<Hash>] the Rust binary's own refusal rows
|
|
51
|
+
# @param ruby_queries [Array<Hash>] Ruby's own query rows for this history
|
|
52
|
+
# @param rust_queries [Array<Hash>] the Rust binary's own query rows
|
|
53
|
+
# @return [Hash] `ruby_refusals:`/`rust_refusals:`/`ruby_queries:`/
|
|
54
|
+
# `rust_queries:` (each with tolerated verbs removed), `skipped:` (a
|
|
55
|
+
# `Set<String>` of every tolerated verb this history actually reached),
|
|
56
|
+
# and `stale:` (an `Array<Hash>` divergence per tolerated verb the Rust
|
|
57
|
+
# binary nonetheless answered)
|
|
58
|
+
def manifest_partition(gaps, ruby_refusals:, rust_refusals:, ruby_queries:, rust_queries:)
|
|
59
|
+
verb_of = ->(row) { row.key?("verb") ? row["verb"] : row["query"] }
|
|
60
|
+
declared = ->(row) { gaps.not_generated?(verb_of.call(row)) }
|
|
61
|
+
stale = rust_queries.select(&declared).map do |row|
|
|
62
|
+
{ field: "manifest", verb: row["query"],
|
|
63
|
+
detail: "#{row['query']} is declared generated: false in manifest.json " \
|
|
64
|
+
"(#{gaps.not_generated(row['query']).values_at('gap_class', 'construct').join('/')}), " \
|
|
65
|
+
"but the Rust binary answered it — regenerate with bin/project_rust" }
|
|
66
|
+
end
|
|
67
|
+
reached = (ruby_refusals + rust_refusals + ruby_queries).select(&declared)
|
|
68
|
+
{ ruby_refusals: ruby_refusals.reject(&declared), rust_refusals: rust_refusals.reject(&declared),
|
|
69
|
+
ruby_queries: ruby_queries.reject(&declared), rust_queries: rust_queries.reject(&declared),
|
|
70
|
+
skipped: reached.to_set(&verb_of), stale: stale }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Runs the standard property battery and reports every one that failed.
|
|
74
|
+
#
|
|
75
|
+
# @param history [Hash] a replayed history as returned by `Replay.call`
|
|
76
|
+
# @return [Array<Hash>] one `{field:, detail:}` entry per failed property
|
|
77
|
+
def property_divergences(history)
|
|
78
|
+
Properties.check(history).reject { |_, result| result == true }
|
|
79
|
+
.map { |name, message| { field: name.to_s, detail: message } }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Flattens `history[:self_consistency]`'s own per-check divergence lists.
|
|
83
|
+
#
|
|
84
|
+
# @param history [Hash] a replayed history as returned by `Replay.call`,
|
|
85
|
+
# optionally carrying `:self_consistency` (`Replay.call`'s own
|
|
86
|
+
# `self_consistency: true`)
|
|
87
|
+
# @return [Array<Hash>] every self-consistency divergence recorded; empty if
|
|
88
|
+
# `history` carries no `:self_consistency`
|
|
89
|
+
def self_consistency_divergences(history)
|
|
90
|
+
return [] unless history[:self_consistency]
|
|
91
|
+
|
|
92
|
+
history[:self_consistency].values.flatten(1)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
96
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
97
|
+
# rubocop:disable-next Metrics/PerceivedComplexity
|
|
98
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
99
|
+
# @param differ [Object] duck-typed comparison helper answering
|
|
100
|
+
# `RustConformanceHelpers`' interface (adapter-defined)
|
|
101
|
+
# @param domain_path [String] path to the domain directory to replay
|
|
102
|
+
# @param steps [Array<Hash>] the step sequence to replay on both engines
|
|
103
|
+
# @param binary [String] path to the compiled Rust conformance binary to run
|
|
104
|
+
# @param modes [Array<Symbol>] which comparisons to run — any of
|
|
105
|
+
# `:differential` (always run), `:self_consistency`,
|
|
106
|
+
# `:properties_in_differential`, `:adapter_parity_sqlite`
|
|
107
|
+
# @param adapter_parity_sqlite [Proc, nil] callable answering the
|
|
108
|
+
# `:adapter_parity_sqlite` mode's own comparison, or `nil` to skip it
|
|
109
|
+
# even when named in `modes`
|
|
110
|
+
# @return [Hash{Symbol => Array<Hash>}] one divergence list per active mode,
|
|
111
|
+
# keyed by the mode name; `:differential` is always present
|
|
112
|
+
def diff(differ, domain_path, steps, binary, modes:, adapter_parity_sqlite: nil)
|
|
113
|
+
self_consistency = modes.include?(:self_consistency)
|
|
114
|
+
ruby_result = Replay.call(domain_path, steps, self_consistency: self_consistency)
|
|
115
|
+
ruby_instances = JSON.parse(JSON.generate(ruby_result[:instances]))
|
|
116
|
+
ruby_events = JSON.parse(JSON.generate(ruby_result[:events]))
|
|
117
|
+
ruby_refusals = ruby_result[:refusals].map do |r|
|
|
118
|
+
{ "verb" => r[:verb].to_s, "kind" => r[:kind].to_s.split("::").last, "error" => r[:error] }
|
|
119
|
+
end
|
|
120
|
+
ruby_queries = JSON.parse(JSON.generate(ruby_result[:queries].map { |row| Nondeterministic.strip(row, :query_row) }))
|
|
121
|
+
ruby_sagas = JSON.parse(JSON.generate(ruby_result[:sagas]))
|
|
122
|
+
ruby_dry_runs = ruby_result[:dry_runs].map { |d| { "verb" => d[:verb].to_s, "ok" => d[:ok] } }
|
|
123
|
+
|
|
124
|
+
outcomes = {}
|
|
125
|
+
outcomes[:properties_in_differential] = property_divergences(ruby_result) if modes.include?(:properties_in_differential)
|
|
126
|
+
if modes.include?(:adapter_parity_sqlite) && adapter_parity_sqlite
|
|
127
|
+
outcomes[:adapter_parity_sqlite] = adapter_parity_sqlite.call
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
stdout, status = Open3.capture2(binary, stdin_data: JSON.generate({ "steps" => steps }))
|
|
131
|
+
unless status.success?
|
|
132
|
+
return outcomes.merge(differential: [{ field: "process",
|
|
133
|
+
detail: "rust binary exited #{status.exitstatus}: #{stdout}" }])
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
rust_output = JSON.parse(stdout)
|
|
137
|
+
rust_live_instances = JSON.parse(JSON.generate(rust_output["instances"])) if self_consistency
|
|
138
|
+
differ.strip_emitted_flags!(rust_output["instances"])
|
|
139
|
+
differ.strip_emitted_flags!(rust_output["queries"])
|
|
140
|
+
differ.strip_occurred_at!(rust_output["events"])
|
|
141
|
+
|
|
142
|
+
divergences = []
|
|
143
|
+
divergences << { field: "instances", ruby: ruby_instances, rust: rust_output["instances"] } \
|
|
144
|
+
unless rust_output["instances"] == ruby_instances
|
|
145
|
+
divergences << { field: "events", ruby: ruby_events, rust: rust_output["events"] } \
|
|
146
|
+
unless rust_output["events"] == ruby_events
|
|
147
|
+
|
|
148
|
+
kept = manifest_partition(RustGapManifest.for_binary(binary),
|
|
149
|
+
ruby_refusals: ruby_refusals, rust_refusals: rust_output["refusals"],
|
|
150
|
+
ruby_queries: ruby_queries, rust_queries: rust_output["queries"])
|
|
151
|
+
differ.structural_skips.merge(kept[:skipped])
|
|
152
|
+
divergences.concat(kept[:stale])
|
|
153
|
+
|
|
154
|
+
by_kind = ->(r) { r.slice("verb", "kind") }
|
|
155
|
+
rust_refusals = kept[:rust_refusals].map(&by_kind)
|
|
156
|
+
kept_ruby_refusals = kept[:ruby_refusals].map(&by_kind)
|
|
157
|
+
divergences << { field: "refusals", ruby: kept_ruby_refusals, rust: rust_refusals } \
|
|
158
|
+
unless rust_refusals == kept_ruby_refusals
|
|
159
|
+
|
|
160
|
+
wordless = ->(row) { differ.reduce_to_wire_precision(row.except("error", "reference_error")) }
|
|
161
|
+
rust_queries = kept[:rust_queries].map(&wordless)
|
|
162
|
+
kept_ruby_queries = kept[:ruby_queries].map(&wordless)
|
|
163
|
+
divergences << { field: "queries", ruby: kept_ruby_queries, rust: rust_queries } \
|
|
164
|
+
unless rust_queries == kept_ruby_queries
|
|
165
|
+
|
|
166
|
+
divergences << { field: "sagas", ruby: ruby_sagas, rust: rust_output["sagas"] } \
|
|
167
|
+
unless rust_output["sagas"] == ruby_sagas
|
|
168
|
+
|
|
169
|
+
cross_domain = differ.cross_domain_policy_names(rust_output)
|
|
170
|
+
kept_ruby_reactions = JSON.parse(JSON.generate(ruby_result[:reactions]))
|
|
171
|
+
.reject { |r| cross_domain.include?(r["policy"]) }
|
|
172
|
+
rust_reactions = rust_output.fetch("reactions")
|
|
173
|
+
divergences << { field: "reactions", ruby: kept_ruby_reactions, rust: rust_reactions } \
|
|
174
|
+
unless rust_reactions == kept_ruby_reactions
|
|
175
|
+
|
|
176
|
+
rust_dry_runs = Array(rust_output["dry_runs"]).map { |d| d.slice("verb", "ok") }
|
|
177
|
+
divergences << { field: "dry_runs", ruby: ruby_dry_runs, rust: rust_dry_runs } \
|
|
178
|
+
unless rust_dry_runs == ruby_dry_runs
|
|
179
|
+
outcomes[:differential] = divergences
|
|
180
|
+
|
|
181
|
+
if self_consistency
|
|
182
|
+
outcomes[:self_consistency] =
|
|
183
|
+
self_consistency_divergences(ruby_result) +
|
|
184
|
+
SelfConsistency.check_rust_rehydration(binary, differ, rust_live_instances) +
|
|
185
|
+
SelfConsistency.check_rust_idempotency(binary, differ, rust_live_instances)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
outcomes
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|