Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation scopes. #357

Merged
merged 9 commits into from
Oct 3, 2024
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