Skip to content

Commit 756ce9d

Browse files
author
Tomas Lycken
authored
Ef core connector (#44)
* Implement provider for EF Core * Improve naming
1 parent 6de1748 commit 756ce9d

14 files changed

+683
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using RdbmsEventStore.EFCore.Tests.Infrastructure;
4+
using Xunit;
5+
using System.Linq;
6+
using Microsoft.EntityFrameworkCore;
7+
using RdbmsEventStore.EFCore.Tests.TestData;
8+
using RdbmsEventStore.EventRegistry;
9+
using RdbmsEventStore.Serialization;
10+
11+
namespace RdbmsEventStore.EFCore.Tests.EventStoreTests
12+
{
13+
public class ExtraMetaTests : IClassFixture<ExtraMetaEventFactoryFixture>
14+
{
15+
private readonly ExtraMetaEventFactoryFixture _fixture;
16+
private readonly EFCoreEventStoreContext<string, ExtraMetaLongStringPersistedEventMetadata> _dbContext;
17+
18+
public ExtraMetaTests(ExtraMetaEventFactoryFixture fixture)
19+
{
20+
_fixture = fixture;
21+
var options = new DbContextOptionsBuilder<EFCoreEventStoreContext<string, ExtraMetaLongStringPersistedEventMetadata>>()
22+
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
23+
.Options;
24+
_dbContext = new EFCoreEventStoreContext<string, ExtraMetaLongStringPersistedEventMetadata>(options);
25+
26+
var stream1 = _fixture.EventFactory.Create("stream-1", 0, new object[] {
27+
new FooEvent { Foo = "Foo" },
28+
new BarEvent { Bar = "Bar" },
29+
new FooEvent { Foo = "Baz" }
30+
})
31+
.Select(_fixture.EventSerializer.Serialize);
32+
var stream2 = _fixture.EventFactory.Create("stream-2", 0, new object[] {
33+
new FooEvent { Foo = "Boo" },
34+
new BarEvent { Bar = "Far" }
35+
})
36+
.Select(_fixture.EventSerializer.Serialize);
37+
38+
_dbContext.Events.AddRange(stream1);
39+
_dbContext.Events.AddRange(stream2);
40+
_dbContext.SaveChanges();
41+
}
42+
43+
[Theory]
44+
[InlineData("stream-1", 3)]
45+
[InlineData("stream-2", 2)]
46+
public async Task ReturnsEventsFromCorrectStreamOnly(string streamId, long expectedCount)
47+
{
48+
var store = _fixture.BuildEventStore(_dbContext) as IEventStore<string, ExtraMetaStringEvent, IExtraMeta>;
49+
var events = await store.Events(streamId);
50+
Assert.Equal(expectedCount, events.Count());
51+
}
52+
53+
[Theory]
54+
[InlineData("stream-1", 2)]
55+
[InlineData("stream-2", 1)]
56+
public async Task ReturnsEventsAccordingToQuery(string streamId, long expectedCount)
57+
{
58+
var store = _fixture.BuildEventStore(_dbContext) as IEventStore<string, ExtraMetaStringEvent, IExtraMeta>;
59+
var events = await store.Events(streamId, es => es.Where(e => e.ExtraMeta.StartsWith("Foo")));
60+
Assert.Equal(expectedCount, events.Count());
61+
}
62+
63+
[Theory]
64+
[InlineData("stream-1", 2)]
65+
[InlineData("stream-2", 1)]
66+
public async Task ReturnsEventsWithMetadata(string streamId, long expectedCount)
67+
{
68+
var store = _fixture.BuildEventStore(_dbContext) as IEventStore<string, ExtraMetaStringEvent, IExtraMeta>;
69+
var events = await store
70+
.Events(streamId, es => es.Where(e => e.ExtraMeta.StartsWith("Foo")))
71+
.ToReadOnlyCollection();
72+
73+
Assert.Equal(expectedCount, events.Count);
74+
Assert.All(events, @event => Assert.StartsWith("Foo", @event.ExtraMeta));
75+
}
76+
77+
[Theory]
78+
[InlineData("stream-1", 2)]
79+
[InlineData("stream-2", 1)]
80+
public async Task CanQueryByExtraMetadata(string streamId, long expectedCount)
81+
{
82+
var store = _fixture.BuildEventStore(_dbContext) as IEventStore<string, ExtraMetaStringEvent, IExtraMeta>;
83+
var events = await store.Events(streamId, es => es.Where(e => e.ExtraMeta.StartsWith("Foo")));
84+
Assert.Equal(expectedCount, events.Count());
85+
}
86+
}
87+
88+
public class ExtraMetaEventFactory : DefaultEventFactory<string, ExtraMetaStringEvent>
89+
{
90+
private int _total;
91+
92+
protected override ExtraMetaStringEvent CreateSingle(string streamId, long version, object payload)
93+
{
94+
var @event = base.CreateSingle(streamId, version, payload);
95+
@event.ExtraMeta = $"{payload.GetType().Name}-{_total++}";
96+
return @event;
97+
}
98+
}
99+
100+
public class ExtraMetaEventSerializer : DefaultEventSerializer<string, ExtraMetaStringEvent, ExtraMetaLongStringPersistedEventMetadata>
101+
{
102+
public ExtraMetaEventSerializer(IEventRegistry registry) : base(registry)
103+
{
104+
}
105+
106+
public override ExtraMetaLongStringPersistedEventMetadata Serialize(ExtraMetaStringEvent @event)
107+
{
108+
var serialized = base.Serialize(@event);
109+
serialized.ExtraMeta = @event.ExtraMeta;
110+
return serialized;
111+
}
112+
113+
public override ExtraMetaStringEvent Deserialize(ExtraMetaLongStringPersistedEventMetadata @event)
114+
{
115+
var deserialized = base.Deserialize(@event);
116+
deserialized.ExtraMeta = @event.ExtraMeta;
117+
return deserialized;
118+
}
119+
}
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System.Linq;
2+
using System.Threading.Tasks;
3+
using RdbmsEventStore.EFCore.Tests.Infrastructure;
4+
using RdbmsEventStore.EFCore.Tests.TestData;
5+
using Xunit;
6+
7+
namespace RdbmsEventStore.EFCore.Tests.EventStoreTests
8+
{
9+
public class QueryEventsTests : EventStoreTestBase<long, string, StringEvent, IEventMetadata<string>, LongStringPersistedEvent>
10+
{
11+
public QueryEventsTests(EventStoreFixture<long, string, StringEvent, IEventMetadata<string>, LongStringPersistedEvent> fixture) : base(fixture)
12+
{
13+
var stream1 = _fixture.EventFactory.Create("stream-1", 0, new object[] {
14+
new FooEvent { Foo = "Foo" },
15+
new BarEvent { Bar = "Bar" },
16+
new FooEvent { Foo = "Baz" }
17+
})
18+
.Select(_fixture.EventSerializer.Serialize);
19+
var stream2 = _fixture.EventFactory.Create("stream-2", 0, new object[] {
20+
new FooEvent { Foo = "Boo" },
21+
new BarEvent { Bar = "Far" }
22+
})
23+
.Select(_fixture.EventSerializer.Serialize);
24+
25+
_dbContext.Events.AddRange(stream1);
26+
_dbContext.Events.AddRange(stream2);
27+
_dbContext.SaveChanges();
28+
}
29+
30+
[Theory]
31+
[InlineData("stream-1", 3)]
32+
[InlineData("stream-2", 2)]
33+
public async Task ReturnsEventsFromCorrectStreamOnly(string streamId, long expectedCount)
34+
{
35+
var store = _fixture.BuildEventStore(_dbContext) as IEventStore<string, StringEvent, IEventMetadata<string>>;
36+
var events = await store.Events(streamId);
37+
Assert.Equal(expectedCount, events.Count());
38+
}
39+
40+
[Theory]
41+
[InlineData("stream-1", 2)]
42+
[InlineData("stream-2", 1)]
43+
public async Task ReturnsEventsAccordingToQuery(string streamId, long expectedCount)
44+
{
45+
var store = _fixture.BuildEventStore(_dbContext) as IEventStore<string, StringEvent, IEventMetadata<string>>;
46+
var events = await store.Events(streamId, es => es.Where(e => e.Version > 1));
47+
Assert.Equal(expectedCount, events.Count());
48+
}
49+
50+
[Fact]
51+
public async Task ReturnsAllEvents()
52+
{
53+
var store = _fixture.BuildEventStore(_dbContext) as IEventStore<string, StringEvent, IEventMetadata<string>>;
54+
var events = await store.Events();
55+
Assert.Equal(5, events.Count());
56+
}
57+
58+
[Fact]
59+
public async Task ReturnsAllEventsAccordingToQuery()
60+
{
61+
var store = _fixture.BuildEventStore(_dbContext) as IEventStore<string, StringEvent, IEventMetadata<string>>;
62+
var events = await store.Events(es => es.Where(e => e.Version > 1));
63+
Assert.Equal(3, events.Count());
64+
}
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using Microsoft.EntityFrameworkCore;
5+
using Moq;
6+
using RdbmsEventStore.EFCore.Tests.Infrastructure;
7+
using RdbmsEventStore.EFCore.Tests.TestData;
8+
using Xunit;
9+
10+
namespace RdbmsEventStore.EFCore.Tests.EventStoreTests
11+
{
12+
public class WriteEventTests : EventStoreTestBase<Guid, Guid, GuidEvent, IEventMetadata<Guid>, GuidGuidPersistedEvent>
13+
{
14+
public WriteEventTests(EventStoreFixture<Guid, Guid, GuidEvent, IEventMetadata<Guid>, GuidGuidPersistedEvent> fixture) : base(fixture)
15+
{
16+
}
17+
18+
[Fact]
19+
public async Task CommittingEventStoresEventInContext()
20+
{
21+
var store = _fixture.BuildEventStore(_dbContext);
22+
await store.Append(Guid.NewGuid(), 0, new[] { new FooEvent { Foo = "Bar" } });
23+
Assert.Equal(1, await _dbContext.Events.CountAsync());
24+
}
25+
26+
[Fact]
27+
public async Task CommittingWithOutOfSyncDataThrowsConflictException()
28+
{
29+
var store = _fixture.BuildEventStore(_dbContext);
30+
var stream = Guid.NewGuid();
31+
_dbContext.Events.AddRange(_fixture.EventFactory.Create(stream, 0, new[] { new FooEvent { Foo = "Bar" } }).Select(_fixture.EventSerializer.Serialize));
32+
await _dbContext.SaveChangesAsync();
33+
34+
await Assert.ThrowsAsync<ConflictException>(() => store.Append(stream, 0, new[] { new FooEvent { Foo = "Qux" } }));
35+
}
36+
37+
[Fact]
38+
public async Task CommittingNoEventsExitsEarly() {
39+
var context = new Mock<EFCoreEventStoreContext<Guid, GuidGuidPersistedEvent>>(MockBehavior.Strict);
40+
var set = new Mock<DbSet<GuidGuidPersistedEvent>>(MockBehavior.Strict);
41+
context.Setup(c => c.Set<GuidGuidPersistedEvent>()).Returns(set.Object);
42+
var stream = Guid.NewGuid();
43+
44+
var store = _fixture.BuildEventStore(context.Object);
45+
46+
try {
47+
await store.Append(stream, 0, new object[] { });
48+
} catch (NotImplementedException) {
49+
// Thrown by the mock DbSet if we try to query for existing events
50+
// This indicates that we didn't exit early
51+
52+
Assert.False(true, "Expected to exit early, but apparently didn't.");
53+
}
54+
}
55+
56+
[Fact]
57+
public async Task CommittingMultipleEventsStoresAllEventsInContext()
58+
{
59+
Assert.Empty(await _dbContext.Events.ToListAsync());
60+
61+
var store = _fixture.BuildEventStore(_dbContext);
62+
63+
var events = new[] { new FooEvent { Foo = "Foo" }, new FooEvent { Foo = "Bar" } };
64+
await store.Append(Guid.NewGuid(), 0, events);
65+
66+
Assert.Equal(2, await _dbContext.Events.CountAsync());
67+
}
68+
69+
[Fact]
70+
public async Task CommittingMultipleEventsStoresEventsInOrder()
71+
{
72+
Assert.Empty(await _dbContext.Events.ToListAsync());
73+
74+
var store = _fixture.BuildEventStore(_dbContext);
75+
76+
var events = new object[] { new FooEvent { Foo = "Foo" }, new BarEvent { Bar = "Bar" } };
77+
await store.Append(Guid.NewGuid(), 0, events);
78+
79+
Assert.Collection(await _dbContext.Events.OrderBy(e => e.Version).ToListAsync(),
80+
foo => Assert.Equal(typeof(FooEvent), _fixture.EventRegistry.TypeFor(foo.Type)),
81+
bar => Assert.Equal(typeof(BarEvent), _fixture.EventRegistry.TypeFor(bar.Type)));
82+
}
83+
84+
[Fact]
85+
public async Task CommittingMultipleEventsIncrementsVersionForEachEvent()
86+
{
87+
Assert.Empty(await _dbContext.Events.ToListAsync());
88+
89+
var store = _fixture.BuildEventStore(_dbContext);
90+
var events = new object[] { new FooEvent { Foo = "Foo" }, new BarEvent { Bar = "Bar" } };
91+
await store.Append(Guid.NewGuid(), 0, events);
92+
93+
var storedEvents = await _dbContext.Events.OrderBy(e => e.Timestamp).ToListAsync();
94+
Assert.Collection(storedEvents,
95+
foo => Assert.Equal(1, foo.Version),
96+
bar => Assert.Equal(2, bar.Version));
97+
}
98+
}
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.ComponentModel.DataAnnotations;
3+
using System.ComponentModel.DataAnnotations.Schema;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Microsoft.EntityFrameworkCore;
7+
using RdbmsEventStore.EFCore.Tests.Infrastructure;
8+
using RdbmsEventStore.EFCore.Tests.TestData;
9+
using RdbmsEventStore.Serialization;
10+
using Xunit;
11+
12+
namespace RdbmsEventStore.EFCore.Tests.ExtensibilityTests
13+
{
14+
public class NonDefaultEvent : IMutableEvent<long>
15+
{
16+
public DateTimeOffset Timestamp { get; set; }
17+
public long StreamId { get; set; }
18+
public long Version { get; set; }
19+
public Type Type { get; set; }
20+
public object Payload { get; set; }
21+
}
22+
23+
public class NonDefaultPersistedEvent : IPersistedEvent<long>
24+
{
25+
[Key]
26+
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
27+
public long EventId { get; set; }
28+
[Required]
29+
public long StreamId { get; set; }
30+
[Required]
31+
public DateTimeOffset Timestamp { get; set; }
32+
[Required]
33+
public long Version { get; set; }
34+
[Required]
35+
public string Type { get; set; }
36+
[Required]
37+
public byte[] Payload { get; set; }
38+
}
39+
40+
public class NonDefaultContext : DbContext, IEFCoreEventStoreContext<NonDefaultPersistedEvent>
41+
{
42+
public NonDefaultContext(DbContextOptions<NonDefaultContext> options) : base(options)
43+
{
44+
}
45+
46+
public DbSet<NonDefaultPersistedEvent> Events { get; set; }
47+
}
48+
49+
public class NonDefaultImplementationsTests : IClassFixture<EventStoreFixture<long, long, NonDefaultEvent, IEventMetadata<long>, NonDefaultPersistedEvent>>, IDisposable
50+
{
51+
private readonly EventStoreFixture<long, long, NonDefaultEvent, IEventMetadata<long>, NonDefaultPersistedEvent> _fixture;
52+
private readonly NonDefaultContext _dbContext;
53+
54+
public NonDefaultImplementationsTests(EventStoreFixture<long, long, NonDefaultEvent, IEventMetadata<long>, NonDefaultPersistedEvent> fixture)
55+
{
56+
_fixture = fixture;
57+
var options = new DbContextOptionsBuilder<NonDefaultContext>()
58+
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
59+
.Options;
60+
61+
_dbContext = new NonDefaultContext(options);
62+
}
63+
64+
[Fact]
65+
public async Task CanCommitEventsToStoreWithDefaultImplementations()
66+
{
67+
var store = _fixture.BuildEventStore(_dbContext);
68+
69+
await store.Append(1, 0, new[] { new FooEvent { Foo = "Bar" } });
70+
}
71+
72+
[Fact]
73+
public async Task CanReadEventsFromStoreWithNonDefaultImplementations()
74+
{
75+
_dbContext.Events.AddRange(new[]
76+
{
77+
new NonDefaultPersistedEvent
78+
{
79+
StreamId = 1,
80+
Timestamp = DateTimeOffset.UtcNow,
81+
Version = 1,
82+
Type = "FooEvent",
83+
Payload = Encoding.UTF8.GetBytes(@"{""Foo"":""Bar""}")
84+
}
85+
});
86+
await _dbContext.SaveChangesAsync();
87+
88+
var store = _fixture.BuildEventStore(_dbContext) as IEventStore<long, NonDefaultEvent, IEventMetadata<long>>;
89+
90+
var events = await store.Events(1);
91+
92+
Assert.Single(events);
93+
}
94+
public void Dispose()
95+
{
96+
_dbContext?.Dispose();
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)