using DeepDrftContent.FileDatabase.Abstractions;
using DeepDrftContent.FileDatabase.Models;
using DeepDrftContent.FileDatabase.Services;
using DeepDrftContent.FileDatabase.Utils;
namespace DeepDrftTests;
///
/// SOLID, DRY tests for IndexSystem components
/// Follows Single Responsibility: Each test class tests one concern
/// Follows DRY: Shared setup and helper methods
///
[TestFixture]
public class IndexSystemTests
{
///
/// Base test class for index-related tests - DRY principle
///
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 */ }
}
}
///
/// Helper method to create test entry IDs - DRY principle
///
protected static string CreateTestEntryId(string key)
=> key;
///
/// Helper method to create test metadata - DRY principle
///
protected static MetaData CreateTestMetaData(string key, string extension = ".png")
=> new(key, extension);
}
///
/// Tests for IndexFactoryService - Single Responsibility Principle
///
[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(), "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(), "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(), "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(), "Should load VaultIndex");
}
[Test]
public void LoadIndexAsync_NonExistentIndex_ThrowsException()
{
// Act & Assert
Assert.ThrowsAsync(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(), "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(), "Should load DirectoryIndex");
Assert.That(loadedIndex, Is.InstanceOf(), "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(), "Should create DirectoryIndex");
Assert.That(index, Is.InstanceOf(), "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(), "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(), "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(), "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(), "Should create VaultIndex");
var queryableIndex = (IEntryQueryable)index;
Assert.That(queryableIndex.GetEntriesSize(), Is.EqualTo(0), "Should be empty initially");
}
}
///
/// Tests for DirectoryIndex - Single Responsibility Principle
///
[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}");
}
}
}
///
/// Tests for VaultIndex - Single Responsibility Principle
///
[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}");
}
}
}
///
/// Integration tests for IndexDirectory classes - Open/Closed Principle
///
[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");
}
}
}