Skip to content

Commit 54eb4e5

Browse files
committed
Address review feedback on #1459 tests
- Convert the closed-generic DerivedDependencyDecorator from a record to a plain class for consistency with the other decorator types in the file. - Add a decorator-chain regression test exercising the DecoratorContext UpdateContext path: the outer decorator's more-derived dependency must still resolve from the container when the chained instance (inner decorator output) is not assignable to it. Fails with InvalidCastException against the pre-fix middleware. - Add a test asserting a clean DependencyResolutionException when the more-derived dependency is unregistered (rather than InvalidCastException). - Make the open-generic test self-contained with a purpose-built IDerivedService<T> sub-interface instead of reusing shared test infra.
1 parent 1cccb63 commit 54eb4e5

2 files changed

Lines changed: 93 additions & 7 deletions

File tree

test/Autofac.Test/Features/Decorators/DecoratorTests.cs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,37 @@ private class DerivedImpl : IDerived
244244
{
245245
}
246246

247-
private record DerivedDependencyDecorator(IDerived Derived, IBase Decorated) : IBase;
247+
private class DerivedDependencyDecorator : IBase
248+
{
249+
public DerivedDependencyDecorator(IDerived derived, IBase decorated)
250+
{
251+
Derived = derived;
252+
Decorated = decorated;
253+
}
254+
255+
public IDerived Derived
256+
{
257+
get;
258+
}
259+
260+
public IBase Decorated
261+
{
262+
get;
263+
}
264+
}
265+
266+
private class BaseDecorator : IBase
267+
{
268+
public BaseDecorator(IBase decorated)
269+
{
270+
Decorated = decorated;
271+
}
272+
273+
public IBase Decorated
274+
{
275+
get;
276+
}
277+
}
248278

249279
[Fact]
250280
public void DecoratorWithMoreDerivedServiceDependencyResolvesDependencyNormally()
@@ -267,6 +297,53 @@ public void DecoratorWithMoreDerivedServiceDependencyResolvesDependencyNormally(
267297
Assert.IsType<DerivedImpl>(decorator.Derived);
268298
}
269299

300+
[Fact]
301+
public void DecoratorWithMoreDerivedServiceDependencyResolvesDependencyNormallyInChain()
302+
{
303+
// Issue 1459: When decorators are chained, the decorated instance seen by
304+
// the outer decorator is the inner decorator's output (via
305+
// DecoratorContext.UpdateContext), which is an IBase but not an IDerived.
306+
// The outer decorator's more-derived "Derived" (IDerived) parameter must
307+
// still be resolved from the container rather than receiving that chained
308+
// instance, while "Decorated" receives the inner decorator.
309+
var builder = new ContainerBuilder();
310+
builder.RegisterType<BaseImpl>().As<IBase>();
311+
builder.RegisterType<DerivedImpl>().As<IDerived>();
312+
313+
// Registered first => innermost decorator.
314+
builder.RegisterDecorator<BaseDecorator, IBase>();
315+
builder.RegisterDecorator<DerivedDependencyDecorator, IBase>();
316+
317+
var container = builder.Build();
318+
319+
var resolved = container.Resolve<IBase>();
320+
321+
var outer = Assert.IsType<DerivedDependencyDecorator>(resolved);
322+
Assert.IsType<DerivedImpl>(outer.Derived);
323+
324+
var inner = Assert.IsType<BaseDecorator>(outer.Decorated);
325+
Assert.IsType<BaseImpl>(inner.Decorated);
326+
}
327+
328+
[Fact]
329+
public void DecoratorWithUnregisteredMoreDerivedServiceDependencyThrowsResolutionException()
330+
{
331+
// Issue 1459: When the more-derived "Derived" (IDerived) parameter is not
332+
// registered, the parameter falls through to normal autowiring, which
333+
// cannot satisfy it. The result should be a clean DependencyResolutionException
334+
// rather than the previous InvalidCastException from force-injecting the
335+
// decorated instance.
336+
var builder = new ContainerBuilder();
337+
builder.RegisterType<BaseImpl>().As<IBase>();
338+
339+
// IDerived is intentionally not registered.
340+
builder.RegisterDecorator<DerivedDependencyDecorator, IBase>();
341+
342+
var container = builder.Build();
343+
344+
Assert.Throws<DependencyResolutionException>(() => container.Resolve<IBase>());
345+
}
346+
270347
private abstract class Decorator : IDecoratedService
271348
{
272349
protected Decorator(IDecoratedService decorated)

test/Autofac.Test/Features/Decorators/OpenGenericDecoratorTests.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -926,19 +926,28 @@ public void ResolvesMultipleDecoratedServicesWhenResolvedByOtherServices()
926926
// Issue 1459: A decorator constructor may take a dependency typed as a more
927927
// derived service than the one being decorated. The decorated instance must
928928
// not be force-injected into that parameter; it should be resolved normally.
929+
// ReSharper disable once UnusedTypeParameter
930+
private interface IDerivedService<T> : IService<T>
931+
{
932+
}
933+
929934
private class PlainService<T> : IService<T>
930935
{
931936
}
932937

938+
private class DerivedServiceImpl<T> : IDerivedService<T>
939+
{
940+
}
941+
933942
private class DerivedDependencyDecorator<T> : IService<T>
934943
{
935-
public DerivedDependencyDecorator(IDecoratedService<T> derived, IService<T> decorated)
944+
public DerivedDependencyDecorator(IDerivedService<T> derived, IService<T> decorated)
936945
{
937946
Derived = derived;
938947
Decorated = decorated;
939948
}
940949

941-
public IDecoratedService<T> Derived
950+
public IDerivedService<T> Derived
942951
{
943952
get;
944953
}
@@ -954,20 +963,20 @@ public void DecoratorWithMoreDerivedServiceDependencyResolvesDependencyNormally(
954963
{
955964
// Issue 1459: The decorated service (IService<T>) should be supplied to
956965
// the "Decorated" parameter, while the more-derived "Derived"
957-
// (IDecoratedService<T>) parameter must be resolved from the container
966+
// (IDerivedService<T>) parameter must be resolved from the container
958967
// rather than receiving the decorated instance (which is only an
959-
// IService<T> here, not an IDecoratedService<T>).
968+
// IService<T> here, not an IDerivedService<T>).
960969
var builder = new ContainerBuilder();
961970
builder.RegisterGeneric(typeof(PlainService<>)).As(typeof(IService<>));
962-
builder.RegisterGeneric(typeof(ImplementorA<>)).As(typeof(IDecoratedService<>));
971+
builder.RegisterGeneric(typeof(DerivedServiceImpl<>)).As(typeof(IDerivedService<>));
963972
builder.RegisterGenericDecorator(typeof(DerivedDependencyDecorator<>), typeof(IService<>));
964973
var container = builder.Build();
965974

966975
var resolved = container.Resolve<IService<int>>();
967976

968977
var decorator = Assert.IsType<DerivedDependencyDecorator<int>>(resolved);
969978
Assert.IsType<PlainService<int>>(decorator.Decorated);
970-
Assert.IsType<ImplementorA<int>>(decorator.Derived);
979+
Assert.IsType<DerivedServiceImpl<int>>(decorator.Derived);
971980
}
972981

973982
private interface ICommandHandler<T>

0 commit comments

Comments
 (0)