Skip to content

Commit

Permalink
Merge branch 'master' into joaks/backtodev
Browse files Browse the repository at this point in the history
  • Loading branch information
JacobOaks authored May 30, 2024
2 parents f2252d3 + e6a361e commit aece6a0
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 121 deletions.
17 changes: 15 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
- No changes yet.
## [1.22.0](https://github.com/uber-go/fx/compare/v1.21.1...v1.22.0) - 2024-05-30

### Added
- Add `fx.Self` which can be passed to the `fx.As` annotation to signify
that a type should be provided as itself.
- Add `fxtest.EnforceTimeout` that can be passed to `fxtest.NewLifecycle`
to force `Start` and `Stop` to return context errors when hook context expires.

### Changed
- `fx.Private` can now be used with `fx.Supply`.

### Fixed
- Fx apps will no longer listen to OS signals when they are stopped,
solving blocking issues in programs that depended on OS signals
after an Fx app stops.

## [1.22.0](https://github.com/uber-go/fx/compare/v1.21.1...v1.22.0) - 2024-05-30

Expand Down
307 changes: 200 additions & 107 deletions app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,134 +391,227 @@ func TestNewApp(t *testing.T) {
})
}

func TestPrivateProvide(t *testing.T) {
// TestPrivate tests Private when used with both fx.Provide and fx.Supply.
func TestPrivate(t *testing.T) {
t.Parallel()

t.Run("CanUsePrivateAndNonPrivateFromOuterModule", func(t *testing.T) {
t.Parallel()
testCases := []struct {
desc string

app := fxtest.New(t,
Module("SubModule", Invoke(func(a int, b string) {})),
Provide(func() int { return 0 }, Private),
Provide(func() string { return "" }),
)
app.RequireStart().RequireStop()
})
// provide is either a Supply or Provide wrapper around the given int
// that allows us to generalize these test cases for both APIs.
provide func(int, bool) Option
}{
{
desc: "Provide",
provide: func(i int, private bool) Option {
opts := []any{func() int { return i }}
if private {
opts = append(opts, Private)
}
return Provide(opts...)
},
},
{
desc: "Supply",
provide: func(i int, private bool) Option {
opts := []any{i}
if private {
opts = append(opts, Private)
}
return Supply(opts...)
},
},
}

t.Run("CantUsePrivateFromSubModule", func(t *testing.T) {
t.Parallel()
for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) {
t.Parallel()

app := NewForTest(t,
Module("SubModule", Provide(func() int { return 0 }, Private)),
Invoke(func(a int) {}),
)
err := app.Err()
require.Error(t, err)
assert.Contains(t, err.Error(), "missing dependencies for function")
assert.Contains(t, err.Error(), "missing type: int")
})
t.Run("CanUsePrivateFromParentModule", func(t *testing.T) {
t.Parallel()

var invoked bool
app := fxtest.New(t,
Module("SubModule", Invoke(func(a int, b string) {
assert.Equal(t, 0, a)
invoked = true
})),
Provide(func() string { return "" }),
tt.provide(0, true /* private */),
)
app.RequireStart().RequireStop()
assert.True(t, invoked)
})

t.Run("DifferentModulesCanProvideSamePrivateType", func(t *testing.T) {
t.Parallel()
t.Run("CannotUsePrivateFromSubModule", func(t *testing.T) {
t.Parallel()

app := fxtest.New(t,
Module("SubModuleA",
Provide(func() int { return 1 }, Private),
Invoke(func(s int) {
assert.Equal(t, 1, s)
}),
),
Module("SubModuleB",
Provide(func() int { return 2 }, Private),
Invoke(func(s int) {
assert.Equal(t, 2, s)
}),
),
Provide(func() int { return 3 }),
Invoke(func(s int) {
assert.Equal(t, 3, s)
}),
)
app.RequireStart().RequireStop()
})
app := NewForTest(t,
Module("SubModule", tt.provide(0, true /* private */)),
Invoke(func(a int) {}),
)
err := app.Err()
require.Error(t, err)
assert.Contains(t, err.Error(), "missing dependencies for function")
assert.Contains(t, err.Error(), "missing type: int")
})

t.Run("MultipleModulesSameType", func(t *testing.T) {
t.Parallel()

var invoked int
app := fxtest.New(t,
Module("SubModuleA",
tt.provide(1, true /* private */),
Invoke(func(s int) {
assert.Equal(t, 1, s)
invoked++
}),
),
Module("SubModuleB",
tt.provide(2, true /* private */),
Invoke(func(s int) {
assert.Equal(t, 2, s)
invoked++
}),
),
tt.provide(3, false /* private */),
Invoke(func(s int) {
assert.Equal(t, 3, s)
invoked++
}),
)
app.RequireStart().RequireStop()
assert.Equal(t, 3, invoked)
})
})
}
}

