Reformat / cleanup
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using System.Reflection;
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using ZB.MOM.WW.CBDD.Core.Transactions;
|
||||
using ZB.MOM.WW.CBDD.Shared;
|
||||
@@ -9,12 +8,12 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
public class CheckpointModeTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies default checkpoint mode truncates WAL.
|
||||
/// Verifies default checkpoint mode truncates WAL.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Checkpoint_Default_ShouldUseTruncate()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var db = new TestDbContext(dbPath);
|
||||
@@ -36,12 +35,12 @@ public class CheckpointModeTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies passive mode skips when checkpoint lock is contended.
|
||||
/// Verifies passive mode skips when checkpoint lock is contended.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Checkpoint_Passive_ShouldSkip_WhenLockIsContended()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
@@ -67,13 +66,13 @@ public class CheckpointModeTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies full checkpoint applies data and appends a checkpoint marker without truncating WAL.
|
||||
/// Verifies full checkpoint applies data and appends a checkpoint marker without truncating WAL.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Checkpoint_Full_ShouldAppendMarker_AndPreserveWal()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
string dbPath = NewDbPath();
|
||||
string walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -82,7 +81,7 @@ public class CheckpointModeTests
|
||||
db.Users.Insert(new User { Name = "checkpoint-full", Age = 50 });
|
||||
db.SaveChanges();
|
||||
|
||||
var walBefore = db.Storage.GetWalSize();
|
||||
long walBefore = db.Storage.GetWalSize();
|
||||
walBefore.ShouldBeGreaterThan(0);
|
||||
|
||||
var result = db.Checkpoint(CheckpointMode.Full);
|
||||
@@ -103,12 +102,12 @@ public class CheckpointModeTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies restart checkpoint clears WAL and allows subsequent writes.
|
||||
/// Verifies restart checkpoint clears WAL and allows subsequent writes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Checkpoint_Restart_ShouldResetWal_AndAcceptNewWrites()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var db = new TestDbContext(dbPath);
|
||||
@@ -134,12 +133,12 @@ public class CheckpointModeTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies recovery remains deterministic after a full checkpoint boundary.
|
||||
/// Verifies recovery remains deterministic after a full checkpoint boundary.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Recover_AfterFullCheckpoint_ShouldApplyLatestCommitDeterministically()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
uint pageId;
|
||||
@@ -182,12 +181,12 @@ public class CheckpointModeTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies asynchronous mode-based checkpoints return expected result metadata.
|
||||
/// Verifies asynchronous mode-based checkpoints return expected result metadata.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CheckpointAsync_Full_ShouldReturnResult()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var db = new TestDbContext(dbPath);
|
||||
@@ -213,16 +212,18 @@ public class CheckpointModeTests
|
||||
}
|
||||
|
||||
private static string NewDbPath()
|
||||
=> Path.Combine(Path.GetTempPath(), $"checkpoint_mode_{Guid.NewGuid():N}.db");
|
||||
{
|
||||
return Path.Combine(Path.GetTempPath(), $"checkpoint_mode_{Guid.NewGuid():N}.db");
|
||||
}
|
||||
|
||||
private static void CleanupFiles(string dbPath)
|
||||
{
|
||||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||||
|
||||
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
string walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
if (File.Exists(walPath)) File.Delete(walPath);
|
||||
|
||||
var markerPath = $"{dbPath}.compact.state";
|
||||
if (File.Exists(markerPath)) File.Delete(markerPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using ZB.MOM.WW.CBDD.Core;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
@@ -10,7 +8,7 @@ public class DictionaryPageTests
|
||||
private const int PageSize = 16384;
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dictionary page initialization sets expected defaults.
|
||||
/// Verifies dictionary page initialization sets expected defaults.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Initialize_ShouldSetupEmptyPage()
|
||||
@@ -30,7 +28,7 @@ public class DictionaryPageTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies insert adds entries and keeps them ordered.
|
||||
/// Verifies insert adds entries and keeps them ordered.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_ShouldAddEntryAndSort()
|
||||
@@ -65,7 +63,7 @@ public class DictionaryPageTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies key lookup returns the expected value.
|
||||
/// Verifies key lookup returns the expected value.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TryFind_ShouldReturnCorrectValue()
|
||||
@@ -86,7 +84,7 @@ public class DictionaryPageTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies inserts fail when the page is full.
|
||||
/// Verifies inserts fail when the page is full.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Overflow_ShouldReturnFalse_WhenFull()
|
||||
@@ -94,18 +92,16 @@ public class DictionaryPageTests
|
||||
var page = new byte[PageSize];
|
||||
DictionaryPage.Initialize(page, 1);
|
||||
|
||||
string bigKey = new string('X', 250);
|
||||
var bigKey = new string('X', 250);
|
||||
|
||||
int count = 0;
|
||||
var count = 0;
|
||||
while (true)
|
||||
{
|
||||
// Use unique keys
|
||||
var key = bigKey + count;
|
||||
string key = bigKey + count;
|
||||
if (!DictionaryPage.Insert(page, key, (ushort)count))
|
||||
{
|
||||
// Should fail here
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
if (count > 1000) throw new ShouldAssertException("Should have filled the page much earlier");
|
||||
}
|
||||
@@ -118,16 +114,16 @@ public class DictionaryPageTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies global lookup finds keys across chained dictionary pages.
|
||||
/// Verifies global lookup finds keys across chained dictionary pages.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Chaining_ShouldFindKeysInLinkedPages()
|
||||
{
|
||||
var dbPath = Path.Combine(Path.GetTempPath(), $"test_dict_chain_{Guid.NewGuid()}.db");
|
||||
string dbPath = Path.Combine(Path.GetTempPath(), $"test_dict_chain_{Guid.NewGuid()}.db");
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
|
||||
// 1. Create First Page
|
||||
var page1Id = storage.AllocatePage();
|
||||
uint page1Id = storage.AllocatePage();
|
||||
var pageBuffer = new byte[storage.PageSize];
|
||||
DictionaryPage.Initialize(pageBuffer, page1Id);
|
||||
|
||||
@@ -136,7 +132,7 @@ public class DictionaryPageTests
|
||||
DictionaryPage.Insert(pageBuffer, "KeyA", 200);
|
||||
|
||||
// 2. Create Second Page
|
||||
var page2Id = storage.AllocatePage();
|
||||
uint page2Id = storage.AllocatePage();
|
||||
var page2Buffer = new byte[storage.PageSize];
|
||||
DictionaryPage.Initialize(page2Buffer, page2Id);
|
||||
|
||||
@@ -174,18 +170,18 @@ public class DictionaryPageTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies global enumeration returns keys across chained dictionary pages.
|
||||
/// Verifies global enumeration returns keys across chained dictionary pages.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FindAllGlobal_ShouldRetrieveAllKeys()
|
||||
{
|
||||
var dbPath = Path.Combine(Path.GetTempPath(), $"test_dict_findall_{Guid.NewGuid()}.db");
|
||||
string dbPath = Path.Combine(Path.GetTempPath(), $"test_dict_findall_{Guid.NewGuid()}.db");
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
|
||||
// 1. Create Chain of 3 Pages
|
||||
var page1Id = storage.AllocatePage();
|
||||
var page2Id = storage.AllocatePage();
|
||||
var page3Id = storage.AllocatePage();
|
||||
uint page1Id = storage.AllocatePage();
|
||||
uint page2Id = storage.AllocatePage();
|
||||
uint page3Id = storage.AllocatePage();
|
||||
|
||||
var buf = new byte[storage.PageSize];
|
||||
|
||||
@@ -226,4 +222,4 @@ public class DictionaryPageTests
|
||||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||||
if (File.Exists(Path.ChangeExtension(dbPath, ".wal"))) File.Delete(Path.ChangeExtension(dbPath, ".wal"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Bson.Schema;
|
||||
using ZB.MOM.WW.CBDD.Core.Collections;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ZB.MOM.WW.CBDD.Bson.Schema;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
@@ -14,7 +12,7 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
private readonly StorageEngine _storage;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DictionaryPersistenceTests"/> class.
|
||||
/// Initializes a new instance of the <see cref="DictionaryPersistenceTests" /> class.
|
||||
/// </summary>
|
||||
public DictionaryPersistenceTests()
|
||||
{
|
||||
@@ -23,55 +21,18 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and removes temporary files.
|
||||
/// Disposes test resources and removes temporary files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_storage.Dispose();
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
var walPath = Path.ChangeExtension(_dbPath, ".wal");
|
||||
string walPath = Path.ChangeExtension(_dbPath, ".wal");
|
||||
if (File.Exists(walPath)) File.Delete(walPath);
|
||||
}
|
||||
|
||||
private class MockMapper : DocumentMapperBase<ObjectId, Dictionary<string, object>>
|
||||
{
|
||||
private readonly string _collectionName;
|
||||
private readonly List<string> _keys;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MockMapper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The collection name.</param>
|
||||
/// <param name="keys">The mapper keys.</param>
|
||||
public MockMapper(string name, params string[] keys)
|
||||
{
|
||||
_collectionName = name;
|
||||
_keys = keys.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string CollectionName => _collectionName;
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<string> UsedKeys => _keys;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BsonSchema GetSchema() => new BsonSchema { Title = _collectionName };
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ObjectId GetId(Dictionary<string, object> entity) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetId(Dictionary<string, object> entity, ObjectId id) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Serialize(Dictionary<string, object> entity, BsonSpanWriter writer) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Dictionary<string, object> Deserialize(BsonSpanReader reader) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies mapper registration adds all unique dictionary keys.
|
||||
/// Verifies mapper registration adds all unique dictionary keys.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RegisterMappers_Registers_All_Unique_Keys()
|
||||
@@ -99,7 +60,7 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dictionary keys persist across storage restarts.
|
||||
/// Verifies dictionary keys persist across storage restarts.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Dictionary_Keys_Persist_Across_Restarts()
|
||||
@@ -107,7 +68,7 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
var mapper = new MockMapper("Coll1", "PersistedKey");
|
||||
_storage.RegisterMappers(new IDocumentMapper[] { mapper });
|
||||
|
||||
var originalId = _storage.GetOrAddDictionaryEntry("PersistedKey");
|
||||
ushort originalId = _storage.GetOrAddDictionaryEntry("PersistedKey");
|
||||
originalId.ShouldNotBe((ushort)0);
|
||||
|
||||
_storage.Dispose();
|
||||
@@ -115,10 +76,78 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
// Re-open
|
||||
using var storage2 = new StorageEngine(_dbPath, PageFileConfig.Default);
|
||||
|
||||
var recoveredId = storage2.GetOrAddDictionaryEntry("PersistedKey");
|
||||
ushort recoveredId = storage2.GetOrAddDictionaryEntry("PersistedKey");
|
||||
recoveredId.ShouldBe(originalId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies nested schema fields are registered as dictionary keys.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RegisterMappers_Handles_Nested_Keys()
|
||||
{
|
||||
var mapper = new NestedMockMapper();
|
||||
_storage.RegisterMappers(new IDocumentMapper[] { mapper });
|
||||
|
||||
_storage.GetOrAddDictionaryEntry("Top").ShouldNotBe((ushort)0);
|
||||
_storage.GetOrAddDictionaryEntry("Child").ShouldNotBe((ushort)0);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "All", Justification = "Test-only stub mapper; members are intentionally not used.")]
|
||||
private class MockMapper : DocumentMapperBase<ObjectId, Dictionary<string, object>>
|
||||
{
|
||||
private readonly string _collectionName;
|
||||
private readonly List<string> _keys;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MockMapper" /> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The collection name.</param>
|
||||
/// <param name="keys">The mapper keys.</param>
|
||||
public MockMapper(string name, params string[] keys)
|
||||
{
|
||||
_collectionName = name;
|
||||
_keys = keys.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string CollectionName => _collectionName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<string> UsedKeys => _keys;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BsonSchema GetSchema()
|
||||
{
|
||||
return new BsonSchema { Title = _collectionName };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ObjectId GetId(Dictionary<string, object> entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetId(Dictionary<string, object> entity, ObjectId id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Serialize(Dictionary<string, object> entity, BsonSpanWriter writer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Dictionary<string, object> Deserialize(BsonSpanReader reader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "All", Justification = "Test-only stub mapper; members are intentionally not used.")]
|
||||
private class NestedMockMapper : DocumentMapperBase<ObjectId, object>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -141,28 +170,27 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ObjectId GetId(object entity) => throw new NotImplementedException();
|
||||
public override ObjectId GetId(object entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetId(object entity, ObjectId id) => throw new NotImplementedException();
|
||||
public override void SetId(object entity, ObjectId id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Serialize(object entity, BsonSpanWriter writer) => throw new NotImplementedException();
|
||||
public override int Serialize(object entity, BsonSpanWriter writer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Deserialize(BsonSpanReader reader) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies nested schema fields are registered as dictionary keys.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RegisterMappers_Handles_Nested_Keys()
|
||||
{
|
||||
var mapper = new NestedMockMapper();
|
||||
_storage.RegisterMappers(new IDocumentMapper[] { mapper });
|
||||
|
||||
_storage.GetOrAddDictionaryEntry("Top").ShouldNotBe((ushort)0);
|
||||
_storage.GetOrAddDictionaryEntry("Child").ShouldNotBe((ushort)0);
|
||||
public override object Deserialize(BsonSpanReader reader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core.Collections;
|
||||
using ZB.MOM.WW.CBDD.Core.Compression;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using ZB.MOM.WW.CBDD.Core.Transactions;
|
||||
using ZB.MOM.WW.CBDD.Shared;
|
||||
using ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers;
|
||||
using System.IO.Compression;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core.Compression;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using ZB.MOM.WW.CBDD.Shared;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class DocumentOverflowTests : IDisposable
|
||||
{
|
||||
private readonly TestDbContext _db;
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DocumentOverflowTests"/> class.
|
||||
/// Initializes a new instance of the <see cref="DocumentOverflowTests" /> class.
|
||||
/// </summary>
|
||||
public DocumentOverflowTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"test_overflow_{Guid.NewGuid()}.db");
|
||||
// Use default PageSize (16KB)
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
_db = new TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases test resources.
|
||||
/// Releases test resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
@@ -36,7 +32,7 @@ public class DocumentOverflowTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies inserting a medium-sized document succeeds.
|
||||
/// Verifies inserting a medium-sized document succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_MediumDoc_64KB_ShouldSucceed()
|
||||
@@ -60,7 +56,7 @@ public class DocumentOverflowTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies inserting a large document succeeds.
|
||||
/// Verifies inserting a large document succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_LargeDoc_100KB_ShouldSucceed()
|
||||
@@ -83,7 +79,7 @@ public class DocumentOverflowTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies inserting a very large document succeeds.
|
||||
/// Verifies inserting a very large document succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_HugeDoc_3MB_ShouldSucceed()
|
||||
@@ -109,7 +105,7 @@ public class DocumentOverflowTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies updating from a small payload to a huge payload succeeds.
|
||||
/// Verifies updating from a small payload to a huge payload succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Update_SmallToHuge_ShouldSucceed()
|
||||
@@ -123,7 +119,7 @@ public class DocumentOverflowTests : IDisposable
|
||||
var hugeString = new string('U', 3 * 1024 * 1024);
|
||||
user.Name = hugeString;
|
||||
|
||||
var updated = _db.Users.Update(user);
|
||||
bool updated = _db.Users.Update(user);
|
||||
_db.SaveChanges();
|
||||
updated.ShouldBeTrue();
|
||||
|
||||
@@ -133,17 +129,17 @@ public class DocumentOverflowTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies bulk inserts with mixed payload sizes succeed.
|
||||
/// Verifies bulk inserts with mixed payload sizes succeed.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void InsertBulk_MixedSizes_ShouldSucceed()
|
||||
{
|
||||
var users = new List<User>
|
||||
{
|
||||
new User { Id = ObjectId.NewObjectId(), Name = "Small 1", Age = 1 },
|
||||
new User { Id = ObjectId.NewObjectId(), Name = new string('M', 100 * 1024), Age = 2 }, // 100KB
|
||||
new User { Id = ObjectId.NewObjectId(), Name = "Small 2", Age = 3 },
|
||||
new User { Id = ObjectId.NewObjectId(), Name = new string('H', 3 * 1024 * 1024), Age = 4 } // 3MB
|
||||
new() { Id = ObjectId.NewObjectId(), Name = "Small 1", Age = 1 },
|
||||
new() { Id = ObjectId.NewObjectId(), Name = new string('M', 100 * 1024), Age = 2 }, // 100KB
|
||||
new() { Id = ObjectId.NewObjectId(), Name = "Small 2", Age = 3 },
|
||||
new() { Id = ObjectId.NewObjectId(), Name = new string('H', 3 * 1024 * 1024), Age = 4 } // 3MB
|
||||
};
|
||||
|
||||
var ids = _db.Users.InsertBulk(users);
|
||||
@@ -158,12 +154,12 @@ public class DocumentOverflowTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies huge inserts succeed with compression enabled and small page configuration.
|
||||
/// Verifies huge inserts succeed with compression enabled and small page configuration.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_HugeDoc_WithCompressionEnabledAndSmallPages_ShouldSucceed()
|
||||
{
|
||||
var localDbPath = Path.Combine(Path.GetTempPath(), $"test_overflow_compression_{Guid.NewGuid():N}.db");
|
||||
string localDbPath = Path.Combine(Path.GetTempPath(), $"test_overflow_compression_{Guid.NewGuid():N}.db");
|
||||
var options = new CompressionOptions
|
||||
{
|
||||
EnableCompression = true,
|
||||
@@ -175,7 +171,7 @@ public class DocumentOverflowTests : IDisposable
|
||||
|
||||
try
|
||||
{
|
||||
using var db = new Shared.TestDbContext(localDbPath, TinyPageConfig(), options);
|
||||
using var db = new TestDbContext(localDbPath, TinyPageConfig(), options);
|
||||
var huge = new string('Z', 2 * 1024 * 1024);
|
||||
var id = db.Users.Insert(new User
|
||||
{
|
||||
@@ -197,12 +193,13 @@ public class DocumentOverflowTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies updates from huge to small payloads succeed with compression enabled.
|
||||
/// Verifies updates from huge to small payloads succeed with compression enabled.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Update_HugeToSmall_WithCompressionEnabled_ShouldSucceed()
|
||||
{
|
||||
var localDbPath = Path.Combine(Path.GetTempPath(), $"test_overflow_compression_update_{Guid.NewGuid():N}.db");
|
||||
string localDbPath =
|
||||
Path.Combine(Path.GetTempPath(), $"test_overflow_compression_update_{Guid.NewGuid():N}.db");
|
||||
var options = new CompressionOptions
|
||||
{
|
||||
EnableCompression = true,
|
||||
@@ -214,7 +211,7 @@ public class DocumentOverflowTests : IDisposable
|
||||
|
||||
try
|
||||
{
|
||||
using var db = new Shared.TestDbContext(localDbPath, TinyPageConfig(), options);
|
||||
using var db = new TestDbContext(localDbPath, TinyPageConfig(), options);
|
||||
var user = new User
|
||||
{
|
||||
Id = ObjectId.NewObjectId(),
|
||||
@@ -251,10 +248,10 @@ public class DocumentOverflowTests : IDisposable
|
||||
|
||||
private static void CleanupLocalFiles(string dbPath)
|
||||
{
|
||||
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
string walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
var markerPath = $"{dbPath}.compact.state";
|
||||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||||
if (File.Exists(walPath)) File.Delete(walPath);
|
||||
if (File.Exists(markerPath)) File.Delete(markerPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core.Compression;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using ZB.MOM.WW.CBDD.Shared;
|
||||
@@ -8,12 +10,12 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
public class MaintenanceDiagnosticsAndMigrationTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies diagnostics APIs return page usage, compression, and fragmentation data.
|
||||
/// Verifies diagnostics APIs return page usage, compression, and fragmentation data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DiagnosticsApis_ShouldReturnPageUsageCompressionAndFragmentationData()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -28,13 +30,11 @@ public class MaintenanceDiagnosticsAndMigrationTests
|
||||
|
||||
using var db = new TestDbContext(dbPath, options);
|
||||
for (var i = 0; i < 40; i++)
|
||||
{
|
||||
db.Users.Insert(new User
|
||||
{
|
||||
Name = BuildPayload(i, 9000),
|
||||
Age = i
|
||||
});
|
||||
}
|
||||
|
||||
db.SaveChanges();
|
||||
db.ForceCheckpoint();
|
||||
@@ -47,7 +47,8 @@ public class MaintenanceDiagnosticsAndMigrationTests
|
||||
byCollection.Any(x => x.CollectionName.Equals("users", StringComparison.OrdinalIgnoreCase)).ShouldBeTrue();
|
||||
|
||||
var compressionByCollection = db.GetCompressionRatioByCollection();
|
||||
var usersCompression = compressionByCollection.First(x => x.CollectionName.Equals("users", StringComparison.OrdinalIgnoreCase));
|
||||
var usersCompression = compressionByCollection.First(x =>
|
||||
x.CollectionName.Equals("users", StringComparison.OrdinalIgnoreCase));
|
||||
usersCompression.DocumentCount.ShouldBeGreaterThan(0);
|
||||
usersCompression.BytesBeforeCompression.ShouldBeGreaterThan(0);
|
||||
usersCompression.BytesAfterCompression.ShouldBeGreaterThan(0);
|
||||
@@ -65,26 +66,24 @@ public class MaintenanceDiagnosticsAndMigrationTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies compression migration dry-run and apply modes return deterministic stats and preserve data.
|
||||
/// Verifies compression migration dry-run and apply modes return deterministic stats and preserve data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MigrateCompression_DryRunAndApply_ShouldReturnDeterministicStatsAndPreserveData()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
|
||||
try
|
||||
{
|
||||
using var db = new TestDbContext(dbPath, CompressionOptions.Default);
|
||||
var ids = new List<ZB.MOM.WW.CBDD.Bson.ObjectId>();
|
||||
var ids = new List<ObjectId>();
|
||||
|
||||
for (var i = 0; i < 60; i++)
|
||||
{
|
||||
ids.Add(db.Users.Insert(new User
|
||||
{
|
||||
Name = BuildPayload(i, 12000),
|
||||
Age = i % 17
|
||||
}));
|
||||
}
|
||||
|
||||
db.SaveChanges();
|
||||
db.ForceCheckpoint();
|
||||
@@ -132,7 +131,7 @@ public class MaintenanceDiagnosticsAndMigrationTests
|
||||
|
||||
private static string BuildPayload(int seed, int approxLength)
|
||||
{
|
||||
var builder = new System.Text.StringBuilder(approxLength + 128);
|
||||
var builder = new StringBuilder(approxLength + 128);
|
||||
var i = 0;
|
||||
while (builder.Length < approxLength)
|
||||
{
|
||||
@@ -148,11 +147,13 @@ public class MaintenanceDiagnosticsAndMigrationTests
|
||||
}
|
||||
|
||||
private static string NewDbPath()
|
||||
=> Path.Combine(Path.GetTempPath(), $"maint_diag_migrate_{Guid.NewGuid():N}.db");
|
||||
{
|
||||
return Path.Combine(Path.GetTempPath(), $"maint_diag_migrate_{Guid.NewGuid():N}.db");
|
||||
}
|
||||
|
||||
private static void CleanupFiles(string dbPath)
|
||||
{
|
||||
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
string walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
var markerPath = $"{dbPath}.compact.state";
|
||||
var tempPath = $"{dbPath}.compact.tmp";
|
||||
var backupPath = $"{dbPath}.compact.bak";
|
||||
@@ -163,4 +164,4 @@ public class MaintenanceDiagnosticsAndMigrationTests
|
||||
if (File.Exists(tempPath)) File.Delete(tempPath);
|
||||
if (File.Exists(backupPath)) File.Delete(backupPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core.Collections;
|
||||
using ZB.MOM.WW.CBDD.Core.Indexing;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using ZB.MOM.WW.CBDD.Core.Transactions;
|
||||
using ZB.MOM.WW.CBDD.Shared;
|
||||
using ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers;
|
||||
using Xunit;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
@@ -15,7 +12,7 @@ public class MetadataPersistenceTests : IDisposable
|
||||
private readonly string _walPath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MetadataPersistenceTests"/> class.
|
||||
/// Initializes a new instance of the <see cref="MetadataPersistenceTests" /> class.
|
||||
/// </summary>
|
||||
public MetadataPersistenceTests()
|
||||
{
|
||||
@@ -24,7 +21,16 @@ public class MetadataPersistenceTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests index definitions are persisted and reloaded.
|
||||
/// Disposes the resources used by this instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
if (File.Exists(_walPath)) File.Delete(_walPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests index definitions are persisted and reloaded.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void IndexDefinitions_ArePersisted_AndReloaded()
|
||||
@@ -66,19 +72,19 @@ public class MetadataPersistenceTests : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests ensure index does not recreate if index exists.
|
||||
/// Tests ensure index does not recreate if index exists.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EnsureIndex_DoesNotRecreate_IfIndexExists()
|
||||
{
|
||||
// 1. Create index
|
||||
using (var context = new Shared.TestDbContext(_dbPath))
|
||||
using (var context = new TestDbContext(_dbPath))
|
||||
{
|
||||
context.Users.EnsureIndex(u => u.Age);
|
||||
}
|
||||
|
||||
// 2. Re-open and EnsureIndex again - should be fast/no-op
|
||||
using (var context = new Shared.TestDbContext(_dbPath))
|
||||
using (var context = new TestDbContext(_dbPath))
|
||||
{
|
||||
var mapper = new ZB_MOM_WW_CBDD_Shared_UserMapper();
|
||||
|
||||
@@ -99,13 +105,4 @@ public class MetadataPersistenceTests : IDisposable
|
||||
results.Count().ShouldBe(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the resources used by this instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
if (File.Exists(_walPath)) File.Delete(_walPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +1,12 @@
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core.Collections;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class RobustnessTests
|
||||
{
|
||||
public struct Point
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the X.
|
||||
/// </summary>
|
||||
public int X { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Y.
|
||||
/// </summary>
|
||||
public int Y { get; set; }
|
||||
}
|
||||
|
||||
public class RobustEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the NullableInts.
|
||||
/// </summary>
|
||||
public List<int?> NullableInts { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Gets or sets the Map.
|
||||
/// </summary>
|
||||
public Dictionary<string, int> Map { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Gets or sets the EnumerableStrings.
|
||||
/// </summary>
|
||||
public IEnumerable<string> EnumerableStrings { get; set; } = Array.Empty<string>();
|
||||
/// <summary>
|
||||
/// Gets or sets the Location.
|
||||
/// </summary>
|
||||
public Point Location { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the NullableLocation.
|
||||
/// </summary>
|
||||
public Point? NullableLocation { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes GenerateSchema_RobustnessChecks.
|
||||
/// Executes GenerateSchema_RobustnessChecks.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GenerateSchema_RobustnessChecks()
|
||||
@@ -83,4 +43,45 @@ public class RobustnessTests
|
||||
nullableLocation.IsNullable.ShouldBeTrue();
|
||||
nullableLocation.NestedSchema.ShouldNotBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
public struct Point
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the X.
|
||||
/// </summary>
|
||||
public int X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Y.
|
||||
/// </summary>
|
||||
public int Y { get; set; }
|
||||
}
|
||||
|
||||
public class RobustEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the NullableInts.
|
||||
/// </summary>
|
||||
public List<int?> NullableInts { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Map.
|
||||
/// </summary>
|
||||
public Dictionary<string, int> Map { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the EnumerableStrings.
|
||||
/// </summary>
|
||||
public IEnumerable<string> EnumerableStrings { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Location.
|
||||
/// </summary>
|
||||
public Point Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the NullableLocation.
|
||||
/// </summary>
|
||||
public Point? NullableLocation { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using Xunit;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class StorageEngineDictionaryTests
|
||||
{
|
||||
private string GetTempDbPath() => Path.Combine(Path.GetTempPath(), $"test_storage_dict_{Guid.NewGuid()}.db");
|
||||
private string GetTempDbPath()
|
||||
{
|
||||
return Path.Combine(Path.GetTempPath(), $"test_storage_dict_{Guid.NewGuid()}.db");
|
||||
}
|
||||
|
||||
private void Cleanup(string path)
|
||||
{
|
||||
@@ -14,34 +16,37 @@ public class StorageEngineDictionaryTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dictionary pages are initialized and return normalized keys.
|
||||
/// Verifies dictionary pages are initialized and return normalized keys.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void StorageEngine_ShouldInitializeDictionary()
|
||||
{
|
||||
var path = GetTempDbPath();
|
||||
string path = GetTempDbPath();
|
||||
try
|
||||
{
|
||||
using (var storage = new StorageEngine(path, PageFileConfig.Default))
|
||||
{
|
||||
// Should generate ID > 100
|
||||
var id = storage.GetOrAddDictionaryEntry("TestKey");
|
||||
ushort id = storage.GetOrAddDictionaryEntry("TestKey");
|
||||
(id > DictionaryPage.ReservedValuesEnd).ShouldBeTrue();
|
||||
|
||||
var key = storage.GetDictionaryKey(id);
|
||||
string? key = storage.GetDictionaryKey(id);
|
||||
key.ShouldBe("testkey");
|
||||
}
|
||||
}
|
||||
finally { Cleanup(path); }
|
||||
finally
|
||||
{
|
||||
Cleanup(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dictionary entries persist across reopen.
|
||||
/// Verifies dictionary entries persist across reopen.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void StorageEngine_ShouldPersistDictionary()
|
||||
{
|
||||
var path = GetTempDbPath();
|
||||
string path = GetTempDbPath();
|
||||
try
|
||||
{
|
||||
ushort id1, id2;
|
||||
@@ -54,8 +59,8 @@ public class StorageEngineDictionaryTests
|
||||
// Reopen
|
||||
using (var storage = new StorageEngine(path, PageFileConfig.Default))
|
||||
{
|
||||
var val1 = storage.GetOrAddDictionaryEntry("Key1");
|
||||
var val2 = storage.GetOrAddDictionaryEntry("Key2");
|
||||
ushort val1 = storage.GetOrAddDictionaryEntry("Key1");
|
||||
ushort val2 = storage.GetOrAddDictionaryEntry("Key2");
|
||||
|
||||
val1.ShouldBe(id1);
|
||||
val2.ShouldBe(id2);
|
||||
@@ -64,16 +69,19 @@ public class StorageEngineDictionaryTests
|
||||
storage.GetDictionaryKey(val2).ShouldBe("key2");
|
||||
}
|
||||
}
|
||||
finally { Cleanup(path); }
|
||||
finally
|
||||
{
|
||||
Cleanup(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dictionary handling scales to many keys and remains durable.
|
||||
/// Verifies dictionary handling scales to many keys and remains durable.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void StorageEngine_ShouldHandleManyKeys()
|
||||
{
|
||||
var path = GetTempDbPath();
|
||||
string path = GetTempDbPath();
|
||||
try
|
||||
{
|
||||
const int keyCount = 3000;
|
||||
@@ -81,10 +89,10 @@ public class StorageEngineDictionaryTests
|
||||
|
||||
using (var storage = new StorageEngine(path, PageFileConfig.Default))
|
||||
{
|
||||
for (int i = 0; i < keyCount; i++)
|
||||
for (var i = 0; i < keyCount; i++)
|
||||
{
|
||||
var key = $"Key_{i}";
|
||||
var id = storage.GetOrAddDictionaryEntry(key);
|
||||
ushort id = storage.GetOrAddDictionaryEntry(key);
|
||||
expectedIds[key] = id;
|
||||
}
|
||||
}
|
||||
@@ -92,22 +100,25 @@ public class StorageEngineDictionaryTests
|
||||
// Reopen and Verify
|
||||
using (var storage = new StorageEngine(path, PageFileConfig.Default))
|
||||
{
|
||||
for (int i = 0; i < keyCount; i++)
|
||||
for (var i = 0; i < keyCount; i++)
|
||||
{
|
||||
var key = $"Key_{i}";
|
||||
var id = storage.GetOrAddDictionaryEntry(key); // Should get existing
|
||||
ushort id = storage.GetOrAddDictionaryEntry(key); // Should get existing
|
||||
id.ShouldBe(expectedIds[key]);
|
||||
|
||||
var loadedKey = storage.GetDictionaryKey(id);
|
||||
string? loadedKey = storage.GetDictionaryKey(id);
|
||||
loadedKey.ShouldBe(key.ToLowerInvariant());
|
||||
}
|
||||
|
||||
// Add new one
|
||||
var newId = storage.GetOrAddDictionaryEntry("NewKeyAfterReopen");
|
||||
ushort newId = storage.GetOrAddDictionaryEntry("NewKeyAfterReopen");
|
||||
(newId > 0).ShouldBeTrue();
|
||||
expectedIds.ContainsValue(newId).ShouldBeFalse();
|
||||
}
|
||||
}
|
||||
finally { Cleanup(path); }
|
||||
finally
|
||||
{
|
||||
Cleanup(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
public class StorageEngineTransactionProtocolTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies preparing an unknown transaction returns false.
|
||||
/// Verifies preparing an unknown transaction returns false.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void PrepareTransaction_Should_ReturnFalse_For_Unknown_Transaction()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
@@ -24,12 +24,12 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies committing a detached transaction object throws.
|
||||
/// Verifies committing a detached transaction object throws.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CommitTransaction_With_TransactionObject_Should_Throw_When_Not_Active()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
@@ -44,18 +44,18 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies committing a transaction object persists writes and clears active state.
|
||||
/// Verifies committing a transaction object persists writes and clears active state.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CommitTransaction_With_TransactionObject_Should_Commit_Writes()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
using var txn = storage.BeginTransaction();
|
||||
|
||||
var pageId = storage.AllocatePage();
|
||||
uint pageId = storage.AllocatePage();
|
||||
var data = new byte[storage.PageSize];
|
||||
data[0] = 0xAB;
|
||||
|
||||
@@ -75,12 +75,12 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies committing by identifier with no writes does not throw.
|
||||
/// Verifies committing by identifier with no writes does not throw.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CommitTransaction_ById_With_NoWrites_Should_Not_Throw()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
@@ -93,18 +93,18 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies committed transaction cache moves into readable state and active count is cleared.
|
||||
/// Verifies committed transaction cache moves into readable state and active count is cleared.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MarkTransactionCommitted_Should_Move_Cache_And_Clear_ActiveCount()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
using var txn = storage.BeginTransaction();
|
||||
|
||||
var pageId = storage.AllocatePage();
|
||||
uint pageId = storage.AllocatePage();
|
||||
var data = new byte[storage.PageSize];
|
||||
data[5] = 0x5A;
|
||||
storage.WritePage(pageId, txn.TransactionId, data);
|
||||
@@ -124,17 +124,17 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies rollback discards uncommitted page writes.
|
||||
/// Verifies rollback discards uncommitted page writes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RollbackTransaction_Should_Discard_Uncommitted_Write()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
|
||||
var pageId = storage.AllocatePage();
|
||||
uint pageId = storage.AllocatePage();
|
||||
var baseline = new byte[storage.PageSize];
|
||||
baseline[0] = 0x11;
|
||||
storage.WritePageImmediate(pageId, baseline);
|
||||
@@ -159,18 +159,18 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies marking a transaction committed transitions state correctly.
|
||||
/// Verifies marking a transaction committed transitions state correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Transaction_MarkCommitted_Should_Transition_State()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
using var txn = storage.BeginTransaction();
|
||||
|
||||
var pageId = storage.AllocatePage();
|
||||
uint pageId = storage.AllocatePage();
|
||||
var data = new byte[storage.PageSize];
|
||||
data[3] = 0x33;
|
||||
storage.WritePage(pageId, txn.TransactionId, data);
|
||||
@@ -191,18 +191,18 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies preparing then committing writes WAL data and updates transaction state.
|
||||
/// Verifies preparing then committing writes WAL data and updates transaction state.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Transaction_Prepare_Should_Write_Wal_And_Transition_State()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
try
|
||||
{
|
||||
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
||||
using var txn = storage.BeginTransaction();
|
||||
|
||||
var pageId = storage.AllocatePage();
|
||||
uint pageId = storage.AllocatePage();
|
||||
var data = new byte[storage.PageSize];
|
||||
data[11] = 0x7B;
|
||||
storage.WritePage(pageId, txn.TransactionId, data);
|
||||
@@ -220,16 +220,18 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
|
||||
private static string NewDbPath()
|
||||
=> Path.Combine(Path.GetTempPath(), $"storage_txn_{Guid.NewGuid():N}.db");
|
||||
{
|
||||
return Path.Combine(Path.GetTempPath(), $"storage_txn_{Guid.NewGuid():N}.db");
|
||||
}
|
||||
|
||||
private static void CleanupFiles(string dbPath)
|
||||
{
|
||||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||||
|
||||
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
string walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
if (File.Exists(walPath)) File.Delete(walPath);
|
||||
|
||||
var altWalPath = dbPath + "-wal";
|
||||
string altWalPath = dbPath + "-wal";
|
||||
if (File.Exists(altWalPath)) File.Delete(altWalPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user