feat: add store interface contract tests & fix Compact dmap cleanup (Task 4)

Port Go store_test.go contract tests for IStreamStore interface using
MemStore. Fix CompactInternal to correctly walk forward to find new
FirstSeq before backward cleanup (matching Go behavior).

21 new tests ported from store_test.go.
This commit is contained in:
Joseph Doherty
2026-02-24 20:38:09 -05:00
parent 44c9b67d39
commit f45c76543a
2 changed files with 550 additions and 6 deletions

View File

@@ -264,10 +264,15 @@ public sealed class MemStore : IStreamStore
{
lock (_gate)
{
// Go: memStore state — when empty but FirstSeq was configured, report it.
// Reference: server/memstore.go — State() preserves first.seq watermark.
var firstSeqForApi = _st.Msgs == 0
? (_st.FirstSeq > 0 ? _st.FirstSeq : 0UL)
: _st.FirstSeq;
return ValueTask.FromResult(new ApiStreamState
{
Messages = _st.Msgs,
FirstSeq = _st.Msgs == 0 ? 0UL : _st.FirstSeq,
FirstSeq = firstSeqForApi,
LastSeq = _st.LastSeq,
Bytes = _st.Bytes,
});
@@ -1048,19 +1053,22 @@ public sealed class MemStore : IStreamStore
if (seq <= _st.LastSeq)
{
var fseq = _st.FirstSeq;
// Find the actual new first seq
for (var s = seq; s <= _st.LastSeq; s++)
// Go: walk forward from seq to find first real message, mutating seq in-place.
// The backward cleanup loop must start from (newFirstSeq - 1) to correctly
// remove any dmap entries at the original compact seq that are no longer interior.
var newFirst = seq;
for (; newFirst <= _st.LastSeq; newFirst++)
{
if (_msgs.TryGetValue(s, out var nm))
if (_msgs.TryGetValue(newFirst, out var nm))
{
_st.FirstSeq = s;
_st.FirstSeq = newFirst;
_st.FirstTime = new DateTime(nm.Ts / 100L, DateTimeKind.Utc);
break;
}
}
ulong purged = 0;
for (var s = seq - 1; s >= fseq && s < ulong.MaxValue; s--)
for (var s = newFirst - 1; s >= fseq && s < ulong.MaxValue; s--)
{
if (_msgs.TryGetValue(s, out var m))
{