- AliasNodeIdentity: stable composite node identifier (path + sibling index) - AliasDataStore: CRUD operations on SQLite AliasMap table - PathDatabase: add AliasMap table creation + index - Unit tests: 30 tests for identity parsing/equality, store CRUD/batch/import - Both projects compile successfully
354 lines
12 KiB
C#
354 lines
12 KiB
C#
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using NavisworksTransport.Core.AliasTree;
|
|
using System;
|
|
using System.Data.SQLite;
|
|
using System.IO;
|
|
|
|
namespace NavisworksTransport.UnitTests.Core
|
|
{
|
|
[TestClass]
|
|
public class AliasNodeIdentityTests
|
|
{
|
|
// ── 构造与序列化 ──
|
|
|
|
[TestMethod]
|
|
public void Constructor_WithValidArgs_ShouldSetProperties()
|
|
{
|
|
var id = new AliasNodeIdentity("building/floor", 2);
|
|
Assert.AreEqual("building/floor", id.HierarchicalPath);
|
|
Assert.AreEqual(2, id.SiblingIndex);
|
|
}
|
|
|
|
[TestMethod]
|
|
[ExpectedException(typeof(ArgumentException))]
|
|
public void Constructor_WithEmptyPath_ShouldThrow()
|
|
{
|
|
new AliasNodeIdentity("", 0);
|
|
}
|
|
|
|
[TestMethod]
|
|
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
|
public void Constructor_WithNegativeIndex_ShouldThrow()
|
|
{
|
|
new AliasNodeIdentity("building/floor", -1);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ToKey_ShouldReturnCorrectFormat()
|
|
{
|
|
var id = new AliasNodeIdentity("building/floor/wall", 3);
|
|
Assert.AreEqual("building/floor/wall#3", id.ToKey());
|
|
}
|
|
|
|
[TestMethod]
|
|
public void FromKey_ShouldRestoreIdentity()
|
|
{
|
|
var restored = AliasNodeIdentity.FromKey("building/floor/wall#3");
|
|
Assert.AreEqual("building/floor/wall", restored.HierarchicalPath);
|
|
Assert.AreEqual(3, restored.SiblingIndex);
|
|
}
|
|
|
|
[TestMethod]
|
|
[ExpectedException(typeof(FormatException))]
|
|
public void FromKey_WithoutSeparator_ShouldThrow()
|
|
{
|
|
AliasNodeIdentity.FromKey("building/floor/wall");
|
|
}
|
|
|
|
[TestMethod]
|
|
[ExpectedException(typeof(FormatException))]
|
|
public void FromKey_EmptyIndex_ShouldThrow()
|
|
{
|
|
AliasNodeIdentity.FromKey("building/floor/wall#");
|
|
}
|
|
|
|
[TestMethod]
|
|
[ExpectedException(typeof(FormatException))]
|
|
public void FromKey_NonNumericIndex_ShouldThrow()
|
|
{
|
|
AliasNodeIdentity.FromKey("building/floor/wall#abc");
|
|
}
|
|
|
|
// ── TryParseKey ──
|
|
|
|
[TestMethod]
|
|
public void TryParseKey_ValidKey_ShouldReturnTrue()
|
|
{
|
|
bool ok = AliasNodeIdentity.TryParseKey("building/floor#0", out var id);
|
|
Assert.IsTrue(ok);
|
|
Assert.AreEqual("building/floor", id.HierarchicalPath);
|
|
Assert.AreEqual(0, id.SiblingIndex);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void TryParseKey_InvalidKey_ShouldReturnFalse()
|
|
{
|
|
Assert.IsFalse(AliasNodeIdentity.TryParseKey("", out _));
|
|
Assert.IsFalse(AliasNodeIdentity.TryParseKey("nohash", out _));
|
|
Assert.IsFalse(AliasNodeIdentity.TryParseKey("hashonly#", out _));
|
|
Assert.IsFalse(AliasNodeIdentity.TryParseKey("path#-1", out _));
|
|
Assert.IsFalse(AliasNodeIdentity.TryParseKey("path#abc", out _));
|
|
}
|
|
|
|
// ── 值语义 ──
|
|
|
|
[TestMethod]
|
|
public void Equals_IdenticalIdentities_ShouldBeEqual()
|
|
{
|
|
var a = new AliasNodeIdentity("root/a", 0);
|
|
var b = new AliasNodeIdentity("root/a", 0);
|
|
Assert.AreEqual(a, b);
|
|
Assert.IsTrue(a == b);
|
|
Assert.IsFalse(a != b);
|
|
Assert.AreEqual(a.GetHashCode(), b.GetHashCode());
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Equals_DifferentPath_ShouldNotBeEqual()
|
|
{
|
|
var a = new AliasNodeIdentity("root/a", 0);
|
|
var b = new AliasNodeIdentity("root/b", 0);
|
|
Assert.AreNotEqual(a, b);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Equals_DifferentIndex_ShouldNotBeEqual()
|
|
{
|
|
var a = new AliasNodeIdentity("root/a", 0);
|
|
var b = new AliasNodeIdentity("root/a", 1);
|
|
Assert.AreNotEqual(a, b);
|
|
}
|
|
|
|
// ── HasPathPrefix ──
|
|
|
|
[TestMethod]
|
|
public void HasPathPrefix_MatchingPrefix_ShouldReturnTrue()
|
|
{
|
|
var id = new AliasNodeIdentity("building/floor/wall", 0);
|
|
Assert.IsTrue(id.HasPathPrefix("building/floor"));
|
|
Assert.IsTrue(id.HasPathPrefix("building"));
|
|
Assert.IsTrue(id.HasPathPrefix("building/floor/wall"));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void HasPathPrefix_NonMatchingPrefix_ShouldReturnFalse()
|
|
{
|
|
var id = new AliasNodeIdentity("building/floor/wall", 0);
|
|
Assert.IsFalse(id.HasPathPrefix("building/roof"));
|
|
Assert.IsFalse(id.HasPathPrefix("Build"));
|
|
}
|
|
|
|
// ── ToString ──
|
|
|
|
[TestMethod]
|
|
public void ToString_ShouldMatchToKey()
|
|
{
|
|
var id = new AliasNodeIdentity("a/b/c", 1);
|
|
Assert.AreEqual(id.ToKey(), id.ToString());
|
|
}
|
|
}
|
|
|
|
[TestClass]
|
|
public class AliasDataStoreTests
|
|
{
|
|
private string _dbPath;
|
|
private SQLiteConnection _connection;
|
|
|
|
[TestInitialize]
|
|
public void Setup()
|
|
{
|
|
// 使用临时文件创建独立的 SQLite 数据库
|
|
_dbPath = Path.Combine(Path.GetTempPath(), $"AliasDataStoreTest_{Guid.NewGuid():N}.db");
|
|
_connection = new SQLiteConnection($"Data Source={_dbPath};Version=3;");
|
|
_connection.Open();
|
|
}
|
|
|
|
[TestCleanup]
|
|
public void Cleanup()
|
|
{
|
|
_connection?.Close();
|
|
_connection?.Dispose();
|
|
try { File.Delete(_dbPath); }
|
|
catch { /* 清理临时文件 */ }
|
|
}
|
|
|
|
private AliasDataStore CreateStore()
|
|
{
|
|
var store = new AliasDataStore(_connection);
|
|
store.EnsureTableExists();
|
|
return store;
|
|
}
|
|
|
|
private AliasNodeIdentity Id(string path, int index = 0)
|
|
=> new AliasNodeIdentity(path, index);
|
|
|
|
// ── CRUD ──
|
|
|
|
[TestMethod]
|
|
public void SetAndGetAlias_ShouldReturnSavedValue()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("building/floor", 0), "1F-办公层");
|
|
string result = store.GetAlias(Id("building/floor", 0));
|
|
Assert.AreEqual("1F-办公层", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void GetAlias_Nonexistent_ShouldReturnNull()
|
|
{
|
|
var store = CreateStore();
|
|
string result = store.GetAlias(Id("some/nonexistent", 0));
|
|
Assert.IsNull(result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SetAlias_Overwrite_ShouldUpdateValue()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("building/floor", 0), "旧名称");
|
|
store.SetAlias(Id("building/floor", 0), "新名称");
|
|
Assert.AreEqual("新名称", store.GetAlias(Id("building/floor", 0)));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SetAlias_EmptyString_ShouldDelete()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("building/floor", 0), "原本");
|
|
Assert.AreEqual(1, store.Count);
|
|
store.SetAlias(Id("building/floor", 0), " ");
|
|
Assert.IsNull(store.GetAlias(Id("building/floor", 0)));
|
|
Assert.AreEqual(0, store.Count);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void DeleteAlias_ShouldRemoveEntry()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("building/floor", 0), "别名");
|
|
Assert.AreEqual(1, store.Count);
|
|
store.DeleteAlias(Id("building/floor", 0));
|
|
Assert.AreEqual(0, store.Count);
|
|
Assert.IsNull(store.GetAlias(Id("building/floor", 0)));
|
|
}
|
|
|
|
// ── 批量操作 ──
|
|
|
|
[TestMethod]
|
|
public void DeleteByPathPrefix_ShouldRemoveAllUnderPath()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("building/floor/a", 0), "别名1");
|
|
store.SetAlias(Id("building/floor/b", 0), "别名2");
|
|
store.SetAlias(Id("building/roof/c", 0), "别名3");
|
|
|
|
store.DeleteAliasesByPathPrefix("building/floor");
|
|
|
|
Assert.IsNull(store.GetAlias(Id("building/floor/a", 0)));
|
|
Assert.IsNull(store.GetAlias(Id("building/floor/b", 0)));
|
|
Assert.AreEqual("别名3", store.GetAlias(Id("building/roof/c", 0)));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void LoadAll_ShouldReturnAllEntries()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("a", 0), "别名A");
|
|
store.SetAlias(Id("b", 1), "别名B");
|
|
store.SetAlias(Id("c", 2), "别名C");
|
|
|
|
var all = store.LoadAll();
|
|
Assert.AreEqual(3, all.Count);
|
|
Assert.AreEqual("别名A", all[Id("a", 0).ToKey()]);
|
|
Assert.AreEqual("别名B", all[Id("b", 1).ToKey()]);
|
|
Assert.AreEqual("别名C", all[Id("c", 2).ToKey()]);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Count_EmptyStore_ShouldBeZero()
|
|
{
|
|
var store = CreateStore();
|
|
Assert.AreEqual(0, store.Count);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Count_AfterInsert_ShouldIncrease()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("a", 0), "alias1");
|
|
store.SetAlias(Id("b", 0), "alias2");
|
|
Assert.AreEqual(2, store.Count);
|
|
}
|
|
|
|
// ── 批量导入 ──
|
|
|
|
[TestMethod]
|
|
public void ImportBatch_ShouldMergeEntries()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("existing", 0), "保留值");
|
|
|
|
var entries = new System.Collections.Generic.Dictionary<string, string>
|
|
{
|
|
{ Id("existing", 0).ToKey(), "覆盖值" },
|
|
{ Id("new", 0).ToKey(), "新值" }
|
|
};
|
|
store.ImportBatch(entries);
|
|
|
|
Assert.AreEqual("覆盖值", store.GetAlias(Id("existing", 0)));
|
|
Assert.AreEqual("新值", store.GetAlias(Id("new", 0)));
|
|
Assert.AreEqual(2, store.Count);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ImportBatch_EmptyDict_ShouldNotAffectStore()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("a", 0), "值");
|
|
store.ImportBatch(new System.Collections.Generic.Dictionary<string, string>());
|
|
Assert.AreEqual(1, store.Count);
|
|
}
|
|
|
|
// ── 清除全部 ──
|
|
|
|
[TestMethod]
|
|
public void ClearAll_ShouldRemoveEverything()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("a", 0), "别名1");
|
|
store.SetAlias(Id("b", 1), "别名2");
|
|
store.ClearAll();
|
|
Assert.AreEqual(0, store.Count);
|
|
}
|
|
|
|
// ── 同名兄弟节点去重 ──
|
|
|
|
[TestMethod]
|
|
public void SiblingNodes_WithDifferentIndex_ShouldStoreSeparately()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("floor/wall", 0), "墙-1");
|
|
store.SetAlias(Id("floor/wall", 1), "墙-2");
|
|
store.SetAlias(Id("floor/wall", 2), "墙-3");
|
|
|
|
Assert.AreEqual("墙-1", store.GetAlias(Id("floor/wall", 0)));
|
|
Assert.AreEqual("墙-2", store.GetAlias(Id("floor/wall", 1)));
|
|
Assert.AreEqual("墙-3", store.GetAlias(Id("floor/wall", 2)));
|
|
Assert.AreEqual(3, store.Count);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void LoadAll_SiblingNodes_ShouldReturnAll()
|
|
{
|
|
var store = CreateStore();
|
|
store.SetAlias(Id("same/path", 0), "第一个");
|
|
store.SetAlias(Id("same/path", 1), "第二个");
|
|
|
|
var all = store.LoadAll();
|
|
Assert.AreEqual(2, all.Count);
|
|
Assert.AreEqual("第一个", all["same/path#0"]);
|
|
Assert.AreEqual("第二个", all["same/path#1"]);
|
|
}
|
|
}
|
|
}
|