Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TestJournal and TestSnaphostStore #3881

Merged
merged 24 commits into from
Aug 31, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
46c77c4
TestJournal with Write intreception and varios failure strategies
valdisz Aug 9, 2019
10ac5b2
unit tests for message interceptors
valdisz Aug 10, 2019
42d7f8c
TestJournal OnRecovery behavior
valdisz Aug 10, 2019
9dbb8ed
Recovery unit tests
valdisz Aug 10, 2019
eaa701c
PersistenceTestKit base for easier test setup
valdisz Aug 11, 2019
4fb4ea9
Merge branch 'dev' into feature/persistence-testkit
Aaronontheweb Aug 12, 2019
8e0f81e
added example of built-in TestKit method
Aaronontheweb Aug 12, 2019
e32dfff
use Async api whenver possible
valdisz Aug 13, 2019
17ac3b3
Merge branch 'dev' into feature/persistence-testkit
valdisz Aug 13, 2019
aceb9aa
(wip) TestSnapshotStore
valdisz Aug 14, 2019
55b9f92
TestSnapshotStore interceptors
valdisz Aug 14, 2019
610999f
unit tests for TestSnapshotStore
valdisz Aug 16, 2019
c97481a
separate package with Xunit2 integration
valdisz Aug 16, 2019
a85d950
XML doc
valdisz Aug 17, 2019
a14b2c9
Journal Write and Recovery behavior documentation
valdisz Aug 18, 2019
c05ec27
removed unneeded statements from csproj files
valdisz Aug 22, 2019
c4cebe0
PersistenceTestKitBase is removed to correctly add ITestOutputHelper …
valdisz Aug 22, 2019
1147968
added mor xml docs
valdisz Aug 23, 2019
fa4c150
Merge branch 'dev' into feature/persistence-testkit
valdisz Aug 23, 2019
7d50ddd
Merge branch 'dev' into feature/persistence-testkit
Aaronontheweb Aug 26, 2019
bd299f2
Merge branch 'dev' into feature/persistence-testkit
Aaronontheweb Aug 30, 2019
0218d9a
Merge branch 'dev' into feature/persistence-testkit
Aaronontheweb Aug 30, 2019
b823a45
Merge branch 'dev' into feature/persistence-testkit
Aaronontheweb Aug 30, 2019
bc6962c
Merge branch 'dev' into feature/persistence-testkit
Aaronontheweb Aug 30, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/Akka.sln
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpawnBenchmark", "benchmark
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Akka.Persistence.FSharp", "core\Akka.Persistence.FSharp\Akka.Persistence.FSharp.fsproj", "{539C3EB6-FCC8-41FA-9373-364605877EE1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Persistence.TestKit", "core\Akka.Persistence.TestKit\Akka.Persistence.TestKit.csproj", "{212A2D35-E8D1-46A7-A1D1-418CF9509D77}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.TestKit.Tests", "core\Akka.Persistence.TestKit.Tests\Akka.Persistence.TestKit.Tests.csproj", "{22F6EA86-0079-41A0-9BD3-82D2D6C34638}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -844,6 +848,30 @@ Global
{539C3EB6-FCC8-41FA-9373-364605877EE1}.Release|x64.Build.0 = Release|Any CPU
{539C3EB6-FCC8-41FA-9373-364605877EE1}.Release|x86.ActiveCfg = Release|Any CPU
{539C3EB6-FCC8-41FA-9373-364605877EE1}.Release|x86.Build.0 = Release|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Debug|Any CPU.Build.0 = Debug|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Debug|x64.ActiveCfg = Debug|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Debug|x64.Build.0 = Debug|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Debug|x86.ActiveCfg = Debug|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Debug|x86.Build.0 = Debug|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Release|Any CPU.ActiveCfg = Release|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Release|Any CPU.Build.0 = Release|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Release|x64.ActiveCfg = Release|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Release|x64.Build.0 = Release|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Release|x86.ActiveCfg = Release|Any CPU
{212A2D35-E8D1-46A7-A1D1-418CF9509D77}.Release|x86.Build.0 = Release|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Debug|x64.ActiveCfg = Debug|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Debug|x64.Build.0 = Debug|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Debug|x86.ActiveCfg = Debug|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Debug|x86.Build.0 = Debug|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Release|Any CPU.Build.0 = Release|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Release|x64.ActiveCfg = Release|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Release|x64.Build.0 = Release|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Release|x86.ActiveCfg = Release|Any CPU
{22F6EA86-0079-41A0-9BD3-82D2D6C34638}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -929,6 +957,8 @@ Global
{A1D57384-A933-480A-9DF4-FA5E60AB1A67} = {73108242-625A-4D7B-AA09-63375DBAE464}
{9BEAF609-B406-4CCB-9708-6E8DFF764232} = {73108242-625A-4D7B-AA09-63375DBAE464}
{539C3EB6-FCC8-41FA-9373-364605877EE1} = {01167D3C-49C4-4CDE-9787-C176D139ACDD}
{212A2D35-E8D1-46A7-A1D1-418CF9509D77} = {01167D3C-49C4-4CDE-9787-C176D139ACDD}
{22F6EA86-0079-41A0-9BD3-82D2D6C34638} = {01167D3C-49C4-4CDE-9787-C176D139ACDD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {03AD8E21-7507-4E68-A4E9-F4A7E7273164}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />

<PropertyGroup>
<AssemblyTitle>Akka.Persistence.TestKit.Tests</AssemblyTitle>
<TargetFrameworks>$(NetFrameworkTestVersion);$(NetCoreTestVersion)</TargetFrameworks>
valdisz marked this conversation as resolved.
Show resolved Hide resolved
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
<PackageReference Include="xunit" Version="$(XunitVersion)" />
<PackageReference Include="xunit.runner.utility" Version="$(XunitVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
<PackageReference Include="TeamCity.ServiceMessages" Version="3.0.8" />
<PackageReference Include="FluentAssertions" Version="$(FluentAssertionsVersion)" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Akka.Persistence.TestKit\Akka.Persistence.TestKit.csproj" />
<ProjectReference Include="..\Akka.Tests.Shared.Internals\Akka.Tests.Shared.Internals.csproj" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="test-journal.conf" />
</ItemGroup>

<PropertyGroup Condition=" '$(TargetFramework)' == '$(NetCoreTestVersion)' ">
<DefineConstants>$(DefineConstants);CORECLR</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
namespace Akka.Persistence.TestKit.Tests
{
using System;
using System.Threading.Tasks;
using Akka.Persistence.TestKit;
using FluentAssertions;
using Xunit;

public class JournalWriteInterceptorsSpecs
{
[Fact]
public void noop_immediately_returns_without_exception()
{
JournalInterceptors.Noop.Instance
.Awaiting(x => x.InterceptAsync(null))
.ShouldNotThrow();
}

[Fact]
public void failure_must_throw_specific_exception()
{
JournalInterceptors.Failure.Instance
.Awaiting(x => x.InterceptAsync(null))
.ShouldThrowExactly<TestJournalFailureException>();
}

[Fact]
public void rejection_must_throw_specific_exception()
{
JournalInterceptors.Rejection.Instance
.Awaiting(x => x.InterceptAsync(null))
.ShouldThrowExactly<TestJournalRejectionException>();
}

[Fact]
public async Task delay_must_call_next_interceptor_after_specified_delay()
{
var duration = TimeSpan.FromMilliseconds(100);
var probe = new InterceptorProbe();
var delay = new JournalInterceptors.Delay(duration, probe);

var startedAt = DateTime.Now;
await delay.InterceptAsync(null);

probe.WasCalled.Should().BeTrue();
probe.CalledAt.Should().BeOnOrAfter(startedAt + duration);
}

[Fact]
public async Task on_type_must_call_next_interceptor_when_message_is_exactly_awaited_type()
{
var probe = new InterceptorProbe();
var onType = new JournalInterceptors.OnType(typeof(SpecificMessage), probe);
var message = new Persistent(new SpecificMessage());

await onType.InterceptAsync(message);

probe.WasCalled.Should().BeTrue();
probe.Message.Should().BeSameAs(message);
}

[Fact]
public async Task on_type_must_call_next_interceptor_when_message_is_subclass_of_awaited_type()
{
var probe = new InterceptorProbe();
var onType = new JournalInterceptors.OnType(typeof(SpecificMessage), probe);
var message = new Persistent(new SubclassMessage());

await onType.InterceptAsync(message);

probe.WasCalled.Should().BeTrue();
probe.Message.Should().BeSameAs(message);
}

[Fact]
public async Task on_type_must_call_next_interceptor_when_message_is_implements_awaited_interface_type()
{
var probe = new InterceptorProbe();
var onType = new JournalInterceptors.OnType(typeof(IMessageWithInterface), probe);
var message = new Persistent(new MessageWithInterface());

await onType.InterceptAsync(message);

probe.WasCalled.Should().BeTrue();
probe.Message.Should().BeSameAs(message);
}

[Fact]
public async Task on_type_must_not_call_next_interceptor_when_message_does_not_correspond_to_described_rules()
{
var probe = new InterceptorProbe();
var onType = new JournalInterceptors.OnType(typeof(SubclassMessage), probe);
var message = new Persistent(new SpecificMessage());

await onType.InterceptAsync(message);

probe.WasCalled.Should().BeFalse();
}

[Fact]
public async Task on_condition_must_accept_sync_lambda()
{
var probe = new InterceptorProbe();
var onCondition = new JournalInterceptors.OnCondition(_ => true, probe);

await onCondition.InterceptAsync(null);

probe.WasCalled.Should().BeTrue();
}

[Fact]
public async Task on_condition_must_accept_async_lambda()
{
var probe = new InterceptorProbe();
var onCondition = new JournalInterceptors.OnCondition(_ => Task.FromResult(true), probe);

await onCondition.InterceptAsync(null);

probe.WasCalled.Should().BeTrue();
}

[Fact]
public async Task on_condition_must_call_next_interceptor_unless_predicate_returns_false()
{
var probe = new InterceptorProbe();
var onCondition = new JournalInterceptors.OnCondition(_ => false, probe);

await onCondition.InterceptAsync(null);

probe.WasCalled.Should().BeFalse();
}

[Fact]
public async Task on_condition_with_negation_must_call_next_interceptor_unless_predicate_returns_true()
{
var probe = new InterceptorProbe();
var onCondition = new JournalInterceptors.OnCondition(_ => false, probe, negate: true);

await onCondition.InterceptAsync(null);

probe.WasCalled.Should().BeTrue();
}

[Fact]
public async Task on_condition_must_pass_the_same_message_to_predicate()
{
var probe = new InterceptorProbe();
var expectedMessage = new Persistent("test");

var onCondition = new JournalInterceptors.OnCondition(message =>
{
message.Should().BeSameAs(expectedMessage);
return false;
}, probe);

await onCondition.InterceptAsync(expectedMessage);
}


private class SpecificMessage { }

private class SubclassMessage : SpecificMessage { }

private interface IMessageWithInterface { }

private class MessageWithInterface : IMessageWithInterface { }

private class InterceptorProbe : IJournalInterceptor
{
public bool WasCalled { get; private set; }
public DateTime CalledAt { get; private set; }
public IPersistentRepresentation Message { get; private set; }

public Task InterceptAsync(IPersistentRepresentation message)
{
CalledAt = DateTime.Now;
WasCalled = true;
Message = message;

return Task.CompletedTask;
}
}
}
}
107 changes: 107 additions & 0 deletions src/core/Akka.Persistence.TestKit.Tests/TestJournalSpec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
namespace Akka.Persistence.TestKit.Tests
{
using System;
using Actor;
using Akka.Persistence.TestKit;
using Akka.TestKit;
using Configuration;
using Xunit;
using Xunit.Abstractions;

public class TestJournalSpec : AkkaSpec
{
public TestJournalSpec(ITestOutputHelper output = null)
: base(GetConfig().ToString(), output)
{
}

[Fact]
public void must_return_ack_after_new_write_interceptor_is_set()
{
var journalActor = GetJournalRef(Sys);

journalActor.Tell(new TestJournal.UseWriteInterceptor(null), TestActor);

ExpectMsg<TestJournal.Ack>(TimeSpan.FromSeconds(3));
}

[Fact]
public void works_as_memory_journal_by_default()
{
var journal = TestJournal.FromRef(GetJournalRef(Sys));

var actor = Sys.ActorOf<PersistActor>();

// should pass
journal.OnWrite.Pass();
actor.Tell("write", TestActor);
ExpectMsg("ack", TimeSpan.FromSeconds(3));
}

[Fact]
public void when_fail_on_write_is_set_all_writes_to_journal_will_fail()
{
var journal = TestJournal.FromRef(GetJournalRef(Sys));
var actor = Sys.ActorOf<PersistActor>();
Watch(actor);

journal.OnWrite.Fail();
actor.Tell("write", TestActor);

ExpectTerminated(actor, TimeSpan.FromSeconds(3));
}

[Fact]
public void when_reject_on_write_is_set_all_writes_to_journal_will_be_rejected()
{
var journal = TestJournal.FromRef(GetJournalRef(Sys));
var actor = Sys.ActorOf<PersistActor>();
Watch(actor);

journal.OnWrite.Reject();
actor.Tell("write", TestActor);

ExpectMsg("rejected", TimeSpan.FromSeconds(3));
}

static IActorRef GetJournalRef(ActorSystem sys) => Persistence.Instance.Apply(sys).JournalFor(null);

static Config GetConfig()
=> ConfigurationFactory.FromResource<TestJournalSpec>("Akka.Persistence.TestKit.Tests.test-journal.conf");
}

public class PersistActor : UntypedPersistentActor
{
public override string PersistenceId => "foo";

protected override void OnCommand(object message)
{
var sender = Sender;
switch (message as string)
{
case "write":
Persist(message, _ =>
{
sender.Tell("ack");
});

break;

default:
return;
}
}

protected override void OnRecover(object message)
{
// noop
}

protected override void OnPersistRejected(Exception cause, object @event, long sequenceNr)
{
Sender.Tell("rejected");

base.OnPersistRejected(cause, @event, sequenceNr);
}
}
}
13 changes: 13 additions & 0 deletions src/core/Akka.Persistence.TestKit.Tests/test-journal.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
akka {
persistence {
journal {
plugin = "akka.persistence.journal.test"
auto-start-journals = ["akka.persistence.journal.test"]

test {
class = "Akka.Persistence.TestKit.TestJournal, Akka.Persistence.TestKit"
valdisz marked this conversation as resolved.
Show resolved Hide resolved
plugin-dispatcher = "akka.actor.default-dispatcher"
}
}
}
}
Loading