Skip to content

Commit

Permalink
Add test case to reproduce issue with nested decoration
Browse files Browse the repository at this point in the history
  • Loading branch information
khellang committed Aug 25, 2020
1 parent 3e5098b commit 3f8e1e1
Showing 1 changed file with 114 additions and 0 deletions.
114 changes: 114 additions & 0 deletions test/Scrutor.Tests/DecorationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,59 @@ public void DisposableServicesAreDisposed()
Assert.True(decorator.Inner.WasDisposed);
}

[Fact]
public void ServicesWithSameServiceTypeAreOnlyDecoratedOnce()
{
// See issue: https://github.com/khellang/Scrutor/issues/125

bool IsHandlerButNotDecorator(Type type)
{
var isHandlerDecorator = false;

var isHandler = type.GetInterfaces().Any(i =>
i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IEventHandler<>)
);

if (isHandler)
{
isHandlerDecorator = type.GetInterfaces().Any(i => i == typeof(IHandlerDecorator));
}

return isHandler && !isHandlerDecorator;
}

var provider = ConfigureProvider(services =>
{
// This should end up with 3 registrations of type IEventHandler<MyEvent>.
services.Scan(s =>
s.FromAssemblyOf<DecorationTests>()
.AddClasses(c => c.Where(IsHandlerButNotDecorator))
.AsImplementedInterfaces()
.WithTransientLifetime());

// This should not decorate each registration 3 times.
services.Decorate(typeof(IEventHandler<>), typeof(MyEventHandlerDecorator<>));
});

var instances = provider.GetRequiredService<IEnumerable<IEventHandler<MyEvent>>>().ToList();

Assert.Equal(3, instances.Count);

Assert.All(instances, instance =>
{
var decorator = Assert.IsType<MyEventHandlerDecorator<MyEvent>>(instance);

// The inner handler should not be a decorator.
Assert.IsNotType<MyEventHandlerDecorator<MyEvent>>(decorator.Handler);

// The return call count should only be 1, we've only called Handle on one decorator.
// If there were nested decorators, this would return a higher call count as it
// would increment at each level.
Assert.Equal(1, decorator.Handle(new MyEvent()));
});
}

[Fact]
public void DecoratingNonRegisteredServiceThrows()
{
Expand Down Expand Up @@ -227,5 +280,66 @@ public void Dispose()
WasDisposed = true;
}
}

public interface IEvent
{
}

public interface IEventHandler<in TEvent> where TEvent : class, IEvent
{
int Handle(TEvent @event);
}

public interface IHandlerDecorator
{
}

public sealed class MyEvent : IEvent
{}

internal sealed class MyEvent1Handler : IEventHandler<MyEvent>
{
private int _callCount;

public int Handle(MyEvent @event)
{
return _callCount++;
}
}

internal sealed class MyEvent2Handler : IEventHandler<MyEvent>
{
private int _callCount;

public int Handle(MyEvent @event)
{
return _callCount++;
}
}

internal sealed class MyEvent3Handler : IEventHandler<MyEvent>
{
private int _callCount;

public int Handle(MyEvent @event)
{
return _callCount++;
}
}

internal sealed class MyEventHandlerDecorator<TEvent> : IEventHandler<TEvent>, IHandlerDecorator where TEvent: class, IEvent
{
public readonly IEventHandler<TEvent> Handler;

public MyEventHandlerDecorator(IEventHandler<TEvent> handler)
{
Handler = handler;
}

public int Handle(TEvent @event)
{
return Handler.Handle(@event) + 1;
}
}
}
}

0 comments on commit 3f8e1e1

Please sign in to comment.