Skip to content
Registry StackDocsDevelopment (unreleased)

Test with journeys

View as Markdown

If you are verifying a Base Registry Engine (BReg) project, compile it offline, inspect what the compiler derived, and run its journeys against an isolated database before packaging it. Start from the facility template in Author a registry project:

Terminal window
bregctl init --template facility-registry facility
Terminal window
bregctl check facility
Authoring check passed; registry revision sha256:<digest>.

An error refuses the project; a warning is a decision to review. Add --production for production package rules and --deny-warnings if your policy requires it. Check also reads dev-clients.yaml and the journey files under tests/, so a renamed Record type, profile, or Action fails there too.

bregctl explain <subject> facility prints one compiled inventory without a database. The subjects are model (Record types and fields), access (what each profile may do), routes, queries (list queries per profile), actions, proposals (Proposal Actions only), events (declared deliveries), and lifecycle (the engine’s Proposal state transitions; it takes no project).

Terminal window
bregctl explain actions facility
correct-address (proposal)
inputs facility, address-line, city, postal-code
plan function plans/correct-address.rhai
write ceiling: patch facility, fields address-line, city, postal-code
commit guard facility: revision unchanged

Add --format json for the complete definition and --production for production rules. For access, --scenario <file> evaluates synthetic claims against the profiles without verifying a token or reading a record. Explain shows the compiled contract, not runtime success.

Terminal window
bregctl generate openapi facility --output facility-openapi

Every artifact family (bregctl generate --help) derives from the same model: fix the project, never a generated copy.

tests/journeys.yaml lists journeys, each a sequence of steps. A step names a Record type or Action, a profile, synthetic claims, a request, and the expected outcome. The facility journey registers a facility, then reads it as a caller from another jurisdiction:

facility/tests/journeys.yaml
- id: conceal-foreign-facility
recordType: facility
accessProfile: facility-reader
claims:
principal: synthetic-foreign-reader
scopes: [registry:facility:read]
purpose: facility-administration
directClaims: {jurisdiction: zone-b}
request: {type: get, recordCapture: facility}
expect: {outcome: refusal, status: 404, problemCode: resource.not_found}

Keep the success and the refusal; add a step for each invariant your change affects.

A plan Function has an offline loop, with input keyed by authored input ID:

correction-input.json
{"facility": "018f0c3e-7b1a-7c00-8000-000000000001", "address-line": "2 Corrected Road", "city": "Corrected city", "postal-code": "10300"}
Terminal window
bregctl project action-test facility --action correct-address --input correction-input.json
Ran the handler. Returned 1 effect.

--expect <file> asserts exact effects or a refusal. This proves the plan, not authority, guards, or a transaction.

This needs a separate schema-test database and runtime file with migration and runtime roles. Bind each protected step to a real short-lived credential whose claims match the step; synthetic claims authenticate nothing. Keep credentials outside the project.

Terminal window
bregctl test facility --runtime-config /path/to/runtime-test.yaml \
--credentials /path/to/credentials.yaml --output /path/to/test-receipt.json

A passing run writes a receipt for that exact candidate; packaging accepts it and refuses changed inputs. A refused step names the failure; repair it and rerun into a fresh receipt path.

Every key tests/journeys.yaml accepts, with its type and bounds. bregctl check adds the rules a schema cannot state.

Key pathTypeRequiredAccepted valuesConstraints
apiVersionThe format and version this file is written in.stringYes
  • id.registrystack.org/formats/breg/journeys/v1
None specified
journeysarrayYesNot specified
  • maxItems: 128
  • minItems: 1
journeys[]Array itemobjectNoNot specifiedNone specified
journeys[].idA local identifier: a lowercase letter, then up to 63 lowercase letters, digits, _, or -.stringYesNot specified
  • pattern: ^[a-z][a-z0-9_-]{0,63}$
journeys[].stepsarrayYesNot specified
  • maxItems: 128
  • minItems: 1
journeys[].steps[]Array itemobjectNoNot specifiedNone specified
journeys[].steps[].accessProfilestringYesNot specifiedNone specified
journeys[].steps[].actionstringNoNot specifiedNone specified
journeys[].steps[].captureLocal capture alias for this step's result. Capture aliases must be unique throughout the journey, including aliases in captureResults; the reader checks this semantic rule.stringNoNot specified
  • pattern: ^[a-z][a-z0-9_-]{0,63}$
