Skip to content

Commit 87cc645

Browse files
Add spec for handling delegates in DI (#4922)
* Add spec for handling delegates in DI * Make sure spec exits cleanly by terminating the actor system. * Add spec where singleton delegate is called from another actor * Fix racy test Co-authored-by: Aaron Stannard <[email protected]>
1 parent 56a787a commit 87cc645

File tree

2 files changed

+137
-2
lines changed

2 files changed

+137
-2
lines changed

src/contrib/dependencyinjection/Akka.DependencyInjection.Tests/ActorServiceProviderPropsWithScopesSpecs.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,13 @@ public void ActorsWithNonDiDependenciesShouldStart()
147147
scoped1.Tell(new Crash());
148148
});
149149

150-
// all previous SCOPED dependencies should be disposed
151-
deps1.Dependencies.Where(x => !(x is AkkaDiFixture.ISingletonDependency)).All(x => x.Disposed).Should().BeTrue();
150+
AwaitAssert(() =>
151+
{
152+
// all previous SCOPED dependencies should eventually be disposed
153+
deps1.Dependencies.Where(x => !(x is AkkaDiFixture.ISingletonDependency)).All(x => x.Disposed).Should().BeTrue();
154+
},
155+
duration: TimeSpan.FromMilliseconds(300),
156+
interval: TimeSpan.FromMilliseconds(100));
152157

153158
// singletons should not be disposed
154159
deps1.Dependencies.Where(x => (x is AkkaDiFixture.ISingletonDependency)).All(x => x.Disposed).Should().BeFalse();
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using Akka.Actor;
8+
using Akka.TestKit;
9+
using Microsoft.Extensions.DependencyInjection;
10+
using Microsoft.Extensions.Hosting;
11+
using Xunit;
12+
using Xunit.Abstractions;
13+
14+
namespace Akka.DependencyInjection.Tests
15+
{
16+
public class DelegateInjectionSpecs : AkkaSpec
17+
{
18+
public delegate IActorRef EchoActorProvider();
19+
20+
private readonly IServiceProvider _serviceProvider;
21+
22+
public static IServiceProvider CreateProvider()
23+
{
24+
var services = new ServiceCollection()
25+
.AddSingleton<AkkaService>()
26+
.AddHostedService<AkkaService>()
27+
.AddSingleton<EchoActorProvider>(p =>
28+
{
29+
var system = p.GetRequiredService<AkkaService>();
30+
var actor = system.ActorSystem.ActorOf<EchoActor>("echoActor");
31+
return () => actor;
32+
});
33+
34+
var provider = services.BuildServiceProvider();
35+
var akkaService = provider.GetRequiredService<AkkaService>();
36+
akkaService.StartAsync(default).Wait();
37+
38+
return provider;
39+
}
40+
41+
public DelegateInjectionSpecs(ITestOutputHelper output) : base(output)
42+
{
43+
_serviceProvider = CreateProvider();
44+
}
45+
46+
[Fact]
47+
public async Task DI_should_be_able_to_retrieve_singleton_using_delegate()
48+
{
49+
var actor = _serviceProvider.GetRequiredService<EchoActorProvider>()();
50+
51+
var task = actor.Ask("echo");
52+
task.Wait(TimeSpan.FromSeconds(3));
53+
task.Result.ShouldBe("echo");
54+
55+
var sys = _serviceProvider.GetRequiredService<AkkaService>().ActorSystem;
56+
await sys.Terminate();
57+
}
58+
59+
[Fact]
60+
public async Task DI_should_be_able_to_retrieve_singleton_using_delegate_from_inside_actor()
61+
{
62+
var system = _serviceProvider.GetRequiredService<AkkaService>().ActorSystem;
63+
var actor = system.ActorOf(ParentActor.Props(system));
64+
65+
var task = actor.Ask("echo");
66+
task.Wait(TimeSpan.FromSeconds(3));
67+
task.Result.ShouldBe("echo");
68+
69+
var sys = _serviceProvider.GetRequiredService<AkkaService>().ActorSystem;
70+
await sys.Terminate();
71+
}
72+
73+
internal class ParentActor : UntypedActor
74+
{
75+
public static Props Props(ActorSystem system) =>
76+
ServiceProvider.For(system).Props<ParentActor>();
77+
78+
private readonly IActorRef _echoActor;
79+
80+
public ParentActor(IServiceProvider provider)
81+
{
82+
_echoActor = provider.GetRequiredService<EchoActorProvider>()();
83+
}
84+
85+
protected override void OnReceive(object message)
86+
{
87+
_echoActor.Forward(message);
88+
}
89+
}
90+
91+
internal class EchoActor : ReceiveActor
92+
{
93+
public static Props Props() => Akka.Actor.Props.Create<EchoActor>();
94+
95+
public EchoActor()
96+
{
97+
Receive<string>(msg =>
98+
{
99+
Sender.Tell(msg);
100+
});
101+
}
102+
}
103+
104+
internal class AkkaService : IHostedService
105+
{
106+
public ActorSystem ActorSystem { get; private set; }
107+
108+
private readonly IServiceProvider _serviceProvider;
109+
110+
public AkkaService(IServiceProvider serviceProvider)
111+
{
112+
_serviceProvider = serviceProvider;
113+
}
114+
115+
public Task StartAsync(CancellationToken cancellationToken)
116+
{
117+
var setup = ServiceProviderSetup.Create(_serviceProvider)
118+
.And(BootstrapSetup.Create().WithConfig(TestKitBase.DefaultConfig));
119+
120+
ActorSystem = ActorSystem.Create("TestSystem", setup);
121+
return Task.CompletedTask;
122+
}
123+
124+
public async Task StopAsync(CancellationToken cancellationToken)
125+
{
126+
await ActorSystem.Terminate();
127+
}
128+
}
129+
}
130+
}

0 commit comments

Comments
 (0)