func TestPrivateProvideWithDecorators(t *testing.T) {
t.Parallel()

t.Run("DecoratedPublicOrPrivateTypeInSubModule", func(t *testing.T) {
t.Parallel()
testCases := []struct {
desc string

runApp := func(private bool) {
provideOpts := []interface{}{func() int { return 0 }}
if private {
provideOpts = append(provideOpts, Private)
}
app := NewForTest(t,
Module("SubModule",
Provide(provideOpts...),
Decorate(func(a int) int { return a + 2 }),
Invoke(func(a int) { assert.Equal(t, 2, a) }),
),
Invoke(func(a int) { assert.Equal(t, 0, a) }),
)
err := app.Err()
if private {
require.Error(t, err)
assert.Contains(t, err.Error(), "missing dependencies for function")
assert.Contains(t, err.Error(), "missing type: int")
} else {
require.NoError(t, err)
}
}
// provide is either a Supply or Provide wrapper around the given int
// that allows us to generalize these test cases for both APIs.
provide func(int) Option
private bool
}{
{
desc: "Private/Provide",
provide: func(i int) Option {
return Provide(
func() int { return i },
Private,
)
},
private: true,
},
{
desc: "Private/Supply",
provide: func(i int) Option {
return Supply(i, Private)
},
private: true,
},
{
desc: "Public/Provide",
provide: func(i int) Option {
return Provide(func() int { return i })
},
private: false,
},
{
desc: "Public/Supply",
provide: func(i int) Option {
return Supply(i)
},
private: false,
},
}

t.Run("Public", func(t *testing.T) { runApp(false) })
t.Run("Private", func(t *testing.T) { runApp(true) })
})
for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) {
t.Parallel()

t.Run("DecoratedPublicOrPrivateTypeInOuterModule", func(t *testing.T) {
t.Parallel()
t.Run("DecoratedTypeInSubModule", func(t *testing.T) {
t.Parallel()

runApp := func(private bool) {
provideOpts := []interface{}{func() int { return 0 }}
if private {
provideOpts = append(provideOpts, Private)
}
app := fxtest.New(t,
Provide(provideOpts...),
Decorate(func(a int) int { return a - 5 }),
Invoke(func(a int) {
assert.Equal(t, -5, a)
}),
Module("Child",
Decorate(func(a int) int { return a + 10 }),
var invoked bool
app := NewForTest(t,
Module("SubModule",
tt.provide(0),
Decorate(func(a int) int { return a + 2 }),
Invoke(func(a int) { assert.Equal(t, 2, a) }),
),
Invoke(func(a int) {
assert.Equal(t, 5, a)
// Decoration is always "private",
// so raw provided value is expected here
// when the submodule provides it as public.
assert.Equal(t, 0, a)
invoked = true
}),
),
)
app.RequireStart().RequireStop()
}
)
err := app.Err()
if tt.private {
require.Error(t, err)
assert.Contains(t, err.Error(), "missing dependencies for function")
assert.Contains(t, err.Error(), "missing type: int")
} else {
require.NoError(t, err)
assert.True(t, invoked)
}
})

t.Run("Public", func(t *testing.T) { runApp(false) })
t.Run("Private", func(t *testing.T) { runApp(true) })
})
t.Run("DecoratedTypeInParentModule", func(t *testing.T) {
t.Parallel()

t.Run("CannotDecoratePrivateChildType", func(t *testing.T) {
t.Parallel()
var invoked int
app := fxtest.New(t,
tt.provide(0),
Decorate(func(a int) int { return a - 5 }),
Invoke(func(a int) {
assert.Equal(t, -5, a)
invoked++
}),
Module("Child",
Decorate(func(a int) int { return a + 10 }),
Invoke(func(a int) {
assert.Equal(t, 5, a)
invoked++
}),
),
)
app.RequireStart().RequireStop()
assert.Equal(t, 2, invoked)
})

app := NewForTest(t,
Module("Child",
Provide(func() int { return 0 }, Private),
),
Decorate(func(a int) int { return a + 5 }),
Invoke(func(a int) {}),
)
err := app.Err()
require.Error(t, err)
assert.Contains(t, err.Error(), "missing dependencies for function")
assert.Contains(t, err.Error(), "missing type: int")
})
t.Run("ParentDecorateChildType", func(t *testing.T) {
t.Parallel()

var invoked bool
app := NewForTest(t,
Module("Child", tt.provide(0)),
Decorate(func(a int) int { return a + 5 }),
Invoke(func(a int) {
invoked = true
}),
)
err := app.Err()
if tt.private {
require.Error(t, err)
assert.Contains(t, err.Error(), "missing dependencies for function")
assert.Contains(t, err.Error(), "missing type: int")
} else {
require.NoError(t, err)
assert.True(t, invoked)
}
})
})
}
}

func TestWithLoggerErrorUseDefault(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion provide.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (o provideOption) apply(mod *module) {

type privateOption struct{}

// Private is an option that can be passed as an argument to [Provide] to
// Private is an option that can be passed as an argument to [Provide] or [Supply] to
// restrict access to the constructors being provided. Specifically,
// corresponding constructors can only be used within the current module
// or modules the current module contains. Other modules that contain this
Expand Down
Loading

0 comments on commit aece6a0

Please sign in to comment.