journeys[].steps[].captureResultsMaps disclosed Action result aliases to distinct local capture aliases. Capture aliases must be unique throughout the journey, including earlier captures; the reader checks this semantic rule.objectNoNot specifiedSchema default, where declared: {}None specified
journeys[].steps[].captureResults.*Map valueA local identifier: a lowercase letter, then up to 63 lowercase letters, digits, _, or -.stringNoNot specified
  • pattern: ^[a-z][a-z0-9_-]{0,63}$
  • propertyNames.pattern: ^[a-z][a-z0-9_-]{0,63}$
journeys[].steps[].claimsobjectNoNot specifiedNone specified
journeys[].steps[].claims.actorKindstringNo
  • agent
  • human
  • service
None specified
journeys[].steps[].claims.actorSubjectstringNoNot specifiedNone specified
journeys[].steps[].claims.directClaimsobjectNoNot specifiedNone specified
journeys[].steps[].claims.directClaims.*Map valuearray | stringNoNot specified
  • propertyNames.maxLength: 512
  • propertyNames.minLength: 1
  • propertyNames.pattern: ^[^\u0000-\u001F\u007F-\u009F]+$
journeys[].steps[].claims.directClaims.*[]Array itemstringConditionalNot specifiedNone specified
journeys[].steps[].claims.principalstringNoNot specifiedNone specified
journeys[].steps[].claims.purposestringNoNot specifiedNone specified
journeys[].steps[].claims.requesterClientstringNoNot specifiedNone specified
journeys[].steps[].claims.scopesarrayNoNot specifiedSchema default, where declared: []
  • uniqueItems: true
journeys[].steps[].claims.scopes[]Array itemstringNoNot specifiedNone specified
journeys[].steps[].expectobjectYesNot specifiedNone specified
journeys[].steps[].expect.countintegerNoNot specified
  • format: uint32
  • maximum: 100
  • minimum: 0
journeys[].steps[].expect.fieldIdstringNoNot specifiedNone specified
journeys[].steps[].expect.fieldsA body of the registry's own HTTP API, carried as written: the registry reads its members, and null clears a field.objectNoNot specifiedSchema default, where declared: {}None specified
journeys[].steps[].expect.membersExact unordered collection membership, keyed by a prior Record capture. Omission keeps the historical shape-only collection check; an empty mapping explicitly expects no rows.objectNoNot specified
  • maxProperties: 100
journeys[].steps[].expect.members.*Map valueobjectNoNot specified
  • propertyNames.pattern: ^[a-z][a-z0-9_-]{0,63}$
journeys[].steps[].expect.members.*.fieldsA body of the registry's own HTTP API, carried as written: the registry reads its members, and null clears a field.objectNoNot specifiedSchema default, where declared: {}None specified
journeys[].steps[].expect.outcomestringYes
  • refusal
  • success
None specified
journeys[].steps[].expect.problemCodestringNoNot specifiedNone specified
journeys[].steps[].expect.recordTypestringNoNot specifiedNone specified
journeys[].steps[].expect.refusalCodestringNoNot specifiedNone specified
journeys[].steps[].expect.statusintegerYesNot specified
  • format: uint32
  • maximum: 599
  • minimum: 100
journeys[].steps[].idA local identifier: a lowercase letter, then up to 63 lowercase letters, digits, _, or -.stringYesNot specified
  • pattern: ^[a-z][a-z0-9_-]{0,63}$
