169 lines
5.9 KiB
C#
Executable File
169 lines
5.9 KiB
C#
Executable File
using ZB.MOM.WW.CBDD.Bson;
|
|
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;
|
|
|
|
namespace ZB.MOM.WW.CBDD.Tests;
|
|
|
|
public class DictionaryPersistenceTests : IDisposable
|
|
{
|
|
private readonly string _dbPath;
|
|
private readonly StorageEngine _storage;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="DictionaryPersistenceTests"/> class.
|
|
/// </summary>
|
|
public DictionaryPersistenceTests()
|
|
{
|
|
_dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_dict_{Guid.NewGuid():N}.db");
|
|
_storage = new StorageEngine(_dbPath, PageFileConfig.Default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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");
|
|
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.
|
|
/// </summary>
|
|
[Fact]
|
|
public void RegisterMappers_Registers_All_Unique_Keys()
|
|
{
|
|
var mapper1 = new MockMapper("Coll1", "Name", "Age");
|
|
var mapper2 = new MockMapper("Coll2", "Name", "Address", "City");
|
|
|
|
_storage.RegisterMappers(new IDocumentMapper[] { mapper1, mapper2 });
|
|
|
|
// Verify keys in cache
|
|
_storage.GetOrAddDictionaryEntry("Name").ShouldNotBe((ushort)0);
|
|
_storage.GetOrAddDictionaryEntry("Age").ShouldNotBe((ushort)0);
|
|
_storage.GetOrAddDictionaryEntry("Address").ShouldNotBe((ushort)0);
|
|
_storage.GetOrAddDictionaryEntry("City").ShouldNotBe((ushort)0);
|
|
|
|
// Verify they have unique IDs (at least 4 unique IDs for 4 unique keys + internal ones)
|
|
var ids = new HashSet<ushort>
|
|
{
|
|
_storage.GetOrAddDictionaryEntry("Name"),
|
|
_storage.GetOrAddDictionaryEntry("Age"),
|
|
_storage.GetOrAddDictionaryEntry("Address"),
|
|
_storage.GetOrAddDictionaryEntry("City")
|
|
};
|
|
ids.Count.ShouldBe(4);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies dictionary keys persist across storage restarts.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Dictionary_Keys_Persist_Across_Restarts()
|
|
{
|
|
var mapper = new MockMapper("Coll1", "PersistedKey");
|
|
_storage.RegisterMappers(new IDocumentMapper[] { mapper });
|
|
|
|
var originalId = _storage.GetOrAddDictionaryEntry("PersistedKey");
|
|
originalId.ShouldNotBe((ushort)0);
|
|
|
|
_storage.Dispose();
|
|
|
|
// Re-open
|
|
using var storage2 = new StorageEngine(_dbPath, PageFileConfig.Default);
|
|
|
|
var recoveredId = storage2.GetOrAddDictionaryEntry("PersistedKey");
|
|
recoveredId.ShouldBe(originalId);
|
|
}
|
|
|
|
private class NestedMockMapper : DocumentMapperBase<ObjectId, object>
|
|
{
|
|
/// <inheritdoc />
|
|
public override string CollectionName => "Nested";
|
|
|
|
/// <inheritdoc />
|
|
public override BsonSchema GetSchema()
|
|
{
|
|
var schema = new BsonSchema { Title = "Nested" };
|
|
schema.Fields.Add(new BsonField
|
|
{
|
|
Name = "Top",
|
|
Type = BsonType.Document,
|
|
NestedSchema = new BsonSchema
|
|
{
|
|
Fields = { new BsonField { Name = "Child", Type = BsonType.String } }
|
|
}
|
|
});
|
|
return schema;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override ObjectId GetId(object entity) => throw new NotImplementedException();
|
|
|
|
/// <inheritdoc />
|
|
public override void SetId(object entity, ObjectId id) => throw new NotImplementedException();
|
|
|
|
/// <inheritdoc />
|
|
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);
|
|
}
|
|
}
|