529 lines
19 KiB
C#
529 lines
19 KiB
C#
using DeepDrftContent.FileDatabase.Abstractions;
|
|
using DeepDrftContent.FileDatabase.Models;
|
|
using DeepDrftContent.FileDatabase.Services;
|
|
using DeepDrftContent.FileDatabase.Utils;
|
|
|
|
namespace DeepDrftTests;
|
|
|
|
/// <summary>
|
|
/// SOLID, DRY tests for IndexSystem components
|
|
/// Follows Single Responsibility: Each test class tests one concern
|
|
/// Follows DRY: Shared setup and helper methods
|
|
/// </summary>
|
|
[TestFixture]
|
|
public class IndexSystemTests
|
|
{
|
|
/// <summary>
|
|
/// Base test class for index-related tests - DRY principle
|
|
/// </summary>
|
|
public abstract class IndexTestBase
|
|
{
|
|
protected string TestDirectory { get; private set; } = null!;
|
|
protected string IndexPath => Path.Combine(TestDirectory, "index");
|
|
|
|
[SetUp]
|
|
public virtual void SetUp()
|
|
{
|
|
TestDirectory = Path.Combine(Path.GetTempPath(), "DeepDrftTests", "IndexSystem", Guid.NewGuid().ToString());
|
|
Directory.CreateDirectory(TestDirectory);
|
|
}
|
|
|
|
[TearDown]
|
|
public virtual void TearDown()
|
|
{
|
|
if (Directory.Exists(TestDirectory))
|
|
{
|
|
try { Directory.Delete(TestDirectory, true); } catch { /* Ignore cleanup errors */ }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to create test entry IDs - DRY principle
|
|
/// </summary>
|
|
protected static string CreateTestEntryId(string key)
|
|
=> key;
|
|
|
|
/// <summary>
|
|
/// Helper method to create test metadata - DRY principle
|
|
/// </summary>
|
|
protected static MetaData CreateTestMetaData(string key, string extension = ".png")
|
|
=> new(key, extension);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests for IndexFactoryService - Single Responsibility Principle
|
|
/// </summary>
|
|
[TestFixture]
|
|
public class IndexFactoryServiceTests : IndexTestBase
|
|
{
|
|
private IndexFactoryService _factory = null!;
|
|
|
|
[SetUp]
|
|
public override void SetUp()
|
|
{
|
|
base.SetUp();
|
|
_factory = new IndexFactoryService();
|
|
}
|
|
|
|
[Test]
|
|
public async Task CreateIndexAsync_DirectoryType_CreatesDirectoryIndex()
|
|
{
|
|
// Act
|
|
var index = await _factory.CreateDirectoryIndexAsync(TestDirectory);
|
|
|
|
// Assert
|
|
Assert.That(index, Is.Not.Null, "Index should be created");
|
|
Assert.That(index, Is.TypeOf<DirectoryIndex>(), "Should create DirectoryIndex");
|
|
Assert.That(File.Exists(IndexPath), Is.True, "Index file should be created");
|
|
}
|
|
|
|
[Test]
|
|
public async Task CreateIndexAsync_VaultType_CreatesVaultIndex()
|
|
{
|
|
// Act
|
|
var index = await _factory.CreateVaultIndexAsync(TestDirectory, MediaVaultType.Media);
|
|
|
|
// Assert
|
|
Assert.That(index, Is.Not.Null, "Index should be created");
|
|
Assert.That(index, Is.TypeOf<VaultIndex>(), "Should create VaultIndex");
|
|
Assert.That(File.Exists(IndexPath), Is.True, "Index file should be created");
|
|
}
|
|
|
|
|
|
[Test]
|
|
public async Task LoadIndexAsync_ExistingDirectoryIndex_LoadsSuccessfully()
|
|
{
|
|
// Arrange - Create an index first
|
|
await _factory.CreateDirectoryIndexAsync(TestDirectory);
|
|
|
|
// Act
|
|
var loadedIndex = await _factory.LoadIndexAsync(IndexType.Directory, TestDirectory);
|
|
|
|
// Assert
|
|
Assert.That(loadedIndex, Is.Not.Null, "Index should be loaded");
|
|
Assert.That(loadedIndex, Is.TypeOf<DirectoryIndex>(), "Should load DirectoryIndex");
|
|
}
|
|
|
|
[Test]
|
|
public async Task LoadIndexAsync_ExistingVaultIndex_LoadsSuccessfully()
|
|
{
|
|
// Arrange - Create an index first
|
|
await _factory.CreateVaultIndexAsync(TestDirectory, MediaVaultType.Media);
|
|
|
|
// Act
|
|
var loadedIndex = await _factory.LoadIndexAsync(IndexType.Vault, TestDirectory);
|
|
|
|
// Assert
|
|
Assert.That(loadedIndex, Is.Not.Null, "Index should be loaded");
|
|
Assert.That(loadedIndex, Is.TypeOf<VaultIndex>(), "Should load VaultIndex");
|
|
}
|
|
|
|
[Test]
|
|
public void LoadIndexAsync_NonExistentIndex_ThrowsException()
|
|
{
|
|
// Act & Assert
|
|
Assert.ThrowsAsync<FileNotFoundException>(async () =>
|
|
await _factory.LoadIndexAsync(IndexType.Directory, TestDirectory),
|
|
"Should throw when index file doesn't exist");
|
|
}
|
|
|
|
[Test]
|
|
public async Task LoadOrCreateIndexAsync_ExistingIndex_LoadsExisting()
|
|
{
|
|
// Arrange - Create an index with data
|
|
var originalIndex = await _factory.CreateDirectoryIndexAsync(TestDirectory);
|
|
Assert.That(originalIndex, Is.TypeOf<DirectoryIndex>(), "Should create DirectoryIndex");
|
|
|
|
var directoryIndex = (DirectoryIndex)originalIndex!;
|
|
var testKey = CreateTestEntryId("test-entry");
|
|
directoryIndex.PutEntry(testKey);
|
|
|
|
// Save the modified index
|
|
var indexData = _factory.CreateIndexData(IndexType.Directory, directoryIndex);
|
|
await FileUtils.PutObjectAsync(IndexPath, indexData);
|
|
|
|
// Act
|
|
var loadedIndex = await _factory.LoadOrCreateDirectoryIndexAsync(TestDirectory);
|
|
|
|
// Assert
|
|
Assert.That(loadedIndex, Is.Not.Null, "Index should be loaded");
|
|
Assert.That(loadedIndex, Is.TypeOf<DirectoryIndex>(), "Should load DirectoryIndex");
|
|
Assert.That(loadedIndex, Is.InstanceOf<IEntryQueryable>(), "Should implement IEntryQueryable");
|
|
|
|
Assert.That(loadedIndex.GetEntriesSize(), Is.EqualTo(1), "Should preserve existing entries");
|
|
}
|
|
|
|
[Test]
|
|
public async Task LoadOrCreateIndexAsync_NonExistentIndex_CreatesNew()
|
|
{
|
|
// Act
|
|
var index = await _factory.LoadOrCreateDirectoryIndexAsync(TestDirectory);
|
|
|
|
// Assert
|
|
Assert.That(index, Is.Not.Null, "Index should be created");
|
|
Assert.That(index, Is.TypeOf<DirectoryIndex>(), "Should create DirectoryIndex");
|
|
Assert.That(index, Is.InstanceOf<IEntryQueryable>(), "Should implement IEntryQueryable");
|
|
|
|
Assert.That(index.GetEntriesSize(), Is.EqualTo(0), "New index should be empty");
|
|
}
|
|
|
|
[Test]
|
|
public void CreateIndexData_DirectoryIndex_CreatesDirectoryIndexData()
|
|
{
|
|
// Arrange
|
|
var directoryIndex = new DirectoryIndex(new DirectoryIndexData("test"));
|
|
var testKey = CreateTestEntryId("test-entry");
|
|
directoryIndex.PutEntry(testKey);
|
|
|
|
// Act
|
|
var indexData = _factory.CreateIndexData(IndexType.Directory, directoryIndex);
|
|
|
|
// Assert
|
|
Assert.That(indexData, Is.TypeOf<DirectoryIndexData>(), "Should create DirectoryIndexData");
|
|
var typedData = (DirectoryIndexData)indexData;
|
|
Assert.That(typedData.IndexKey, Is.EqualTo("test"), "Should preserve index key");
|
|
}
|
|
|
|
[Test]
|
|
public void CreateIndexData_VaultIndex_CreatesVaultIndexData()
|
|
{
|
|
// Arrange
|
|
var vaultIndex = new VaultIndex(new VaultIndexData("test"));
|
|
var testKey = CreateTestEntryId("test-entry");
|
|
var testMetaData = CreateTestMetaData("test-entry");
|
|
vaultIndex.PutEntry(testKey, testMetaData);
|
|
|
|
// Act
|
|
var indexData = _factory.CreateIndexData(IndexType.Vault, vaultIndex);
|
|
|
|
// Assert
|
|
Assert.That(indexData, Is.TypeOf<VaultIndexData>(), "Should create VaultIndexData");
|
|
var typedData = (VaultIndexData)indexData;
|
|
Assert.That(typedData.IndexKey, Is.EqualTo("test"), "Should preserve index key");
|
|
}
|
|
|
|
[Test]
|
|
public void CreateIndexFromData_DirectoryIndexData_CreatesDirectoryIndex()
|
|
{
|
|
// Arrange
|
|
var indexData = new DirectoryIndexData("test");
|
|
|
|
// Act
|
|
var index = _factory.CreateIndexFromData(IndexType.Directory, indexData);
|
|
|
|
// Assert
|
|
Assert.That(index, Is.TypeOf<DirectoryIndex>(), "Should create DirectoryIndex");
|
|
var queryableIndex = (IEntryQueryable)index;
|
|
Assert.That(queryableIndex.GetEntriesSize(), Is.EqualTo(0), "Should be empty initially");
|
|
}
|
|
|
|
[Test]
|
|
public void CreateIndexFromData_VaultIndexData_CreatesVaultIndex()
|
|
{
|
|
// Arrange
|
|
var indexData = new VaultIndexData("test");
|
|
|
|
// Act
|
|
var index = _factory.CreateIndexFromData(IndexType.Vault, indexData);
|
|
|
|
// Assert
|
|
Assert.That(index, Is.TypeOf<VaultIndex>(), "Should create VaultIndex");
|
|
var queryableIndex = (IEntryQueryable)index;
|
|
Assert.That(queryableIndex.GetEntriesSize(), Is.EqualTo(0), "Should be empty initially");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests for DirectoryIndex - Single Responsibility Principle
|
|
/// </summary>
|
|
[TestFixture]
|
|
public class DirectoryIndexTests : IndexTestBase
|
|
{
|
|
private DirectoryIndex _directoryIndex = null!;
|
|
|
|
[SetUp]
|
|
public override void SetUp()
|
|
{
|
|
base.SetUp();
|
|
_directoryIndex = new DirectoryIndex(new DirectoryIndexData("test-directory"));
|
|
}
|
|
|
|
[Test]
|
|
public void DirectoryIndex_InitialState_IsEmpty()
|
|
{
|
|
// Assert
|
|
Assert.That(_directoryIndex.GetEntriesSize(), Is.EqualTo(0), "Should be empty initially");
|
|
Assert.That(_directoryIndex.GetEntries(), Is.Empty, "Should have no entries");
|
|
}
|
|
|
|
[Test]
|
|
public void PutEntry_NewEntry_AddsSuccessfully()
|
|
{
|
|
// Arrange
|
|
var testKey = CreateTestEntryId("new-entry");
|
|
|
|
// Act
|
|
_directoryIndex.PutEntry(testKey);
|
|
|
|
// Assert
|
|
Assert.That(_directoryIndex.GetEntriesSize(), Is.EqualTo(1), "Should have one entry");
|
|
Assert.That(_directoryIndex.HasEntry(testKey), Is.True, "Should contain the entry");
|
|
Assert.That(_directoryIndex.GetEntries(), Contains.Item(testKey), "Should include entry in collection");
|
|
}
|
|
|
|
[Test]
|
|
public void PutEntry_DuplicateEntry_DoesNotDuplicate()
|
|
{
|
|
// Arrange
|
|
var testKey = CreateTestEntryId("duplicate-entry");
|
|
_directoryIndex.PutEntry(testKey);
|
|
|
|
// Act
|
|
_directoryIndex.PutEntry(testKey);
|
|
|
|
// Assert
|
|
Assert.That(_directoryIndex.GetEntriesSize(), Is.EqualTo(1), "Should still have only one entry");
|
|
}
|
|
|
|
[Test]
|
|
public void PutEntry_MultipleEntries_AddsAll()
|
|
{
|
|
// Arrange
|
|
var keys = new[]
|
|
{
|
|
CreateTestEntryId("entry1"),
|
|
CreateTestEntryId("entry2"),
|
|
CreateTestEntryId("entry3")
|
|
};
|
|
|
|
// Act
|
|
foreach (var key in keys)
|
|
{
|
|
_directoryIndex.PutEntry(key);
|
|
}
|
|
|
|
// Assert
|
|
Assert.That(_directoryIndex.GetEntriesSize(), Is.EqualTo(3), "Should have three entries");
|
|
foreach (var key in keys)
|
|
{
|
|
Assert.That(_directoryIndex.HasEntry(key), Is.True, $"Should contain {key}");
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void HasEntry_ExistingEntry_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var testKey = CreateTestEntryId("existing-entry");
|
|
_directoryIndex.PutEntry(testKey);
|
|
|
|
// Act & Assert
|
|
Assert.That(_directoryIndex.HasEntry(testKey), Is.True, "Should find existing entry");
|
|
}
|
|
|
|
[Test]
|
|
public void HasEntry_NonExistentEntry_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var testKey = CreateTestEntryId("non-existent");
|
|
|
|
// Act & Assert
|
|
Assert.That(_directoryIndex.HasEntry(testKey), Is.False, "Should not find non-existent entry");
|
|
}
|
|
|
|
[Test]
|
|
public void GetEntries_WithMultipleEntries_ReturnsAllEntries()
|
|
{
|
|
// Arrange
|
|
var keys = new[]
|
|
{
|
|
CreateTestEntryId("entry1"),
|
|
CreateTestEntryId("entry2"),
|
|
CreateTestEntryId("entry3")
|
|
};
|
|
|
|
foreach (var key in keys)
|
|
{
|
|
_directoryIndex.PutEntry(key);
|
|
}
|
|
|
|
// Act
|
|
var entries = _directoryIndex.GetEntries();
|
|
|
|
// Assert
|
|
Assert.That(entries.Count, Is.EqualTo(3), "Should return all entries");
|
|
foreach (var key in keys)
|
|
{
|
|
Assert.That(entries, Contains.Item(key), $"Should contain {key}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests for VaultIndex - Single Responsibility Principle
|
|
/// </summary>
|
|
[TestFixture]
|
|
public class VaultIndexTests : IndexTestBase
|
|
{
|
|
private VaultIndex _vaultIndex = null!;
|
|
|
|
[SetUp]
|
|
public override void SetUp()
|
|
{
|
|
base.SetUp();
|
|
_vaultIndex = new VaultIndex(new VaultIndexData("test-vault"));
|
|
}
|
|
|
|
[Test]
|
|
public void VaultIndex_InitialState_IsEmpty()
|
|
{
|
|
// Assert
|
|
Assert.That(_vaultIndex.GetEntriesSize(), Is.EqualTo(0), "Should be empty initially");
|
|
Assert.That(_vaultIndex.GetEntries(), Is.Empty, "Should have no entries");
|
|
}
|
|
|
|
[Test]
|
|
public void PutEntry_NewEntryWithMetadata_AddsSuccessfully()
|
|
{
|
|
// Arrange
|
|
var testKey = CreateTestEntryId("new-entry");
|
|
var testMetaData = CreateTestMetaData("new-entry");
|
|
|
|
// Act
|
|
_vaultIndex.PutEntry(testKey, testMetaData);
|
|
|
|
// Assert
|
|
Assert.That(_vaultIndex.GetEntriesSize(), Is.EqualTo(1), "Should have one entry");
|
|
Assert.That(_vaultIndex.HasEntry(testKey), Is.True, "Should contain the entry");
|
|
Assert.That(_vaultIndex.GetEntry(testKey), Is.EqualTo(testMetaData), "Should return correct metadata");
|
|
}
|
|
|
|
[Test]
|
|
public void PutEntry_DuplicateEntry_UpdatesMetadata()
|
|
{
|
|
// Arrange
|
|
var testKey = CreateTestEntryId("duplicate-entry");
|
|
var originalMetaData = CreateTestMetaData("original");
|
|
var updatedMetaData = CreateTestMetaData("updated");
|
|
|
|
_vaultIndex.PutEntry(testKey, originalMetaData);
|
|
|
|
// Act
|
|
_vaultIndex.PutEntry(testKey, updatedMetaData);
|
|
|
|
// Assert
|
|
Assert.That(_vaultIndex.GetEntriesSize(), Is.EqualTo(1), "Should still have only one entry");
|
|
Assert.That(_vaultIndex.GetEntry(testKey), Is.EqualTo(updatedMetaData), "Should have updated metadata");
|
|
}
|
|
|
|
[Test]
|
|
public void GetEntry_ExistingEntry_ReturnsMetadata()
|
|
{
|
|
// Arrange
|
|
var testKey = CreateTestEntryId("existing-entry");
|
|
var testMetaData = CreateTestMetaData("existing-entry");
|
|
_vaultIndex.PutEntry(testKey, testMetaData);
|
|
|
|
// Act
|
|
var retrievedMetaData = _vaultIndex.GetEntry(testKey);
|
|
|
|
// Assert
|
|
Assert.That(retrievedMetaData, Is.EqualTo(testMetaData), "Should return correct metadata");
|
|
}
|
|
|
|
[Test]
|
|
public void GetEntry_NonExistentEntry_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
var testKey = CreateTestEntryId("non-existent");
|
|
|
|
// Act
|
|
var retrievedMetaData = _vaultIndex.GetEntry(testKey);
|
|
|
|
// Assert
|
|
Assert.That(retrievedMetaData, Is.Null, "Should return null for non-existent entry");
|
|
}
|
|
|
|
[Test]
|
|
public void PutEntry_MultipleEntriesWithDifferentMetadata_AddsAll()
|
|
{
|
|
// Arrange
|
|
var entries = new[]
|
|
{
|
|
(CreateTestEntryId("entry1"), CreateTestMetaData("entry1", ".png")),
|
|
(CreateTestEntryId("entry2"), CreateTestMetaData("entry2", ".jpg")),
|
|
(CreateTestEntryId("entry3"), CreateTestMetaData("entry3", ".gif"))
|
|
};
|
|
|
|
// Act
|
|
foreach (var (key, metaData) in entries)
|
|
{
|
|
_vaultIndex.PutEntry(key, metaData);
|
|
}
|
|
|
|
// Assert
|
|
Assert.That(_vaultIndex.GetEntriesSize(), Is.EqualTo(3), "Should have three entries");
|
|
foreach (var (key, metaData) in entries)
|
|
{
|
|
Assert.That(_vaultIndex.HasEntry(key), Is.True, $"Should contain {key}");
|
|
Assert.That(_vaultIndex.GetEntry(key), Is.EqualTo(metaData), $"Should have correct metadata for {key}");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Integration tests for IndexDirectory classes - Open/Closed Principle
|
|
/// </summary>
|
|
[TestFixture]
|
|
public class IndexDirectoryIntegrationTests : IndexTestBase
|
|
{
|
|
[Test]
|
|
public async Task DirectoryIndexDirectory_AddToIndex_PersistsChanges()
|
|
{
|
|
// This test would require access to protected methods, so we'll test through the FileDatabase instead
|
|
// which properly encapsulates the DirectoryIndexDirectory functionality
|
|
|
|
// Arrange
|
|
var database = await FileDatabase.FromAsync(TestDirectory);
|
|
var testVaultKey = CreateTestEntryId("test-vault");
|
|
|
|
// Act - This internally uses DirectoryIndexDirectory.AddToIndexAsync
|
|
await database!.CreateVaultAsync(testVaultKey, MediaVaultType.Image);
|
|
|
|
// Assert
|
|
Assert.That(await database.HasIndexEntry(testVaultKey), Is.True, "Should contain added entry");
|
|
Assert.That(database.GetIndexSize(), Is.EqualTo(1), "Should have one entry");
|
|
Assert.That(File.Exists(IndexPath), Is.True, "Should persist to file");
|
|
|
|
// Verify persistence by reloading database
|
|
var reloadedDatabase = await FileDatabase.FromAsync(TestDirectory);
|
|
Assert.That(await reloadedDatabase!.HasIndexEntry(testVaultKey), Is.True, "Should persist entry across restarts");
|
|
}
|
|
|
|
[Test]
|
|
public async Task VaultIndexDirectory_AddToIndex_PersistsChanges()
|
|
{
|
|
// This test would require access to protected methods, so we'll test through MediaVault instead
|
|
// which properly encapsulates the VaultIndexDirectory functionality
|
|
|
|
// Arrange
|
|
var vault = await ImageVault.FromAsync(TestDirectory);
|
|
var testKey = CreateTestEntryId("test-entry");
|
|
var testImage = TestData.CreateTestImageBinary(1.0);
|
|
|
|
// Act - This internally uses VaultIndexDirectory.AddToIndexAsync
|
|
await vault!.AddEntryAsync(testKey, testImage);
|
|
|
|
// Assert
|
|
Assert.That(await vault.HasIndexEntry(testKey), Is.True, "Should contain added entry");
|
|
Assert.That(vault.GetIndexSize(), Is.EqualTo(1), "Should have one entry");
|
|
Assert.That(File.Exists(IndexPath), Is.True, "Should persist to file");
|
|
|
|
// Verify persistence by reloading vault
|
|
var reloadedVault = await ImageVault.FromAsync(TestDirectory);
|
|
Assert.That(await reloadedVault!.HasIndexEntry(testKey), Is.True, "Should persist entry across restarts");
|
|
}
|
|
}
|
|
}
|