From 537be8a7a43e932675e52f3f0e496dff215c385b Mon Sep 17 00:00:00 2001 From: James Harris Date: Thu, 26 Sep 2024 09:42:38 +1000 Subject: [PATCH 1/9] Update `inflect` package to use `message.Kind` instead of `Role`. --- internal/inflect/inflect.go | 24 +++++----- internal/inflect/inflect_test.go | 76 ++++++++++++++++---------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/internal/inflect/inflect.go b/internal/inflect/inflect.go index c50413c8..7873cf50 100644 --- a/internal/inflect/inflect.go +++ b/internal/inflect/inflect.go @@ -9,8 +9,8 @@ import ( "github.com/dogmatiq/configkit/message" ) -var substitutions = map[message.Role]map[string]string{ - message.CommandRole: { +var substitutions = map[message.Kind]map[string]string{ + message.CommandKind: { "": "command", "": "commands", "": "execute", @@ -18,7 +18,7 @@ var substitutions = map[message.Role]map[string]string{ "": "executing", "": "dogma.CommandExecutor", }, - message.EventRole: { + message.EventKind: { "": "event", "": "events", "": "record", @@ -26,7 +26,7 @@ var substitutions = map[message.Role]map[string]string{ "": "recording", "": "dogma.EventRecorder", }, - message.TimeoutRole: { + message.TimeoutKind: { "": "timeout", "": "timeouts", "": "schedule", @@ -45,8 +45,8 @@ var corrections = map[string]string{ } // Sprint formats a string, inflecting words in s match the message role r. -func Sprint(r message.Role, s string) string { - for k, v := range substitutions[r] { +func Sprint(k message.Kind, s string) string { + for k, v := range substitutions[k] { s = strings.ReplaceAll(s, k, v) s = strings.ReplaceAll(s, strings.ToUpper(k), strings.ToUpper(v)) } @@ -60,21 +60,21 @@ func Sprint(r message.Role, s string) string { } // Sprintf formats a string, inflecting words in f match the message role r. -func Sprintf(r message.Role, f string, v ...any) string { +func Sprintf(k message.Kind, f string, v ...any) string { return Sprint( - r, + k, fmt.Sprintf(f, v...), ) } // Error returns a new error, inflecting words in s to match the message role r. -func Error(r message.Role, s string) error { - return errors.New(Sprint(r, s)) +func Error(k message.Kind, s string) error { + return errors.New(Sprint(k, s)) } // Errorf returns a new error, inflecting words in f to match the message role r. -func Errorf(r message.Role, f string, v ...any) error { - return errors.New(Sprintf(r, f, v...)) +func Errorf(k message.Kind, f string, v ...any) error { + return errors.New(Sprintf(k, f, v...)) } // replaceAll replaces all instances of k with v, at word boundaries. diff --git a/internal/inflect/inflect_test.go b/internal/inflect/inflect_test.go index 1e43e787..c0228285 100644 --- a/internal/inflect/inflect_test.go +++ b/internal/inflect/inflect_test.go @@ -11,10 +11,10 @@ import ( ) var _ = g.Describe("func Sprint()", func() { - entry := func(r message.Role, in, out string) g.TableEntry { + entry := func(k message.Kind, in, out string) g.TableEntry { return g.Entry( - in+" ("+r.String()+")", - r, + in+" ("+k.String()+")", + k, in, out, ) @@ -22,51 +22,51 @@ var _ = g.Describe("func Sprint()", func() { g.DescribeTable( "returns a properly inflected string", - func(r message.Role, in, out string) { - gm.Expect(Sprint(r, in)).To(gm.Equal(out)) + func(k message.Kind, in, out string) { + gm.Expect(Sprint(k, in)).To(gm.Equal(out)) in = strings.ToUpper(in) out = strings.ToUpper(out) - gm.Expect(Sprint(r, in)).To(gm.Equal(out)) + gm.Expect(Sprint(k, in)).To(gm.Equal(out)) }, - entry(message.CommandRole, "a ", "a command"), - entry(message.EventRole, "a ", "an event"), - entry(message.TimeoutRole, "a ", "a timeout"), + entry(message.CommandKind, "a ", "a command"), + entry(message.EventKind, "a ", "an event"), + entry(message.TimeoutKind, "a ", "a timeout"), - entry(message.CommandRole, "an ", "a command"), - entry(message.EventRole, "an ", "an event"), - entry(message.TimeoutRole, "an ", "a timeout"), + entry(message.CommandKind, "an ", "a command"), + entry(message.EventKind, "an ", "an event"), + entry(message.TimeoutKind, "an ", "a timeout"), - entry(message.CommandRole, "the ", "the commands"), - entry(message.EventRole, "the ", "the events"), - entry(message.TimeoutRole, "the ", "the timeouts"), + entry(message.CommandKind, "the ", "the commands"), + entry(message.EventKind, "the ", "the events"), + entry(message.TimeoutKind, "the ", "the timeouts"), - entry(message.CommandRole, "1 ", "1 command"), - entry(message.EventRole, "1 ", "1 event"), - entry(message.TimeoutRole, "1 ", "1 timeout"), + entry(message.CommandKind, "1 ", "1 command"), + entry(message.EventKind, "1 ", "1 event"), + entry(message.TimeoutKind, "1 ", "1 timeout"), - entry(message.CommandRole, "21 ", "21 commands"), - entry(message.EventRole, "21 ", "21 events"), - entry(message.TimeoutRole, "21 ", "21 timeouts"), + entry(message.CommandKind, "21 ", "21 commands"), + entry(message.EventKind, "21 ", "21 events"), + entry(message.TimeoutKind, "21 ", "21 timeouts"), - entry(message.CommandRole, "only 1 ", "only 1 command"), - entry(message.EventRole, "only 1 ", "only 1 event"), - entry(message.TimeoutRole, "only 1 ", "only 1 timeout"), + entry(message.CommandKind, "only 1 ", "only 1 command"), + entry(message.EventKind, "only 1 ", "only 1 event"), + entry(message.TimeoutKind, "only 1 ", "only 1 timeout"), - entry(message.CommandRole, "only 21 ", "only 21 commands"), - entry(message.EventRole, "only 21 ", "only 21 events"), - entry(message.TimeoutRole, "only 21 ", "only 21 timeouts"), + entry(message.CommandKind, "only 21 ", "only 21 commands"), + entry(message.EventKind, "only 21 ", "only 21 events"), + entry(message.TimeoutKind, "only 21 ", "only 21 timeouts"), - entry(message.CommandRole, " a specific ", "execute a specific command"), - entry(message.EventRole, " a specific ", "record a specific event"), - entry(message.TimeoutRole, " a specific ", "schedule a specific timeout"), + entry(message.CommandKind, " a specific ", "execute a specific command"), + entry(message.EventKind, " a specific ", "record a specific event"), + entry(message.TimeoutKind, " a specific ", "schedule a specific timeout"), - entry(message.CommandRole, "the was ", "the command was executed"), - entry(message.EventRole, "the was ", "the event was recorded"), - entry(message.TimeoutRole, "the was ", "the timeout was scheduled"), + entry(message.CommandKind, "the was ", "the command was executed"), + entry(message.EventKind, "the was ", "the event was recorded"), + entry(message.TimeoutKind, "the was ", "the timeout was scheduled"), - entry(message.CommandRole, "via a ", "via a dogma.CommandExecutor"), - entry(message.EventRole, "via a ", "via a dogma.EventRecorder"), + entry(message.CommandKind, "via a ", "via a dogma.CommandExecutor"), + entry(message.EventKind, "via a ", "via a dogma.EventRecorder"), ) }) @@ -74,7 +74,7 @@ var _ = g.Describe("func Sprintf()", func() { g.It("returns the inflected and substituted string", func() { gm.Expect( Sprintf( - message.CommandRole, + message.CommandKind, "the %T ", CommandA1, ), @@ -86,7 +86,7 @@ var _ = g.Describe("func Error()", func() { g.It("returns an error with the inflected message", func() { gm.Expect( Error( - message.CommandRole, + message.CommandKind, "the ", ), ).To(gm.MatchError("the command")) @@ -97,7 +97,7 @@ var _ = g.Describe("func Errorf()", func() { g.It("returns an error with the inflected and substituted message", func() { gm.Expect( Errorf( - message.CommandRole, + message.CommandKind, "the %T ", CommandA1, ), From e582c3a196d32b9a9f7692ce905be9adae0c0511 Mon Sep 17 00:00:00 2001 From: James Harris Date: Thu, 26 Sep 2024 09:43:38 +1000 Subject: [PATCH 2/9] Remove use of `message.Type` and `Role` from `envelope` package. --- envelope/envelope.go | 70 ++++++++++++++++----------------------- envelope/envelope_test.go | 13 +------- fact/logger.go | 20 ++++++++--- 3 files changed, 44 insertions(+), 59 deletions(-) diff --git a/envelope/envelope.go b/envelope/envelope.go index 8fa8b196..3ed0b73a 100644 --- a/envelope/envelope.go +++ b/envelope/envelope.go @@ -3,7 +3,6 @@ package envelope import ( "time" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" ) @@ -24,12 +23,6 @@ type Envelope struct { // Message is the application-defined message that the envelope represents. Message dogma.Message - // Type is the type of the message. - Type message.Type - - // Role is the message's role. - Role message.Role - // CreatedAt is the time at which the message was created. CreatedAt time.Time @@ -50,7 +43,17 @@ func NewCommand( m dogma.Command, t time.Time, ) *Envelope { - return newEnvelope(id, m, message.CommandRole, t) + if id == "" { + panic("message ID must not be empty") + } + + return &Envelope{ + MessageID: id, + CausationID: id, + CorrelationID: id, + Message: m, + CreatedAt: t, + } } // NewEvent constructs a new envelope containing the given event message. @@ -60,33 +63,16 @@ func NewEvent( id string, m dogma.Event, t time.Time, -) *Envelope { - return newEnvelope(id, m, message.EventRole, t) -} - -// newEnvelope constructs a newEnvelope envelope containing the given message. -// -// It panics if r is message.TimeoutRole, as a timeout cannot occur except as a -// result of some other message. -func newEnvelope( - id string, - m dogma.Message, - r message.Role, - t time.Time, ) *Envelope { if id == "" { panic("message ID must not be empty") } - r.MustNotBe(message.TimeoutRole) - return &Envelope{ MessageID: id, CausationID: id, CorrelationID: id, Message: m, - Type: message.TypeOf(m), - Role: r, CreatedAt: t, } } @@ -101,7 +87,14 @@ func (e *Envelope) NewCommand( t time.Time, o Origin, ) *Envelope { - return e.new(id, m, message.CommandRole, t, o) + return &Envelope{ + MessageID: id, + CausationID: e.MessageID, + CorrelationID: e.CorrelationID, + Message: m, + CreatedAt: t, + Origin: &o, + } } // NewEvent constructs a new envelope as a child of e, indicating that the event @@ -114,7 +107,14 @@ func (e *Envelope) NewEvent( t time.Time, o Origin, ) *Envelope { - return e.new(id, m, message.EventRole, t, o) + return &Envelope{ + MessageID: id, + CausationID: e.MessageID, + CorrelationID: e.CorrelationID, + Message: m, + CreatedAt: t, + Origin: &o, + } } // NewTimeout constructs a new envelope as a child of e, indicating that the @@ -128,28 +128,14 @@ func (e *Envelope) NewTimeout( t time.Time, s time.Time, o Origin, -) *Envelope { - env := e.new(id, m, message.TimeoutRole, t, o) - env.ScheduledFor = s - return env -} - -// new constructs a new envelope as a child of e. -func (e *Envelope) new( - id string, - m dogma.Message, - r message.Role, - t time.Time, - o Origin, ) *Envelope { return &Envelope{ MessageID: id, CausationID: e.MessageID, CorrelationID: e.CorrelationID, Message: m, - Type: message.TypeOf(m), - Role: r, CreatedAt: t, + ScheduledFor: s, Origin: &o, } } diff --git a/envelope/envelope_test.go b/envelope/envelope_test.go index 2fdb6215..6297ad9b 100644 --- a/envelope/envelope_test.go +++ b/envelope/envelope_test.go @@ -4,7 +4,6 @@ import ( "time" "github.com/dogmatiq/configkit" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" . "github.com/dogmatiq/enginekit/enginetest/stubs" . "github.com/dogmatiq/testkit/envelope" @@ -28,8 +27,6 @@ var _ = g.Describe("type Envelope", func() { CorrelationID: "100", CausationID: "100", Message: CommandA1, - Type: message.TypeOf(CommandA1), - Role: message.CommandRole, CreatedAt: now, }, )) @@ -51,8 +48,6 @@ var _ = g.Describe("type Envelope", func() { CorrelationID: "100", CausationID: "100", Message: EventA1, - Type: message.TypeOf(EventA1), - Role: message.EventRole, CreatedAt: now, }, )) @@ -73,7 +68,7 @@ var _ = g.Describe("type Envelope", func() { g.It("returns the expected envelope", func() { parent := NewEvent( "100", - CommandP1, + EventP1, time.Now(), ) origin := Origin{ @@ -95,8 +90,6 @@ var _ = g.Describe("type Envelope", func() { CorrelationID: "100", CausationID: "100", Message: CommandA1, - Type: message.TypeOf(CommandA1), - Role: message.CommandRole, CreatedAt: now, Origin: &origin, }, @@ -140,8 +133,6 @@ var _ = g.Describe("type Envelope", func() { CorrelationID: "100", CausationID: "100", Message: EventA1, - Type: message.TypeOf(EventA1), - Role: message.EventRole, CreatedAt: now, Origin: &origin, }, @@ -188,8 +179,6 @@ var _ = g.Describe("type Envelope", func() { CorrelationID: "100", CausationID: "100", Message: TimeoutA1, - Type: message.TypeOf(TimeoutA1), - Role: message.TimeoutRole, CreatedAt: now, ScheduledFor: s, Origin: &origin, diff --git a/fact/logger.go b/fact/logger.go index 8129944a..fa47a9ae 100644 --- a/fact/logger.go +++ b/fact/logger.go @@ -101,6 +101,8 @@ func (l *Logger) dispatchCycleBegun(f DispatchCycleBegun) { // dispatchBegun returns the log message for f. func (l *Logger) dispatchBegun(f DispatchBegun) { + mt := message.TypeOf(f.Envelope.Message) + l.log( f.Envelope, []logging.Icon{ @@ -108,7 +110,7 @@ func (l *Logger) dispatchBegun(f DispatchBegun) { logging.SystemIcon, "", }, - message.TypeOf(f.Envelope.Message).String()+f.Envelope.Role.Marker(), + mt.String()+mt.Kind().Symbol(), f.Envelope.Message.MessageDescription(), ) } @@ -257,6 +259,8 @@ func (l *Logger) aggregateInstanceDestructionReverted(f AggregateInstanceDestruc // eventRecordedByAggregate returns the log message for f. func (l *Logger) eventRecordedByAggregate(f EventRecordedByAggregate) { + mt := message.TypeOf(f.EventEnvelope.Message) + l.log( f.EventEnvelope, []logging.Icon{ @@ -266,7 +270,7 @@ func (l *Logger) eventRecordedByAggregate(f EventRecordedByAggregate) { }, f.Handler.Identity().Name+" "+f.InstanceID, "recorded an event", - f.EventEnvelope.Type.String()+f.EventEnvelope.Role.Marker(), + mt.String()+mt.Kind().Symbol(), f.EventEnvelope.Message.MessageDescription(), ) } @@ -385,6 +389,8 @@ func (l *Logger) processInstanceEndingReverted(f ProcessInstanceEndingReverted) // commandExecutedByProcess returns the log message for f. func (l *Logger) commandExecutedByProcess(f CommandExecutedByProcess) { + mt := message.TypeOf(f.CommandEnvelope.Message) + l.log( f.CommandEnvelope, []logging.Icon{ @@ -394,13 +400,15 @@ func (l *Logger) commandExecutedByProcess(f CommandExecutedByProcess) { }, f.Handler.Identity().Name+" "+f.InstanceID, "executed a command", - f.CommandEnvelope.Type.String()+f.CommandEnvelope.Role.Marker(), + mt.String()+mt.Kind().Symbol(), f.CommandEnvelope.Message.MessageDescription(), ) } // timeoutScheduledByProcess returns the log message for f. func (l *Logger) timeoutScheduledByProcess(f TimeoutScheduledByProcess) { + mt := message.TypeOf(f.TimeoutEnvelope.Message) + l.log( f.TimeoutEnvelope, []logging.Icon{ @@ -413,7 +421,7 @@ func (l *Logger) timeoutScheduledByProcess(f TimeoutScheduledByProcess) { "scheduled a timeout for %s", f.TimeoutEnvelope.ScheduledFor.Format(time.RFC3339), ), - f.TimeoutEnvelope.Type.String()+f.TimeoutEnvelope.Role.Marker(), + mt.String()+mt.Kind().Symbol(), f.TimeoutEnvelope.Message.MessageDescription(), ) } @@ -434,6 +442,8 @@ func (l *Logger) messageLoggedByProcess(f MessageLoggedByProcess) { // eventRecordedByIntegration returns the log message for f. func (l *Logger) eventRecordedByIntegration(f EventRecordedByIntegration) { + mt := message.TypeOf(f.EventEnvelope.Message) + l.log( f.EventEnvelope, []logging.Icon{ @@ -443,7 +453,7 @@ func (l *Logger) eventRecordedByIntegration(f EventRecordedByIntegration) { }, f.Handler.Identity().Name, "recorded an event", - f.EventEnvelope.Type.String()+f.EventEnvelope.Role.Marker(), + mt.String()+mt.Kind().Symbol(), f.EventEnvelope.Message.MessageDescription(), ) } From 698ccf011319066f34a0b7175b6007db80a5ef7c Mon Sep 17 00:00:00 2001 From: James Harris Date: Thu, 26 Sep 2024 09:45:20 +1000 Subject: [PATCH 3/9] Update `engine` to use message kinds. --- engine/configurer.go | 6 +- engine/engine.go | 109 ++++++--------------- engine/engine_test.go | 5 - engine/executor.go | 3 +- engine/executor_test.go | 8 +- engine/internal/aggregate/controller.go | 24 +++-- engine/internal/aggregate/scope.go | 3 +- engine/internal/integration/controller.go | 14 ++- engine/internal/integration/scope.go | 3 +- engine/internal/process/controller.go | 63 ++++++------ engine/internal/process/controller_test.go | 2 - engine/internal/process/scope.go | 9 +- engine/internal/projection/controller.go | 10 +- internal/validation/doc.go | 2 + internal/validation/validation.go | 18 ++++ 15 files changed, 136 insertions(+), 143 deletions(-) create mode 100644 internal/validation/doc.go create mode 100644 internal/validation/validation.go diff --git a/engine/configurer.go b/engine/configurer.go index 5f6ccfc1..77919087 100644 --- a/engine/configurer.go +++ b/engine/configurer.go @@ -17,8 +17,6 @@ type configurer struct { } func (c *configurer) VisitRichApplication(ctx context.Context, cfg configkit.RichApplication) error { - c.engine.roles = cfg.MessageTypes().All() - return cfg.RichHandlers().AcceptRichVisitor(ctx, c) } @@ -76,11 +74,11 @@ func (c *configurer) VisitRichProjection(_ context.Context, cfg configkit.RichPr func (c *configurer) registerController( ctrl controller, - types map[message.Type]message.Role, + consumed message.Set[message.Type], ) { c.engine.controllers[ctrl.HandlerConfig().Identity().Name] = ctrl - for t := range types { + for t := range consumed.All() { c.engine.routes[t] = append(c.engine.routes[t], ctrl) } } diff --git a/engine/engine.go b/engine/engine.go index 570af330..087a7b4e 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -10,14 +10,13 @@ import ( "github.com/dogmatiq/dogma" "github.com/dogmatiq/testkit/envelope" "github.com/dogmatiq/testkit/fact" - "github.com/dogmatiq/testkit/internal/inflect" + "github.com/dogmatiq/testkit/internal/validation" "go.uber.org/multierr" ) // Engine is an in-memory Dogma engine that is used to execute tests. type Engine struct { messageIDs envelope.MessageIDGenerator - roles map[message.Type]message.Role // m protects the controllers, routes and resetters collections. The // collections themselves are static and hence may be read without acquiring @@ -34,7 +33,6 @@ func New(app configkit.RichApplication, options ...Option) (_ *Engine, err error eo := newEngineOptions(options) e := &Engine{ - roles: map[message.Type]message.Role{}, controllers: map[string]controller{}, routes: map[message.Type][]controller{}, resetters: eo.resetters, @@ -180,45 +178,42 @@ func (e *Engine) tick( ) } -// Dispatch processes a message. +// Dispatch processes a [dogma.Command] or [dogma.Event]. // -// It is not an error to process a message that is not routed to any handlers. -// -// It panics if the message is invalid. +// It panics if the message is a [dogma.Timeout], or is otherwise invalid. func (e *Engine) Dispatch( ctx context.Context, m dogma.Message, options ...OperationOption, ) error { - t := message.TypeOf(m) - - if err := m.Validate(); err != nil { - panic(fmt.Sprintf( - "cannot dispatch invalid %s message: %s", - t, - err, - )) - } - + mt := message.TypeOf(m) + id := e.messageIDs.Next() oo := newOperationOptions(e, options) - if _, ok := e.routes[t]; !ok { - panic(fmt.Sprintf( - "the %s message type is not consumed by any handlers", - t, - )) + env, err := message.TryMap( + m, + func(m dogma.Command) (*envelope.Envelope, error) { + if err := m.Validate(validation.CommandValidationScope()); err != nil { + return nil, err + } + return envelope.NewCommand(id, m, oo.now), nil + }, + func(m dogma.Event) (*envelope.Envelope, error) { + if err := m.Validate(validation.EventValidationScope()); err != nil { + return nil, err + } + return envelope.NewEvent(id, m, oo.now), nil + }, + func(t dogma.Timeout) (*envelope.Envelope, error) { + panic("cannot dispatch timeout messages") + }, + ) + if err != nil { + panic(fmt.Sprintf("cannot dispatch invalid %s message: %s", mt, err)) } - r := e.roles[t] - r.MustBe(message.CommandRole, message.EventRole) - - var env *envelope.Envelope - id := e.messageIDs.Next() - - if r == message.CommandRole { - env = envelope.NewCommand(id, m, oo.now) - } else { - env = envelope.NewEvent(id, m, oo.now) + if _, ok := e.routes[mt]; !ok { + panic(fmt.Sprintf("the %s message type is not consumed by any handlers", mt)) } oo.observers.Notify( @@ -230,7 +225,7 @@ func (e *Engine) Dispatch( }, ) - err := e.m.Lock(ctx) + err = e.m.Lock(ctx) if err == nil { defer e.m.Unlock() err = e.dispatch(ctx, oo, env) @@ -248,39 +243,6 @@ func (e *Engine) Dispatch( return err } -// mustDispatch is a variant of Dispatch() that panics if m is does not -// have the expected role. -func (e *Engine) mustDispatch( - ctx context.Context, - expected message.Role, - m dogma.Message, - options ...OperationOption, -) error { - t := message.TypeOf(m) - - if r, ok := e.roles[t]; ok { - if r.Is(expected) { - return e.Dispatch(ctx, m, options...) - } - - panic(inflect.Sprintf( - expected, - "cannot , %s", - inflect.Sprintf( - r, - "%s is configured as a ", - t, - ), - )) - } - - panic(inflect.Sprintf( - expected, - "cannot , %s is a not a recognized message type", - t, - )) -} - func (e *Engine) dispatch( ctx context.Context, oo *operationOptions, @@ -294,22 +256,15 @@ func (e *Engine) dispatch( var controllers []controller - if env.Role == message.TimeoutRole { + mt := message.TypeOf(env.Message) + + if mt.Kind() == message.TimeoutKind { // always dispatch timeouts back to their origin handler controllers = []controller{ e.controllers[env.Origin.Handler.Identity().Name], } } else { - // for all other message types check to see the role matches the - // expected role from the configuration, and if so dispatch it to - // all of the handlers associated with that type - r, ok := e.roles[env.Type] - if !ok { - continue - } - - env.Role.MustBe(r) - controllers = e.routes[env.Type] + controllers = e.routes[mt] } oo.observers.Notify( diff --git a/engine/engine_test.go b/engine/engine_test.go index c13f400b..f1ccb2bf 100644 --- a/engine/engine_test.go +++ b/engine/engine_test.go @@ -7,7 +7,6 @@ import ( "time" "github.com/dogmatiq/configkit" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" "github.com/dogmatiq/enginekit/enginetest" . "github.com/dogmatiq/enginekit/enginetest/stubs" @@ -164,8 +163,6 @@ var _ = g.Describe("type Engine", func() { CausationID: "1", CorrelationID: "1", Message: AggregateCommand{}, - Type: message.TypeFor[AggregateCommand](), - Role: message.CommandRole, CreatedAt: now, }, Reason: fact.HandlerTypeDisabled, @@ -202,8 +199,6 @@ var _ = g.Describe("type Engine", func() { CausationID: "1", CorrelationID: "1", Message: AggregateCommand{}, - Type: message.TypeFor[AggregateCommand](), - Role: message.CommandRole, CreatedAt: now, }, Reason: fact.IndividualHandlerDisabled, diff --git a/engine/executor.go b/engine/executor.go index 34f99c3c..b19a75f5 100644 --- a/engine/executor.go +++ b/engine/executor.go @@ -3,7 +3,6 @@ package engine import ( "context" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" ) @@ -25,5 +24,5 @@ func (e CommandExecutor) ExecuteCommand( m dogma.Command, _ ...dogma.ExecuteCommandOption, ) error { - return e.Engine.mustDispatch(ctx, message.CommandRole, m, e.Options...) + return e.Engine.Dispatch(ctx, m, e.Options...) } diff --git a/engine/executor_test.go b/engine/executor_test.go index 5819e41e..e5ee868e 100644 --- a/engine/executor_test.go +++ b/engine/executor_test.go @@ -66,16 +66,10 @@ var _ = g.Describe("type CommandExecutor", func() { gm.Expect(called).To(gm.BeTrue()) }) - g.It("panics if the message is not a command", func() { - gm.Expect(func() { - executor.ExecuteCommand(context.Background(), EventA1) - }).To(gm.PanicWith("cannot execute command, stubs.EventStub[TypeA] is configured as an event")) - }) - g.It("panics if the message is unrecognized", func() { gm.Expect(func() { executor.ExecuteCommand(context.Background(), CommandX1) - }).To(gm.PanicWith("cannot execute command, stubs.CommandStub[TypeX] is a not a recognized message type")) + }).To(gm.PanicWith("the stubs.CommandStub[TypeX] message type is not consumed by any handlers")) }) }) }) diff --git a/engine/internal/aggregate/controller.go b/engine/internal/aggregate/controller.go index e503c606..6ac82af9 100644 --- a/engine/internal/aggregate/controller.go +++ b/engine/internal/aggregate/controller.go @@ -6,6 +6,8 @@ import ( "time" "github.com/dogmatiq/configkit" + "github.com/dogmatiq/configkit/message" + "github.com/dogmatiq/dogma" "github.com/dogmatiq/testkit/engine/internal/panicx" "github.com/dogmatiq/testkit/envelope" "github.com/dogmatiq/testkit/fact" @@ -43,8 +45,10 @@ func (c *Controller) Handle( now time.Time, env *envelope.Envelope, ) ([]*envelope.Envelope, error) { - if !c.Config.MessageTypes().Consumed.Has(env.Type) { - panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), env.Type)) + mt := message.TypeOf(env.Message) + + if !c.Config.MessageTypes().Consumed.Has(mt) { + panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), mt)) } var id string @@ -55,7 +59,9 @@ func (c *Controller) Handle( c.Config.Handler(), env.Message, func() { - id = c.Config.Handler().RouteCommandToInstance(env.Message) + id = c.Config.Handler().RouteCommandToInstance( + env.Message.(dogma.Command), + ) }, ) @@ -66,7 +72,7 @@ func (c *Controller) Handle( Method: "RouteCommandToInstance", Implementation: c.Config.Handler(), Message: env.Message, - Description: fmt.Sprintf("routed a command of type %s to an empty ID", env.Type), + Description: fmt.Sprintf("routed a command of type %s to an empty ID", mt), Location: location.OfMethod(c.Config.Handler(), "RouteCommandToInstance"), }) } @@ -94,7 +100,9 @@ func (c *Controller) Handle( r, env.Message, func() { - r.ApplyEvent(env.Message) + r.ApplyEvent( + env.Message.(dogma.Event), + ) }, ) } @@ -131,7 +139,11 @@ func (c *Controller) Handle( c.Config.Handler(), env.Message, func() { - c.Config.Handler().HandleCommand(r, s, env.Message) + c.Config.Handler().HandleCommand( + r, + s, + env.Message.(dogma.Command), + ) }, ) diff --git a/engine/internal/aggregate/scope.go b/engine/internal/aggregate/scope.go index 7dbc7fce..6d1124a4 100644 --- a/engine/internal/aggregate/scope.go +++ b/engine/internal/aggregate/scope.go @@ -10,6 +10,7 @@ import ( "github.com/dogmatiq/testkit/engine/internal/panicx" "github.com/dogmatiq/testkit/envelope" "github.com/dogmatiq/testkit/fact" + "github.com/dogmatiq/testkit/internal/validation" "github.com/dogmatiq/testkit/location" ) @@ -63,7 +64,7 @@ func (s *scope) RecordEvent(m dogma.Event) { }) } - if err := m.Validate(); err != nil { + if err := m.Validate(validation.EventValidationScope()); err != nil { panic(panicx.UnexpectedBehavior{ Handler: s.config, Interface: "AggregateMessageHandler", diff --git a/engine/internal/integration/controller.go b/engine/internal/integration/controller.go index 509ab6bc..37d68e12 100644 --- a/engine/internal/integration/controller.go +++ b/engine/internal/integration/controller.go @@ -6,6 +6,8 @@ import ( "time" "github.com/dogmatiq/configkit" + "github.com/dogmatiq/configkit/message" + "github.com/dogmatiq/dogma" "github.com/dogmatiq/testkit/engine/internal/panicx" "github.com/dogmatiq/testkit/envelope" "github.com/dogmatiq/testkit/fact" @@ -40,8 +42,10 @@ func (c *Controller) Handle( now time.Time, env *envelope.Envelope, ) ([]*envelope.Envelope, error) { - if !c.Config.MessageTypes().Consumed.Has(env.Type) { - panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), env.Type)) + mt := message.TypeOf(env.Message) + + if !c.Config.MessageTypes().Consumed.Has(mt) { + panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), mt)) } s := &scope{ @@ -60,7 +64,11 @@ func (c *Controller) Handle( c.Config.Handler(), env.Message, func() { - err = c.Config.Handler().HandleCommand(ctx, s, env.Message) + err = c.Config.Handler().HandleCommand( + ctx, + s, + env.Message.(dogma.Command), + ) }, ) diff --git a/engine/internal/integration/scope.go b/engine/internal/integration/scope.go index 27e10813..9abe22d3 100644 --- a/engine/internal/integration/scope.go +++ b/engine/internal/integration/scope.go @@ -10,6 +10,7 @@ import ( "github.com/dogmatiq/testkit/engine/internal/panicx" "github.com/dogmatiq/testkit/envelope" "github.com/dogmatiq/testkit/fact" + "github.com/dogmatiq/testkit/internal/validation" "github.com/dogmatiq/testkit/location" ) @@ -38,7 +39,7 @@ func (s *scope) RecordEvent(m dogma.Event) { }) } - if err := m.Validate(); err != nil { + if err := m.Validate(validation.EventValidationScope()); err != nil { panic(panicx.UnexpectedBehavior{ Handler: s.config, Interface: "IntegrationMessageHandler", diff --git a/engine/internal/process/controller.go b/engine/internal/process/controller.go index a0ff9d63..4e8731c3 100644 --- a/engine/internal/process/controller.go +++ b/engine/internal/process/controller.go @@ -64,8 +64,10 @@ func (c *Controller) Handle( now time.Time, env *envelope.Envelope, ) ([]*envelope.Envelope, error) { - if !c.Config.MessageTypes().Consumed.Has(env.Type) { - panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), env.Type)) + mt := message.TypeOf(env.Message) + + if !c.Config.MessageTypes().Consumed.Has(mt) { + panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), mt)) } id, ok, err := c.route(ctx, obs, env) @@ -112,18 +114,19 @@ func (c *Controller) Handle( } s := &scope{ - instanceID: id, - config: c.Config, - handleMethod: "HandleEvent", - messageIDs: c.MessageIDs, - observer: obs, - now: now, - root: r, - env: env, - } - - if s.env.Role == message.TimeoutRole { - s.handleMethod = "HandleTimeout" + instanceID: id, + config: c.Config, + handleMethod: message.Map( + env.Message, + func(dogma.Command) string { panic("unexpected message kind") }, + func(dogma.Event) string { return "HandleEvent" }, + func(dogma.Timeout) string { return "HandleTimeout" }, + ), + messageIDs: c.MessageIDs, + observer: obs, + now: now, + root: r, + env: env, } if err := c.handle(ctx, s); err != nil { @@ -154,18 +157,21 @@ func (c *Controller) route( ctx context.Context, obs fact.Observer, env *envelope.Envelope, -) (string, bool, error) { - if env.Role == message.EventRole { - return c.routeEvent(ctx, obs, env) - } - - return c.routeTimeout(ctx, obs, env) +) (id string, ok bool, err error) { + message.Switch( + env.Message, + func(m dogma.Command) { panic("unexpected message kind") }, + func(m dogma.Event) { id, ok, err = c.routeEvent(ctx, obs, env, m) }, + func(m dogma.Timeout) { id, ok, err = c.routeTimeout(ctx, obs, env) }, + ) + return id, ok, err } func (c *Controller) routeEvent( ctx context.Context, obs fact.Observer, env *envelope.Envelope, + m dogma.Event, ) (string, bool, error) { handler := c.Config.Handler() @@ -179,9 +185,9 @@ func (c *Controller) routeEvent( "ProcessMessageHandler", "RouteEventToInstance", handler, - env.Message, + m, func() { - id, ok, err = handler.RouteEventToInstance(ctx, env.Message) + id, ok, err = handler.RouteEventToInstance(ctx, m) }, ) @@ -196,8 +202,8 @@ func (c *Controller) routeEvent( Interface: "ProcessMessageHandler", Method: "RouteEventToInstance", Implementation: handler, - Message: env.Message, - Description: fmt.Sprintf("routed an event of type %s to an empty ID", env.Type), + Message: m, + Description: fmt.Sprintf("routed an event of type %s to an empty ID", message.TypeOf(m)), Location: location.OfMethod(c.Config.Handler(), "RouteEventToInstance"), }) } @@ -241,10 +247,11 @@ func (c *Controller) handle(ctx context.Context, s *scope) error { c.Config.Handler(), s.env.Message, func() { - if s.env.Role == message.EventRole { - err = c.Config.Handler().HandleEvent(ctx, s.root, s, s.env.Message) - } else { - err = c.Config.Handler().HandleTimeout(ctx, s.root, s, s.env.Message) + switch m := s.env.Message.(type) { + case dogma.Event: + err = c.Config.Handler().HandleEvent(ctx, s.root, s, m) + case dogma.Timeout: + err = c.Config.Handler().HandleTimeout(ctx, s.root, s, m) } }, ) diff --git a/engine/internal/process/controller_test.go b/engine/internal/process/controller_test.go index c8b31902..79113439 100644 --- a/engine/internal/process/controller_test.go +++ b/engine/internal/process/controller_test.go @@ -55,8 +55,6 @@ var _ = g.Describe("type Controller", func() { dogma.SchedulesTimeout[TimeoutStub[TypeA]](), ) }, - // setup routes for "E" (event) messages to an instance ID based on the - // message's content RouteEventToInstanceFunc: func( _ context.Context, m dogma.Event, diff --git a/engine/internal/process/scope.go b/engine/internal/process/scope.go index d9d5becf..f51a5fe4 100644 --- a/engine/internal/process/scope.go +++ b/engine/internal/process/scope.go @@ -10,6 +10,7 @@ import ( "github.com/dogmatiq/testkit/engine/internal/panicx" "github.com/dogmatiq/testkit/envelope" "github.com/dogmatiq/testkit/fact" + "github.com/dogmatiq/testkit/internal/validation" "github.com/dogmatiq/testkit/location" ) @@ -52,7 +53,7 @@ func (s *scope) End() { func (s *scope) ExecuteCommand(m dogma.Command) { mt := message.TypeOf(m) - if s.config.MessageTypes().Produced[mt] != message.CommandRole { + if !s.config.MessageTypes().Produced.Has(mt) { panic(panicx.UnexpectedBehavior{ Handler: s.config, Interface: "ProcessMessageHandler", @@ -64,7 +65,7 @@ func (s *scope) ExecuteCommand(m dogma.Command) { }) } - if err := m.Validate(); err != nil { + if err := m.Validate(validation.CommandValidationScope()); err != nil { panic(panicx.UnexpectedBehavior{ Handler: s.config, Interface: "ProcessMessageHandler", @@ -116,7 +117,7 @@ func (s *scope) RecordedAt() time.Time { func (s *scope) ScheduleTimeout(m dogma.Timeout, t time.Time) { mt := message.TypeOf(m) - if s.config.MessageTypes().Produced[mt] != message.TimeoutRole { + if !s.config.MessageTypes().Produced.Has(mt) { panic(panicx.UnexpectedBehavior{ Handler: s.config, Interface: "ProcessMessageHandler", @@ -128,7 +129,7 @@ func (s *scope) ScheduleTimeout(m dogma.Timeout, t time.Time) { }) } - if err := m.Validate(); err != nil { + if err := m.Validate(validation.TimeoutValidationScope()); err != nil { panic(panicx.UnexpectedBehavior{ Handler: s.config, Interface: "ProcessMessageHandler", diff --git a/engine/internal/projection/controller.go b/engine/internal/projection/controller.go index d303cc5e..ab9e2725 100644 --- a/engine/internal/projection/controller.go +++ b/engine/internal/projection/controller.go @@ -6,6 +6,8 @@ import ( "time" "github.com/dogmatiq/configkit" + "github.com/dogmatiq/configkit/message" + "github.com/dogmatiq/dogma" "github.com/dogmatiq/testkit/engine/internal/panicx" "github.com/dogmatiq/testkit/envelope" "github.com/dogmatiq/testkit/fact" @@ -72,8 +74,10 @@ func (c *Controller) Handle( now time.Time, env *envelope.Envelope, ) ([]*envelope.Envelope, error) { - if !c.Config.MessageTypes().Consumed.Has(env.Type) { - panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), env.Type)) + mt := message.TypeOf(env.Message) + + if !c.Config.MessageTypes().Consumed.Has(mt) { + panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), mt)) } handler := c.Config.Handler() @@ -145,7 +149,7 @@ func (c *Controller) Handle( nil, // current version []byte{1}, // next version s, - env.Message, + env.Message.(dogma.Event), ) }, ) diff --git a/internal/validation/doc.go b/internal/validation/doc.go new file mode 100644 index 00000000..e637e23d --- /dev/null +++ b/internal/validation/doc.go @@ -0,0 +1,2 @@ +// Package validation provides utilities for validation of Dogma message types. +package validation diff --git a/internal/validation/validation.go b/internal/validation/validation.go new file mode 100644 index 00000000..eb9a4bae --- /dev/null +++ b/internal/validation/validation.go @@ -0,0 +1,18 @@ +package validation + +import "github.com/dogmatiq/dogma" + +// CommandValidationScope returns the validation scope for command messages. +func CommandValidationScope() dogma.CommandValidationScope { + return struct{ dogma.CommandValidationScope }{} +} + +// EventValidationScope returns the validation scope for event messages. +func EventValidationScope() dogma.EventValidationScope { + return struct{ dogma.EventValidationScope }{} +} + +// TimeoutValidationScope returns the validation scope for timeout messages. +func TimeoutValidationScope() dogma.TimeoutValidationScope { + return struct{ dogma.TimeoutValidationScope }{} +} From c2c152122892a0771a2cb31afb119384bff149bf Mon Sep 17 00:00:00 2001 From: James Harris Date: Thu, 26 Sep 2024 09:56:12 +1000 Subject: [PATCH 4/9] Update expectation internals to use `message.Role`. --- action.call_test.go | 3 - action.dispatch.command_test.go | 16 ----- action.dispatch.event_test.go | 16 ----- action.dispatch.go | 28 +++----- expectation.message.command_test.go | 13 ---- expectation.message.event_test.go | 13 ---- expectation.message.go | 48 ++++++------- expectation.messagecommon.go | 54 ++++++-------- expectation.messagematch.command_test.go | 17 ----- expectation.messagematch.commandonly_test.go | 17 ----- expectation.messagematch.event_test.go | 17 ----- expectation.messagematch.eventonly_test.go | 17 ----- expectation.messagematch.go | 74 ++++++++++---------- expectation.messagetype.command_test.go | 13 ---- expectation.messagetype.event_test.go | 13 ---- expectation.messagetype.go | 31 ++++---- go.mod | 10 +-- go.sum | 21 ++++-- 18 files changed, 121 insertions(+), 300 deletions(-) diff --git a/action.call_test.go b/action.call_test.go index 045c1e22..030c7206 100644 --- a/action.call_test.go +++ b/action.call_test.go @@ -5,7 +5,6 @@ import ( "time" "github.com/dogmatiq/configkit" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" . "github.com/dogmatiq/enginekit/enginetest/stubs" . "github.com/dogmatiq/testkit" @@ -75,8 +74,6 @@ var _ = g.Describe("func Call()", func() { CausationID: "1", CorrelationID: "1", Message: CommandA1, - Type: message.TypeOf(CommandA1), - Role: message.CommandRole, CreatedAt: startTime, }, EngineTime: startTime, diff --git a/action.dispatch.command_test.go b/action.dispatch.command_test.go index 32971ac2..ccf29c1e 100644 --- a/action.dispatch.command_test.go +++ b/action.dispatch.command_test.go @@ -4,7 +4,6 @@ import ( "time" "github.com/dogmatiq/configkit" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" . "github.com/dogmatiq/enginekit/enginetest/stubs" . "github.com/dogmatiq/testkit" @@ -68,8 +67,6 @@ var _ = g.Describe("func ExecuteCommand()", func() { CausationID: "1", CorrelationID: "1", Message: CommandA1, - Type: message.TypeOf(CommandA1), - Role: message.CommandRole, CreatedAt: startTime, }, EngineTime: startTime, @@ -97,19 +94,6 @@ var _ = g.Describe("func ExecuteCommand()", func() { )) }) - g.It("fails the test if the message type is not a command", func() { - t.FailSilently = true - - test.Prepare( - ExecuteCommand(EventA1), - ) - - gm.Expect(t.Failed()).To(gm.BeTrue()) - gm.Expect(t.Logs).To(gm.ContainElement( - "cannot execute command, stubs.EventStub[TypeA] is configured as an event", - )) - }) - g.It("does not satisfy its own expectations", func() { t.FailSilently = true diff --git a/action.dispatch.event_test.go b/action.dispatch.event_test.go index 958b76c4..afbe439a 100644 --- a/action.dispatch.event_test.go +++ b/action.dispatch.event_test.go @@ -5,7 +5,6 @@ import ( "time" "github.com/dogmatiq/configkit" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" . "github.com/dogmatiq/enginekit/enginetest/stubs" . "github.com/dogmatiq/testkit" @@ -75,8 +74,6 @@ var _ = g.Describe("func RecordEvent()", func() { CausationID: "1", CorrelationID: "1", Message: EventA1, - Type: message.TypeOf(EventA1), - Role: message.EventRole, CreatedAt: startTime, }, EngineTime: startTime, @@ -104,19 +101,6 @@ var _ = g.Describe("func RecordEvent()", func() { )) }) - g.It("fails the test if the message type is not an event", func() { - t.FailSilently = true - - test.Prepare( - RecordEvent(CommandA1), - ) - - gm.Expect(t.Failed()).To(gm.BeTrue()) - gm.Expect(t.Logs).To(gm.ContainElement( - "cannot record event, stubs.CommandStub[TypeA] is configured as a command", - )) - }) - g.It("does not satisfy its own expectations", func() { t.FailSilently = true diff --git a/action.dispatch.go b/action.dispatch.go index 136fd016..5a65cae3 100644 --- a/action.dispatch.go +++ b/action.dispatch.go @@ -7,6 +7,7 @@ import ( "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" "github.com/dogmatiq/testkit/internal/inflect" + "github.com/dogmatiq/testkit/internal/validation" "github.com/dogmatiq/testkit/location" ) @@ -18,12 +19,11 @@ func ExecuteCommand(m dogma.Command) Action { mt := message.TypeOf(m) - if err := m.Validate(); err != nil { + if err := m.Validate(validation.CommandValidationScope()); err != nil { panic(fmt.Sprintf("ToRecordEvent(%s): %s", mt, err)) } return dispatchAction{ - message.CommandRole, m, location.OfCall(), } @@ -37,12 +37,11 @@ func RecordEvent(m dogma.Event) Action { mt := message.TypeOf(m) - if err := m.Validate(); err != nil { + if err := m.Validate(validation.EventValidationScope()); err != nil { panic(fmt.Sprintf("RecordEvent(%s): %s", mt, err)) } return dispatchAction{ - message.EventRole, m, location.OfCall(), } @@ -51,16 +50,16 @@ func RecordEvent(m dogma.Event) Action { // dispatchAction is an implementation of Action that dispatches a message to // the engine. type dispatchAction struct { - r message.Role m dogma.Message loc location.Location } func (a dispatchAction) Caption() string { + mt := message.TypeOf(a.m) return inflect.Sprintf( - a.r, + mt.Kind(), " %s ", - message.TypeOf(a.m), + mt, ) } @@ -73,28 +72,17 @@ func (a dispatchAction) ConfigurePredicate(*PredicateOptions) { func (a dispatchAction) Do(ctx context.Context, s ActionScope) error { mt := message.TypeOf(a.m) - r, ok := s.App.MessageTypes().RoleOf(mt) // TODO: These checks should result in information being added to the // report, not just returning an error. // // See https://github.com/dogmatiq/testkit/issues/162 - if !ok { + if !s.App.MessageTypes().Has(mt) { return inflect.Errorf( - a.r, + mt.Kind(), "cannot , %s is a not a recognized message type", mt, ) - } else if r != a.r { - return inflect.Errorf( - a.r, - "cannot , %s", - inflect.Sprintf( - r, - "%s is configured as a ", - mt, - ), - ) } return s.Engine.Dispatch(ctx, a.m, s.OperationOptions...) diff --git a/expectation.message.command_test.go b/expectation.message.command_test.go index c8aa32c1..6fbfd0fb 100644 --- a/expectation.message.command_test.go +++ b/expectation.message.command_test.go @@ -254,19 +254,6 @@ var _ = g.Describe("func ToExecuteCommand()", func() { )) }) - g.It("fails the test if the message type is not a command", func() { - test := Begin(testingT, app) - test.Expect( - noop, - ToExecuteCommand(EventThatIsIgnored{}), - ) - - gm.Expect(testingT.Failed()).To(gm.BeTrue()) - gm.Expect(testingT.Logs).To(gm.ContainElement( - "stubs.EventStub[TypeX] is an event, it can never be executed as a command", - )) - }) - g.It("fails the test if the message type is not produced by any handlers", func() { test := Begin(testingT, app) test.Expect( diff --git a/expectation.message.event_test.go b/expectation.message.event_test.go index 50020b49..7407e2f3 100644 --- a/expectation.message.event_test.go +++ b/expectation.message.event_test.go @@ -277,19 +277,6 @@ var _ = g.Describe("func ToRecordEvent()", func() { )) }) - g.It("fails the test if the message type is not an event", func() { - test := Begin(testingT, app) - test.Expect( - noop, - ToRecordEvent(CommandThatIsIgnored{}), - ) - - gm.Expect(testingT.Failed()).To(gm.BeTrue()) - gm.Expect(testingT.Logs).To(gm.ContainElement( - "stubs.CommandStub[TypeX] is a command, it can never be recorded as an event", - )) - }) - g.It("fails the test if the message type is not produced by any handlers", func() { test := Begin(testingT, app) test.Expect( diff --git a/expectation.message.go b/expectation.message.go index 7a3541f2..c506b7ef 100644 --- a/expectation.message.go +++ b/expectation.message.go @@ -11,6 +11,7 @@ import ( "github.com/dogmatiq/testkit/internal/inflect" "github.com/dogmatiq/testkit/internal/report" "github.com/dogmatiq/testkit/internal/typecmp" + "github.com/dogmatiq/testkit/internal/validation" ) // ToExecuteCommand returns an expectation that passes if a command is executed @@ -22,14 +23,12 @@ func ToExecuteCommand(m dogma.Command) Expectation { mt := message.TypeOf(m) - if err := m.Validate(); err != nil { + if err := m.Validate(validation.CommandValidationScope()); err != nil { panic(fmt.Sprintf("ToExecuteCommand(%s): %s", mt, err)) } return &messageExpectation{ expectedMessage: m, - expectedType: mt, - expectedRole: message.CommandRole, } } @@ -42,14 +41,12 @@ func ToRecordEvent(m dogma.Event) Expectation { mt := message.TypeOf(m) - if err := m.Validate(); err != nil { + if err := m.Validate(validation.EventValidationScope()); err != nil { panic(fmt.Sprintf("ToRecordEvent(%s): %s", mt, err)) } return &messageExpectation{ expectedMessage: m, - expectedType: mt, - expectedRole: message.EventRole, } } @@ -59,38 +56,38 @@ func ToRecordEvent(m dogma.Event) Expectation { // It is the implementation used by ToExecuteCommand() and ToRecordEvent(). type messageExpectation struct { expectedMessage dogma.Message - expectedType message.Type - expectedRole message.Role } func (e *messageExpectation) Caption() string { return inflect.Sprintf( - e.expectedRole, + message.KindOf(e.expectedMessage), "to a specific '%s' ", - e.expectedType, + message.TypeOf(e.expectedMessage), ) } func (e *messageExpectation) Predicate(s PredicateScope) (Predicate, error) { + mt := message.TypeOf(e.expectedMessage) + + if err := guardAgainstExpectationOnImpossibleType(s, mt); err != nil { + return nil, err + } + return &messagePredicate{ messageComparator: s.Options.MessageComparator, expectedMessage: e.expectedMessage, - expectedType: e.expectedType, - expectedRole: e.expectedRole, bestMatchDistance: typecmp.Unrelated, tracker: tracker{ - role: e.expectedRole, + kind: mt.Kind(), options: s.Options, }, - }, validateRole(s, e.expectedType, e.expectedRole) + }, nil } // messagePredicate is the Predicate implementation for messageExpectation. type messagePredicate struct { messageComparator MessageComparator expectedMessage dogma.Message - expectedRole message.Role - expectedType message.Type ok bool bestMatch *envelope.Envelope bestMatchDistance typecmp.Distance @@ -123,10 +120,7 @@ func (p *messagePredicate) messageProduced(env *envelope.Envelope) { p.bestMatch = env p.bestMatchDistance = typecmp.Identical - - if env.Role == p.expectedRole { - p.ok = true - } + p.ok = true } // updateBestMatch replaces p.bestMatch with env if it is a better match. @@ -150,13 +144,15 @@ func (p *messagePredicate) Done() { } func (p *messagePredicate) Report(ctx ReportGenerationContext) *Report { + mt := message.TypeOf(p.expectedMessage) + rep := &Report{ TreeOk: ctx.TreeOk, Ok: p.ok, Criteria: inflect.Sprintf( - p.expectedRole, + mt.Kind(), " a specific '%s' ", - message.TypeOf(p.expectedMessage), + mt, ), } @@ -174,12 +170,12 @@ func (p *messagePredicate) Report(ctx ReportGenerationContext) *Report { if p.bestMatchDistance == typecmp.Identical { if p.bestMatch.Origin == nil { rep.Explanation = inflect.Sprint( - p.expectedRole, + mt.Kind(), "a similar was via a ", ) } else { rep.Explanation = inflect.Sprintf( - p.expectedRole, + mt.Kind(), "a similar was by the '%s' %s message handler", p.bestMatch.Origin.Handler.Identity().Name, p.bestMatch.Origin.HandlerType, @@ -190,12 +186,12 @@ func (p *messagePredicate) Report(ctx ReportGenerationContext) *Report { } else { if p.bestMatch.Origin == nil { rep.Explanation = inflect.Sprint( - p.expectedRole, + mt.Kind(), "a of a similar type was via a ", ) } else { rep.Explanation = inflect.Sprintf( - p.expectedRole, + mt.Kind(), "a of a similar type was by the '%s' %s message handler", p.bestMatch.Origin.Handler.Identity().Name, p.bestMatch.Origin.HandlerType, diff --git a/expectation.messagecommon.go b/expectation.messagecommon.go index 4bf5b5e0..2d6df389 100644 --- a/expectation.messagecommon.go +++ b/expectation.messagecommon.go @@ -23,7 +23,7 @@ func reportNoMatch(rep *Report, t *tracker) { for _, ht := range configkit.HandlerTypes { e := t.enabled[ht] - if ht.IsProducerOf(t.role) { + if ht.IsProducerOf(t.kind) { relevant = append(relevant, ht.String()) if e { @@ -56,11 +56,11 @@ func reportNoMatch(rep *Report, t *tracker) { if t.total == 0 { rep.Explanation = "no messages were produced at all" } else if t.produced == 0 { - rep.Explanation = inflect.Sprint(t.role, "no were at all") + rep.Explanation = inflect.Sprint(t.kind, "no were at all") } else if t.options.MatchDispatchCycleStartedFacts { - rep.Explanation = inflect.Sprint(t.role, "nothing a matching ") + rep.Explanation = inflect.Sprint(t.kind, "nothing a matching ") } else { - rep.Explanation = inflect.Sprint(t.role, "none of the engaged handlers a matching ") + rep.Explanation = inflect.Sprint(t.kind, "none of the engaged handlers a matching ") } for _, n := range t.engagedOrder { @@ -68,43 +68,33 @@ func reportNoMatch(rep *Report, t *tracker) { } if t.options.MatchDispatchCycleStartedFacts { - s.AppendListItem(inflect.Sprint(t.role, "verify the logic within the code that uses the ")) + s.AppendListItem(inflect.Sprint(t.kind, "verify the logic within the code that uses the ")) } } -// validateRole returns an error if the message type t does not have a role of r -// within the application. -func validateRole( +// guardAgainstExpectationOnImpossibleType returns an error if the predicate +// with scope s cannot possible match a message of type t. +func guardAgainstExpectationOnImpossibleType( s PredicateScope, t message.Type, - r message.Role, ) error { - actual, ok := s.App.MessageTypes().RoleOf(t) - // TODO: These checks should result in information being added to the // report, not just returning an error. // // See https://github.com/dogmatiq/testkit/issues/162 - if !ok { + if !s.App.MessageTypes().Has(t) { return inflect.Errorf( - r, + t.Kind(), "a of type %s can never be , the application does not use this message type", t, ) - } else if actual != r { - return inflect.Errorf( - r, - "%s is a %s, it can never be as a ", - t, - actual, - ) } else if !s.Options.MatchDispatchCycleStartedFacts { // If we're NOT matching messages from DispatchCycleStarted facts that // means this expectation can only ever pass if the message is produced // by a handler. - if _, ok := s.App.MessageTypes().Produced[t]; !ok { + if !s.App.MessageTypes().Produced.Has(t) { return inflect.Errorf( - r, + t.Kind(), "no handlers of type %s, it is only ever consumed", t, ) @@ -117,8 +107,8 @@ func validateRole( // tracker is a fact.Observer used by expectations that need to keep track of // information about handlers and the messages they produce. type tracker struct { - // role is the role that the message is expecting to find. - role message.Role + // kind is the kind of message the tracker is expecting to find. + kind message.Kind // options is the set of options passed to the predicate. options PredicateOptions @@ -151,7 +141,7 @@ func (t *tracker) Notify(f fact.Fact) (*envelope.Envelope, bool) { t.cycleBegun = true t.enabled = x.EnabledHandlerTypes if t.options.MatchDispatchCycleStartedFacts { - t.messageProduced(x.Envelope.Role) + t.messageProduced(x.Envelope) return x.Envelope, true } case fact.HandlingBegun: @@ -160,16 +150,16 @@ func (t *tracker) Notify(f fact.Fact) (*envelope.Envelope, bool) { x.Handler.HandlerType(), ) case fact.EventRecordedByAggregate: - t.messageProduced(x.EventEnvelope.Role) + t.messageProduced(x.EventEnvelope) return x.EventEnvelope, true case fact.EventRecordedByIntegration: - t.messageProduced(x.EventEnvelope.Role) + t.messageProduced(x.EventEnvelope) return x.EventEnvelope, true case fact.CommandExecutedByProcess: - t.messageProduced(x.CommandEnvelope.Role) + t.messageProduced(x.CommandEnvelope) return x.CommandEnvelope, true case fact.TimeoutScheduledByProcess: - t.messageProduced(x.TimeoutEnvelope.Role) + t.messageProduced(x.TimeoutEnvelope) return x.TimeoutEnvelope, true } @@ -177,7 +167,7 @@ func (t *tracker) Notify(f fact.Fact) (*envelope.Envelope, bool) { } func (t *tracker) updateEngaged(n string, ht configkit.HandlerType) { - if ht.IsProducerOf(t.role) { + if ht.IsProducerOf(t.kind) { if t.engagedType == nil { t.engagedType = map[string]configkit.HandlerType{} } @@ -189,10 +179,10 @@ func (t *tracker) updateEngaged(n string, ht configkit.HandlerType) { } } -func (t *tracker) messageProduced(r message.Role) { +func (t *tracker) messageProduced(env *envelope.Envelope) { t.total++ - if r == t.role { + if message.KindOf(env.Message) == t.kind { t.produced++ } } diff --git a/expectation.messagematch.command_test.go b/expectation.messagematch.command_test.go index 34a80eef..798408ed 100644 --- a/expectation.messagematch.command_test.go +++ b/expectation.messagematch.command_test.go @@ -347,23 +347,6 @@ var _ = g.Describe("func ToExecuteCommandMatching()", func() { )) }) - g.It("fails the test if the message type is not a command", func() { - test := Begin(testingT, app) - test.Expect( - noop, - ToExecuteCommandMatching( - func(EventThatExecutesCommand) error { - return nil - }, - ), - ) - - gm.Expect(testingT.Failed()).To(gm.BeTrue()) - gm.Expect(testingT.Logs).To(gm.ContainElement( - "stubs.EventStub[TypeC] is an event, it can never be executed as a command", - )) - }) - g.It("fails the test if the message type is not produced by any handlers", func() { test := Begin(testingT, app) test.Expect( diff --git a/expectation.messagematch.commandonly_test.go b/expectation.messagematch.commandonly_test.go index 2578a5d5..806b047d 100644 --- a/expectation.messagematch.commandonly_test.go +++ b/expectation.messagematch.commandonly_test.go @@ -223,23 +223,6 @@ var _ = g.Describe("func ToOnlyExecuteCommandsMatching()", func() { )) }) - g.It("fails the test if the message type is not a command", func() { - test := Begin(testingT, app) - test.Expect( - noop, - ToOnlyExecuteCommandsMatching( - func(EventThatExecutesCommands) error { - return nil - }, - ), - ) - - gm.Expect(testingT.Failed()).To(gm.BeTrue()) - gm.Expect(testingT.Logs).To(gm.ContainElement( - "stubs.EventStub[TypeC] is an event, it can never be executed as a command", - )) - }) - g.It("fails the test if the message type is not produced by any handlers", func() { test := Begin(testingT, app) test.Expect( diff --git a/expectation.messagematch.event_test.go b/expectation.messagematch.event_test.go index fcdb8459..6b16b524 100644 --- a/expectation.messagematch.event_test.go +++ b/expectation.messagematch.event_test.go @@ -351,23 +351,6 @@ var _ = g.Describe("func ToRecordEventMatching()", func() { )) }) - g.It("fails the test if the message type is not an event", func() { - test := Begin(testingT, app) - test.Expect( - noop, - ToRecordEventMatching( - func(CommandThatRecordsEvent) error { - return nil - }, - ), - ) - - gm.Expect(testingT.Failed()).To(gm.BeTrue()) - gm.Expect(testingT.Logs).To(gm.ContainElement( - "stubs.CommandStub[TypeE] is a command, it can never be recorded as an event", - )) - }) - g.It("fails the test if the message type is not produced by any handlers", func() { test := Begin(testingT, app) test.Expect( diff --git a/expectation.messagematch.eventonly_test.go b/expectation.messagematch.eventonly_test.go index c38da38a..b59e7c93 100644 --- a/expectation.messagematch.eventonly_test.go +++ b/expectation.messagematch.eventonly_test.go @@ -220,23 +220,6 @@ var _ = g.Describe("func ToOnlyRecordEventsMatching()", func() { )) }) - g.It("fails the test if the message type is not an event", func() { - test := Begin(testingT, app) - test.Expect( - noop, - ToOnlyRecordEventsMatching( - func(CommandThatRecordsEvent) error { - return nil - }, - ), - ) - - gm.Expect(testingT.Failed()).To(gm.BeTrue()) - gm.Expect(testingT.Logs).To(gm.ContainElement( - "stubs.CommandStub[TypeE] is a command, it can never be recorded as an event", - )) - }) - g.It("fails the test if the message type is not produced by any handlers", func() { test := Begin(testingT, app) test.Expect( diff --git a/expectation.messagematch.go b/expectation.messagematch.go index ba6e6597..e6fe80f1 100644 --- a/expectation.messagematch.go +++ b/expectation.messagematch.go @@ -33,9 +33,8 @@ func ToExecuteCommandMatching[T dogma.Command]( } return &messageMatchExpectation[T]{ - pred: pred, - expectedRole: message.CommandRole, - exhaustive: false, + pred: pred, + exhaustive: false, } } @@ -60,9 +59,8 @@ func ToOnlyExecuteCommandsMatching[T dogma.Command]( } return &messageMatchExpectation[T]{ - pred: pred, - expectedRole: message.CommandRole, - exhaustive: true, + pred: pred, + exhaustive: true, } } @@ -86,9 +84,8 @@ func ToRecordEventMatching[T dogma.Event]( } return &messageMatchExpectation[T]{ - pred: pred, - expectedRole: message.EventRole, - exhaustive: false, + pred: pred, + exhaustive: false, } } @@ -113,9 +110,8 @@ func ToOnlyRecordEventsMatching[T dogma.Event]( } return &messageMatchExpectation[T]{ - pred: pred, - expectedRole: message.EventRole, - exhaustive: true, + pred: pred, + exhaustive: true, } } @@ -131,22 +127,21 @@ var IgnoreMessage = errors.New("this message does not need to be inspected by th // [ToRecordEventMatching], [ToOnlyExecuteCommandsMatching], and // [ToOnlyRecordEventsMatching]. type messageMatchExpectation[T dogma.Message] struct { - pred func(T) error - expectedRole message.Role - exhaustive bool + pred func(T) error + exhaustive bool } func (e *messageMatchExpectation[T]) Caption() string { if e.exhaustive { return inflect.Sprintf( - e.expectedRole, + message.KindFor[T](), "to only that match the predicate near %s", location.OfFunc(e.pred), ) } return inflect.Sprintf( - e.expectedRole, + message.KindFor[T](), "to a that matches the predicate near %s", location.OfFunc(e.pred), ) @@ -155,17 +150,16 @@ func (e *messageMatchExpectation[T]) Caption() string { func (e *messageMatchExpectation[T]) Predicate(s PredicateScope) (Predicate, error) { t := message.TypeFor[T]() if t.ReflectType().Kind() != reflect.Interface { - if err := validateRole(s, t, e.expectedRole); err != nil { + if err := guardAgainstExpectationOnImpossibleType(s, t); err != nil { return nil, err } } return &messageMatchPredicate[T]{ - pred: e.pred, - expectedRole: e.expectedRole, - exhaustive: e.exhaustive, + pred: e.pred, + exhaustive: e.exhaustive, tracker: tracker{ - role: e.expectedRole, + kind: message.KindFor[T](), options: s.Options, }, }, nil @@ -174,14 +168,13 @@ func (e *messageMatchExpectation[T]) Predicate(s PredicateScope) (Predicate, err // messageMatchPredicate is the [Predicate] implementation for // [messageMatchExpectation]. type messageMatchPredicate[T dogma.Message] struct { - pred func(T) error - expectedRole message.Role - exhaustive bool - failures []*failedMatch - matched int - ignored int - ok bool - tracker tracker + pred func(T) error + exhaustive bool + failures []*failedMatch + matched int + ignored int + ok bool + tracker tracker } // Notify updates the expectation's state in response to a new fact. @@ -198,7 +191,10 @@ func (p *messageMatchPredicate[T]) Notify(f fact.Fact) { // messageProduced updates the predicate's state to reflect the fact that a // message of the expected role has been produced. func (p *messageMatchPredicate[T]) messageProduced(env *envelope.Envelope) { - if env.Role != p.expectedRole { + expectedType := message.TypeFor[T]() + producedType := message.TypeOf(env.Message) + + if producedType.Kind() != expectedType.Kind() { return } @@ -206,7 +202,7 @@ func (p *messageMatchPredicate[T]) messageProduced(env *envelope.Envelope) { if m, ok := env.Message.(T); ok { err = p.pred(m) } else if p.exhaustive { - err = fmt.Errorf("predicate function expected %s", message.TypeFor[T]()) + err = fmt.Errorf("predicate function expected %s", expectedType) } else { err = IgnoreMessage } @@ -230,7 +226,7 @@ func (p *messageMatchPredicate[T]) messageProduced(env *envelope.Envelope) { } for _, f := range p.failures { - if f.MessageType == env.Type && f.Error == err.Error() { + if f.MessageType == producedType && f.Error == err.Error() { f.Count++ return } @@ -239,7 +235,7 @@ func (p *messageMatchPredicate[T]) messageProduced(env *envelope.Envelope) { p.failures = append( p.failures, &failedMatch{ - MessageType: env.Type, + MessageType: producedType, Error: err.Error(), Count: 1, }, @@ -257,11 +253,13 @@ func (p *messageMatchPredicate[T]) Done() { } func (p *messageMatchPredicate[T]) Report(ctx ReportGenerationContext) *Report { + k := message.KindFor[T]() + rep := &Report{ TreeOk: ctx.TreeOk, Ok: p.ok, Criteria: inflect.Sprintf( - p.expectedRole, + k, " a that matches the predicate near %s", location.OfFunc(p.pred), ), @@ -269,7 +267,7 @@ func (p *messageMatchPredicate[T]) Report(ctx ReportGenerationContext) *Report { if p.exhaustive { rep.Criteria = inflect.Sprintf( - p.expectedRole, + k, "only that match the predicate near %s", location.OfFunc(p.pred), ) @@ -305,7 +303,7 @@ func (p *messageMatchPredicate[T]) Report(ctx ReportGenerationContext) *Report { if p.ignored > 0 { suggestions.AppendListItem( "verify the logic within the predicate function, it ignored %s", - inflect.Sprintf(p.expectedRole, "%d ", p.ignored), + inflect.Sprintf(k, "%d ", p.ignored), ) } else if len(p.failures) > 0 { suggestions.AppendListItem("verify the logic within the predicate function") @@ -320,7 +318,7 @@ func (p *messageMatchPredicate[T]) Report(ctx ReportGenerationContext) *Report { } rep.Explanation = inflect.Sprintf( - p.expectedRole, + k, "%s relevant matched the predicate", matched, ) diff --git a/expectation.messagetype.command_test.go b/expectation.messagetype.command_test.go index f619a50e..78043d3e 100644 --- a/expectation.messagetype.command_test.go +++ b/expectation.messagetype.command_test.go @@ -239,19 +239,6 @@ var _ = g.Describe("func ToExecuteCommandType()", func() { )) }) - g.It("fails the test if the message type is not a command", func() { - test := Begin(testingT, app) - test.Expect( - noop, - ToExecuteCommandType[EventThatIsIgnored](), - ) - - gm.Expect(testingT.Failed()).To(gm.BeTrue()) - gm.Expect(testingT.Logs).To(gm.ContainElement( - "stubs.EventStub[TypeX] is an event, it can never be executed as a command", - )) - }) - g.It("fails the test if the message type is not produced by any handlers", func() { test := Begin(testingT, app) test.Expect( diff --git a/expectation.messagetype.event_test.go b/expectation.messagetype.event_test.go index 373df25e..bb39532b 100644 --- a/expectation.messagetype.event_test.go +++ b/expectation.messagetype.event_test.go @@ -256,19 +256,6 @@ var _ = g.Describe("func ToRecordEventType()", func() { )) }) - g.It("fails the test if the message type is not an event", func() { - test := Begin(testingT, app) - test.Expect( - noop, - ToRecordEventType[CommandThatRecordsEvent](), - ) - - gm.Expect(testingT.Failed()).To(gm.BeTrue()) - gm.Expect(testingT.Logs).To(gm.ContainElement( - "stubs.CommandStub[TypeE] is a command, it can never be recorded as an event", - )) - }) - g.It("fails the test if the message type is not produced by any handlers", func() { test := Begin(testingT, app) test.Expect( diff --git a/expectation.messagetype.go b/expectation.messagetype.go index 60306aa8..fb8a636b 100644 --- a/expectation.messagetype.go +++ b/expectation.messagetype.go @@ -15,7 +15,6 @@ import ( func ToExecuteCommandType[T dogma.Command]() Expectation { return &messageTypeExpectation{ expectedType: message.TypeFor[T](), - expectedRole: message.CommandRole, } } @@ -30,7 +29,6 @@ func ToExecuteCommandOfType(m dogma.Command) Expectation { return &messageTypeExpectation{ expectedType: message.TypeOf(m), - expectedRole: message.CommandRole, } } @@ -39,7 +37,6 @@ func ToExecuteCommandOfType(m dogma.Command) Expectation { func ToRecordEventType[T dogma.Event]() Expectation { return &messageTypeExpectation{ expectedType: message.TypeFor[T](), - expectedRole: message.EventRole, } } @@ -54,7 +51,6 @@ func ToRecordEventOfType(m dogma.Event) Expectation { return &messageTypeExpectation{ expectedType: message.TypeOf(m), - expectedRole: message.EventRole, } } @@ -65,34 +61,35 @@ func ToRecordEventOfType(m dogma.Event) Expectation { // ToRecordEventOfType(). type messageTypeExpectation struct { expectedType message.Type - expectedRole message.Role } func (e *messageTypeExpectation) Caption() string { return inflect.Sprintf( - e.expectedRole, + e.expectedType.Kind(), "to a of type %s", e.expectedType, ) } func (e *messageTypeExpectation) Predicate(s PredicateScope) (Predicate, error) { + if err := guardAgainstExpectationOnImpossibleType(s, e.expectedType); err != nil { + return nil, err + } + return &messageTypePredicate{ expectedType: e.expectedType, - expectedRole: e.expectedRole, bestMatchDistance: typecmp.Unrelated, tracker: tracker{ - role: e.expectedRole, + kind: e.expectedType.Kind(), options: s.Options, }, - }, validateRole(s, e.expectedType, e.expectedRole) + }, nil } // messageTypePredicate is the Predicate implementation for // messageTypeExpectation. type messageTypePredicate struct { expectedType message.Type - expectedRole message.Role ok bool bestMatch *envelope.Envelope bestMatchDistance typecmp.Distance @@ -112,9 +109,11 @@ func (p *messageTypePredicate) Notify(f fact.Fact) { // messageProduced updates the predicates's state to reflect the fact that a // message has been produced. func (p *messageTypePredicate) messageProduced(env *envelope.Envelope) { + producedType := message.TypeOf(env.Message) + dist := typecmp.MeasureDistance( p.expectedType.ReflectType(), - env.Type.ReflectType(), + producedType.ReflectType(), ) if dist < p.bestMatchDistance { @@ -122,7 +121,7 @@ func (p *messageTypePredicate) messageProduced(env *envelope.Envelope) { p.bestMatchDistance = dist } - if dist == typecmp.Identical && p.expectedRole == env.Role { + if dist == typecmp.Identical { p.ok = true } } @@ -139,7 +138,7 @@ func (p *messageTypePredicate) Report(ctx ReportGenerationContext) *Report { TreeOk: ctx.TreeOk, Ok: p.ok, Criteria: inflect.Sprintf( - p.expectedRole, + p.expectedType.Kind(), " any '%s' ", p.expectedType, ), @@ -158,12 +157,12 @@ func (p *messageTypePredicate) Report(ctx ReportGenerationContext) *Report { if p.bestMatch.Origin == nil { rep.Explanation = inflect.Sprint( - p.expectedRole, + p.expectedType.Kind(), "a of a similar type was via a ", ) } else { rep.Explanation = inflect.Sprintf( - p.expectedRole, + p.expectedType.Kind(), "a of a similar type was by the '%s' %s message handler", p.bestMatch.Origin.Handler.Identity().Name, p.bestMatch.Origin.Handler.HandlerType(), @@ -184,6 +183,6 @@ func (p *messageTypePredicate) buildDiff(rep *Report) { report.WriteDiff( &rep.Section("Message Type Diff").Content, p.expectedType.String(), - p.bestMatch.Type.String(), + message.TypeOf(p.bestMatch.Message).String(), ) } diff --git a/go.mod b/go.mod index 83c87468..5b36743b 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/dogmatiq/testkit go 1.23 require ( - github.com/dogmatiq/configkit v0.14.0 + github.com/dogmatiq/configkit v0.13.9-0.20240925232509-f4838f33e657 github.com/dogmatiq/cosyne v0.2.0 github.com/dogmatiq/dapper v0.6.0 - github.com/dogmatiq/dogma v0.14.3 - github.com/dogmatiq/enginekit v0.15.1 + github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 + github.com/dogmatiq/enginekit v0.13.1-0.20240929214803-d23339eac9a9 github.com/dogmatiq/iago v0.4.0 github.com/dogmatiq/linger v1.1.0 github.com/onsi/ginkgo/v2 v2.20.2 @@ -19,17 +19,13 @@ require ( ) require ( - github.com/dogmatiq/interopspec v0.5.4 // indirect github.com/dogmatiq/jumble v0.1.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect - github.com/google/uuid v1.6.0 // indirect golang.org/x/net v0.29.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/tools v0.25.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/grpc v1.67.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ebafd091..9d988af6 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,25 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dogmatiq/configkit v0.14.0 h1:Gr79vNfmHzEwPn/6QbNSUjitvYk9jJRuLGZSmyXeIEk= -github.com/dogmatiq/configkit v0.14.0/go.mod h1:BMGhQBVMJuTN7KYd99M0qWUEQcuStlTZ2jxykua+MEI= +github.com/dogmatiq/configkit v0.13.9-0.20240925232509-f4838f33e657 h1:karWGZKGrSam2JBitE57CxREF4AqFP3uAwAX0+3xeJ8= +github.com/dogmatiq/configkit v0.13.9-0.20240925232509-f4838f33e657/go.mod h1:yRpGriDLiSMPOUi7jfR+1e69bIAoa5Y1UD/lPdUTiv8= github.com/dogmatiq/cosyne v0.2.0 h1:tO957BpS4I9kqSw31ds6Ef4CXvV8zPAqWzbXKElsGWg= github.com/dogmatiq/cosyne v0.2.0/go.mod h1:dD8EZjbRX7FFw9t6P7l1nwoZbA7YxtOCfl9ZZAHPucU= github.com/dogmatiq/dapper v0.6.0 h1:hnWUsjnt3nUiC9hmkPvuxrnMd7fYNz1i+/GS3gOx0Xs= github.com/dogmatiq/dapper v0.6.0/go.mod h1:ubRHWzt73s0MsPpGhWvnfW/Z/1YPnrkCsQv6CUOZVEw= +<<<<<<< HEAD github.com/dogmatiq/dogma v0.14.3 h1:qwZqU1yqp80toUJcJBdFxLlh6xvlFd7jb7rycmriRUo= github.com/dogmatiq/dogma v0.14.3/go.mod h1:9lyVA+6V2+E/exV0IrBOrkUiyFwIATEhv+b0vnB2umQ= github.com/dogmatiq/enginekit v0.15.1 h1:i5FhnfTNyIZwgqHlH9MNU/9kDoOXvnDLi40FtIkUhbo= github.com/dogmatiq/enginekit v0.15.1/go.mod h1:VYNGtuEGGYOnJqvSzi40QnkptFSfuSGeVk0jV83lzUA= +======= +github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 h1:SKhtRnDs7CC3ZNMux7lYCxCFz5ZhmeXjvo7/uMNISLo= +github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1/go.mod h1:9lyVA+6V2+E/exV0IrBOrkUiyFwIATEhv+b0vnB2umQ= +github.com/dogmatiq/enginekit v0.13.1-0.20240929214803-d23339eac9a9 h1:CuQ1OBcfZVfnfAZ4zcqCadsOLVtnfCN4vRsprt/Nncw= +github.com/dogmatiq/enginekit v0.13.1-0.20240929214803-d23339eac9a9/go.mod h1:XsY6KGPIC6zHx1duYDXbHTomqH9y+QOJX/tEiNZE2Qg= +>>>>>>> 7f0d058 (Update expectation internals to use `message.Role`.) github.com/dogmatiq/iago v0.4.0 h1:57nZqVT34IZxtCZEW/RFif7DNUEjMXgevfr/Mmd0N8I= github.com/dogmatiq/iago v0.4.0/go.mod h1:fishMWBtzYcjgis6d873VTv9kFm/wHYLOzOyO9ECBDc= -github.com/dogmatiq/interopspec v0.5.4 h1:klyGPy16zUKJartJIJOBZ4JrQ4LxFiY3wgzFTTiNlDk= -github.com/dogmatiq/interopspec v0.5.4/go.mod h1:JL0QFXBTRGH+RgQqExhEUdhtv5Vn802w43RxKQbk8Co= github.com/dogmatiq/jumble v0.1.0 h1:Cb3ExfxY+AoUP4G9/sOwoOdYX8o+kOLK8+dhXAry+QA= github.com/dogmatiq/jumble v0.1.0/go.mod h1:FCGV2ImXu8zvThxhd4QLstiEdu74vbIVw9bFJSBcKr4= github.com/dogmatiq/linger v1.1.0 h1:kGL9sL79qRa6Cr8PhadeJ/ptbum+b48pAaNWWlyVVKg= @@ -46,9 +51,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -105,10 +109,13 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +<<<<<<< HEAD google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +======= +>>>>>>> 7f0d058 (Update expectation internals to use `message.Role`.) google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -130,3 +137,5 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= From eadb8f74692fc0ece53d854566c29f50c226869b Mon Sep 17 00:00:00 2001 From: James Harris Date: Thu, 26 Sep 2024 10:27:03 +1000 Subject: [PATCH 5/9] Add tests for dispatching commands and events directly. --- engine/engine.go | 16 ++++------- engine/engine_test.go | 40 +++++++++++++++++++++++++++ engine/internal/process/controller.go | 4 +-- go.mod | 2 +- go.sum | 4 +-- 5 files changed, 50 insertions(+), 16 deletions(-) diff --git a/engine/engine.go b/engine/engine.go index 087a7b4e..7af1db47 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -193,20 +193,14 @@ func (e *Engine) Dispatch( env, err := message.TryMap( m, func(m dogma.Command) (*envelope.Envelope, error) { - if err := m.Validate(validation.CommandValidationScope()); err != nil { - return nil, err - } - return envelope.NewCommand(id, m, oo.now), nil + return envelope.NewCommand(id, m, oo.now), + m.Validate(validation.CommandValidationScope()) }, func(m dogma.Event) (*envelope.Envelope, error) { - if err := m.Validate(validation.EventValidationScope()); err != nil { - return nil, err - } - return envelope.NewEvent(id, m, oo.now), nil - }, - func(t dogma.Timeout) (*envelope.Envelope, error) { - panic("cannot dispatch timeout messages") + return envelope.NewEvent(id, m, oo.now), + m.Validate(validation.EventValidationScope()) }, + nil, ) if err != nil { panic(fmt.Sprintf("cannot dispatch invalid %s message: %s", mt, err)) diff --git a/engine/engine_test.go b/engine/engine_test.go index f1ccb2bf..78e0e4be 100644 --- a/engine/engine_test.go +++ b/engine/engine_test.go @@ -134,6 +134,46 @@ var _ = g.Describe("type Engine", func() { }) g.Describe("func Dispatch()", func() { + g.It("allows dispatching commands", func() { + called := false + aggregate.HandleCommandFunc = func( + dogma.AggregateRoot, + dogma.AggregateCommandScope, + dogma.Command, + ) { + called = true + } + + err := engine.Dispatch( + context.Background(), + AggregateCommand{}, + ) + Expect(err).ShouldNot(HaveOccurred()) + Expect(called).To(BeTrue()) + }) + + g.It("allows dispatching events", func() { + called := false + projection.HandleEventFunc = func( + context.Context, + []byte, + []byte, + []byte, + dogma.ProjectionEventScope, + dogma.Event, + ) (bool, error) { + called = true + return true, nil + } + + err := engine.Dispatch( + context.Background(), + ForeignEventForProjection{}, + ) + Expect(err).ShouldNot(HaveOccurred()) + Expect(called).To(BeTrue()) + }) + g.It("skips handlers that are disabled by type", func() { aggregate.HandleCommandFunc = func( dogma.AggregateRoot, diff --git a/engine/internal/process/controller.go b/engine/internal/process/controller.go index 4e8731c3..8dbc35d4 100644 --- a/engine/internal/process/controller.go +++ b/engine/internal/process/controller.go @@ -118,7 +118,7 @@ func (c *Controller) Handle( config: c.Config, handleMethod: message.Map( env.Message, - func(dogma.Command) string { panic("unexpected message kind") }, + nil, func(dogma.Event) string { return "HandleEvent" }, func(dogma.Timeout) string { return "HandleTimeout" }, ), @@ -160,7 +160,7 @@ func (c *Controller) route( ) (id string, ok bool, err error) { message.Switch( env.Message, - func(m dogma.Command) { panic("unexpected message kind") }, + nil, func(m dogma.Event) { id, ok, err = c.routeEvent(ctx, obs, env, m) }, func(m dogma.Timeout) { id, ok, err = c.routeTimeout(ctx, obs, env) }, ) diff --git a/go.mod b/go.mod index 5b36743b..114d45ff 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/dogmatiq/testkit go 1.23 require ( - github.com/dogmatiq/configkit v0.13.9-0.20240925232509-f4838f33e657 + github.com/dogmatiq/configkit v0.13.9-0.20240926002042-bd25b536fc21 github.com/dogmatiq/cosyne v0.2.0 github.com/dogmatiq/dapper v0.6.0 github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 diff --git a/go.sum b/go.sum index 9d988af6..a9b7c584 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dogmatiq/configkit v0.13.9-0.20240925232509-f4838f33e657 h1:karWGZKGrSam2JBitE57CxREF4AqFP3uAwAX0+3xeJ8= -github.com/dogmatiq/configkit v0.13.9-0.20240925232509-f4838f33e657/go.mod h1:yRpGriDLiSMPOUi7jfR+1e69bIAoa5Y1UD/lPdUTiv8= +github.com/dogmatiq/configkit v0.13.9-0.20240926002042-bd25b536fc21 h1:ZfdXr1zjBRjkUF5D8pn0jAQ89pDUbwYghkDSJAbQENg= +github.com/dogmatiq/configkit v0.13.9-0.20240926002042-bd25b536fc21/go.mod h1:yRpGriDLiSMPOUi7jfR+1e69bIAoa5Y1UD/lPdUTiv8= github.com/dogmatiq/cosyne v0.2.0 h1:tO957BpS4I9kqSw31ds6Ef4CXvV8zPAqWzbXKElsGWg= github.com/dogmatiq/cosyne v0.2.0/go.mod h1:dD8EZjbRX7FFw9t6P7l1nwoZbA7YxtOCfl9ZZAHPucU= github.com/dogmatiq/dapper v0.6.0 h1:hnWUsjnt3nUiC9hmkPvuxrnMd7fYNz1i+/GS3gOx0Xs= From ef7040f42d464aa062500437ea4b0177cb9d8a9d Mon Sep 17 00:00:00 2001 From: James Harris Date: Thu, 26 Sep 2024 10:33:34 +1000 Subject: [PATCH 6/9] Add validation scopes. --- CHANGELOG.md | 2 ++ go.mod | 2 +- go.sum | 4 ++-- validation.go | 42 ++++++++++++++++++++++++++++++++++++++++++ validation_test.go | 21 +++++++++++++++++++++ 5 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 validation.go create mode 100644 validation_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f780ef7..23af5513 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ The format is based on [Keep a Changelog], and this project adheres to - Added `Not()` expectation, which negates a single expectation. `Not()` is functionally equivalent to using `NoneOf()` with a single argument, but produces more intuitive test reports. +- Added `CommandValidationScope()`, `EventValidationScope()`, and + `TimeoutValidationScope()` to help when testing message validation logic. ## [0.17.2] - 2024-09-25 diff --git a/go.mod b/go.mod index 114d45ff..48185b41 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/dogmatiq/testkit go 1.23 require ( - github.com/dogmatiq/configkit v0.13.9-0.20240926002042-bd25b536fc21 + github.com/dogmatiq/configkit v0.14.1-0.20240929215230-3af80ab0c2e9 github.com/dogmatiq/cosyne v0.2.0 github.com/dogmatiq/dapper v0.6.0 github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 diff --git a/go.sum b/go.sum index a9b7c584..539a4aa6 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dogmatiq/configkit v0.13.9-0.20240926002042-bd25b536fc21 h1:ZfdXr1zjBRjkUF5D8pn0jAQ89pDUbwYghkDSJAbQENg= -github.com/dogmatiq/configkit v0.13.9-0.20240926002042-bd25b536fc21/go.mod h1:yRpGriDLiSMPOUi7jfR+1e69bIAoa5Y1UD/lPdUTiv8= +github.com/dogmatiq/configkit v0.14.1-0.20240929215230-3af80ab0c2e9 h1:kFoWErDVOq1zLJziPDWdXFldyRM8/4zQwdlQ+dNfQI8= +github.com/dogmatiq/configkit v0.14.1-0.20240929215230-3af80ab0c2e9/go.mod h1:WhUSbkhYksNVSbn2Q0LIhkY+bFV2BERNTUMVy4Zsjr4= github.com/dogmatiq/cosyne v0.2.0 h1:tO957BpS4I9kqSw31ds6Ef4CXvV8zPAqWzbXKElsGWg= github.com/dogmatiq/cosyne v0.2.0/go.mod h1:dD8EZjbRX7FFw9t6P7l1nwoZbA7YxtOCfl9ZZAHPucU= github.com/dogmatiq/dapper v0.6.0 h1:hnWUsjnt3nUiC9hmkPvuxrnMd7fYNz1i+/GS3gOx0Xs= diff --git a/validation.go b/validation.go new file mode 100644 index 00000000..8c1c50ff --- /dev/null +++ b/validation.go @@ -0,0 +1,42 @@ +package testkit + +import ( + "github.com/dogmatiq/dogma" + "github.com/dogmatiq/testkit/internal/validation" +) + +// CommandValidationScope returns a [dogma.CommandValidationScope] that can be +// used when testing command validation logic. +func CommandValidationScope(...CommandValidationScopeOption) dogma.CommandValidationScope { + return validation.CommandValidationScope() +} + +// EventValidationScope returns a [dogma.EventValidationScope] that can be used +// when testing event validation logic. +func EventValidationScope(...EventValidationScopeOption) dogma.EventValidationScope { + return validation.EventValidationScope() +} + +// TimeoutValidationScope returns a [dogma.TimeoutValidationScope] that can be +// used when testing timeout validation logic. +func TimeoutValidationScope(...TimeoutValidationScopeOption) dogma.TimeoutValidationScope { + return validation.TimeoutValidationScope() +} + +// CommandValidationScopeOption is an option that changes the behavior of a +// [dogma.CommandValidationScope] created by calling [CommandValidationScope]. +type CommandValidationScopeOption interface { + reservedCommandValidationScopeOption() +} + +// EventValidationScopeOption is an option that changes the behavior of a +// [dogma.EventValidationScope] created by calling [EventValidationScope]. +type EventValidationScopeOption interface { + reservedEventValidationScopeOption() +} + +// TimeoutValidationScopeOption is an option that changes the behavior of a +// [dogma.TimeoutValidationScope] created by calling [TimeoutValidationScope]. +type TimeoutValidationScopeOption interface { + reservedTimeoutValidationScopeOption() +} diff --git a/validation_test.go b/validation_test.go new file mode 100644 index 00000000..1590153d --- /dev/null +++ b/validation_test.go @@ -0,0 +1,21 @@ +package testkit_test + +import ( + "testing" + + . "github.com/dogmatiq/testkit" +) + +func TestValidationScopeCreation(t *testing.T) { + if CommandValidationScope() == nil { + t.Errorf("CommandValidationScope() returned nil") + } + + if EventValidationScope() == nil { + t.Errorf("EventValidationScope() returned nil") + } + + if TimeoutValidationScope() == nil { + t.Errorf("TimeoutValidationScope() returned nil") + } +} From af292b6e2cfe930ebd947b421a9d95871e023797 Mon Sep 17 00:00:00 2001 From: James Harris Date: Mon, 30 Sep 2024 13:02:45 +1000 Subject: [PATCH 7/9] Replace `message.Set` with `collection.Set`. --- engine/configurer.go | 3 ++- go.mod | 4 ++-- go.sum | 22 ++++------------------ 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/engine/configurer.go b/engine/configurer.go index 77919087..c34543bc 100644 --- a/engine/configurer.go +++ b/engine/configurer.go @@ -5,6 +5,7 @@ import ( "github.com/dogmatiq/configkit" "github.com/dogmatiq/configkit/message" + "github.com/dogmatiq/enginekit/collection" "github.com/dogmatiq/testkit/engine/internal/aggregate" "github.com/dogmatiq/testkit/engine/internal/integration" "github.com/dogmatiq/testkit/engine/internal/process" @@ -74,7 +75,7 @@ func (c *configurer) VisitRichProjection(_ context.Context, cfg configkit.RichPr func (c *configurer) registerController( ctrl controller, - consumed message.Set[message.Type], + consumed collection.Set[message.Type], ) { c.engine.controllers[ctrl.HandlerConfig().Identity().Name] = ctrl diff --git a/go.mod b/go.mod index 48185b41..b4c1503b 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/dogmatiq/testkit go 1.23 require ( - github.com/dogmatiq/configkit v0.14.1-0.20240929215230-3af80ab0c2e9 + github.com/dogmatiq/configkit v0.14.1-0.20240930030138-f9e71cc9ddcb github.com/dogmatiq/cosyne v0.2.0 github.com/dogmatiq/dapper v0.6.0 github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 - github.com/dogmatiq/enginekit v0.13.1-0.20240929214803-d23339eac9a9 + github.com/dogmatiq/enginekit v0.14.1-0.20240930025837-001cd27abe23 github.com/dogmatiq/iago v0.4.0 github.com/dogmatiq/linger v1.1.0 github.com/onsi/ginkgo/v2 v2.20.2 diff --git a/go.sum b/go.sum index 539a4aa6..1bd54f93 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,16 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dogmatiq/configkit v0.14.1-0.20240929215230-3af80ab0c2e9 h1:kFoWErDVOq1zLJziPDWdXFldyRM8/4zQwdlQ+dNfQI8= -github.com/dogmatiq/configkit v0.14.1-0.20240929215230-3af80ab0c2e9/go.mod h1:WhUSbkhYksNVSbn2Q0LIhkY+bFV2BERNTUMVy4Zsjr4= +github.com/dogmatiq/configkit v0.14.1-0.20240930030138-f9e71cc9ddcb h1:K959v1eWzeH15OOs/lfEc5c3ohNgnPQYh5dkLH6SWfI= +github.com/dogmatiq/configkit v0.14.1-0.20240930030138-f9e71cc9ddcb/go.mod h1:KQfg3ODcxen+JGnc7Ae+0aqMMgA8jUT7LbfjjNslod8= github.com/dogmatiq/cosyne v0.2.0 h1:tO957BpS4I9kqSw31ds6Ef4CXvV8zPAqWzbXKElsGWg= github.com/dogmatiq/cosyne v0.2.0/go.mod h1:dD8EZjbRX7FFw9t6P7l1nwoZbA7YxtOCfl9ZZAHPucU= github.com/dogmatiq/dapper v0.6.0 h1:hnWUsjnt3nUiC9hmkPvuxrnMd7fYNz1i+/GS3gOx0Xs= github.com/dogmatiq/dapper v0.6.0/go.mod h1:ubRHWzt73s0MsPpGhWvnfW/Z/1YPnrkCsQv6CUOZVEw= -<<<<<<< HEAD -github.com/dogmatiq/dogma v0.14.3 h1:qwZqU1yqp80toUJcJBdFxLlh6xvlFd7jb7rycmriRUo= -github.com/dogmatiq/dogma v0.14.3/go.mod h1:9lyVA+6V2+E/exV0IrBOrkUiyFwIATEhv+b0vnB2umQ= -github.com/dogmatiq/enginekit v0.15.1 h1:i5FhnfTNyIZwgqHlH9MNU/9kDoOXvnDLi40FtIkUhbo= -github.com/dogmatiq/enginekit v0.15.1/go.mod h1:VYNGtuEGGYOnJqvSzi40QnkptFSfuSGeVk0jV83lzUA= -======= github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 h1:SKhtRnDs7CC3ZNMux7lYCxCFz5ZhmeXjvo7/uMNISLo= github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1/go.mod h1:9lyVA+6V2+E/exV0IrBOrkUiyFwIATEhv+b0vnB2umQ= -github.com/dogmatiq/enginekit v0.13.1-0.20240929214803-d23339eac9a9 h1:CuQ1OBcfZVfnfAZ4zcqCadsOLVtnfCN4vRsprt/Nncw= -github.com/dogmatiq/enginekit v0.13.1-0.20240929214803-d23339eac9a9/go.mod h1:XsY6KGPIC6zHx1duYDXbHTomqH9y+QOJX/tEiNZE2Qg= ->>>>>>> 7f0d058 (Update expectation internals to use `message.Role`.) +github.com/dogmatiq/enginekit v0.14.1-0.20240930025837-001cd27abe23 h1:2UQ8POi88gvkVfM3r9LfylDS0SvqKQgHC94R3nhu9XQ= +github.com/dogmatiq/enginekit v0.14.1-0.20240930025837-001cd27abe23/go.mod h1:XsY6KGPIC6zHx1duYDXbHTomqH9y+QOJX/tEiNZE2Qg= github.com/dogmatiq/iago v0.4.0 h1:57nZqVT34IZxtCZEW/RFif7DNUEjMXgevfr/Mmd0N8I= github.com/dogmatiq/iago v0.4.0/go.mod h1:fishMWBtzYcjgis6d873VTv9kFm/wHYLOzOyO9ECBDc= github.com/dogmatiq/jumble v0.1.0 h1:Cb3ExfxY+AoUP4G9/sOwoOdYX8o+kOLK8+dhXAry+QA= @@ -109,13 +102,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -<<<<<<< HEAD -google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -======= ->>>>>>> 7f0d058 (Update expectation internals to use `message.Role`.) google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From 1f9dc0fec15984b8b1bbe304028d24395063eddd Mon Sep 17 00:00:00 2001 From: James Harris Date: Thu, 3 Oct 2024 07:36:38 +1000 Subject: [PATCH 8/9] Replace `EntityMessage[Names|Types]` with generic `EntityMessages[K]` type. --- action.dispatch.go | 2 +- engine/configurer.go | 25 +++++------------------ engine/engine_test.go | 8 ++++---- engine/internal/aggregate/controller.go | 2 +- engine/internal/aggregate/scope.go | 2 +- engine/internal/integration/controller.go | 2 +- engine/internal/integration/scope.go | 2 +- engine/internal/process/controller.go | 2 +- engine/internal/process/scope.go | 4 ++-- engine/internal/projection/controller.go | 2 +- expectation.messagecommon.go | 9 +++++--- go.mod | 4 ++-- go.sum | 10 ++++----- 13 files changed, 30 insertions(+), 44 deletions(-) diff --git a/action.dispatch.go b/action.dispatch.go index 5a65cae3..e0659180 100644 --- a/action.dispatch.go +++ b/action.dispatch.go @@ -77,7 +77,7 @@ func (a dispatchAction) Do(ctx context.Context, s ActionScope) error { // report, not just returning an error. // // See https://github.com/dogmatiq/testkit/issues/162 - if !s.App.MessageTypes().Has(mt) { + if _, ok := s.App.MessageTypes()[mt]; !ok { return inflect.Errorf( mt.Kind(), "cannot , %s is a not a recognized message type", diff --git a/engine/configurer.go b/engine/configurer.go index c34543bc..d2bbf90e 100644 --- a/engine/configurer.go +++ b/engine/configurer.go @@ -4,8 +4,6 @@ import ( "context" "github.com/dogmatiq/configkit" - "github.com/dogmatiq/configkit/message" - "github.com/dogmatiq/enginekit/collection" "github.com/dogmatiq/testkit/engine/internal/aggregate" "github.com/dogmatiq/testkit/engine/internal/integration" "github.com/dogmatiq/testkit/engine/internal/process" @@ -22,64 +20,51 @@ func (c *configurer) VisitRichApplication(ctx context.Context, cfg configkit.Ric } func (c *configurer) VisitRichAggregate(_ context.Context, cfg configkit.RichAggregate) error { - mt := cfg.MessageTypes() c.registerController( &aggregate.Controller{ Config: cfg, MessageIDs: &c.engine.messageIDs, }, - mt.Consumed, ) - return nil } func (c *configurer) VisitRichProcess(_ context.Context, cfg configkit.RichProcess) error { - mt := cfg.MessageTypes() c.registerController( &process.Controller{ Config: cfg, MessageIDs: &c.engine.messageIDs, }, - mt.Consumed, ) - return nil } func (c *configurer) VisitRichIntegration(_ context.Context, cfg configkit.RichIntegration) error { - mt := cfg.MessageTypes() c.registerController( &integration.Controller{ Config: cfg, MessageIDs: &c.engine.messageIDs, }, - mt.Consumed, ) - return nil } func (c *configurer) VisitRichProjection(_ context.Context, cfg configkit.RichProjection) error { - mt := cfg.MessageTypes() c.registerController( &projection.Controller{ Config: cfg, CompactDuringHandling: c.options.compactDuringHandling, }, - mt.Consumed, ) - return nil } -func (c *configurer) registerController( - ctrl controller, - consumed collection.Set[message.Type], -) { - c.engine.controllers[ctrl.HandlerConfig().Identity().Name] = ctrl +func (c *configurer) registerController(ctrl controller) { + cfg := ctrl.HandlerConfig() + + c.engine.controllers[cfg.Identity().Name] = ctrl - for t := range consumed.All() { + for t := range cfg.MessageTypes().Consumed() { c.engine.routes[t] = append(c.engine.routes[t], ctrl) } } diff --git a/engine/engine_test.go b/engine/engine_test.go index 78e0e4be..6a0636dc 100644 --- a/engine/engine_test.go +++ b/engine/engine_test.go @@ -148,8 +148,8 @@ var _ = g.Describe("type Engine", func() { context.Background(), AggregateCommand{}, ) - Expect(err).ShouldNot(HaveOccurred()) - Expect(called).To(BeTrue()) + gm.Expect(err).ShouldNot(gm.HaveOccurred()) + gm.Expect(called).To(gm.BeTrue()) }) g.It("allows dispatching events", func() { @@ -170,8 +170,8 @@ var _ = g.Describe("type Engine", func() { context.Background(), ForeignEventForProjection{}, ) - Expect(err).ShouldNot(HaveOccurred()) - Expect(called).To(BeTrue()) + gm.Expect(err).ShouldNot(gm.HaveOccurred()) + gm.Expect(called).To(gm.BeTrue()) }) g.It("skips handlers that are disabled by type", func() { diff --git a/engine/internal/aggregate/controller.go b/engine/internal/aggregate/controller.go index 6ac82af9..a006030b 100644 --- a/engine/internal/aggregate/controller.go +++ b/engine/internal/aggregate/controller.go @@ -47,7 +47,7 @@ func (c *Controller) Handle( ) ([]*envelope.Envelope, error) { mt := message.TypeOf(env.Message) - if !c.Config.MessageTypes().Consumed.Has(mt) { + if !c.Config.MessageTypes()[mt].IsConsumed { panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), mt)) } diff --git a/engine/internal/aggregate/scope.go b/engine/internal/aggregate/scope.go index 6d1124a4..bd7cfe04 100644 --- a/engine/internal/aggregate/scope.go +++ b/engine/internal/aggregate/scope.go @@ -52,7 +52,7 @@ func (s *scope) Destroy() { func (s *scope) RecordEvent(m dogma.Event) { mt := message.TypeOf(m) - if !s.config.MessageTypes().Produced.Has(mt) { + if !s.config.MessageTypes()[mt].IsProduced { panic(panicx.UnexpectedBehavior{ Handler: s.config, Interface: "AggregateMessageHandler", diff --git a/engine/internal/integration/controller.go b/engine/internal/integration/controller.go index 37d68e12..9c7753a1 100644 --- a/engine/internal/integration/controller.go +++ b/engine/internal/integration/controller.go @@ -44,7 +44,7 @@ func (c *Controller) Handle( ) ([]*envelope.Envelope, error) { mt := message.TypeOf(env.Message) - if !c.Config.MessageTypes().Consumed.Has(mt) { + if !c.Config.MessageTypes()[mt].IsConsumed { panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), mt)) } diff --git a/engine/internal/integration/scope.go b/engine/internal/integration/scope.go index 9abe22d3..9945b36c 100644 --- a/engine/internal/integration/scope.go +++ b/engine/internal/integration/scope.go @@ -27,7 +27,7 @@ type scope struct { func (s *scope) RecordEvent(m dogma.Event) { mt := message.TypeOf(m) - if !s.config.MessageTypes().Produced.Has(mt) { + if !s.config.MessageTypes()[mt].IsProduced { panic(panicx.UnexpectedBehavior{ Handler: s.config, Interface: "IntegrationMessageHandler", diff --git a/engine/internal/process/controller.go b/engine/internal/process/controller.go index 8dbc35d4..6ac33280 100644 --- a/engine/internal/process/controller.go +++ b/engine/internal/process/controller.go @@ -66,7 +66,7 @@ func (c *Controller) Handle( ) ([]*envelope.Envelope, error) { mt := message.TypeOf(env.Message) - if !c.Config.MessageTypes().Consumed.Has(mt) { + if !c.Config.MessageTypes()[mt].IsConsumed { panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), mt)) } diff --git a/engine/internal/process/scope.go b/engine/internal/process/scope.go index f51a5fe4..b4a5e939 100644 --- a/engine/internal/process/scope.go +++ b/engine/internal/process/scope.go @@ -53,7 +53,7 @@ func (s *scope) End() { func (s *scope) ExecuteCommand(m dogma.Command) { mt := message.TypeOf(m) - if !s.config.MessageTypes().Produced.Has(mt) { + if !s.config.MessageTypes()[mt].IsProduced { panic(panicx.UnexpectedBehavior{ Handler: s.config, Interface: "ProcessMessageHandler", @@ -117,7 +117,7 @@ func (s *scope) RecordedAt() time.Time { func (s *scope) ScheduleTimeout(m dogma.Timeout, t time.Time) { mt := message.TypeOf(m) - if !s.config.MessageTypes().Produced.Has(mt) { + if !s.config.MessageTypes()[mt].IsProduced { panic(panicx.UnexpectedBehavior{ Handler: s.config, Interface: "ProcessMessageHandler", diff --git a/engine/internal/projection/controller.go b/engine/internal/projection/controller.go index ab9e2725..fb6f6d2a 100644 --- a/engine/internal/projection/controller.go +++ b/engine/internal/projection/controller.go @@ -76,7 +76,7 @@ func (c *Controller) Handle( ) ([]*envelope.Envelope, error) { mt := message.TypeOf(env.Message) - if !c.Config.MessageTypes().Consumed.Has(mt) { + if !c.Config.MessageTypes()[mt].IsConsumed { panic(fmt.Sprintf("%s does not handle %s messages", c.Config.Identity(), mt)) } diff --git a/expectation.messagecommon.go b/expectation.messagecommon.go index 2d6df389..a9051158 100644 --- a/expectation.messagecommon.go +++ b/expectation.messagecommon.go @@ -82,17 +82,20 @@ func guardAgainstExpectationOnImpossibleType( // report, not just returning an error. // // See https://github.com/dogmatiq/testkit/issues/162 - if !s.App.MessageTypes().Has(t) { + em, ok := s.App.MessageTypes()[t] + if !ok { return inflect.Errorf( t.Kind(), "a of type %s can never be , the application does not use this message type", t, ) - } else if !s.Options.MatchDispatchCycleStartedFacts { + } + + if !s.Options.MatchDispatchCycleStartedFacts { // If we're NOT matching messages from DispatchCycleStarted facts that // means this expectation can only ever pass if the message is produced // by a handler. - if !s.App.MessageTypes().Produced.Has(t) { + if !em.IsProduced { return inflect.Errorf( t.Kind(), "no handlers of type %s, it is only ever consumed", diff --git a/go.mod b/go.mod index b4c1503b..666ba387 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/dogmatiq/testkit go 1.23 require ( - github.com/dogmatiq/configkit v0.14.1-0.20240930030138-f9e71cc9ddcb + github.com/dogmatiq/configkit v0.14.1-0.20241002212501-f9f58f1e927c github.com/dogmatiq/cosyne v0.2.0 github.com/dogmatiq/dapper v0.6.0 github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 - github.com/dogmatiq/enginekit v0.14.1-0.20240930025837-001cd27abe23 + github.com/dogmatiq/enginekit v0.15.2-0.20241002024509-1d30c2ed8b6c github.com/dogmatiq/iago v0.4.0 github.com/dogmatiq/linger v1.1.0 github.com/onsi/ginkgo/v2 v2.20.2 diff --git a/go.sum b/go.sum index 1bd54f93..a09ea87e 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,16 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dogmatiq/configkit v0.14.1-0.20240930030138-f9e71cc9ddcb h1:K959v1eWzeH15OOs/lfEc5c3ohNgnPQYh5dkLH6SWfI= -github.com/dogmatiq/configkit v0.14.1-0.20240930030138-f9e71cc9ddcb/go.mod h1:KQfg3ODcxen+JGnc7Ae+0aqMMgA8jUT7LbfjjNslod8= +github.com/dogmatiq/configkit v0.14.1-0.20241002212501-f9f58f1e927c h1:JMBpZC2pPDZcGhbhx6OQ+KG81KhSCTJCMbTjoLSSqtA= +github.com/dogmatiq/configkit v0.14.1-0.20241002212501-f9f58f1e927c/go.mod h1:Oi01MkoR3cf12xfbo+86UA1Ilaq1iT9h0L4aDDYCP2s= github.com/dogmatiq/cosyne v0.2.0 h1:tO957BpS4I9kqSw31ds6Ef4CXvV8zPAqWzbXKElsGWg= github.com/dogmatiq/cosyne v0.2.0/go.mod h1:dD8EZjbRX7FFw9t6P7l1nwoZbA7YxtOCfl9ZZAHPucU= github.com/dogmatiq/dapper v0.6.0 h1:hnWUsjnt3nUiC9hmkPvuxrnMd7fYNz1i+/GS3gOx0Xs= github.com/dogmatiq/dapper v0.6.0/go.mod h1:ubRHWzt73s0MsPpGhWvnfW/Z/1YPnrkCsQv6CUOZVEw= github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 h1:SKhtRnDs7CC3ZNMux7lYCxCFz5ZhmeXjvo7/uMNISLo= github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1/go.mod h1:9lyVA+6V2+E/exV0IrBOrkUiyFwIATEhv+b0vnB2umQ= -github.com/dogmatiq/enginekit v0.14.1-0.20240930025837-001cd27abe23 h1:2UQ8POi88gvkVfM3r9LfylDS0SvqKQgHC94R3nhu9XQ= -github.com/dogmatiq/enginekit v0.14.1-0.20240930025837-001cd27abe23/go.mod h1:XsY6KGPIC6zHx1duYDXbHTomqH9y+QOJX/tEiNZE2Qg= +github.com/dogmatiq/enginekit v0.15.2-0.20241002024509-1d30c2ed8b6c h1:+Ho0OHj1C+x+v9kX7fIaiUV0tGt528MKftPDXPDzsQU= +github.com/dogmatiq/enginekit v0.15.2-0.20241002024509-1d30c2ed8b6c/go.mod h1:IVmmw+ut4ZK8NrUVnt35WUC8fs0WZoR5wo+Hb4zwp4g= github.com/dogmatiq/iago v0.4.0 h1:57nZqVT34IZxtCZEW/RFif7DNUEjMXgevfr/Mmd0N8I= github.com/dogmatiq/iago v0.4.0/go.mod h1:fishMWBtzYcjgis6d873VTv9kFm/wHYLOzOyO9ECBDc= github.com/dogmatiq/jumble v0.1.0 h1:Cb3ExfxY+AoUP4G9/sOwoOdYX8o+kOLK8+dhXAry+QA= @@ -123,5 +123,3 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= -pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= From c99fc4b1316f05196b86d0c7d0fb635d8ae428e2 Mon Sep 17 00:00:00 2001 From: James Harris Date: Thu, 3 Oct 2024 12:50:08 +1000 Subject: [PATCH 9/9] Bump dogma and enginekit to stable versions. --- engine/internal/process/controller.go | 2 +- envelope/envelope.go | 2 +- expectation.messagecommon.go | 2 +- expectation.messagematch.go | 2 +- go.mod | 6 +++--- go.sum | 12 ++++++------ internal/inflect/inflect.go | 8 ++++---- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/engine/internal/process/controller.go b/engine/internal/process/controller.go index 6ac33280..515c6c26 100644 --- a/engine/internal/process/controller.go +++ b/engine/internal/process/controller.go @@ -237,7 +237,7 @@ func (c *Controller) routeTimeout( return "", false, nil } -// handle calls the appropriate method on the handler based on the message role. +// handle calls the appropriate method on the handler based on the message kind. func (c *Controller) handle(ctx context.Context, s *scope) error { var err error panicx.EnrichUnexpectedMessage( diff --git a/envelope/envelope.go b/envelope/envelope.go index 3ed0b73a..ef22be1c 100644 --- a/envelope/envelope.go +++ b/envelope/envelope.go @@ -27,7 +27,7 @@ type Envelope struct { CreatedAt time.Time // ScheduledFor holds the time at which a timeout message is scheduled to - // occur. Its value is undefined unless Role is message.TimeoutRole. + // occur. Its value is undefined for commands and events. ScheduledFor time.Time // Origin describes the message handler that produced this message. diff --git a/expectation.messagecommon.go b/expectation.messagecommon.go index a9051158..111fe483 100644 --- a/expectation.messagecommon.go +++ b/expectation.messagecommon.go @@ -122,7 +122,7 @@ type tracker struct { // total is the total number of messages that were produced. total int - // produced is the number of messages of the expected role that were + // produced is the number of messages of the expected kind that were // produced. produced int diff --git a/expectation.messagematch.go b/expectation.messagematch.go index e6fe80f1..9e36bc1e 100644 --- a/expectation.messagematch.go +++ b/expectation.messagematch.go @@ -189,7 +189,7 @@ func (p *messageMatchPredicate[T]) Notify(f fact.Fact) { } // messageProduced updates the predicate's state to reflect the fact that a -// message of the expected role has been produced. +// message of the expected kind has been produced. func (p *messageMatchPredicate[T]) messageProduced(env *envelope.Envelope) { expectedType := message.TypeFor[T]() producedType := message.TypeOf(env.Message) diff --git a/go.mod b/go.mod index 666ba387..6ad04e41 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/dogmatiq/testkit go 1.23 require ( - github.com/dogmatiq/configkit v0.14.1-0.20241002212501-f9f58f1e927c + github.com/dogmatiq/configkit v0.15.0 github.com/dogmatiq/cosyne v0.2.0 github.com/dogmatiq/dapper v0.6.0 - github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 - github.com/dogmatiq/enginekit v0.15.2-0.20241002024509-1d30c2ed8b6c + github.com/dogmatiq/dogma v0.15.0 + github.com/dogmatiq/enginekit v0.16.0 github.com/dogmatiq/iago v0.4.0 github.com/dogmatiq/linger v1.1.0 github.com/onsi/ginkgo/v2 v2.20.2 diff --git a/go.sum b/go.sum index a09ea87e..0ee61ebe 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,16 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dogmatiq/configkit v0.14.1-0.20241002212501-f9f58f1e927c h1:JMBpZC2pPDZcGhbhx6OQ+KG81KhSCTJCMbTjoLSSqtA= -github.com/dogmatiq/configkit v0.14.1-0.20241002212501-f9f58f1e927c/go.mod h1:Oi01MkoR3cf12xfbo+86UA1Ilaq1iT9h0L4aDDYCP2s= +github.com/dogmatiq/configkit v0.15.0 h1:aNtfF8bgFEJwGCc/yNGguZetejs1dSmxWVzQw9co/3A= +github.com/dogmatiq/configkit v0.15.0/go.mod h1:f1tSQPPd+fAgHYuHVChIYhwll95vx9SosY3zF3NRXHo= github.com/dogmatiq/cosyne v0.2.0 h1:tO957BpS4I9kqSw31ds6Ef4CXvV8zPAqWzbXKElsGWg= github.com/dogmatiq/cosyne v0.2.0/go.mod h1:dD8EZjbRX7FFw9t6P7l1nwoZbA7YxtOCfl9ZZAHPucU= github.com/dogmatiq/dapper v0.6.0 h1:hnWUsjnt3nUiC9hmkPvuxrnMd7fYNz1i+/GS3gOx0Xs= github.com/dogmatiq/dapper v0.6.0/go.mod h1:ubRHWzt73s0MsPpGhWvnfW/Z/1YPnrkCsQv6CUOZVEw= -github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 h1:SKhtRnDs7CC3ZNMux7lYCxCFz5ZhmeXjvo7/uMNISLo= -github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1/go.mod h1:9lyVA+6V2+E/exV0IrBOrkUiyFwIATEhv+b0vnB2umQ= -github.com/dogmatiq/enginekit v0.15.2-0.20241002024509-1d30c2ed8b6c h1:+Ho0OHj1C+x+v9kX7fIaiUV0tGt528MKftPDXPDzsQU= -github.com/dogmatiq/enginekit v0.15.2-0.20241002024509-1d30c2ed8b6c/go.mod h1:IVmmw+ut4ZK8NrUVnt35WUC8fs0WZoR5wo+Hb4zwp4g= +github.com/dogmatiq/dogma v0.15.0 h1:aXOTd2K4wLvlwHc1D9OsFREp0BusNJ9o9KssxURftmg= +github.com/dogmatiq/dogma v0.15.0/go.mod h1:TF6xisRxQ2RE3JQwFr6MCI4nWLKQQp7KRWXVHOq9K0k= +github.com/dogmatiq/enginekit v0.16.0 h1:1+Fr0117jihoiY2SABsSAGhJYLi8DeNv35rR8PWgBfk= +github.com/dogmatiq/enginekit v0.16.0/go.mod h1:nODdKEljyHQHDRlwQrKD3NCJ/4y1jFFKYDwJ0yeHcVo= github.com/dogmatiq/iago v0.4.0 h1:57nZqVT34IZxtCZEW/RFif7DNUEjMXgevfr/Mmd0N8I= github.com/dogmatiq/iago v0.4.0/go.mod h1:fishMWBtzYcjgis6d873VTv9kFm/wHYLOzOyO9ECBDc= github.com/dogmatiq/jumble v0.1.0 h1:Cb3ExfxY+AoUP4G9/sOwoOdYX8o+kOLK8+dhXAry+QA= diff --git a/internal/inflect/inflect.go b/internal/inflect/inflect.go index 7873cf50..3673da35 100644 --- a/internal/inflect/inflect.go +++ b/internal/inflect/inflect.go @@ -44,7 +44,7 @@ var corrections = map[string]string{ "1 timeouts": "1 timeout", } -// Sprint formats a string, inflecting words in s match the message role r. +// Sprint formats a string, inflecting words in s match the message kind k. func Sprint(k message.Kind, s string) string { for k, v := range substitutions[k] { s = strings.ReplaceAll(s, k, v) @@ -59,7 +59,7 @@ func Sprint(k message.Kind, s string) string { return s } -// Sprintf formats a string, inflecting words in f match the message role r. +// Sprintf formats a string, inflecting words in f match the message kind k. func Sprintf(k message.Kind, f string, v ...any) string { return Sprint( k, @@ -67,12 +67,12 @@ func Sprintf(k message.Kind, f string, v ...any) string { ) } -// Error returns a new error, inflecting words in s to match the message role r. +// Error returns a new error, inflecting words in s to match the message kind k. func Error(k message.Kind, s string) error { return errors.New(Sprint(k, s)) } -// Errorf returns a new error, inflecting words in f to match the message role r. +// Errorf returns a new error, inflecting words in f to match the message kind k. func Errorf(k message.Kind, f string, v ...any) error { return errors.New(Sprintf(k, f, v...)) }