diff --git a/backend/core/container.go b/backend/core/container.go index 5112150a..ed783572 100644 --- a/backend/core/container.go +++ b/backend/core/container.go @@ -5,6 +5,7 @@ package core import ( + "errors" "fmt" "reflect" "sync" @@ -179,8 +180,8 @@ func Using[T1 any](ctx *Context, fn func(s1 T1)) error { func Using2[T1, T2 any](ctx *Context, fn func(s1 T1, s2 T2)) error { s1, err1 := Inject[T1](ctx) s2, err2 := Inject[T2](ctx) - if err1 != nil || err2 != nil { - return fmt.Errorf("%w: (dep1: %v, dep2: %v)", ErrServiceNotReady, err1, err2) + if err := errors.Join(err1, err2); err != nil { + return fmt.Errorf("%w: %w", ErrServiceNotReady, err) } fn(s1, s2) return nil @@ -191,8 +192,8 @@ func Using3[T1, T2, T3 any](ctx *Context, fn func(s1 T1, s2 T2, s3 T3)) error { s1, err1 := Inject[T1](ctx) s2, err2 := Inject[T2](ctx) s3, err3 := Inject[T3](ctx) - if err1 != nil || err2 != nil || err3 != nil { - return fmt.Errorf("%w: (dep1: %v, dep2: %v, dep3: %v)", ErrServiceNotReady, err1, err2, err3) + if err := errors.Join(err1, err2, err3); err != nil { + return fmt.Errorf("%w: %w", ErrServiceNotReady, err) } fn(s1, s2, s3) return nil diff --git a/backend/core/context_test.go b/backend/core/context_test.go index 56439fd9..d6c66bc6 100644 --- a/backend/core/context_test.go +++ b/backend/core/context_test.go @@ -222,6 +222,33 @@ func TestContextUsingMultiple(t *testing.T) { assert.True(t, called3) } +// UsingN must keep every dependency failure reachable through the error chain, +// not just report that something went wrong. +func TestContextUsingMultipleErrorChain(t *testing.T) { + ctx := core.NewContext(context.Background()) + + err := core.Using2(ctx, func(s SampleService, l LogService) { + t.Fatal("callback must not run when dependencies are missing") + }) + require.Error(t, err) + assert.ErrorIs(t, err, core.ErrServiceNotReady) + assert.ErrorIs(t, err, core.ErrServiceNotFound) + + // Only LogService is missing now, so exactly one joined cause must be present. + core.Provide[SampleService](ctx, &sampleServiceImpl{}) + err = core.Using2(ctx, func(s SampleService, l LogService) { + t.Fatal("callback must not run when a dependency is missing") + }) + assert.ErrorIs(t, err, core.ErrServiceNotReady) + assert.ErrorIs(t, err, core.ErrServiceNotFound) + + err = core.Using3(ctx, func(s SampleService, l LogService, c ConfigService) { + t.Fatal("callback must not run when a dependency is missing") + }) + assert.ErrorIs(t, err, core.ErrServiceNotReady) + assert.ErrorIs(t, err, core.ErrServiceNotFound) +} + func TestContextHierarchyAndFork(t *testing.T) { parent := core.NewContext(nil) // nil base context test core.Provide[SampleService](parent, &sampleServiceImpl{prefix: "Parent:"})