/ GUNRPG.Application / Operators / InMemoryOperatorEventStore.cs
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  }