Skip to content

Commit

Permalink
fix: ftltest.Call always allows direct initial verb
Browse files Browse the repository at this point in the history
when using ftltest.Call, you don't need to mock the verb directly invoked. only downstream
verbs must be mocked (or overridden by setting WithCallsAllowedWithinModule globally)

fixes #2984
  • Loading branch information
worstell committed Oct 10, 2024
1 parent 008ee44 commit d370dee
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 9 deletions.
7 changes: 6 additions & 1 deletion go-runtime/ftl/ftltest/ftltest.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,8 +523,13 @@ func CallEmpty[VerbClient any](ctx context.Context) error {

func call[VerbClient, Req, Resp any](ctx context.Context, req Req) (resp Resp, err error) {
ref := reflection.ClientRef[VerbClient]()
// always allow direct behavior for the verb triggered by this call
moduleCtx := modulecontext.NewBuilderFromContext(
modulecontext.FromContext(ctx).CurrentContext(),
).AddAllowedDirectVerb(ref).Build()
ctx = mcu.MakeDynamic(ctx, moduleCtx).ApplyToContext(ctx)

inline := server.Call[Req, Resp](ref)
moduleCtx := modulecontext.FromContext(ctx).CurrentContext()
override, err := moduleCtx.BehaviorForVerb(schema.Ref{Module: ref.Module, Name: ref.Name})
if err != nil {
return resp, fmt.Errorf("test harness failed to retrieve behavior for verb %s: %w", ref, err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ func TestVerbErrors(t *testing.T) {

func TestTransitiveVerbMock(t *testing.T) {
ctx := ftltest.Context(
ftltest.WithCallsAllowedWithinModule(),
ftltest.WhenVerb[CalleeVerbClient](func(ctx context.Context, req Request) (Response, error) {
return Response{Output: fmt.Sprintf("mocked: %s", req.Input)}, nil
}),
Expand Down
2 changes: 1 addition & 1 deletion go-runtime/ftl/ftltest/testdata/go/wrapped/wrapped_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func TestWrapped(t *testing.T) {
},
configValue: "helloworld",
secretValue: "shhhhh",
expectedError: ftl.Some("test harness failed to retrieve behavior for verb wrapped.outer: no mock found: provide a mock with ftltest.WhenVerb(Outer, ...) or enable all calls within the module with ftltest.WithCallsAllowedWithinModule()"),
expectedError: ftl.Some("test harness failed to call verb wrapped.outer: wrapped.inner: no mock found: provide a mock with ftltest.WhenVerb(Inner, ...) or enable all calls within the module with ftltest.WithCallsAllowedWithinModule()"),
},
{
name: "AllowCallsWithinModule",
Expand Down
34 changes: 28 additions & 6 deletions internal/modulecontext/module_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"connectrpc.com/connect"
"github.com/TBD54566975/ftl/go-runtime/ftl/reflection"
"github.com/alecthomas/atomic"
"github.com/alecthomas/types/optional"
_ "github.com/jackc/pgx/v5/stdlib" // SQL driver
Expand Down Expand Up @@ -37,10 +38,11 @@ type ModuleContext struct {
secrets map[string][]byte
databases map[string]Database

isTesting bool
mockVerbs map[schema.RefKey]Verb
allowDirectVerbBehavior bool
leaseClient optional.Option[LeaseClient]
isTesting bool
mockVerbs map[schema.RefKey]Verb
allowDirectVerbBehaviorGlobal bool
allowDirectVerb schema.RefKey
leaseClient optional.Option[LeaseClient]
}

// DynamicModuleContext provides up-to-date ModuleContext instances supplied by the controller
Expand Down Expand Up @@ -68,6 +70,20 @@ func NewBuilder(module string) *Builder {
}
}

func NewBuilderFromContext(ctx ModuleContext) *Builder {
return &Builder{
module: ctx.module,
configs: ctx.configs,
secrets: ctx.secrets,
databases: ctx.databases,
isTesting: ctx.isTesting,
mockVerbs: ctx.mockVerbs,
allowDirectVerbBehaviorGlobal: ctx.allowDirectVerbBehaviorGlobal,
allowDirectVerb: ctx.allowDirectVerb,
leaseClient: ctx.leaseClient,
}
}

// AddConfigs adds configuration values (as bytes) to the builder
func (b *Builder) AddConfigs(configs map[string][]byte) *Builder {
for name, data := range configs {
Expand All @@ -92,13 +108,19 @@ func (b *Builder) AddDatabases(databases map[string]Database) *Builder {
return b
}

// AddAllowedDirectVerb adds a verb that can be called directly within the current context
func (b *Builder) AddAllowedDirectVerb(ref reflection.Ref) *Builder {
b.allowDirectVerb = schema.RefKey(ref)
return b
}

// UpdateForTesting marks the builder as part of a test environment and adds mock verbs and flags for other test features.
func (b *Builder) UpdateForTesting(mockVerbs map[schema.RefKey]Verb, allowDirectVerbBehavior bool, leaseClient LeaseClient) *Builder {
b.isTesting = true
for name, verb := range mockVerbs {
b.mockVerbs[name] = verb
}
b.allowDirectVerbBehavior = allowDirectVerbBehavior
b.allowDirectVerbBehaviorGlobal = allowDirectVerbBehavior
b.leaseClient = optional.Some[LeaseClient](leaseClient)
return b
}
Expand Down Expand Up @@ -168,7 +190,7 @@ func (m ModuleContext) MockLeaseClient() optional.Option[LeaseClient] {
func (m ModuleContext) BehaviorForVerb(ref schema.Ref) (optional.Option[VerbBehavior], error) {
if mock, ok := m.mockVerbs[ref.ToRefKey()]; ok {
return optional.Some(VerbBehavior(MockBehavior{Mock: mock})), nil
} else if m.allowDirectVerbBehavior && ref.Module == m.module {
} else if (m.allowDirectVerbBehaviorGlobal || m.allowDirectVerb == ref.ToRefKey()) && ref.Module == m.module {
return optional.Some(VerbBehavior(DirectBehavior{})), nil
} else if m.isTesting {
if ref.Module == m.module {
Expand Down

0 comments on commit d370dee

Please sign in to comment.