InMemoryOperatorEventStore.cs
1 using System.Collections.Concurrent; 2 using GUNRPG.Core.Operators; 3 4 namespace GUNRPG.Application.Operators; 5 6 /// <summary> 7 /// In-memory implementation of IOperatorEventStore for testing and InMemory configuration. 8 /// This is a simple no-op store that returns empty event lists. 9 /// </summary> 10 public sealed class InMemoryOperatorEventStore : IOperatorEventStore 11 { 12 private readonly ConcurrentDictionary<OperatorId, List<OperatorEvent>> _eventsByOperator = new(); 13 private readonly ConcurrentDictionary<OperatorId, Guid> _accountByOperator = new(); 14 15 public Task<IReadOnlyList<OperatorEvent>> LoadEventsAsync(OperatorId operatorId) 16 { 17 if (_eventsByOperator.TryGetValue(operatorId, out var events)) 18 { 19 return Task.FromResult<IReadOnlyList<OperatorEvent>>(events); 20 } 21 return Task.FromResult<IReadOnlyList<OperatorEvent>>(new List<OperatorEvent>()); 22 } 23 24 public Task AppendEventAsync(OperatorEvent evt) 25 { 26 var operatorId = evt.OperatorId; 27 _eventsByOperator.AddOrUpdate( 28 operatorId, 29 _ => new List<OperatorEvent> { evt }, 30 (_, events) => { events.Add(evt); return events; }); 31 return Task.CompletedTask; 32 } 33 34 public Task AppendEventsAsync(IReadOnlyList<OperatorEvent> events) 35 { 36 if (events.Count == 0) return Task.CompletedTask; 37 38 var operatorId = events[0].OperatorId; 39 _eventsByOperator.AddOrUpdate( 40 operatorId, 41 _ => new List<OperatorEvent>(events), 42 (_, existingEvents) => { existingEvents.AddRange(events); return existingEvents; }); 43 return Task.CompletedTask; 44 } 45 46 public Task<bool> OperatorExistsAsync(OperatorId operatorId) 47 { 48 return Task.FromResult(_eventsByOperator.ContainsKey(operatorId) && _eventsByOperator[operatorId].Count > 0); 49 } 50 51 public Task<long> GetCurrentSequenceAsync(OperatorId operatorId) 52 { 53 if (_eventsByOperator.TryGetValue(operatorId, out var events) && events.Count > 0) 54 { 55 return Task.FromResult(events[^1].SequenceNumber); 56 } 57 return Task.FromResult(-1L); 58 } 59 60 public Task<IReadOnlyList<OperatorId>> ListOperatorIdsAsync() 61 { 62 var operatorIds = _eventsByOperator.Keys.ToList(); 63 return Task.FromResult<IReadOnlyList<OperatorId>>(operatorIds); 64 } 65 66 public Task<IReadOnlyList<OperatorId>> ListOperatorIdsByAccountAsync(Guid accountId) 67 { 68 var operatorIds = _accountByOperator 69 .Where(kvp => kvp.Value == accountId) 70 .Select(kvp => kvp.Key) 71 .ToList(); 72 return Task.FromResult<IReadOnlyList<OperatorId>>(operatorIds); 73 } 74 75 public Task<Guid?> GetOperatorAccountIdAsync(OperatorId operatorId) 76 { 77 Guid? accountId = _accountByOperator.TryGetValue(operatorId, out var id) ? id : null; 78 return Task.FromResult(accountId); 79 } 80 81 public Task AssociateOperatorWithAccountAsync(OperatorId operatorId, Guid accountId) 82 { 83 _accountByOperator[operatorId] = accountId; 84 return Task.CompletedTask; 85 } 86 }