Skip to content

Commit

Permalink
Merge pull request #357 from dogmatiq/validation-scopes
Browse files Browse the repository at this point in the history
Validation scopes.
  • Loading branch information
jmalloc authored Oct 3, 2024
2 parents 79b69ed + c99fc4b commit b137006
Show file tree
Hide file tree
Showing 41 changed files with 458 additions and 587 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 0 additions & 3 deletions action.call_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down
16 changes: 0 additions & 16 deletions action.dispatch.command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand Down
16 changes: 0 additions & 16 deletions action.dispatch.event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand Down
28 changes: 8 additions & 20 deletions action.dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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(),
}
Expand All @@ -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(),
}
Expand All @@ -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(),
"<producing> %s <message>",
message.TypeOf(a.m),
mt,
)
}

Expand All @@ -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 _, ok := s.App.MessageTypes()[mt]; !ok {
return inflect.Errorf(
a.r,
mt.Kind(),
"cannot <produce> <message>, %s is a not a recognized message type",
mt,
)
} else if r != a.r {
return inflect.Errorf(
a.r,
"cannot <produce> <message>, %s",
inflect.Sprintf(
r,
"%s is configured as a <message>",
mt,
),
)
}

return s.Engine.Dispatch(ctx, a.m, s.OperationOptions...)
Expand Down
26 changes: 5 additions & 21 deletions engine/configurer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"

"github.com/dogmatiq/configkit"
"github.com/dogmatiq/configkit/message"
"github.com/dogmatiq/testkit/engine/internal/aggregate"
"github.com/dogmatiq/testkit/engine/internal/integration"
"github.com/dogmatiq/testkit/engine/internal/process"
Expand All @@ -17,70 +16,55 @@ 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)
}

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,
types map[message.Type]message.Role,
) {
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 types {
for t := range cfg.MessageTypes().Consumed() {
c.engine.routes[t] = append(c.engine.routes[t], ctrl)
}
}
Loading

0 comments on commit b137006

Please sign in to comment.