journeys[].steps[].recordTypestringNoNot specifiedSchema default, where declared: ""None specified
journeys[].steps[].requestOne request form. type names the form; the members beside it are the form's own. Request bodies (items, data, values, input, and a change's value) are the registry's HTTP API bodies, carried as written.objectYesNot specifiedNone specified
journeys[].steps[].request.bboxobjectConditionalNot specifiedNone specified
journeys[].steps[].request.bbox.eaststringConditionalNot specifiedNone specified
journeys[].steps[].request.bbox.northstringConditionalNot specifiedNone specified
journeys[].steps[].request.bbox.southstringConditionalNot specifiedNone specified
journeys[].steps[].request.bbox.weststringConditionalNot specifiedNone specified
journeys[].steps[].request.changesarrayConditionalNot specifiedNone specified
journeys[].steps[].request.changes[]Array itemobjectConditionalNot specifiedNone specified
journeys[].steps[].request.changes[].fieldstringConditionalNot specifiedNone specified
journeys[].steps[].request.changes[].valueA body of the registry's own HTTP API, carried as written: the registry reads its members, and null clears a field.Not specifiedConditionalNot specifiedNone specified
journeys[].steps[].request.countbooleanConditionalNot specifiedSchema default, where declared: falseNone specified
journeys[].steps[].request.dataA body of the registry's own HTTP API, carried as written: the registry reads its members, and null clears a field.objectConditionalNot specifiedNone specified
journeys[].steps[].request.etagCaptureA local identifier: a lowercase letter, then up to 63 lowercase letters, digits, _, or -.stringConditionalNot specified
  • pattern: ^[a-z][a-z0-9_-]{0,63}$

or

No bound

journeys[].steps[].request.idempotencyKeystringConditionalNot specifiedNone specified
journeys[].steps[].request.inputA body of the registry's own HTTP API, carried as written: the registry reads its members, and null clears a field.objectConditionalNot specifiedSchema default, where declared: {}None specified
journeys[].steps[].request.itemsarrayConditionalNot specifiedNone specified
journeys[].steps[].request.items[]Array itemA body of the registry's own HTTP API, carried as written: the registry reads its members, and null clears a field.objectConditionalNot specifiedNone specified
journeys[].steps[].request.items[].dataA body of the registry's own HTTP API, carried as written: the registry reads its members, and null clears a field.objectConditionalNot specifiedNone specified
journeys[].steps[].request.items[].typestringConditional
  • create
None specified
journeys[].steps[].request.pathstringConditionalNot specifiedNone specified
journeys[].steps[].request.preconditionsobjectConditionalNot specifiedNone specified
journeys[].steps[].request.preconditions.*Map valueobjectConditionalNot specified
  • propertyNames.maxLength: 512
  • propertyNames.minLength: 1
  • propertyNames.pattern: ^[^\u0000-\u001F\u007F-\u009F]+$
journeys[].steps[].request.preconditions.*.conditionCapturestringConditionalNot specifiedNone specified
journeys[].steps[].request.preconditions.*.ifMatchstringConditionalNot specifiedNone specified
journeys[].steps[].request.proposalCaptureA local identifier: a lowercase letter, then up to 63 lowercase letters, digits, _, or -.stringConditionalNot specified
  • pattern: ^[a-z][a-z0-9_-]{0,63}$
journeys[].steps[].request.recordCapturestringConditionalNot specifiedNone specified
journeys[].steps[].request.selectarrayConditionalNot specifiedSchema default, where declared: []
  • uniqueItems: true
journeys[].steps[].request.select[]Array itemstringConditionalNot specifiedNone specified
journeys[].steps[].request.selectorstringConditionalNot specifiedNone specified
journeys[].steps[].request.submissionCaptureA local identifier: a lowercase letter, then up to 63 lowercase letters, digits, _, or -.stringConditionalNot specified
  • pattern: ^[a-z][a-z0-9_-]{0,63}$
journeys[].steps[].request.topintegerConditionalNot specified
  • format: uint32
  • maximum: 100
  • minimum: 1
journeys[].steps[].request.transitionstringConditional
  • apply
  • cancel
  • edit
  • get
  • get-review-context
  • get-review-status
  • rebase
  • retry
  • revise
  • start
  • submit
None specified
journeys[].steps[].request.typestringYes
  • batch
  • create
  • get
  • import
  • invoke
  • list
  • lookup
  • patch
  • proposal
  • query
  • read-path
  • target-conditions
None specified
journeys[].steps[].request.valuesA body of the registry's own HTTP API, carried as written: the registry reads its members, and null clears a field.objectConditionalNot specifiedSchema default, where declared: {}None specified
kindThe kind of document this file is.stringYes
  • BRegJourneys
None specified