From bcf116996c3ec825dfbefc9f03c83619cfba1567 Mon Sep 17 00:00:00 2001 From: Joshua Temple Date: Thu, 4 Jun 2026 18:31:03 -0400 Subject: [PATCH] refactor: rename state Assay check to Verify Signed-off-by: Joshua Temple --- .../{assay-gate.png => verify-gate.png} | Bin docs/src/content/docs/authoring/assay.md | 49 ------------------ docs/src/content/docs/authoring/verify.md | 49 ++++++++++++++++++ .../content/docs/concepts/value-semantics.md | 2 +- .../integrating/pointer-heavy-codebases.md | 6 +-- docs/src/content/docs/source/with-state.md | 2 +- .../content/docs/start/foundry-vocabulary.md | 6 +-- source/errors.go | 2 +- source/statemachine/README.md | 2 +- source/statemachine/doc.go | 2 +- state/CHANGELOG.md | 12 +++-- state/README.md | 7 +-- state/coverage_test.go | 8 +-- state/doc.go | 10 ++-- state/errors.go | 8 +-- state/example_test.go | 8 +-- state/kernel.go | 4 +- state/options.go | 10 ++-- state/{assay.go => verify.go} | 10 ++-- state/{assay_test.go => verify_test.go} | 30 +++++------ 20 files changed, 117 insertions(+), 110 deletions(-) rename docs/src/assets/{assay-gate.png => verify-gate.png} (100%) delete mode 100644 docs/src/content/docs/authoring/assay.md create mode 100644 docs/src/content/docs/authoring/verify.md rename state/{assay.go => verify.go} (77%) rename state/{assay_test.go => verify_test.go} (54%) diff --git a/docs/src/assets/assay-gate.png b/docs/src/assets/verify-gate.png similarity index 100% rename from docs/src/assets/assay-gate.png rename to docs/src/assets/verify-gate.png diff --git a/docs/src/content/docs/authoring/assay.md b/docs/src/content/docs/authoring/assay.md deleted file mode 100644 index 75b4649..0000000 --- a/docs/src/content/docs/authoring/assay.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Assay -description: Verify that an externally-hydrated entity legally belongs in a given state. -sidebar: - order: 11 ---- - - -![Assay at the trust boundary](../../../assets/assay-gate.png) - -When an entity arrives from outside, whether loaded from a store, deserialized off the wire, or rebuilt by a foreign system, you cannot trust that it actually *belongs* in the state it claims. **`Assay`** is the trust-boundary check: it runs a state's declarative requirements (its guards and invariants) against an entity *without firing a transition*, answering "is this entity legally in this state?" - -```go -order := loadFromStore(id) // hydrated externally; claims to be Cooking - -if err := machine.Assay(Cooking, order); err != nil { - return fmt.Errorf("order %s is not legally in Cooking: %w", id, err) -} -// Safe to resume from here. -``` - -By default `Assay` is **fail-fast**: it returns an `*AssayError` carrying the first requirement that failed. To collect *every* violation in one pass, useful for reporting or validation UIs, pass `state.Aggregate()`: - -```go -err := machine.Assay(Cooking, order, state.Aggregate()) - -var assayErr *state.AssayError -if errors.As(err, &assayErr) { - for _, f := range assayErr.Failures { - log.Printf("violation: %s: %s", f.Name, f.Reason) - } -} -``` - -The error type is uniform across both modes; only how many failures it carries differs. - -```mermaid -stateDiagram-v2 - [*] --> Hydrated - Hydrated --> Assay: external entity - Assay --> Resumed: requirements pass - Assay --> Rejected: AssayError - note right of Assay - runs the state's guards/invariants, - fires nothing - end note -``` - -Use `Assay` wherever an entity crosses into your control before you resume driving it. It turns "I hope this object is valid" into a checked guarantee, without mutating the entity or advancing the machine. diff --git a/docs/src/content/docs/authoring/verify.md b/docs/src/content/docs/authoring/verify.md new file mode 100644 index 0000000..8cf008d --- /dev/null +++ b/docs/src/content/docs/authoring/verify.md @@ -0,0 +1,49 @@ +--- +title: Verify +description: Verify that an externally-hydrated entity legally belongs in a given state. +sidebar: + order: 11 +--- + + +![Verify at the trust boundary](../../../assets/verify-gate.png) + +When an entity arrives from outside, whether loaded from a store, deserialized off the wire, or rebuilt by a foreign system, you cannot trust that it actually *belongs* in the state it claims. **`Verify`** is the trust-boundary check: it runs a state's declarative requirements (its guards and invariants) against an entity *without firing a transition*, answering "is this entity legally in this state?" + +```go +order := loadFromStore(id) // hydrated externally; claims to be Cooking + +if err := machine.Verify(Cooking, order); err != nil { + return fmt.Errorf("order %s is not legally in Cooking: %w", id, err) +} +// Safe to resume from here. +``` + +By default `Verify` is **fail-fast**: it returns an `*VerifyError` carrying the first requirement that failed. To collect *every* violation in one pass, useful for reporting or validation UIs, pass `state.Aggregate()`: + +```go +err := machine.Verify(Cooking, order, state.Aggregate()) + +var verifyErr *state.VerifyError +if errors.As(err, &verifyErr) { + for _, f := range verifyErr.Failures { + log.Printf("violation: %s: %s", f.Name, f.Reason) + } +} +``` + +The error type is uniform across both modes; only how many failures it carries differs. + +```mermaid +stateDiagram-v2 + [*] --> Hydrated + Hydrated --> Verify: external entity + Verify --> Resumed: requirements pass + Verify --> Rejected: VerifyError + note right of Verify + runs the state's guards/invariants, + fires nothing + end note +``` + +Use `Verify` wherever an entity crosses into your control before you resume driving it. It turns "I hope this object is valid" into a checked guarantee, without mutating the entity or advancing the machine. diff --git a/docs/src/content/docs/concepts/value-semantics.md b/docs/src/content/docs/concepts/value-semantics.md index a9e8ca1..10c66eb 100644 --- a/docs/src/content/docs/concepts/value-semantics.md +++ b/docs/src/content/docs/concepts/value-semantics.md @@ -38,7 +38,7 @@ Treating context as an immutable value, replaced and never edited, is what gives value reproduces the same results, every time. - **Durable execution.** A snapshot can be persisted, reloaded, and resumed because nothing lives in hidden pointers or background goroutines. -- **Verification.** `Assay` can check an entity's legality against a state +- **Verification.** `Verify` can check an entity's legality against a state because the entity *is* the value the guards see. ## The pointer escape hatch diff --git a/docs/src/content/docs/integrating/pointer-heavy-codebases.md b/docs/src/content/docs/integrating/pointer-heavy-codebases.md index ef4952f..24f90dd 100644 --- a/docs/src/content/docs/integrating/pointer-heavy-codebases.md +++ b/docs/src/content/docs/integrating/pointer-heavy-codebases.md @@ -31,13 +31,13 @@ A value `C` is not a stylistic preference. It is what makes the kernel's guarant ### The boundary recipe -On the way in, when you hydrate an aggregate that *claims* to be in some state, use [`Assay`](/crucible/authoring/assay/) to check it is legally there before you resume driving it: +On the way in, when you hydrate an aggregate that *claims* to be in some state, use [`Verify`](/crucible/authoring/verify/) to check it is legally there before you resume driving it: ```go order := repo.Load(ctx, id) // hydrated externally view := order.project() -if err := machine.Assay(order.State(), view); err != nil { +if err := machine.Verify(order.State(), view); err != nil { return fmt.Errorf("order %s not legal in %s: %w", id, order.State(), err) } ``` @@ -58,7 +58,7 @@ repo.Save(ctx, order) // YOUR persistence, YOUR transaction ```mermaid flowchart LR L[load aggregate] --> P[project to value C] - P --> A[Assay legal?] + P --> A[Verify legal?] A --> F[Fire] F --> E[apply effects to aggregate] E --> S[persist in your txn] diff --git a/docs/src/content/docs/source/with-state.md b/docs/src/content/docs/source/with-state.md index 705d9de..a8b65d3 100644 --- a/docs/src/content/docs/source/with-state.md +++ b/docs/src/content/docs/source/with-state.md @@ -56,7 +56,7 @@ state, not a side table you have to operate. ## State-aware rejection is first-class -An event that is invalid for the current state is a guard (or Assay) rejection, +An event that is invalid for the current state is a guard (or Verify) rejection, not an infra failure. The bridge classifies it as `Term` (poison) and routes it to the [DLQ](/crucible/source/reliability/#dlq), distinct from a transient error that becomes a `Nak` and retries. Offset-based libraries cannot tell these apart; diff --git a/docs/src/content/docs/start/foundry-vocabulary.md b/docs/src/content/docs/start/foundry-vocabulary.md index 7afc5ba..2b8ba17 100644 --- a/docs/src/content/docs/start/foundry-vocabulary.md +++ b/docs/src/content/docs/start/foundry-vocabulary.md @@ -1,6 +1,6 @@ --- title: Foundry vocabulary -description: The lifecycle verbs (Forge, Temper, Quench, Cast, Fire, Assay) and what each one does. +description: The lifecycle verbs (Forge, Temper, Quench, Cast, Fire) and what each one does. sidebar: order: 3 --- @@ -19,7 +19,6 @@ authoring, an immutable definition, and a running instance. | **Quench** | `b.Quench() *Machine` | Freezes the builder into an immutable `*Machine`. **Panics on misconfiguration** (unknown states, dangling refs). | Once, when the definition is complete. The `*Machine` is safe to share across goroutines. | | **Cast** | `m.Cast(entity, opts...) *Instance` | Creates a running `*Instance` seeded with your context entity (by value). | Per entity you want to track. Cheap; cast freely. | | **Fire** | `inst.Fire(ctx, event, opts...) FireResult` | Advances the instance. Returns `FireResult{NewState, Effects, Trace, Err}`. Performs **no IO**; effects are data. | Every time an event arrives. | -| **Assay** | `m.Assay(state, entity, opts...) error` | Verifies that an externally-built entity is *legally* in a given state, running the relevant guards. `FailFast` by default; `Aggregate()` collects all violations. | When an entity is reconstructed from storage or another system and you need to trust its state. | ## Plain verbs @@ -27,11 +26,12 @@ Not everything earns a metaphor. These read literally: | Verb | Signature (shape) | What it does | | --- | --- | --- | +| `Verify` | `m.Verify(state, entity, opts...) error` | Checks that an externally-built entity is *legally* in a given state, running the relevant requirements. Fail-fast by default; `Aggregate()` collects all violations. | | `PlanPath` | `m.PlanPath(from, to, entity, opts...) ([]E, error)` | Computes a sequence of events that would drive an entity from one state to another. | | `Trace` | (field on `FireResult`) | The ordered record of what happened during a `Fire`: transitions, guards, regions. | | `ToJSON` / `LoadFromJSON` | `m.ToJSON()` / `LoadFromJSON[S,E,C](b)` | Round-trip the canonical IR losslessly to and from JSON. | | `ToMermaid` / `ToDOT` | `m.ToMermaid()` / `m.ToDOT()` | Render the machine as a diagram for docs or inspection. | A typical session uses **Forge → (Temper) → Quench** once, then **Cast** and -**Fire** many times, with **Assay** at the edges where entities cross trust +**Fire** many times, with **Verify** at the edges where entities cross trust boundaries. diff --git a/source/errors.go b/source/errors.go index a1337ee..4a80140 100644 --- a/source/errors.go +++ b/source/errors.go @@ -77,7 +77,7 @@ func (e *DecodeError) Unwrap() error { return e.Err } // decode failure to dead-letter with a single errors.Is(err, ErrPoison) check. func (e *DecodeError) Is(target error) bool { return target == ErrPoison } -// GuardRejection wraps a guard/Assay rejection: a well-formed event that is not +// GuardRejection wraps a guard/Verify rejection: a well-formed event that is not // legal for the target's current state. It is errors.Is / errors.As friendly via // Unwrap and reports ErrInvalidForState from Is, so a guard rejection is // recognized as state-invalid (and routed to dead-letter as a distinct, "wrong diff --git a/source/statemachine/README.md b/source/statemachine/README.md index 375d77e..11d6e1b 100644 --- a/source/statemachine/README.md +++ b/source/statemachine/README.md @@ -37,7 +37,7 @@ The ack comes only after a successful durable `Store.Save` so redelivery is provably idempotent with no external dedup store. Make the id extractor `WithEventID`. - **State-aware rejection.** A `Fire` rejected because the event is illegal for - the current state (no transition, or a failing guard/`Assay`) returns + the current state (no transition, or a failing guard/`Verify`) returns `source.Reject` (Term, `InvalidForState`) carrying a `*source.GuardRejection` — distinct from a transient `Store`/infra error, which returns `source.Nak` (Retryable). diff --git a/source/statemachine/doc.go b/source/statemachine/doc.go index d297088..6a76035 100644 --- a/source/statemachine/doc.go +++ b/source/statemachine/doc.go @@ -27,7 +27,7 @@ // is the dedup key. // - State-aware rejection. A [state.Instance.Fire] that fails because the // event is illegal for the current state (no declared transition, or a -// failing guard/[state.Machine.Assay]) returns [source.Reject] +// failing guard/[state.Machine.Verify]) returns [source.Reject] // (Term, classified InvalidForState) carrying a [*source.GuardRejection] — // distinct from a transient [Store] or infrastructure error, which returns // [source.Nak] (Retryable). "Wrong time" and "try again later" are diff --git a/state/CHANGELOG.md b/state/CHANGELOG.md index 843523a..d62245f 100644 --- a/state/CHANGELOG.md +++ b/state/CHANGELOG.md @@ -435,8 +435,14 @@ representative hot-path numbers. directly, or that parsed the type-name suffix of an `EffectsEmitted` label, must update; type-switching on the effect structs is unaffected (the structs only gained methods and tags). -- **BREAKING: the `Assay` option `WithAggregate` is renamed `Aggregate`.** The - option that makes `Assay` collect all failing requirements in one pass instead +- **BREAKING: the state trust-boundary check `Assay` is renamed `Verify`.** The + method `Machine.Assay`, its error type `AssayError`, and its option type + `AssayOption` become `Machine.Verify`, `VerifyError`, and `VerifyOption`. The + rename trades the foundry metaphor for a plain, discoverable verb; the + behavior and signatures are otherwise unchanged. Replace `Assay`, `AssayError`, + and `AssayOption` at the call site. +- **BREAKING: the `Verify` option `WithAggregate` is renamed `Aggregate`.** The + option that makes `Verify` collect all failing requirements in one pass instead of failing fast is now `Aggregate()`. Replace `WithAggregate()` with `Aggregate()` at the call site. - The determinism and ordering contract is now explicit and frozen: emission @@ -485,7 +491,7 @@ Initial release of the pure state-machine kernel. ### Added -- Kernel core: `Forge`/`Temper`/`Quench`/`Cast`/`Fire`/`Assay` foundry API with +- Kernel core: `Forge`/`Temper`/`Quench`/`Cast`/`Fire`/`Verify` foundry API with pure-function step semantics. Firing an event returns `(newState, effects, trace)` with no IO. - Serializable definition IR with lossless JSON round-trip; guards, actions, and diff --git a/state/README.md b/state/README.md index e49a445..2f7ab7b 100644 --- a/state/README.md +++ b/state/README.md @@ -123,10 +123,11 @@ The lifecycle API uses a small "foundry" verb vocabulary. The noun stays plain | `Quench` | Freeze the definition into an immutable `Machine`; binds refs. | | `Cast` | Pour a running instance from the machine. | | `Fire` | Send an event to an instance and advance it. | -| `Assay` | Check that an externally-built entity is legally in a given state. | -Operations that favor discoverability over metaphor stay plain: `PlanPath`, -`Requirements`, `Trace`, and the `To*` / `LoadFromJSON` serializers. +Operations that favor discoverability over metaphor stay plain: `Verify`, +`PlanPath`, `Requirements`, `Trace`, and the `To*` / `LoadFromJSON` serializers. + +`Verify` checks that an externally-built entity is legally in a given state. The public API follows the suite's functional-options convention: required inputs stay positional; everything optional is a variadic option, so a diff --git a/state/coverage_test.go b/state/coverage_test.go index 0c481bb..5d7c8b8 100644 --- a/state/coverage_test.go +++ b/state/coverage_test.go @@ -27,7 +27,7 @@ func TestErrorMessages(t *testing.T) { {&state.ErrNoPath{From: "A", To: "B"}, "no path from \"A\" to \"B\""}, {&state.ErrNoInitialState{Machine: "m"}, "no CurrentStateFn"}, {&state.MultiRegionErr{Errors: []error{errors.New("r1"), errors.New("r2")}}, "2 regions errored"}, - {&state.AssayError{Failures: []state.RequirementFailure{{Name: "req"}}}, "assay failed"}, + {&state.VerifyError{Failures: []state.RequirementFailure{{Name: "req"}}}, "verify failed"}, } for _, c := range cases { if got := c.err.Error(); !strings.Contains(got, c.want) { @@ -153,10 +153,10 @@ func TestQuenchError_Message(t *testing.T) { Quench() } -// TestAssay_UndeclaredState covers the Assay early return on an unknown state. -func TestAssay_UndeclaredState(t *testing.T) { +// TestVerify_UndeclaredState covers the Verify early return on an unknown state. +func TestVerify_UndeclaredState(t *testing.T) { m := buildDocMachine() - err := m.Assay(DocState(99), &Document{}) + err := m.Verify(DocState(99), &Document{}) var us *state.ErrUndeclaredState if !errors.As(err, &us) { t.Fatalf("err = %v, want *ErrUndeclaredState", err) diff --git a/state/doc.go b/state/doc.go index 0e633e4..f07f00f 100644 --- a/state/doc.go +++ b/state/doc.go @@ -64,11 +64,11 @@ // finalizer that binds refs and panics on misconfiguration. // - Cast — pour a running instance from the machine. // - Fire — send an event to an instance and advance it. -// - Assay — check that an externally-constructed entity is legally in a -// given state. +// - Verify — plain verb (favoring discoverability over metaphor): check that +// an externally-constructed entity is legally in a given state. // -// Operations that favor discoverability over metaphor stay plain: PlanPath, -// Requirements, Trace, and the To*/LoadFromJSON serializers. +// Operations that favor discoverability over metaphor stay plain: Verify, +// PlanPath, Requirements, Trace, and the To*/LoadFromJSON serializers. // // # Context: assigns and value semantics // @@ -200,7 +200,7 @@ // // The kernel implements the Forge/Temper/Quench build path, Cast/Fire pure step // semantics with guards, actions, typed errors and an opt-in structured Trace, -// Assay/Requirements, PlanPath (BFS), FireSeq/FireEach batch helpers, and +// Verify/Requirements, PlanPath (BFS), FireSeq/FireEach batch helpers, and // lossless ToJSON/LoadFromJSON/Provide round-trip. // // Hierarchical and orthogonal states extend the same surface: a state may diff --git a/state/errors.go b/state/errors.go index f0def7f..f550218 100644 --- a/state/errors.go +++ b/state/errors.go @@ -236,17 +236,17 @@ func (e *MultiRegionErr) Error() string { // Unwrap exposes the per-region errors for errors.As / errors.Is traversal. func (e *MultiRegionErr) Unwrap() []error { return e.Errors } -// AssayError aggregates one or more failing requirements found by Assay. -type AssayError struct { +// VerifyError aggregates one or more failing requirements found by Verify. +type VerifyError struct { Failures []RequirementFailure } -func (e *AssayError) Error() string { +func (e *VerifyError) Error() string { names := make([]string, 0, len(e.Failures)) for _, f := range e.Failures { names = append(names, f.Name) } - return fmt.Sprintf("crucible/state: assay failed: %s", strings.Join(names, ", ")) + return fmt.Sprintf("crucible/state: verify failed: %s", strings.Join(names, ", ")) } // ErrUnsupportedSchema is returned by LoadFromJSON when an IR document declares a diff --git a/state/example_test.go b/state/example_test.go index 82ee084..b8f85d5 100644 --- a/state/example_test.go +++ b/state/example_test.go @@ -66,13 +66,13 @@ func ExampleMachine_ToJSON() { // stable: true } -// ExampleMachine_Assay checks an externally-built entity against a state's +// ExampleMachine_Verify checks an externally-built entity against a state's // declarative requirements without firing a transition. -func ExampleMachine_Assay() { +func ExampleMachine_Verify() { m := buildDocMachine() - missing := m.Assay(Approved, &Document{Status: Approved}) - ok := m.Assay(Approved, &Document{Status: Approved, ReviewerID: strptr("rev-1")}) + missing := m.Verify(Approved, &Document{Status: Approved}) + ok := m.Verify(Approved, &Document{Status: Approved, ReviewerID: strptr("rev-1")}) fmt.Println("missing reviewer:", missing != nil) fmt.Println("with reviewer:", ok) diff --git a/state/kernel.go b/state/kernel.go index f2583af..b19d4b0 100644 --- a/state/kernel.go +++ b/state/kernel.go @@ -72,7 +72,7 @@ const ( type WaitMode int // Wait modes. SyncReply awaits a reply, FireAndForget emits and moves on, and -// ValidatePoll signals the consumer to poll the entity (re-running Assay) until +// ValidatePoll signals the consumer to poll the entity (re-running Verify) until // it validates. const ( SyncReply WaitMode = iota @@ -508,7 +508,7 @@ type ActionCtx[C any] struct { // ActionFn produces an effect (or error) for a transition. type ActionFn[C any] func(ctx ActionCtx[C]) (Effect, error) -// Requirement is a declarative condition for a state, used by Assay. +// Requirement is a declarative condition for a state, used by Verify. type Requirement[C any] struct { Name string Predicate func(C) bool diff --git a/state/options.go b/state/options.go index f4670e3..2c34e1a 100644 --- a/state/options.go +++ b/state/options.go @@ -252,16 +252,16 @@ func WithEventData(data any) FireOption { } } -// AssayOption configures Assay. -type AssayOption func(*assayConfig) +// VerifyOption configures Verify. +type VerifyOption func(*verifyConfig) -type assayConfig struct{ aggregate bool } +type verifyConfig struct{ aggregate bool } -// Aggregate makes Assay collect all failing requirements in one pass instead of +// Aggregate makes Verify collect all failing requirements in one pass instead of // failing fast at the first. It is a pure directive option (it carries no value), // so it drops the With prefix that value-carrying options keep — matching Strict // and CollectAll. -func Aggregate() AssayOption { return func(c *assayConfig) { c.aggregate = true } } +func Aggregate() VerifyOption { return func(c *verifyConfig) { c.aggregate = true } } // PlanOption configures PlanPath. type PlanOption func(*planConfig) diff --git a/state/assay.go b/state/verify.go similarity index 77% rename from state/assay.go rename to state/verify.go index 23a097f..7820394 100644 --- a/state/assay.go +++ b/state/verify.go @@ -12,12 +12,12 @@ func (m *Machine[S, E, C]) Requirements(s S) []Requirement[C] { return append([]Requirement[C](nil), reqs...) } -// Assay checks that an externally-constructed entity legally satisfies a +// Verify checks that an externally-constructed entity legally satisfies a // state's declarative requirements, without firing. The default mode is -// fail-fast (the returned *AssayError carries the first failure); Aggregate +// fail-fast (the returned *VerifyError carries the first failure); Aggregate // collects every failure in one pass. The error type is uniform across modes. -func (m *Machine[S, E, C]) Assay(s S, entity C, opts ...AssayOption) error { - cfg := assayConfig{} +func (m *Machine[S, E, C]) Verify(s S, entity C, opts ...VerifyOption) error { + cfg := verifyConfig{} for _, o := range opts { o(&cfg) } @@ -43,7 +43,7 @@ func (m *Machine[S, E, C]) Assay(s S, entity C, opts ...AssayOption) error { } if len(failures) > 0 { - return &AssayError{Failures: failures} + return &VerifyError{Failures: failures} } return nil } diff --git a/state/assay_test.go b/state/verify_test.go similarity index 54% rename from state/assay_test.go rename to state/verify_test.go index 11ceea0..1470f97 100644 --- a/state/assay_test.go +++ b/state/verify_test.go @@ -7,52 +7,52 @@ import ( "github.com/stablekernel/crucible/state" ) -// TestAssay_FailFast asserts Assay returns *AssayError with a single failure in +// TestVerify_FailFast asserts Verify returns *VerifyError with a single failure in // the default fail-fast mode when the entity does not satisfy the state. -func TestAssay_FailFast(t *testing.T) { +func TestVerify_FailFast(t *testing.T) { m, rec := safeBuild(t) if rec != nil { t.Skipf("build not implemented yet: %v", rec) } // A document in Approved state requires a reviewer; this one has none. doc := &Document{} - err := m.Assay(Approved, doc) - var ae *state.AssayError + err := m.Verify(Approved, doc) + var ae *state.VerifyError if !errors.As(err, &ae) { - t.Fatalf("err = %v, want *AssayError", err) + t.Fatalf("err = %v, want *VerifyError", err) } if len(ae.Failures) != 1 { t.Fatalf("Failures = %d, want 1 (fail-fast)", len(ae.Failures)) } } -// TestAssay_Aggregate asserts Aggregate collects all failing requirements -// and that the error type is uniform (*AssayError) across both modes. -func TestAssay_Aggregate(t *testing.T) { +// TestVerify_Aggregate asserts Aggregate collects all failing requirements +// and that the error type is uniform (*VerifyError) across both modes. +func TestVerify_Aggregate(t *testing.T) { m, rec := safeBuild(t) if rec != nil { t.Skipf("build not implemented yet: %v", rec) } doc := &Document{} - err := m.Assay(Approved, doc, state.Aggregate()) - var ae *state.AssayError + err := m.Verify(Approved, doc, state.Aggregate()) + var ae *state.VerifyError if !errors.As(err, &ae) { - t.Fatalf("err = %v, want *AssayError", err) + t.Fatalf("err = %v, want *VerifyError", err) } if len(ae.Failures) < 1 { t.Fatalf("Failures = %d, want >= 1 (aggregate)", len(ae.Failures)) } } -// TestAssay_Satisfied asserts Assay returns nil when the entity satisfies the +// TestVerify_Satisfied asserts Verify returns nil when the entity satisfies the // state's requirements. -func TestAssay_Satisfied(t *testing.T) { +func TestVerify_Satisfied(t *testing.T) { m, rec := safeBuild(t) if rec != nil { t.Skipf("build not implemented yet: %v", rec) } doc := &Document{ReviewerID: strptr("rev-1")} - if err := m.Assay(Approved, doc); err != nil { - t.Fatalf("Assay err = %v, want nil", err) + if err := m.Verify(Approved, doc); err != nil { + t.Fatalf("Verify err = %v, want nil", err) } }