@cratis/pi 7.34.0 → 7.35.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.
- package/package/corpus/agents/security-reviewer.md +1 -0
- package/package/corpus/agents/spec-writer.md +1 -1
- package/package/corpus/harnesses/pi/extensions/shared/rules.ts +3 -1
- package/package/corpus/hooks/scripts/cratis-nuget-pins.txt +7 -6
- package/package/corpus/hooks/scripts/cratis-release-notes-reviewed.cjs +7 -6
- package/package/corpus/hooks/scripts/cratis-store-mutations.json +7 -3
- package/package/corpus/product-sync.json +896 -0
- package/package/corpus/prompts/add-business-rule.prompt.md +1 -1
- package/package/corpus/prompts/ship-changes.prompt.md +2 -2
- package/package/corpus/rules/ai-distribution.md +31 -3
- package/package/corpus/rules/application-profile.md +2 -1
- package/package/corpus/rules/components.md +66 -7
- package/package/corpus/rules/concepts.md +11 -3
- package/package/corpus/rules/csharp.md +3 -1
- package/package/corpus/rules/dialogs.md +9 -0
- package/package/corpus/rules/efcore.md +1 -1
- package/package/corpus/rules/elixir.md +49 -0
- package/package/corpus/rules/framework.md +5 -3
- package/package/corpus/rules/general.md +1 -0
- package/package/corpus/rules/glossary.md +4 -4
- package/package/corpus/rules/go-engineering.md +60 -0
- package/package/corpus/rules/go.md +115 -0
- package/package/corpus/rules/java.md +5 -2
- package/package/corpus/rules/kotlin.md +7 -5
- package/package/corpus/rules/orleans.md +65 -9
- package/package/corpus/rules/profiles.md +43 -6
- package/package/corpus/rules/pull-requests.md +3 -3
- package/package/corpus/rules/react.md +8 -1
- package/package/corpus/rules/reactors.md +27 -3
- package/package/corpus/rules/specs.csharp.md +11 -4
- package/package/corpus/rules/specs.scenarios.csharp.md +35 -4
- package/package/corpus/rules/specs.typescript.md +2 -1
- package/package/corpus/rules/storybook.md +1 -1
- package/package/corpus/rules/typescript.md +1 -1
- package/package/corpus/rules/vertical-slices.md +37 -5
- package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +4 -2
- package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +18 -8
- package/package/corpus/skills/cratis-application-slice-specifications/references/query-and-decision-scenarios.md +65 -0
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +53 -57
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +38 -42
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +18 -7
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authproxy.md +30 -0
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +12 -9
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +9 -9
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/observable-emission-guards.md +8 -5
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +20 -4
- package/package/corpus/skills/cratis-arc-command/SKILL.md +32 -10
- package/package/corpus/skills/cratis-arc-command/references/command-result.md +12 -5
- package/package/corpus/skills/cratis-arc-command/references/decisions-and-routing.md +95 -0
- package/package/corpus/skills/cratis-arc-command/references/generated-values.md +3 -2
- package/package/corpus/skills/cratis-arc-command/references/handler-shapes.md +8 -8
- package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +22 -3
- package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +13 -1
- package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +19 -9
- package/package/corpus/skills/cratis-arc-command-kotlin/SKILL.md +44 -18
- package/package/corpus/skills/cratis-arc-command-kotlin/references/read-model-naming.md +29 -0
- package/package/corpus/skills/cratis-arc-command-operation/SKILL.md +10 -9
- package/package/corpus/skills/cratis-arc-command-operation/references/execution-and-recovery.md +2 -2
- package/package/corpus/skills/cratis-arc-command-operation/references/testing.md +4 -3
- package/package/corpus/skills/cratis-arc-command-typescript/SKILL.md +42 -22
- package/package/corpus/skills/cratis-arc-command-typescript/references/chronicle-events.md +36 -5
- package/package/corpus/skills/cratis-arc-command-typescript/references/command-operations.md +114 -0
- package/package/corpus/skills/cratis-arc-command-typescript/references/hosting-and-storage.md +37 -16
- package/package/corpus/skills/cratis-arc-command-typescript/references/identity-and-introspection.md +88 -0
- package/package/corpus/skills/cratis-arc-command-typescript/references/observability-and-host-scopes.md +25 -0
- package/package/corpus/skills/cratis-arc-command-typescript/references/proxy-generation.md +35 -4
- package/package/corpus/skills/cratis-arc-command-typescript/references/slice-layout.md +11 -0
- package/package/corpus/skills/cratis-arc-command-typescript/verification.json +8 -1
- package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +49 -19
- package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +12 -6
- package/package/corpus/skills/cratis-arc-go/SKILL.md +148 -0
- package/package/corpus/skills/cratis-arc-go/references/queries.md +80 -0
- package/package/corpus/skills/cratis-arc-hosting/SKILL.md +96 -0
- package/package/corpus/skills/cratis-arc-hosting/references/integrations-and-publish.md +80 -0
- package/package/corpus/skills/cratis-arc-identity-and-tenancy-kotlin/SKILL.md +137 -0
- package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +20 -2
- package/package/corpus/skills/cratis-arc-query-kotlin/SKILL.md +26 -6
- package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +25 -20
- package/package/corpus/skills/cratis-arc-query-typescript/SKILL.md +32 -6
- package/package/corpus/skills/cratis-arc-query-typescript/references/storage-observation-and-compliance.md +66 -0
- package/package/corpus/skills/cratis-arc-query-typescript/verification.json +5 -1
- package/package/corpus/skills/cratis-arc-react-page/SKILL.md +24 -13
- package/package/corpus/skills/cratis-arc-react-page/references/data-page.md +20 -10
- package/package/corpus/skills/cratis-arc-react-page/references/data-tables.md +17 -3
- package/package/corpus/skills/cratis-arc-react-page/references/dialogs.md +14 -3
- package/package/corpus/skills/cratis-arc-react-page/references/from-screenplay.md +6 -1
- package/package/corpus/skills/cratis-arc-react-page/references/provenance.md +1 -1
- package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +11 -1
- package/package/corpus/skills/cratis-arc-react-page/verification.json +6 -1
- package/package/corpus/skills/cratis-arc-spring-data-kotlin/SKILL.md +163 -0
- package/package/corpus/skills/cratis-arc-testing-kotlin/SKILL.md +131 -0
- package/package/corpus/skills/cratis-arc-validation-kotlin/SKILL.md +44 -11
- package/package/corpus/skills/cratis-arc-validation-typescript/SKILL.md +5 -3
- package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +79 -25
- package/package/corpus/skills/cratis-chronicle-cli-operations/references/command-effects.md +57 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +87 -99
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/additional-surfaces.md +65 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/client-construction.md +42 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/compatibility-check.md +12 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/discovery.md +43 -19
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/hosting.md +29 -1
- package/package/corpus/skills/cratis-chronicle-client-elixir/SKILL.md +158 -162
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/append-api.md +36 -15
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/compliance.md +32 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/connections.md +56 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/event-sources.md +64 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/pitfalls.md +25 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/projections.md +48 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/upgrading.md +34 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/variants.md +13 -5
- package/package/corpus/skills/cratis-chronicle-client-elixir/verification.json +23 -0
- package/package/corpus/skills/cratis-chronicle-client-go/SKILL.md +162 -0
- package/package/corpus/skills/cratis-chronicle-client-go/references/projections.md +73 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +87 -88
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/connection-lifecycle.md +13 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/discovery-and-registration.md +14 -4
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/event-sources.md +65 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/options.md +29 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/spring-boot.md +13 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/variants.md +5 -4
- package/package/corpus/skills/cratis-chronicle-client-typescript/SKILL.md +165 -156
- package/package/corpus/skills/cratis-chronicle-client-typescript/references/read-models-and-projections.md +54 -11
- package/package/corpus/skills/cratis-chronicle-client-typescript/references/testing.md +126 -0
- package/package/corpus/skills/cratis-chronicle-client-typescript/references/variants.md +3 -5
- package/package/corpus/skills/cratis-chronicle-client-typescript/verification.json +19 -0
- package/package/corpus/skills/cratis-chronicle-compliance/SKILL.md +36 -13
- package/package/corpus/skills/cratis-chronicle-decision-reads/SKILL.md +110 -0
- package/package/corpus/skills/cratis-chronicle-decision-reads/references/admission-and-ownership.md +57 -0
- package/package/corpus/skills/cratis-chronicle-decision-reads/verification.json +12 -0
- package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +58 -19
- package/package/corpus/skills/cratis-chronicle-event-constraints/references/closing-streams.md +73 -0
- package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +32 -3
- package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +18 -5
- package/package/corpus/skills/cratis-chronicle-event-type-migration/SKILL.md +32 -2
- package/package/corpus/skills/cratis-chronicle-hosting/SKILL.md +134 -0
- package/package/corpus/skills/cratis-chronicle-hosting/references/operations.md +89 -0
- package/package/corpus/skills/cratis-chronicle-hosting/verification.json +14 -0
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/SKILL.md +93 -66
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/blocked-tools.md +21 -7
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/deployment.md +77 -0
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/observational-tools.md +46 -4
- package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +15 -1
- package/package/corpus/skills/cratis-chronicle-projection/SKILL.md +44 -10
- package/package/corpus/skills/cratis-chronicle-projection/references/fluent-builder.md +7 -1
- package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +8 -0
- package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +85 -105
- package/package/corpus/skills/cratis-chronicle-reactor/references/slice-delivery-checklist.md +1 -1
- package/package/corpus/skills/cratis-chronicle-reactor/references/specifications.md +20 -1
- package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +2 -2
- package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +35 -16
- package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +15 -3
- package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +23 -4
- package/package/corpus/skills/cratis-chronicle-web-workbench/SKILL.md +40 -15
- package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +20 -6
- package/package/corpus/skills/cratis-code-review/SKILL.md +25 -3
- package/package/corpus/skills/cratis-components-accessibility/SKILL.md +20 -4
- package/package/corpus/skills/cratis-components-schema-editor/SKILL.md +40 -28
- package/package/corpus/skills/cratis-components-schema-editor/references/tree-layout.md +62 -0
- package/package/corpus/skills/cratis-components-schema-editor/verification.json +14 -0
- package/package/corpus/skills/cratis-components-stepper-command-dialog/SKILL.md +16 -8
- package/package/corpus/skills/cratis-components-styling/SKILL.md +57 -20
- package/package/corpus/skills/cratis-components-styling/verification.json +13 -0
- package/package/corpus/skills/cratis-components-toolbar/SKILL.md +37 -7
- package/package/corpus/skills/cratis-components-toolbar/verification.json +12 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/SKILL.md +6 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/references/exceptions-logging-and-di.md +5 -3
- package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +19 -6
- package/package/corpus/skills/cratis-fundamentals-concept/references/typescript-concepts.md +97 -0
- package/package/corpus/skills/cratis-fundamentals-concept/verification.json +4 -1
- package/package/corpus/skills/cratis-fundamentals-concept-go/SKILL.md +112 -0
- package/package/corpus/skills/cratis-fundamentals-type-discovery/SKILL.md +54 -17
- package/package/corpus/skills/cratis-fundamentals-type-discovery/verification.json +14 -0
- package/package/corpus/skills/cratis-lens-browser-extension/SKILL.md +55 -28
- package/package/corpus/skills/cratis-performance-review/SKILL.md +5 -2
- package/package/corpus/skills/cratis-release-notes/SKILL.md +3 -1
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/SKILL.md +5 -4
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/realization-and-gap-fill.md +1 -1
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/SKILL.md +1 -1
- package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +5 -6
- package/package/corpus/skills/cratis-screenplay-command-surface/references/compliance.md +25 -7
- package/package/corpus/skills/cratis-screenplay-command-surface/references/enum-numbers.md +4 -2
- package/package/corpus/skills/cratis-screenplay-command-surface/references/generated-values-and-responses.md +4 -1
- package/package/corpus/skills/cratis-screenplay-event-modeling/SKILL.md +4 -3
- package/package/corpus/skills/cratis-screenplay-event-modeling/references/nine-steps.md +1 -1
- package/package/corpus/skills/cratis-screenplay-event-modeling/references/parsed-not-runnable.md +2 -2
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/SKILL.md +3 -2
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/generate-notes.md +34 -4
- package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +5 -3
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +1 -1
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +4 -2
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/property-links.md +42 -3
- package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +3 -1
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/vocabulary-map.md +2 -2
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/SKILL.md +6 -6
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/drift-and-ledger.md +1 -1
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/gap-fill-handoff.md +6 -4
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +1 -1
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/SKILL.md +3 -1
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/invoicing-dues-design.md +1 -1
- package/package/corpus/skills/cratis-screenplay-slice-design/SKILL.md +1 -1
- package/package/corpus/skills/cratis-screenplay-slice-design/references/rule-layers.md +2 -2
- package/package/corpus/skills/cratis-screenplay-slice-design/references/worked-example.md +1 -1
- package/package/corpus/skills/cratis-screenplay-specifications/references/examples-and-routes.md +48 -0
- package/package/corpus/skills/cratis-screenplay-specifications/verification.json +3 -1
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/SKILL.md +7 -5
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/chronicle-boundaries.md +2 -2
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/consistency-and-concurrency.md +2 -2
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/streams-example.md +5 -4
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +8 -6
- package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet-example.md +2 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +4 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/doc-contradictions.md +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +16 -11
- package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +8 -5
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +1329 -32
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/verdicts.md +2 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/versions.md +23 -7
- package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +1 -1
- package/package/corpus/skills/cratis-screenplay-ui-composition/SKILL.md +2 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/references/screen-composition-contract.md +27 -9
- package/package/corpus/skills/cratis-security-review/SKILL.md +9 -3
- package/package/corpus/skills/cratis-security-review/references/arc-boundaries.md +59 -0
- package/package/corpus/skills/cratis-specification-by-example/SKILL.md +7 -0
- package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +43 -11
- package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +16 -4
- package/package/corpus/skills/cratis-specifications-csharp/references/csharp-patterns.md +18 -5
- package/package/corpus/skills/cratis-specifications-csharp/references/integration-specs.md +21 -0
- package/package/corpus/skills/cratis-specifications-csharp/references/shared-lifecycle.md +34 -0
- package/package/corpus/skills/cratis-specifications-csharp/verification.json +13 -0
- package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +14 -5
- package/package/corpus/skills/cratis-specifications-typescript/verification.json +12 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +11 -5
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +7 -4
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/rendered-application.md +10 -1
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/scene-runtime.md +75 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +6 -1
- package/package/corpus/skills/cratis-studio-mcp-safety-guidance/SKILL.md +14 -1
- package/package/profile-catalog.json +68 -1
- package/package.json +1 -1
- package/src/index.ts +3 -1
|
@@ -20,6 +20,7 @@ A command's `Handle()` is an ordinary method; with dependencies resolved by `Pro
|
|
|
20
20
|
|---|---|---|
|
|
21
21
|
| `Specification` (plain call) | `Handle()`, `Handle(providedValue)`, reducer logic or a reactor's decision as a method call; or an isolated class with mocked collaborators | **default** for a decision that is a pure function of its arguments |
|
|
22
22
|
| `CommandScenario<TCommand>` | the real Arc command pipeline — authorization + validators + `Provide()` + `Handle()` + appended events | the pipeline, validation, authorization, or appended events matter (State Change slices) |
|
|
23
|
+
| `QueryScenario<TReadModel>` | query pipeline: argument conversion, authorization, validation, interceptors; `Observe` buffers emissions | query composition matters; real MongoDB observations still need infrastructure |
|
|
23
24
|
| `EventScenario` | Chronicle-level append semantics, no command pipeline | constraint violations, raw stream sequencing/concurrency |
|
|
24
25
|
| `ReadModelScenario<TReadModel>` | projection/reducer state from a sequence of events (auto-detects model-bound / fluent / reducer) | **default for projections**, which are declarative (State View slices) |
|
|
25
26
|
| `ReactorScenario<TReactor>` | reactor handler invocation + side effects via mocked services | the reactor wiring and side effects (Automation / Translation slices) |
|
|
@@ -74,7 +75,7 @@ public class and_all_information_is_valid : Specification
|
|
|
74
75
|
- `CommandScenario<TCommand>` exposes `Services` (an `IServiceCollection`), `Context`, `Execute(command[, ct])`, `Validate(command)`, `LastResult`, `Operations` and the operation assertions `ShouldHaveExecutedOperation<T>()` / `ShouldHaveCompensatedOperation<T>()` / `ShouldHaveNoOperationInvocations()`; with the Chronicle integration it also gains `Given`, `EventScenario`, `EventLog`, `EventSequence` and `AppendedEvents` as extension members. Everything else is configured through `Services` and asserted with extension methods.
|
|
75
76
|
- **Event assertions are extension methods on the scenario** (from the Chronicle testing package), keyed by **command + event** type: `await _scenario.ShouldHaveAppendedEvent<TCommand, TEvent>(eventSourceId)` or the `(eventSourceId, Func<TEvent,bool> predicate)` overload; plus `await _scenario.ShouldHaveTailSequenceNumber<TCommand>(...)`. They return `Task`, so the fact is `async Task`.
|
|
76
77
|
- **`CommandResult` assertions** (Arc extensions): `ShouldBeSuccessful()`, `ShouldNotBeSuccessful()`, `ShouldBeValid()`, `ShouldHaveValidationErrors()`, `ShouldHaveValidationErrorFor(message)`, `ShouldBeAuthorized()`, `ShouldNotBeAuthorized()`, `ShouldHaveExceptions()`/`ShouldNotHaveExceptions()`.
|
|
77
|
-
- **Seed prior state through `_scenario.Given` or `_scenario.Services`.** `_scenario.Given.ForEventSource(id).Events(...)` materializes the seeded events into an **in-memory read-model dictionary** the harness answers keyed reads from — it never reaches the event log (see the false-greens list below). To populate
|
|
78
|
+
- **Seed prior state through `_scenario.Given` or `_scenario.Services`.** `_scenario.Given.ForEventSource(id).Events(...)` materializes the seeded events into an **in-memory read-model dictionary** the harness answers keyed reads from — in **legacy mode** it never reaches the event log (see the false-greens list below). To populate advisory read models the validator/`Provide()`/`Handle()` inject, substitute `IReadModels` and register it (`_scenario.Services.Replace(new ServiceDescriptor(typeof(IReadModels), mock))`, mocking `GetInstanceById(...)`) or register projections with `_scenario.Services.AddReadModels(...)`.
|
|
78
79
|
- **Validator/`Provide()` dependencies:** register them in `_scenario.Services`; Arc testing discovers the concrete validator automatically. When several specs need different injected validator states, test rejected variants by instantiating the validator directly (per-scenario state can be order-sensitive under parallel xUnit).
|
|
79
80
|
- **`Provide()`:** drive it through `CommandScenario` end-to-end; when the handler's decision is pure given provided data, test `Handle(providedValue)` directly as the default decision spec and keep the scenario for the wiring.
|
|
80
81
|
- **Command operations:** use **cratis-arc-command-operation** for decision/adapter specs and real `CommandScenario` execution, forward failure, compensation, and Chronicle commit rejection; a direct `Handle()` does not execute operations.
|
|
@@ -99,14 +100,29 @@ Each of these passes while proving nothing. They are the reason the two-assertio
|
|
|
99
100
|
1. **A discarded assertion `Task`.** The `IEventSequence` assertions `ShouldHaveAppendedEvent`/`ShouldHaveTailSequenceNumber` (Chronicle Testing) are genuinely `async`: in a `void [Fact]` that does not `await` them the exception lands on a `Task` nobody observes and the spec passes regardless of behavior, while the compiler's `CS4014` stays silent outside an `async` method. (The same-named `CommandScenario` overloads in Arc's Chronicle.Testing throw synchronously and only *return* a completed `Task`, so those fail even when discarded — but you cannot tell the two apart at the call site.) Make the fact `async Task` and `await` every `Task`-returning `Should*`. Chronicle's analyzer **`CHR0039` (`NonAwaitedAssertion`)** reports any discarded `Task`/`ValueTask`-returning `Should*` call from a Cratis testing package (Chronicle's and Arc's alike).
|
|
100
101
|
2. **A dependency the spec forgot to seed.** When a validator's constructor, `Provide()` or `Handle()` asks for a read model that does not exist for the command's key, the validator is never built and no rule runs — yet the command is rejected (one validation result, reason **`DependencyUnavailable`**, HTTP 400), so `ShouldNotBeSuccessful()` passes and keeps passing with the rule it is named after deleted. `ShouldHaveValidationErrors()` **refuses** a result whose only errors carry that reason and tells you to seed the read model — or, when that rejection *is* the behavior under spec, assert it deliberately with `ShouldHaveValidationErrorBecauseOf(ValidationResultReason.DependencyUnavailable)`. A missing *service* (not a read model) surfaces as an exception instead (`ShouldHaveExceptions()`), and `ShouldNotBeSuccessful()` alone cannot tell the two apart from a genuine rejection.
|
|
101
102
|
3. **`.Validate()` authorizes first.** `Validate(command)` runs the command filters, and authorization filters are ordered before validation. A validate-only spec on a `[Roles]`/`[Authorize]` command with no identity registered is denied for authorization — the rule it is named after never ran. Register the identity, or assert `ShouldNotBeAuthorized()` and say so in the name.
|
|
102
|
-
4. **
|
|
103
|
-
5.
|
|
103
|
+
4. **Legacy-mode constraint preconditions go to the event log, not `CommandScenario.Given`.** `_scenario.Given.ForEventSource(id).Events(...)` on a `CommandScenario` seeds an **in-memory read-model materialization only**; nothing reaches the event log a `[Unique]`/`IConstraint` is enforced against, so a "second registration with the same name" spec passes because the first one was never appended. Seed constraint preconditions through **`EventScenario`**'s `Given`, and assert the violation by constraint **name** (`ShouldHaveConstraintViolationFor`) — the same helper exists on `CommandResult` and on `IAppendResult`.
|
|
104
|
+
5. **Legacy `GetInstances<T>` is not harness-backed.** Under legacy `CommandScenario` the read-model harness answers keyed reads (`GetInstanceById`) from its seeded dictionary and forwards `GetInstances<T>`/`GetSnapshotsById`/`Watch` to the real client, which is not there. Code under spec that enumerates a read model needs a fake lookup registered in `Services`, not `Given`.
|
|
104
105
|
6. **One child is not a children spec.** `[ChildrenFrom<T>]` without `key:` identifies children by the **event source id**, so every child event on one stream upserts the same child and a one-child spec stays green with the key wrong. Drive **two** children with distinct keys and assert the count.
|
|
105
106
|
7. **Independently derived expectations.** An expected value computed in the spec with the same expression as the code under spec agrees with it by construction. Write the expected literal.
|
|
106
107
|
|
|
107
108
|
The `ReadModelScenario` and `EventScenario` equivalents of these live in the **cratis-chronicle-read-model-specifications** and **cratis-chronicle-event-specifications** skills.
|
|
108
109
|
|
|
109
|
-
> **
|
|
110
|
+
> **Historical validator-state order-sensitivity:** observed in the 22.16-era harness. Arc 22.23.2 stopped replacing the process-wide provider; later releases isolated scenario stores and event-source services. Reproduce at 22.75.0 before applying the old direct-validator/small `[Collection]` workaround. No current reproduction is verified here.
|
|
111
|
+
|
|
112
|
+
#### Query and protected-decision modes (Arc v22.75.0)
|
|
113
|
+
|
|
114
|
+
`QueryScenario<TReadModel>` (`Cratis.Arc.Testing.Queries`) runs `Perform(nameof(Model.Method),
|
|
115
|
+
arguments?, paging?, sorting?)`; `Observe(...)` returns `QueryObservation.Initial`
|
|
116
|
+
and buffered `Next()`. Configure principal/policies in `Services`; schemes fail
|
|
117
|
+
closed without HTTP. Chronicle query `Given` can seed keyed models/events.
|
|
118
|
+
|
|
119
|
+
For guarded commands use `UseDecisionReads()` **before** seeding events. Given
|
|
120
|
+
state, guarded reads and appends share one real in-process log, unlike legacy
|
|
121
|
+
mode above. Pinned models cannot supply guards. Use `OnRoute(route)` and
|
|
122
|
+
`AppendConcurrently` to prove conflicts; `ShouldHaveCompletedStream` proves
|
|
123
|
+
completion and `DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)`
|
|
124
|
+
checks admission. See **cratis-application-slice-specifications**,
|
|
125
|
+
`references/query-and-decision-scenarios.md`, for sources and limitations.
|
|
110
126
|
|
|
111
127
|
### `EventScenario` — constraints & append semantics
|
|
112
128
|
|
|
@@ -144,6 +160,14 @@ Use the named-parameter constructor when services/initial state are needed: `new
|
|
|
144
160
|
|
|
145
161
|
**Bridging `IReadModels` into a `CommandScenario`** (current harness limitation): when a command handler injects `IReadModels` directly, build a focused `ReadModelScenario<T>`, seed it via `Given.ForEventSourceId(id).ReadModel(...)`, then register its read models into the command scenario — `_scenario.Services.AddSingleton(readModelScenario.ReadModels)`. Pass `scenario.ReadModels` into code under test for keyed `GetInstanceById` reads; for non-key searches/filters, register a fake lookup or an `IMongoCollection<T>` mock in the scenario services instead.
|
|
146
162
|
|
|
163
|
+
**Chronicle v19.50.0 scenario additions** (`Source/Clients/Testing/ReadModels/`):
|
|
164
|
+
`WithProjection(Action<IProjectionBuilderFor<T>>)` overrides discovery, including
|
|
165
|
+
reducers. `Given.ForEventSource(id).OnStream(eventSourceType, eventStreamType,
|
|
166
|
+
eventStreamId).Events(...)` seeds routing for filtered reducers; unrouted events
|
|
167
|
+
do not reach a differently filtered handler. Dispose scenarios in `Destroy()`.
|
|
168
|
+
Per-run `Defaults(clientArtifactsProvider, serviceProvider, options)` supports
|
|
169
|
+
`InMemoryClientArtifactsProvider` and `ExplicitArtifacts` without global discovery.
|
|
170
|
+
|
|
147
171
|
**Skip discipline:** don't pre-emptively `[Fact(Skip=...)]` a read-model assertion — assume scalar `ConceptAs<string>`, enum, and identifier properties populate correctly; if one fails, investigate the projection. Only skip on a *reproduced* harness gap, and put the specific reason in the skip message.
|
|
148
172
|
|
|
149
173
|
### `ReactorScenario<TReactor>` — Automation / Translation
|
|
@@ -160,6 +184,13 @@ async Task Because() => await _scenario.Given.ForEventSource(_id).Events(new Aut
|
|
|
160
184
|
[Fact] async Task should_notify() => await _service.Received(1).Notify("Jane Austen");
|
|
161
185
|
```
|
|
162
186
|
|
|
187
|
+
In **Chronicle v19.50.0** (`Source/Clients/Testing/Reactors/`), `OnStream` also
|
|
188
|
+
applies to class-based and fluent reactors. `ProducedEvents` carries payload and
|
|
189
|
+
destination; `ShouldHaveProduced<TEvent>(eventSourceId)` or the predicate
|
|
190
|
+
`((produced, @event) => ...)` verifies cross-stream routing. The non-generic
|
|
191
|
+
`ReactorScenario(reactorId, defaults)` exercises an explicitly registered reactor.
|
|
192
|
+
Dispose each scenario in `Destroy()`; await async assertions (CHR0039).
|
|
193
|
+
|
|
163
194
|
#### Read-model setup — Chronicle 19.46.0+
|
|
164
195
|
|
|
165
196
|
In a class-based `ReactorScenario<TReactor>`, `Given` events invoke the reactor;
|
|
@@ -78,7 +78,8 @@ export class an_events_command_response_value_handler {
|
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
- Properties are public (not protected) — tests access them via `context.propertyName`.
|
|
81
|
-
-
|
|
81
|
+
- `given` is a package-local helper file, not a published export. Import it with the relative path to that file, for example `import { given } from '../../given';`; create it if absent. Fundamentals v7.23.0 constructs parameterless contexts; Arc v22.75.0 passes the Mocha Suite and binds callback `this` to it.
|
|
82
|
+
- Configure Vitest's setup file to call `chai.should()` and register `sinon-chai` and `chai-as-promised`. Load the reflection polyfill for metadata-dependent specifications.
|
|
82
83
|
- Simple tests without shared setup don't need a reusable context.
|
|
83
84
|
|
|
84
85
|
## Simple Test Pattern (without context)
|
|
@@ -17,7 +17,7 @@ Good candidates: a presentational card/list-item/form-field with multiple visual
|
|
|
17
17
|
|
|
18
18
|
## Preview infrastructure (what you get for free)
|
|
19
19
|
|
|
20
|
-
A configured `.storybook/preview` should wrap every story in `CratisComponentsProvider` (from `@cratis/components`)
|
|
20
|
+
A configured `.storybook/preview` should wrap every story in `CratisComponentsProvider` (from `@cratis/components`) with `toaster` when stories show notifications. Import one `index.css` containing `@import '@cratis/components/tokens'`, `@import '@cratis/components/styles'` and (for the baseline look) `@import '@cratis/components/theme'`, each terminated by a semicolon — the same entries as the app. CSS imports from TypeScript need ambient declarations on TypeScript 6; keeping the subpaths in CSS avoids TS2882. Mount only one toaster. With that in place:
|
|
21
21
|
|
|
22
22
|
- **Auto prop tables** via `react-docgen-typescript` — a component's `interface` + per-prop JSDoc renders as a Docs prop table, and union props become `select` controls automatically. Keep a JSDoc comment on every prop. **`argTypes` is an override layer** (change control type, group under `table.category`, disable a control) — don't re-list every prop.
|
|
23
23
|
- **Accessibility panel** (`@storybook/addon-a11y`) scans each story with axe-core. Treat new violations as defects.
|
|
@@ -121,7 +121,7 @@ All identifiers, comments, JSDoc, and string literals must use **American Englis
|
|
|
121
121
|
|
|
122
122
|
## Folder Structure
|
|
123
123
|
|
|
124
|
-
- **Never give a `.tsx` the same basename as a `.ts` in the same folder.** The Arc proxy generator emits `<Slice>.ts` beside the slice's C#; a component named `<Slice>.tsx` next to it is **silently dropped from the program**: for one basename TypeScript keeps only the `.ts`, so the component's type errors are never reported and `tsc -b` stays green while the file is broken (reproduced on TypeScript 6.0.3 and 7.0.2 with `include: ["src/**/*"]`).
|
|
124
|
+
- **Never give a `.tsx` the same basename as a `.ts` in the same folder.** The Arc proxy generator emits `<Slice>.ts` beside the slice's C#; a component named `<Slice>.tsx` next to it is **silently dropped from the program**: for one basename TypeScript keeps only the `.ts`, so the component's type errors are never reported and `tsc -b` stays green while the file is broken (reproduced on TypeScript 6.0.3 and 7.0.2 with `include: ["src/**/*"]`). At Arc v22.75.0 use `<CratisProxiesUseProxyFileSuffix>true</CratisProxiesUseProxyFileSuffix>` to emit `<Slice>.proxy.ts` with matching barrels; that is the clean generator fix. Otherwise name the component for what it renders (`Listing.tsx`, `RegisterAuthor.tsx`), never for the slice, and consider a repository spec that fails on any `.tsx` with a sibling `.ts` of the same name.
|
|
125
125
|
- Do not prefix a file, component, type, or symbol with the name of its containing folder or the concept it belongs to. Instead, use folder structure to provide that context.
|
|
126
126
|
- Favor functional folder structure over technical folder structure.
|
|
127
127
|
- Group files by the feature or concept they belong to, not by their technical role.
|
|
@@ -88,7 +88,7 @@ The command carries input from the caller. `Handle()` is defined directly on the
|
|
|
88
88
|
| Reusable value invariant (length, format, range) | `ConceptValidator<T>` on the concept type |
|
|
89
89
|
| Command-input / cross-field / pre-handler rule (incl. injected read-model/service/identity checks) | `CommandValidator<TCommand>` with `RuleFor(...)` |
|
|
90
90
|
| Handler needs fetched/computed data before it can build the event | `Provide()` — fetches the data; may validate it and short-circuit |
|
|
91
|
-
| State-dependent rule that must hold **under concurrency** |
|
|
91
|
+
| State-dependent rule that must hold **under concurrency** | `IStreamReads`/`StreamDecision` for one stream, or `[ProtectedDecision]` + `DecisionRead<T>` for admitted projected state; plain injection is advisory |
|
|
92
92
|
| Uniqueness | Chronicle `[Unique]` / `IConstraint` (race-safe) — not a read-model pre-check |
|
|
93
93
|
| Genuinely exceptional failure (bug, missing infra) | `throw` a domain exception |
|
|
94
94
|
|
|
@@ -164,7 +164,7 @@ Return the event(s) directly — Arc appends them; never inject `IEventLog` to a
|
|
|
164
164
|
| `(EventSourceId, TEvent)` | command decides the event source id (tuple order doesn't matter) |
|
|
165
165
|
| `(TResponse, TEvent)` | a response value plus an event |
|
|
166
166
|
| `IEnumerable<object>` | multiple events; `EventForEventSourceId(id, @event)` wrappers for cross-stream |
|
|
167
|
-
| `Result<TEvent, ValidationResult>` | success event or a typed validation error
|
|
167
|
+
| `Result<TEvent, ValidationResult>` | success event or a typed validation error; does not itself guard concurrency |
|
|
168
168
|
| `ICommandOperation` / `CommandOperations` | immediate server-side work after event enrollment, never the client response |
|
|
169
169
|
| `void` | no event |
|
|
170
170
|
|
|
@@ -182,7 +182,25 @@ Three attributes scope an append broad→narrow, and each one *also* (only with
|
|
|
182
182
|
|
|
183
183
|
Each attribute always tags the appended events (visible in `EventContext`). **Only when the command also carries `concurrency: true`** does each dimension join the server-side `ConcurrencyScope` (plus the event source id), with the optimistic strategy supplying the expected tail sequence number. A command **without** `concurrency: true` contributes no scope of its own, and the append falls to whatever concurrency strategy the event sequence is configured with — by default the optimistic one, scoped to the event source. So the attribute chooses *which dimensions* bound the check, not whether a check happens at all. Reach for it on high-contention state transitions where a stale read would let two commands both "win" — it narrows the boundary so appends that are genuinely independent stop colliding (concurrency failures surface on `CommandResult`/`AppendResult`). Adopt it deliberately per behavior, not as a blanket default.
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
At Arc v22.75.0, prefer `IStreamReads.ForCommand()` in `Provide()` and return
|
|
186
|
+
`StreamDecision.Append(...)` when deciding over one routed stream. For projected
|
|
187
|
+
state use `[ProtectedDecision]` + `DecisionRead<T>`; plain injection and keyed
|
|
188
|
+
reads are advisory, including in `Handle()`. Protected projections must be
|
|
189
|
+
Chronicle-admitted (direct source key, known events; not reducers, joins, children
|
|
190
|
+
or passive shapes); validators must be parameterless and certified by Arc.
|
|
191
|
+
Exact returned scopes replace automatic `concurrency: true` capture.
|
|
192
|
+
|
|
193
|
+
`[EventStreamId("{ReportingScopeId}:{Period}")]` resolves command property values
|
|
194
|
+
after validation; concepts supply their underlying values. An interface can
|
|
195
|
+
compute an id too. `AmbiguousEventStreamId` rejects any non-NotSet attribute
|
|
196
|
+
value combined with `ICanProvideEventStreamId`, including a template; pick one.
|
|
197
|
+
`[EventSource<TSource>(stream)]` selects a definition-backed route on commands and
|
|
198
|
+
aggregate roots. `EventRoute`/`IEventRoutes` reuse it; explicit wrapper routing
|
|
199
|
+
wins over command context. `[EventTag]`, `ICanProvideEventTags`, returned
|
|
200
|
+
`EventTags` and discovered `ICanProvideCommandEventTags` add tags to returned
|
|
201
|
+
events. `CompleteStream` runs after commit, not atomically with append, and closes
|
|
202
|
+
stream type/id across the log, not just one source. Read the Arc command skill's
|
|
203
|
+
`references/decisions-and-routing.md` for source-pinned contracts and limitations.
|
|
186
204
|
|
|
187
205
|
### Event-source id resolution order **[contract]**
|
|
188
206
|
|
|
@@ -232,6 +250,14 @@ public record AuthorRegistered(AuthorName Name);
|
|
|
232
250
|
|
|
233
251
|
**[contract]** `[ReadModel]` records expose queries as `public static` methods; dependencies are plain method parameters (no `[FromServices]`). Default to model-bound projection shape; drop to fluent only when needed. For step-by-step creation, invoke the **cratis-chronicle-read-model** skill.
|
|
234
252
|
|
|
253
|
+
Use `[ReadModel(RequireQueryAttribute = true)]` with `[Query]` on intended
|
|
254
|
+
endpoints when public folds/helpers exist (ARC0026/ARCCHR0019). `[Query]` alone
|
|
255
|
+
does not hide siblings; private/compiler-generated statics are never queries.
|
|
256
|
+
A `CancellationToken` is supplied by Arc; concept arguments come from the caller,
|
|
257
|
+
not DI. Bad GET/QUERY argument conversions return 400. Single-instance MongoDB/EF
|
|
258
|
+
observations emit `default` on absence/deletion/filter exit: guard `hasData` and
|
|
259
|
+
`isReady` in React. Verified at Arc v22.75.0; see the read-model query reference.
|
|
260
|
+
|
|
235
261
|
### Query return shapes **[contract]**
|
|
236
262
|
|
|
237
263
|
Stay within analyzer-supported shapes: the read model, `T?`, a collection/array/`IQueryable<T>` of it, `Task<...>` of those, `IAsyncEnumerable<T>`, or `ISubject<...>` of read model/collection.
|
|
@@ -268,7 +294,7 @@ query reference for versioned behavior and composition trade-offs.
|
|
|
268
294
|
|
|
269
295
|
### Choosing read-model access in command-side code **[convention]**
|
|
270
296
|
|
|
271
|
-
Ladder, first that fits: (1) direct read-model
|
|
297
|
+
Ladder, first that fits: (1) advisory direct read-model injection — only when keyed by the command's own event-source id; (2) a `[Passive]` projection; (3) `IReadModels.GetInstanceById<T>((EventSourceId)key)` for a different/derived key; (4) a materialized projection. A lookup *interface* is justified **only** for a genuine non-key search (e.g. find-by-email) — never to wrap a keyed `GetInstanceById`.
|
|
272
298
|
|
|
273
299
|
**[contract] Passive state is not sink state.** `IMongoCollection<T>` cannot
|
|
274
300
|
compute a passive read model; any leftover documents are not maintained state.
|
|
@@ -366,7 +392,13 @@ Redaction is a system-event request that removes payload availability from the e
|
|
|
366
392
|
|
|
367
393
|
Cross-slice access is **read-only and goes through Chronicle** — inject another slice's read model (validator/`Provide()`/`Handle()`), read by explicit key with `IReadModels.GetInstanceById`, mark a `[Passive]` projection for command-side decisions, or react to its events. **Never** instantiate or DI another slice's command/handler/service.
|
|
368
394
|
|
|
369
|
-
**DCB (Dynamic Consistency Boundary):**
|
|
395
|
+
**DCB (Dynamic Consistency Boundary):** the boundary is the event state explicitly
|
|
396
|
+
enrolled in guarded decisions, not every model a command reads. Use stream
|
|
397
|
+
reads/exact scopes or `[ProtectedDecision]` + admitted `DecisionRead<T>`;
|
|
398
|
+
plain validator/`Provide()`/`Handle()` injection is advisory. Stream-level
|
|
399
|
+
`concurrency: true` scopes are another explicit boundary, not protection for an
|
|
400
|
+
arbitrary snapshot. Specify conflicts with `UseDecisionReads()` and actual seeded
|
|
401
|
+
events, not pinned models.
|
|
370
402
|
|
|
371
403
|
## Dialogs
|
|
372
404
|
|
|
@@ -132,8 +132,10 @@ behavior truly requires DOM events — do not make it the default. Avoid
|
|
|
132
132
|
snapshots; they hide behavior and make refactors noisy.
|
|
133
133
|
|
|
134
134
|
Cratis Components mount inside `CratisComponentsProvider`, so a specification
|
|
135
|
-
that renders one for real must provide it
|
|
136
|
-
stub for
|
|
135
|
+
that renders one for real must provide it (enable `toaster` only when asserting
|
|
136
|
+
toasts). In jsdom, provide a `ResizeObserver` stub for any Components area that
|
|
137
|
+
observes size, including observable tables, Filter and toolbars. Source-checked
|
|
138
|
+
at Components v4.26.4 (`Source/**`); this is not a split-pane-only requirement.
|
|
137
139
|
|
|
138
140
|
## Async and time
|
|
139
141
|
|
|
@@ -23,12 +23,14 @@ This skill decides **when to add a scenario** and **what to cover**. The C# mech
|
|
|
23
23
|
|
|
24
24
|
| Package | Version | Purpose |
|
|
25
25
|
| --- | --- | --- |
|
|
26
|
-
| `Cratis.Arc.Testing` | `22.
|
|
27
|
-
| `Cratis.Arc.Chronicle.Testing` | `22.
|
|
26
|
+
| `Cratis.Arc.Testing` | `22.75.0` | `CommandScenario<TCommand>` and the `CommandResult` assertions |
|
|
27
|
+
| `Cratis.Arc.Chronicle.Testing` | `22.75.0` | `Given`, `EventLog`, `AppendedEvents` and the appended-event assertions on a command scenario |
|
|
28
28
|
| `Cratis.Chronicle.Testing` | `18.3.0` | `EventScenario`, `ReadModelScenario<T>`, `ReactorScenario<T>` |
|
|
29
29
|
| `Cratis.Specifications.XUnit` | `4.x` | The `Specification` base and the `ShouldXxx` assertions |
|
|
30
30
|
|
|
31
|
-
>
|
|
31
|
+
> Arc command/query/decision scenario surface rechecked at v22.75.0. Arc depends
|
|
32
|
+
> on Chronicle 19.39.1; later Chronicle reactor setup below has its own minimum
|
|
33
|
+
> version and is not downgraded by this Arc verification.
|
|
32
34
|
|
|
33
35
|
Reactor read-model setup and the `EventContext.From` UTC default below are
|
|
34
36
|
verified separately at **Chronicle 19.46.0**, the minimum version for that
|
|
@@ -112,6 +114,7 @@ keep a scenario for that wiring and for appended-event behavior.
|
|
|
112
114
|
| Pure command decision, reducer logic or reactor decision | Plain method call with `Specification` (default) | `cratis-specifications-csharp` |
|
|
113
115
|
| State Change — command pipeline and appended events | `CommandScenario<TCommand>` | this one |
|
|
114
116
|
| Append semantics, a constraint | `EventScenario` | `cratis-chronicle-event-specifications` |
|
|
117
|
+
| Query pipeline — arguments, authorization, validation, interceptors | `QueryScenario<TReadModel>` | [query and decision scenarios](references/query-and-decision-scenarios.md) |
|
|
115
118
|
| State View — a projection or reducer | `ReadModelScenario<TReadModel>` | `cratis-chronicle-read-model-specifications` |
|
|
116
119
|
| Automation or Translation — a reactor | `ReactorScenario<TReactor>` | `cratis-specifications-csharp` |
|
|
117
120
|
| A host, transport, or real-infrastructure boundary | Out-of-process integration | `cratis-specifications-csharp` |
|
|
@@ -203,6 +206,13 @@ produces. Pin an instance only when the events would be noise.
|
|
|
203
206
|
|
|
204
207
|
Both builder methods return `void`; there is nothing to await.
|
|
205
208
|
|
|
209
|
+
That is **legacy** read-model mode: seeded events populate an in-memory model,
|
|
210
|
+
not the append log. For protected or stream decisions call `UseDecisionReads()`
|
|
211
|
+
before seeding; actual events and decisions then share one in-process log.
|
|
212
|
+
Pinned models cannot supply decision guards. Read
|
|
213
|
+
[query and decision scenarios](references/query-and-decision-scenarios.md) when
|
|
214
|
+
specifying query pipelines, routed conflicts, projection admission or completion.
|
|
215
|
+
|
|
206
216
|
Validator and `Provide()` dependencies are registered in `_scenario.Services`;
|
|
207
217
|
the concrete validator itself is discovered automatically.
|
|
208
218
|
|
|
@@ -296,11 +306,11 @@ ideas recorded in `references/provenance.md`. Read `references/provenance.md` wh
|
|
|
296
306
|
- **The command scenario reports success but nothing was appended.** The
|
|
297
307
|
assertion is on `CommandResult` only; add a
|
|
298
308
|
`ShouldHaveAppendedEvent<TCommand, TEvent>` fact.
|
|
299
|
-
- **Injected-validator branches flip with test ordering.**
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
309
|
+
- **Injected-validator branches flip with test ordering.** This was observed in
|
|
310
|
+
the 22.16-era harness. Arc 22.23.2 stopped replacing the process-wide provider;
|
|
311
|
+
later releases isolated stores and event-source services per scenario. Reproduce
|
|
312
|
+
at 22.75.0 before applying the old direct-validator/small `[Collection]`
|
|
313
|
+
workaround; no current reproduction was verified here.
|
|
304
314
|
|
|
305
315
|
## How it is proven
|
|
306
316
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Query and decision scenarios
|
|
2
|
+
|
|
3
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
4
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
5
|
+
|
|
6
|
+
Verified at **Arc v22.75.0**. Use the plain `Specification` base for pure decisions;
|
|
7
|
+
these helpers add proof of framework composition, not HTTP transport.
|
|
8
|
+
|
|
9
|
+
## Query pipeline
|
|
10
|
+
|
|
11
|
+
`QueryScenario<TReadModel>` is in `Cratis.Arc.Testing.Queries` (`Cratis.Arc.Testing`).
|
|
12
|
+
Configure `Services` before the first call. `Perform(nameof(Model.Method),
|
|
13
|
+
arguments?, paging?, sorting?, cancellationToken?)` returns `QueryResult` through
|
|
14
|
+
conversion, validation, authorization and rendering. Use `QueryArguments` from
|
|
15
|
+
`Cratis.Arc.Queries`; there is no principal parameter. Register the principal
|
|
16
|
+
under test with `ICurrentPrincipalAccessor` in `Services`. Scheme-selected queries
|
|
17
|
+
fail closed in-process: HTTP authentication needs a hosted integration spec.
|
|
18
|
+
|
|
19
|
+
At Arc 22.75.1+ (re-verified at 22.76.0), both query and command scenarios preserve
|
|
20
|
+
explicit `ICurrentPrincipalAccessor`, `ICurrentPrincipalOverride` and
|
|
21
|
+
`IAuthorizationPolicyRuntime` registrations rather than replacing them with defaults.
|
|
22
|
+
Configure them before first execution and specify both authorized and denied callers.
|
|
23
|
+
Source: `Source/DotNET/Testing/ScenarioServices.cs`, `Queries/QueryScenario.cs`,
|
|
24
|
+
`Commands/CommandScenario.cs`.
|
|
25
|
+
|
|
26
|
+
`Observe(...)` returns a disposable `QueryObservation`; `Initial` includes the
|
|
27
|
+
first result or rejection and `Next(timeout?, cancellationToken?)` reads the next
|
|
28
|
+
buffered emission (not necessarily distinct). Default deadlines are five seconds;
|
|
29
|
+
timeouts throw. Dispose asynchronously to await cleanup. Interceptors and guards
|
|
30
|
+
run when emissions are consumed. MongoDB `.Observe()` still needs a real DB with
|
|
31
|
+
change streams; the harness does not emulate it.
|
|
32
|
+
|
|
33
|
+
The discovered `IQueryScenarioExtender` supports additional context/services.
|
|
34
|
+
With `Cratis.Arc.Chronicle.Testing` and its Queries namespace, use
|
|
35
|
+
`Given.ForEventSource(id).ReadModel(instance)` or `.Events(...)` before performing.
|
|
36
|
+
The query must read that source via injected `IReadModels`; seeding does not
|
|
37
|
+
replace arbitrary stores or produce later observable emissions after appends.
|
|
38
|
+
|
|
39
|
+
## Guarded decisions
|
|
40
|
+
|
|
41
|
+
Call `new CommandScenario<TCommand>().UseDecisionReads()` before seeding. This
|
|
42
|
+
mode shares **one real in-process event log** among given events, guarded reads,
|
|
43
|
+
constraints and returned appends. Seed actual events, not pinned read models.
|
|
44
|
+
`Given.ForEventSource(id).OnRoute(route).Events(...)` preserves routing;
|
|
45
|
+
`scenario.EventRoutes.For<TSource>(stream).WithStreamId(...)` creates the route.
|
|
46
|
+
Queue interference using `AppendConcurrently(id, route, event)` and assert a
|
|
47
|
+
nonempty decision is rejected on the intended boundary. An empty identical-retry
|
|
48
|
+
append deliberately returns no scope and cannot prove the conflict guard.
|
|
49
|
+
|
|
50
|
+
`ShouldHaveCompletedStream(type, id)` proves successful completion.
|
|
51
|
+
`DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)` checks projection
|
|
52
|
+
admission in assembly-wide specs. These APIs are in
|
|
53
|
+
`Cratis.Arc.Chronicle.Testing.Commands`. Routed seeding is refused in legacy mode.
|
|
54
|
+
|
|
55
|
+
The old warnings that command `Given` never reaches the event log and that its
|
|
56
|
+
read-model dictionary is the only backing are **legacy-mode** warnings, not a
|
|
57
|
+
reason to fake protected decision readers. Keep non-key enumeration/provider
|
|
58
|
+
boundaries explicit in either mode; decision mode is not a MongoDB substitute.
|
|
59
|
+
|
|
60
|
+
## Sources
|
|
61
|
+
|
|
62
|
+
- [Query scenarios](https://github.com/Cratis/Arc/blob/v22.75.0/Documentation/backend/csharp/testing/query-scenario.md)
|
|
63
|
+
- [Stream decisions and routed specs](https://github.com/Cratis/Arc/blob/v22.75.0/Documentation/backend/csharp/chronicle/commands/stream-decisions.md)
|
|
64
|
+
- [Decision-mode extension](https://github.com/Cratis/Arc/blob/v22.75.0/Source/DotNET/Chronicle.Testing/Commands/CommandScenarioChronicleExtensions.cs)
|
|
65
|
+
- [Admission assertion](https://github.com/Cratis/Arc/blob/v22.75.0/Source/DotNET/Chronicle.Testing/Commands/DecisionReadAdmissions.cs)
|
|
@@ -15,12 +15,14 @@ told about them. Each has its own extension point.
|
|
|
15
15
|
|
|
16
16
|
| Package | Version | Purpose |
|
|
17
17
|
| --- | --- | --- |
|
|
18
|
-
| `Cratis.Arc.Core` | `22.
|
|
19
|
-
| `Cratis.Arc` | `22.
|
|
20
|
-
| `@cratis/arc` | `22.
|
|
21
|
-
| `@cratis/arc.react` | `22.
|
|
18
|
+
| `Cratis.Arc.Core` | `22.75.0` | `Cratis.Arc.Identity`, `Cratis.Arc.Authorization`, `Cratis.Arc.Authentication`, `Cratis.Arc.Tenancy` |
|
|
19
|
+
| `Cratis.Arc` | `22.75.0` | ASP.NET Core hosting, `AddMicrosoftIdentityPlatformIdentityAuthentication` |
|
|
20
|
+
| `@cratis/arc` | `22.75.0` | `IdentityProvider`, `IIdentity` |
|
|
21
|
+
| `@cratis/arc.react` | `22.75.0` | `IdentityProvider` component, `useIdentity`, `RequireRole` |
|
|
22
|
+
| Cratis AuthProxy (`cratis/authproxy`) | `v2.30.5` | the trusted ingress described in [AuthProxy](references/authproxy.md) |
|
|
22
23
|
|
|
23
|
-
>
|
|
24
|
+
> Rechecked at Arc v22.75.0 against the source-pinned authentication, identity,
|
|
25
|
+
> discovery and authorization references below. **Authorization behavior changed materially after `22.16.0`**, the version this skill used to pin: named policies, schemes, fallback baselines, guest policies and Microsoft-attribute enforcement all arrived later. [authorization](references/authorization.md) has a version table; check it if you run an older version.
|
|
24
26
|
|
|
25
27
|
Reverify before claiming support for another version.
|
|
26
28
|
|
|
@@ -43,14 +45,17 @@ is the trust boundary. But it means:
|
|
|
43
45
|
the application itself to validate a token, that is ordinary ASP.NET Core
|
|
44
46
|
authentication you configure yourself.
|
|
45
47
|
|
|
46
|
-
At
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`
|
|
53
|
-
[authentication](references/authentication.md).
|
|
48
|
+
At **Arc v22.75.0**, forwarded identity headers are ignored unless the host opts
|
|
49
|
+
in with `Cratis:Arc:TrustForwardedIdentityHeaders=true` or
|
|
50
|
+
`builder.AddCratis(options => options.TrustForwardedIdentityHeaders = true)`.
|
|
51
|
+
Header-only requests otherwise remain anonymous. `AddCratis()` still registers
|
|
52
|
+
the header scheme; it does not make the headers trusted. Opt in only behind the
|
|
53
|
+
ingress above, otherwise configure a credential-validating scheme. The old
|
|
54
|
+
`Introspection:TrustForwardedIdentityHeaders` setting is obsolete and enables the
|
|
55
|
+
same host-wide flag. See [authentication](references/authentication.md).
|
|
56
|
+
Source: [Arc v22.75.0 secure defaults](https://github.com/Cratis/Arc/blob/v22.75.0/Documentation/upgrading/secure-defaults.mdx).
|
|
57
|
+
|
|
58
|
+
Cratis AuthProxy is the ingress that meets these obligations for Cratis applications. Read [AuthProxy](references/authproxy.md) when the application is deployed behind it (header contract, `/.cratis/me` fail-closed verification, tenant headers).
|
|
54
59
|
|
|
55
60
|
## Identity details — what the frontend is told
|
|
56
61
|
|
|
@@ -85,45 +90,36 @@ public class <Name>IdentityDetailsProvider : IProvideIdentityDetails<<Details>>
|
|
|
85
90
|
|
|
86
91
|
| Route | What it does |
|
|
87
92
|
| --- | --- |
|
|
88
|
-
| `GET /.cratis/me` | The current identity. 401 when not authenticated, 403 when the provider says not authorized, otherwise the
|
|
93
|
+
| `GET /.cratis/me` | The current identity. 401 when not authenticated, 403 when the provider says not authorized, otherwise the principal-derived identity JSON |
|
|
89
94
|
| `GET /.cratis/identity-details/schema` | The JSON schema of the details type (`{}` without a details provider) |
|
|
90
95
|
| `GET /.cratis/users` | Development tooling — aggregates every `ICanProvideUsers` |
|
|
91
96
|
| `GET /.cratis/tenants` | Development tooling — aggregates every `ICanProvideTenants` |
|
|
92
97
|
| `GET /.cratis/commands`, `GET /.cratis/queries` | Introspection catalogs of operation names, types and routes (`MapIntrospectionEndpoints()`) |
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
is `Lax`, `Path` is `/`.
|
|
119
|
-
|
|
120
|
-
⚠️ A non-`HttpOnly` cookie the browser can read is a cookie the user can edit.
|
|
121
|
-
Treat everything in it as a **display** input. Never let it decide anything the
|
|
122
|
-
server has not already decided. At `22.41.1` the default is that `/.cratis/me` returns
|
|
123
|
-
whatever a presented `.cratis-identity` cookie decodes to *before* it looks at the
|
|
124
|
-
authenticated principal, so a client can make it report any identity or roles
|
|
125
|
-
(open Arc#2939). Commands and queries authorize on the request principal and are
|
|
126
|
-
not affected; a frontend that shows or branches on `/.cratis/me` can be misled.
|
|
99
|
+
At **v22.75.0**, discovery is anonymous only when the host's
|
|
100
|
+
`IHostEnvironment.IsDevelopment()` is true. Elsewhere it requires authentication;
|
|
101
|
+
if the host cannot authenticate callers, the routes are not mapped (404) and a
|
|
102
|
+
warning is logged. `RequireAuthentication` unset selects that environment default;
|
|
103
|
+
explicit `true` requires authentication everywhere (startup fails if unavailable);
|
|
104
|
+
explicit `false` exposes discovery anonymously everywhere, warning outside
|
|
105
|
+
Development. `Roles` implies authentication and cannot combine with explicit
|
|
106
|
+
`false`. `Enabled=false` removes the two catalogs; `IdentityDiscovery=false`
|
|
107
|
+
removes users, tenants and the details schema. Setting both removes all discovery
|
|
108
|
+
without authentication checks; `/.cratis/me` remains. `[ExcludeFromDiscovery]`
|
|
109
|
+
hides commands, read models, queries and controllers from catalogs/OpenAPI, **not
|
|
110
|
+
from invocation or authorization**. These are .NET settings, not a JVM contract.
|
|
111
|
+
Source: [secure defaults](https://github.com/Cratis/Arc/blob/v22.75.0/Documentation/upgrading/secure-defaults.mdx).
|
|
112
|
+
|
|
113
|
+
`/.cratis/me` derives identity only from the authenticated request principal,
|
|
114
|
+
never from `.cratis-identity`. The details provider runs for each authenticated
|
|
115
|
+
identity request; `ModifyDetails` changes only the current response. Arc no
|
|
116
|
+
longer writes the readable identity cookie (it expires a legacy one). Identity
|
|
117
|
+
responses carry `Cache-Control: no-store, private` and `Vary: Cookie`.
|
|
118
|
+
Stop reading the cookie; use `IdentityProvider.getCurrent()`/`refresh()` and
|
|
119
|
+
`clearCache()` on logout. The frontend caches in memory, with a warned legacy
|
|
120
|
+
cookie fallback **only after a 404**, never after 401/403.
|
|
121
|
+
Sources: [IdentityProvider.cs](https://github.com/Cratis/Arc/blob/v22.75.0/Source/DotNET/Arc.Core/Identity/IdentityProvider.cs),
|
|
122
|
+
[IdentityProvider.ts](https://github.com/Cratis/Arc/blob/v22.75.0/Source/JavaScript/Arc/identity/IdentityProvider.ts).
|
|
127
123
|
|
|
128
124
|
## Authorization — what Arc evaluates
|
|
129
125
|
|
|
@@ -144,10 +140,10 @@ and `AuthenticationSchemes` enforce nothing, and Microsoft's `[Authorize]` is **
|
|
|
144
140
|
honored on model-bound commands and read models, so a command protected only by it
|
|
145
141
|
is open. From `22.21.0` Microsoft's `[Authorize]`/`[AllowAnonymous]` are enforced on
|
|
146
142
|
the ASP.NET Core host (never on the standalone Core host), and from `22.23.0` named
|
|
147
|
-
policies and schemes are evaluated. The rest of this section is `22.
|
|
143
|
+
policies and schemes are evaluated. The rest of this section is `22.75.0` behavior;
|
|
148
144
|
see [authorization](references/authorization.md) for the version table.
|
|
149
145
|
|
|
150
|
-
At `22.
|
|
146
|
+
At `22.75.0`:
|
|
151
147
|
|
|
152
148
|
- **Named policies work.** `[Authorize(Policy = "…")]` requires the policy to
|
|
153
149
|
succeed asynchronously. Implement
|
|
@@ -204,7 +200,8 @@ restrictive verdict wins, and a guard that throws, or cannot be constructed, fai
|
|
|
204
200
|
closed — the latter denies every observable query in the process. The principal comes
|
|
205
201
|
from `ObservableQueryEmissionContext.Principal`, never from ambient accessors or
|
|
206
202
|
`IHttpContextAccessor`; the guard is constructed per emission, so keep it cheap and
|
|
207
|
-
cache in a singleton.
|
|
203
|
+
cache in a singleton. Since 22.69.1, guards and read-model interceptors also
|
|
204
|
+
protect plain HTTP snapshots, waits and long polls. Read
|
|
208
205
|
[observable emission guards](references/observable-emission-guards.md) when implementing or reviewing revocation of an observable-query subscription.
|
|
209
206
|
|
|
210
207
|
### Validators are not an authorization boundary
|
|
@@ -281,7 +278,7 @@ no details to give, not only when the identity is explicitly unset.
|
|
|
281
278
|
`whileLoading` and `forbidden` slots.
|
|
282
279
|
|
|
283
280
|
⚠️ **`RequireRole` hides UI; it does not protect data.** The identity it reads
|
|
284
|
-
comes from the
|
|
281
|
+
comes from the principal-derived identity endpoint, but browser state is editable. Every rule it expresses
|
|
285
282
|
must also exist on the server as `[Authorize]`/`[Roles]` or an authorization
|
|
286
283
|
filter.
|
|
287
284
|
|
|
@@ -300,11 +297,10 @@ implements as `ICanProvideUsers` and `ICanProvideTenants`.
|
|
|
300
297
|
To run locally with an identity, supply the forwarded headers yourself — see
|
|
301
298
|
[local development](references/local-development.md).
|
|
302
299
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
environment check.
|
|
300
|
+
Those endpoints follow the discovery exposure settings above. Return development
|
|
301
|
+
fixtures from development providers; test deployed discovery access explicitly.
|
|
302
|
+
Forwarded headers sent locally require the host-wide trust opt-in too; do not
|
|
303
|
+
carry that shortcut into a directly reachable deployed host.
|
|
308
304
|
|
|
309
305
|
## Verify
|
|
310
306
|
|
|
@@ -330,7 +326,7 @@ environment check.
|
|
|
330
326
|
- Discovery endpoints (`/.cratis/commands`, `/.cratis/queries`,
|
|
331
327
|
`/.cratis/identity-details/schema`, `/.cratis/users`, `/.cratis/tenants`) are
|
|
332
328
|
tested under Production settings; introspection is restricted or disabled where it
|
|
333
|
-
must not be public,
|
|
329
|
+
must not be public, including identity discovery.
|
|
334
330
|
- `TrustForwardedIdentityHeaders` is set only behind an ingress that authenticates
|
|
335
331
|
callers, strips caller-supplied identity headers, writes trusted ones and blocks
|
|
336
332
|
direct backend access; custom handlers honor it.
|