diff --git a/DeepDrftContent/Controllers/TrackController.cs b/DeepDrftContent/Controllers/TrackController.cs index 1511d47..0505d64 100644 --- a/DeepDrftContent/Controllers/TrackController.cs +++ b/DeepDrftContent/Controllers/TrackController.cs @@ -8,7 +8,6 @@ namespace DeepDrftContent.Controllers; [Route("api/[controller]")] public class TrackController : ControllerBase { - private readonly EntryKey _vaultKey = new("tracks", MediaVaultType.Audio); private readonly FileDatabase.Services.FileDatabase _fileDatabase; public TrackController(FileDatabase.Services.FileDatabase fileDatabase) @@ -19,8 +18,12 @@ public class TrackController : ControllerBase [HttpGet("{trackId}")] public async Task> GetTrack([FromQuery] string trackId) { - if (_fileDatabase.GetVault(_vaultKey) is not AudioVault vault) { return NotFound(); } - var file = await vault.GetEntryAsync(MediaVaultType.Audio, new EntryKey(trackId, MediaVaultType.Audio)); + // BEFORE: Complex with EntryKey objects and redundant MediaVaultType + // var entryKey = new EntryKey(trackId, MediaVaultTypeMap.GetVaultType()); + // var file = await _fileDatabase.LoadResourceAsync(_vaultKey, entryKey); + + // AFTER: Ultra clean - just string identifiers, types inferred + var file = await _fileDatabase.LoadResourceAsync("tracks", trackId); if (file == null) { return NotFound(); } return File(file.Buffer, MimeTypeExtensions.GetMimeType(file.Extension)); } diff --git a/DeepDrftContent/FileDatabase/Abstractions/IIndexFactory.cs b/DeepDrftContent/FileDatabase/Abstractions/IIndexFactory.cs index 6d7a66f..4d9ac57 100644 --- a/DeepDrftContent/FileDatabase/Abstractions/IIndexFactory.cs +++ b/DeepDrftContent/FileDatabase/Abstractions/IIndexFactory.cs @@ -8,20 +8,30 @@ namespace DeepDrftContent.FileDatabase.Abstractions; /// public interface IIndexFactory { - /// - /// Creates an index of the specified type - /// - Task CreateIndexAsync(IndexType type, string rootPath); - /// /// Loads an existing index of the specified type /// Task LoadIndexAsync(IndexType type, string rootPath); /// - /// Loads existing index or creates new one if loading fails + /// Creates a directory index /// - Task LoadOrCreateIndexAsync(IndexType type, string rootPath); + Task CreateDirectoryIndexAsync(string rootPath); + + /// + /// Loads existing directory index or creates new one if loading fails + /// + Task LoadOrCreateDirectoryIndexAsync(string rootPath); + + /// + /// Creates a vault index with the specified vault type + /// + Task CreateVaultIndexAsync(string rootPath, MediaVaultType vaultType); + + /// + /// Loads existing vault index or creates new one with the specified vault type if loading fails + /// + Task LoadOrCreateVaultIndexAsync(string rootPath, MediaVaultType vaultType); } /// diff --git a/DeepDrftContent/FileDatabase/Abstractions/IMediaTypeRegistry.cs b/DeepDrftContent/FileDatabase/Abstractions/IMediaTypeRegistry.cs index 749c3d2..c7b9033 100644 --- a/DeepDrftContent/FileDatabase/Abstractions/IMediaTypeRegistry.cs +++ b/DeepDrftContent/FileDatabase/Abstractions/IMediaTypeRegistry.cs @@ -66,4 +66,14 @@ public interface IMediaTypeRegistry /// Get the metadata type for a vault type /// Type GetMetaDataType(MediaVaultType vaultType); + + /// + /// Get the vault type for a binary type (reverse mapping) + /// + MediaVaultType GetVaultType(Type binaryType); + + /// + /// Get the vault type for a binary type using generics (reverse mapping) + /// + MediaVaultType GetVaultType() where T : FileBinary; } diff --git a/DeepDrftContent/FileDatabase/Models/EntryKey.cs b/DeepDrftContent/FileDatabase/Models/EntryKey.cs deleted file mode 100644 index 71040e5..0000000 --- a/DeepDrftContent/FileDatabase/Models/EntryKey.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace DeepDrftContent.FileDatabase.Models; - -/// -/// Represents a key for entries in the file database system. -/// Combines a string key with a media vault type for type-safe operations. -/// -/// The string identifier for the entry -/// The media vault type this entry belongs to -public record EntryKey(string Key, MediaVaultType Type); diff --git a/DeepDrftContent/FileDatabase/Models/IIndex.cs b/DeepDrftContent/FileDatabase/Models/IIndex.cs index 24ebcbd..fe3bc57 100644 --- a/DeepDrftContent/FileDatabase/Models/IIndex.cs +++ b/DeepDrftContent/FileDatabase/Models/IIndex.cs @@ -17,9 +17,9 @@ public interface IIndex public interface IEntryQueryable : IIndex { /// - /// Gets all entry keys in this index + /// Gets all entry IDs in this index /// - IReadOnlyList GetEntries(); + IReadOnlyList GetEntries(); /// /// Gets the number of entries in this index @@ -27,9 +27,9 @@ public interface IEntryQueryable : IIndex int GetEntriesSize(); /// - /// Checks if the index contains the specified entry key + /// Checks if the index contains the specified entry ID /// - bool HasEntry(EntryKey entryKey); + bool HasEntry(string entryId); } /// @@ -40,7 +40,7 @@ public interface IDirectoryIndex : IEntryQueryable /// /// Adds an entry to the directory index /// - void PutEntry(EntryKey entryKey); + void PutEntry(string entryId); } /// @@ -51,10 +51,10 @@ public interface IVaultIndex : IEntryQueryable /// /// Gets metadata for a specific entry /// - MetaData? GetEntry(EntryKey entryKey); + MetaData? GetEntry(string entryId); /// /// Adds an entry with metadata to the vault index /// - void PutEntry(EntryKey entryKey, MetaData metaData); + void PutEntry(string entryId, MetaData metaData); } diff --git a/DeepDrftContent/FileDatabase/Models/IndexData.cs b/DeepDrftContent/FileDatabase/Models/IndexData.cs index 9e47454..a2fd92a 100644 --- a/DeepDrftContent/FileDatabase/Models/IndexData.cs +++ b/DeepDrftContent/FileDatabase/Models/IndexData.cs @@ -1,4 +1,5 @@ using DeepDrftContent.FileDatabase.Utils; +using System.Text.Json.Serialization; namespace DeepDrftContent.FileDatabase.Models; @@ -20,7 +21,7 @@ public abstract class IndexData /// public class DirectoryIndexData : IndexData { - public List Entries { get; set; } = new(); + public List Entries { get; set; } = new(); public DirectoryIndexData(string indexKey) : base(indexKey) { } @@ -39,7 +40,7 @@ public class DirectoryIndexData : IndexData /// public class VaultEntryData { - public EntryKey Key { get; set; } = null!; + public string Key { get; set; } = null!; public MetaData Value { get; set; } = null!; } @@ -49,12 +50,22 @@ public class VaultEntryData public class VaultIndexData : IndexData { public List Entries { get; set; } = new(); + public MediaVaultType VaultType { get; set; } - public VaultIndexData(string indexKey) : base(indexKey) { } + public VaultIndexData(string indexKey) : base(indexKey) + { + VaultType = MediaVaultType.Media; // Default vault type for legacy compatibility + } + + [JsonConstructor] + public VaultIndexData(string indexKey, MediaVaultType vaultType) : base(indexKey) + { + VaultType = vaultType; + } public static VaultIndexData FromIndex(VaultIndex index) { - var data = new VaultIndexData(index.GetKey()) + var data = new VaultIndexData(index.GetKey(), index.VaultType) { Entries = index.Entries.Select(kvp => new VaultEntryData { Key = kvp.Key, Value = kvp.Value }).ToList() }; @@ -67,11 +78,11 @@ public class VaultIndexData : IndexData /// public class DirectoryIndex : IndexData, IDirectoryIndex { - public StructuralSet Entries { get; } + public StructuralSet Entries { get; } public DirectoryIndex(DirectoryIndexData indexData) : base(indexData.IndexKey) { - Entries = new StructuralSet(); + Entries = new StructuralSet(); // Load entries from data foreach (var entry in indexData.Entries) { @@ -81,13 +92,13 @@ public class DirectoryIndex : IndexData, IDirectoryIndex public string GetKey() => IndexKey; - public IReadOnlyList GetEntries() => Entries.ToList().AsReadOnly(); + public IReadOnlyList GetEntries() => Entries.ToList().AsReadOnly(); public int GetEntriesSize() => Entries.Size; - public bool HasEntry(EntryKey entryKey) => Entries.Has(entryKey); + public bool HasEntry(string entryId) => Entries.Has(entryId); - public void PutEntry(EntryKey entryKey) => Entries.Add(entryKey); + public void PutEntry(string entryId) => Entries.Add(entryId); } /// @@ -95,11 +106,13 @@ public class DirectoryIndex : IndexData, IDirectoryIndex /// public class VaultIndex : IndexData, IVaultIndex { - public StructuralMap Entries { get; } + public StructuralMap Entries { get; } + public MediaVaultType VaultType { get; } public VaultIndex(VaultIndexData indexData) : base(indexData.IndexKey) { - Entries = new StructuralMap(); + Entries = new StructuralMap(); + VaultType = indexData.VaultType; // Load entries from data foreach (var entry in indexData.Entries) { @@ -109,13 +122,13 @@ public class VaultIndex : IndexData, IVaultIndex public string GetKey() => IndexKey; - public IReadOnlyList GetEntries() => Entries.Keys.ToList().AsReadOnly(); + public IReadOnlyList GetEntries() => Entries.Keys.ToList().AsReadOnly(); public int GetEntriesSize() => Entries.Size; - public bool HasEntry(EntryKey entryKey) => Entries.Has(entryKey); + public bool HasEntry(string entryId) => Entries.Has(entryId); - public MetaData? GetEntry(EntryKey entryKey) => Entries.Get(entryKey); + public MetaData? GetEntry(string entryId) => Entries.Get(entryId); - public void PutEntry(EntryKey entryKey, MetaData metaData) => Entries.Set(entryKey, metaData); + public void PutEntry(string entryId, MetaData metaData) => Entries.Set(entryId, metaData); } diff --git a/DeepDrftContent/FileDatabase/Models/MediaFactories.cs b/DeepDrftContent/FileDatabase/Models/MediaFactories.cs index 674c622..ac51a8f 100644 --- a/DeepDrftContent/FileDatabase/Models/MediaFactories.cs +++ b/DeepDrftContent/FileDatabase/Models/MediaFactories.cs @@ -17,6 +17,16 @@ public static class MediaVaultTypeMap public static Type GetParamsType(MediaVaultType vaultType) => _registry.GetParamsType(vaultType); public static Type GetMetaDataType(MediaVaultType vaultType) => _registry.GetMetaDataType(vaultType); + + /// + /// Get the vault type for a binary type (reverse mapping) + /// + public static MediaVaultType GetVaultType(Type binaryType) => _registry.GetVaultType(binaryType); + + /// + /// Get the vault type for a binary type using generics (reverse mapping) + /// + public static MediaVaultType GetVaultType() where T : FileBinary => _registry.GetVaultType(); } /// diff --git a/DeepDrftContent/FileDatabase/README.md b/DeepDrftContent/FileDatabase/README.md index 26ca9b2..1d55970 100644 --- a/DeepDrftContent/FileDatabase/README.md +++ b/DeepDrftContent/FileDatabase/README.md @@ -10,7 +10,7 @@ The C# port preserves the original three-layer architecture: - **Location**: `Services/FileDatabase.cs` - **Purpose**: Root-level manager coordinating multiple media vaults - **Key Features**: - - Manages collection of `MediaVault` instances using `StructuralMap` + - Manages collection of `MediaVault` instances using `StructuralMap` - Provides async factory method `FromAsync()` for initialization - Handles vault creation, resource loading, and resource registration @@ -38,7 +38,7 @@ The C# port preserves the original three-layer architecture: ## Key Components ### Models (`Models/` directory) -- **`EntryKey`**: Composite key structure with string key + MediaVaultType +- **String-based keys**: Simple string identifiers for vault and entry management - **`MetaData` hierarchy**: Base metadata → `ImageMetaData` with aspect ratio - **Media Types**: `FileBinary` → `MediaBinary` → `ImageBinary` - **Factory Classes**: Type-safe creation of media objects and metadata @@ -58,7 +58,7 @@ The C# port preserves the original three-layer architecture: 5. **Dependency Inversion**: Abstract base classes and interfaces ### C# Language Features -- **Records**: Immutable data structures for `EntryKey`, `MetaData` +- **Records**: Immutable data structures for `MetaData` hierarchy - **Pattern Matching**: Switch expressions for type-safe factory methods - **Nullable Reference Types**: Explicit nullability handling - **Async/Await**: Full async support with `Task` and `ValueTask` @@ -76,17 +76,15 @@ The C# port preserves the original three-layer architecture: var database = await FileDatabase.FromAsync("/path/to/database"); // Create a vault -var vaultKey = new EntryKey("images", MediaVaultType.Image); -await database.CreateVaultAsync(vaultKey); +var vaultId = "images"; +await database.CreateVaultAsync(vaultId, MediaVaultType.Image); -// Store an image -var entryKey = new EntryKey("photo1", MediaVaultType.Image); +// Store an image (MediaVaultType inferred from ImageBinary) var imageData = new ImageBinary(new ImageBinaryParams(buffer, size, ".jpg", 1.5)); -await database.RegisterResourceAsync(MediaVaultType.Image, vaultKey, entryKey, imageData); +await database.RegisterResourceAsync("gallery", "photo1", imageData); -// Load an image -var loadedImage = await database.LoadResourceAsync( - MediaVaultType.Image, vaultKey, entryKey); +// Load an image (MediaVaultType inferred from ImageBinary generic type) +var loadedImage = await database.LoadResourceAsync("gallery", "photo1"); ``` ## Project Structure @@ -94,7 +92,7 @@ var loadedImage = await database.LoadResourceAsync( ``` FileDatabase/ ├── Models/ -│ ├── EntryKey.cs # Composite key structure +│ ├── [EntryKey removed] # Now using simple string keys │ ├── MetaData.cs # Metadata hierarchy │ ├── MediaModels.cs # Binary data classes │ ├── MediaFactories.cs # Factory pattern implementations diff --git a/DeepDrftContent/FileDatabase/Services/FileDatabase.cs b/DeepDrftContent/FileDatabase/Services/FileDatabase.cs index 8abcef1..4146967 100644 --- a/DeepDrftContent/FileDatabase/Services/FileDatabase.cs +++ b/DeepDrftContent/FileDatabase/Services/FileDatabase.cs @@ -1,5 +1,6 @@ using DeepDrftContent.FileDatabase.Models; using DeepDrftContent.FileDatabase.Utils; +using IndexType = DeepDrftContent.FileDatabase.Services.IndexType; namespace DeepDrftContent.FileDatabase.Services; @@ -8,19 +9,19 @@ namespace DeepDrftContent.FileDatabase.Services; /// public class FileDatabase : DirectoryIndexDirectory { - private readonly StructuralMap _vaults; + private readonly StructuralMap _vaults; /// /// Factory method to create a FileDatabase instance /// public static async Task FromAsync(string rootPath) { - var factory = new IndexFactory(rootPath, IndexType.Directory); - var rootIndex = await factory.BuildIndexAsync(); + var factoryService = new IndexFactoryService(); + var rootIndex = await factoryService.LoadOrCreateDirectoryIndexAsync(rootPath); - if (rootIndex is DirectoryIndex directoryIndex) + if (rootIndex != null) { - var db = new FileDatabase(rootPath, directoryIndex); + var db = new FileDatabase(rootPath, (DirectoryIndex)rootIndex); await db.InitVaultsAsync(); return db; } @@ -30,7 +31,7 @@ public class FileDatabase : DirectoryIndexDirectory private FileDatabase(string rootPath, DirectoryIndex index) : base(rootPath, index) { - _vaults = new StructuralMap(); + _vaults = new StructuralMap(); } /// @@ -38,56 +39,86 @@ public class FileDatabase : DirectoryIndexDirectory /// private async Task InitVaultsAsync() { - foreach (var vaultKey in GetIndexEntries()) + foreach (var vaultId in GetIndexEntries()) { - await InitVaultAsync(vaultKey); + var vaultType = await GetVaultTypeFromIndex(vaultId); + if (vaultType.HasValue) + { + await InitVaultAsync(vaultId, vaultType.Value); + } } } /// /// Initializes a specific vault /// - private async Task InitVaultAsync(EntryKey vaultKey) + private async Task InitVaultAsync(string vaultId, MediaVaultType vaultType) { - var path = Path.Combine(RootPath, vaultKey.Key); - var directoryVault = await MediaVaultFactory.From(path, vaultKey.Type); + var path = Path.Combine(RootPath, vaultId); + var directoryVault = await MediaVaultFactory.From(path, vaultType); if (directoryVault != null) { - _vaults.Set(vaultKey, directoryVault); + _vaults.Set(vaultId, directoryVault); } } /// - /// Checks if a vault exists for the given key + /// Gets vault type from the vault's index file /// - public bool HasVault(EntryKey vaultKey) + private async Task GetVaultTypeFromIndex(string vaultId) { - return _vaults.Has(vaultKey); + try + { + var factoryService = new IndexFactoryService(); + var vaultPath = Path.Combine(RootPath, vaultId); + var index = await factoryService.LoadIndexAsync(IndexType.Vault, vaultPath); + + if (index is VaultIndex vaultIndex) + { + return vaultIndex.VaultType; + } + } + catch + { + // If we can't load the index, we can't determine the vault type + // This might happen for legacy vaults or corrupted indexes + } + + return null; } /// - /// Gets a vault by key + /// Checks if a vault exists for the given vault ID /// - public MediaVault? GetVault(EntryKey vaultKey) + public bool HasVault(string vaultId) { - return HasVault(vaultKey) ? _vaults.Get(vaultKey) : null; + return _vaults.Has(vaultId); + } + + /// + /// Gets a vault by vault ID + /// + public MediaVault? GetVault(string vaultId) + { + return HasVault(vaultId) ? _vaults.Get(vaultId) : null; } /// /// Creates a new vault /// - public async Task CreateVaultAsync(EntryKey vaultKey) + public async Task CreateVaultAsync(string vaultId, MediaVaultType vaultType) { try { - var path = Path.Combine(RootPath, vaultKey.Key); - var directoryVault = await MediaVaultFactory.From(path, vaultKey.Type); + var path = Path.Combine(RootPath, vaultId); + var directoryVault = await MediaVaultFactory.From(path, vaultType); if (directoryVault != null) { - _vaults.Set(vaultKey, directoryVault); - await AddToIndexAsync(vaultKey); + _vaults.Set(vaultId, directoryVault); + // Now using string-based index + await AddToIndexAsync(vaultId); } } catch @@ -97,17 +128,17 @@ public class FileDatabase : DirectoryIndexDirectory } /// - /// Loads a resource from a specific vault + /// Loads a resource from a specific vault (MediaVaultType inferred from T) /// - public async Task LoadResourceAsync(MediaVaultType vaultType, EntryKey vaultKey, EntryKey entryKey) + public async Task LoadResourceAsync(string vaultId, string entryId) where T : FileBinary { try { - var vault = _vaults.Get(vaultKey); + var vault = _vaults.Get(vaultId); if (vault != null) { - return await vault.GetEntryAsync(vaultType, entryKey); + return await vault.GetEntryAsync(entryId); } } catch @@ -119,16 +150,16 @@ public class FileDatabase : DirectoryIndexDirectory } /// - /// Registers a resource in a specific vault + /// Registers a resource in a specific vault (MediaVaultType inferred from media type) /// - public async Task RegisterResourceAsync(MediaVaultType vaultType, EntryKey vaultKey, EntryKey entryKey, object media) + public async Task RegisterResourceAsync(string vaultId, string entryId, FileBinary media) { try { - var directoryVault = _vaults.Get(vaultKey); + var directoryVault = _vaults.Get(vaultId); if (directoryVault != null) { - await directoryVault.AddEntryAsync(vaultType, entryKey, media); + await directoryVault.AddEntryAsync(entryId, media); return true; } } @@ -141,9 +172,9 @@ public class FileDatabase : DirectoryIndexDirectory } /// - /// Gets all vault keys managed by this database + /// Gets all vault IDs managed by this database /// - public IReadOnlyList GetVaultKeys() + public IReadOnlyList GetVaultIds() { return _vaults.Keys.ToList().AsReadOnly(); } @@ -155,4 +186,5 @@ public class FileDatabase : DirectoryIndexDirectory { return _vaults.Size; } + } diff --git a/DeepDrftContent/FileDatabase/Services/IndexFactoryService.cs b/DeepDrftContent/FileDatabase/Services/IndexFactoryService.cs index c3d07bb..9e16bb7 100644 --- a/DeepDrftContent/FileDatabase/Services/IndexFactoryService.cs +++ b/DeepDrftContent/FileDatabase/Services/IndexFactoryService.cs @@ -10,18 +10,11 @@ namespace DeepDrftContent.FileDatabase.Services; /// public class IndexFactoryService : IIndexFactory, IIndexDataFactory { - private readonly Dictionary> _indexCreators; private readonly Dictionary> _indexFromDataCreators; private readonly Dictionary> _indexDataCreators; public IndexFactoryService() { - _indexCreators = new Dictionary> - { - { IndexType.Directory, rootPath => new DirectoryIndex(new DirectoryIndexData(Path.GetFileName(rootPath))) }, - { IndexType.Vault, rootPath => new VaultIndex(new VaultIndexData(Path.GetFileName(rootPath))) } - }; - _indexFromDataCreators = new Dictionary> { { IndexType.Directory, data => new DirectoryIndex((DirectoryIndexData)data) }, @@ -35,18 +28,14 @@ public class IndexFactoryService : IIndexFactory, IIndexDataFactory }; } - public async Task CreateIndexAsync(IndexType type, string rootPath) + public async Task CreateDirectoryIndexAsync(string rootPath) { - if (!_indexCreators.TryGetValue(type, out var creator)) - { - throw new ArgumentException($"Unknown index type: {type}"); - } - - var index = creator(rootPath); + var indexData = new DirectoryIndexData(Path.GetFileName(rootPath)); + var index = new DirectoryIndex(indexData); // Ensure directory exists and save the index await FileUtils.MakeVaultDirectoryAsync(rootPath); - await SaveIndexAsync(rootPath, type, index); + await SaveIndexAsync(rootPath, IndexType.Directory, index); return index; } @@ -70,15 +59,41 @@ public class IndexFactoryService : IIndexFactory, IIndexDataFactory return creator(indexData); } - public async Task LoadOrCreateIndexAsync(IndexType type, string rootPath) + public async Task LoadOrCreateDirectoryIndexAsync(string rootPath) { try { - return await LoadIndexAsync(type, rootPath); + var index = await LoadIndexAsync(IndexType.Directory, rootPath); + return index as IDirectoryIndex; } catch { - return await CreateIndexAsync(type, rootPath); + return await CreateDirectoryIndexAsync(rootPath); + } + } + + public async Task CreateVaultIndexAsync(string rootPath, MediaVaultType vaultType) + { + var vaultIndexData = new VaultIndexData(Path.GetFileName(rootPath), vaultType); + var index = new VaultIndex(vaultIndexData); + + // Ensure directory exists and save the index + await FileUtils.MakeVaultDirectoryAsync(rootPath); + await SaveIndexAsync(rootPath, IndexType.Vault, index); + + return index; + } + + public async Task LoadOrCreateVaultIndexAsync(string rootPath, MediaVaultType vaultType) + { + try + { + var index = await LoadIndexAsync(IndexType.Vault, rootPath); + return index as IVaultIndex; + } + catch + { + return await CreateVaultIndexAsync(rootPath, vaultType); } } diff --git a/DeepDrftContent/FileDatabase/Services/IndexSystem.cs b/DeepDrftContent/FileDatabase/Services/IndexSystem.cs index 1b0401c..43b2ad7 100644 --- a/DeepDrftContent/FileDatabase/Services/IndexSystem.cs +++ b/DeepDrftContent/FileDatabase/Services/IndexSystem.cs @@ -39,27 +39,6 @@ public abstract class AbstractIndexContainer } } -/// -/// Factory for creating and loading indexes - delegates to IIndexFactory -/// -public class IndexFactory : AbstractIndexContainer -{ - private readonly IIndexFactory _factoryService; - - public IndexFactory(string path, IndexType type, IIndexFactory? factoryService = null, IIndexDataFactory? indexDataFactory = null) - : base(path, type, indexDataFactory) - { - _factoryService = factoryService ?? new IndexFactoryService(); - } - - /// - /// Builds an index by loading existing or creating new - /// - public async Task BuildIndexAsync() - { - return await _factoryService.LoadOrCreateIndexAsync(Type, RootPath); - } -} /// /// Abstract base class for directory containers that manage indexes @@ -74,11 +53,11 @@ public abstract class IndexDirectory : AbstractIndexContainer Index = index; } - protected IReadOnlyList GetIndexEntries() => Index.GetEntries(); + protected IReadOnlyList GetIndexEntries() => Index.GetEntries(); public int GetIndexSize() => Index.GetEntriesSize(); - public bool HasIndexEntry(EntryKey entryKey) => Index.HasEntry(entryKey); + public bool HasIndexEntry(string entryId) => Index.HasEntry(entryId); } /// @@ -94,9 +73,9 @@ public class DirectoryIndexDirectory : IndexDirectory _directoryIndex = index; } - protected async Task AddToIndexAsync(EntryKey entryKey) + protected async Task AddToIndexAsync(string entryId) { - _directoryIndex.PutEntry(entryKey); + _directoryIndex.PutEntry(entryId); await SaveIndexAsync(_directoryIndex); } } @@ -114,9 +93,9 @@ public class VaultIndexDirectory : IndexDirectory _vaultIndex = index; } - protected async Task AddToIndexAsync(EntryKey entryKey, MetaData metaData) + protected async Task AddToIndexAsync(string entryId, MetaData metaData) { - _vaultIndex.PutEntry(entryKey, metaData); + _vaultIndex.PutEntry(entryId, metaData); await SaveIndexAsync(_vaultIndex); } } diff --git a/DeepDrftContent/FileDatabase/Services/MediaVault.cs b/DeepDrftContent/FileDatabase/Services/MediaVault.cs index 7b52f55..abbdbaa 100644 --- a/DeepDrftContent/FileDatabase/Services/MediaVault.cs +++ b/DeepDrftContent/FileDatabase/Services/MediaVault.cs @@ -37,32 +37,39 @@ public abstract class MediaVault : VaultIndexDirectory } /// - /// Adds a new entry to the vault with the specified media data + /// Adds a new entry to the vault with the specified media data (MediaVaultType inferred from media type) /// - public async Task AddEntryAsync(MediaVaultType vaultType, EntryKey entryKey, object media) + public async Task AddEntryAsync(string entryId, FileBinary media) { // Extract properties from media object based on type var (buffer, extension) = ExtractMediaProperties(media); - var mediaPath = GetMediaPathFromEntryKey(entryKey.Key, extension); - var metaData = MetaDataFactory.CreateFromMedia(vaultType, entryKey.Key, extension, media); + // Infer MediaVaultType from the media object type + var vaultType = MediaVaultTypeMap.GetVaultType(media.GetType()); - await AddToIndexAsync(entryKey, metaData); + var mediaPath = GetMediaPathFromEntryKey(entryId, extension); + var metaData = MetaDataFactory.CreateFromMedia(vaultType, entryId, extension, media); + + // Use string-based index operations + await AddToIndexAsync(entryId, metaData); await FileUtils.PutFileAsync(mediaPath, buffer); } /// - /// Retrieves an entry from the vault + /// Retrieves an entry from the vault (MediaVaultType inferred from T) /// - public async Task GetEntryAsync(MediaVaultType vaultType, EntryKey entryKey) where T : FileBinary + public async Task GetEntryAsync(string entryId) where T : FileBinary { - if (!HasIndexEntry(entryKey)) + // Infer MediaVaultType from the generic type T + var vaultType = MediaVaultTypeMap.GetVaultType(); + + if (!HasIndexEntry(entryId)) return null; if (Index is not VaultIndex vaultIndex) return null; - var metaData = vaultIndex.GetEntry(entryKey); + var metaData = vaultIndex.GetEntry(entryId); if (metaData == null) return null; @@ -79,15 +86,16 @@ public abstract class MediaVault : VaultIndexDirectory } /// - /// Extracts buffer and extension from a media object + /// Extracts buffer and extension from a media binary /// - private static (byte[] buffer, string extension) ExtractMediaProperties(object media) + private static (byte[] buffer, string extension) ExtractMediaProperties(FileBinary media) { return media switch { ImageBinary imageBinary => (imageBinary.Buffer, imageBinary.Extension), AudioBinary audioBinary => (audioBinary.Buffer, audioBinary.Extension), MediaBinary mediaBinary => (mediaBinary.Buffer, mediaBinary.Extension), + FileBinary fileBinary => throw new ArgumentException($"FileBinary must be a specific media type (ImageBinary, AudioBinary, or MediaBinary), not base FileBinary"), _ => throw new ArgumentException($"Unsupported media type: {media.GetType()}") }; } @@ -105,12 +113,12 @@ public class ImageVault : MediaVault /// public static async Task FromAsync(string rootPath) { - var factory = new IndexFactory(rootPath, IndexType.Vault); - var index = await factory.BuildIndexAsync(); + var factoryService = new IndexFactoryService(); + var index = await factoryService.LoadOrCreateVaultIndexAsync(rootPath, MediaVaultType.Image); - if (index is VaultIndex vaultIndex) + if (index != null) { - return new ImageVault(rootPath, vaultIndex); + return new ImageVault(rootPath, (VaultIndex)index); } return null; @@ -123,12 +131,12 @@ public class AudioVault : MediaVault public static async Task FromAsync(string rootPath) { - var factory = new IndexFactory(rootPath, IndexType.Vault); - var index = await factory.BuildIndexAsync(); + var factoryService = new IndexFactoryService(); + var index = await factoryService.LoadOrCreateVaultIndexAsync(rootPath, MediaVaultType.Audio); - if (index is VaultIndex vaultIndex) + if (index != null) { - return new AudioVault(rootPath, vaultIndex); + return new AudioVault(rootPath, (VaultIndex)index); } return null; diff --git a/DeepDrftContent/FileDatabase/Services/SimpleMediaTypeRegistry.cs b/DeepDrftContent/FileDatabase/Services/SimpleMediaTypeRegistry.cs index 1413021..585592f 100644 --- a/DeepDrftContent/FileDatabase/Services/SimpleMediaTypeRegistry.cs +++ b/DeepDrftContent/FileDatabase/Services/SimpleMediaTypeRegistry.cs @@ -18,6 +18,9 @@ public class SimpleMediaTypeRegistry : IMediaTypeRegistry private readonly Dictionary _dtoTypes = new(); private readonly Dictionary _paramsTypes = new(); private readonly Dictionary _metaDataTypes = new(); + + // Reverse mapping: Type -> MediaVaultType + private readonly Dictionary _typeToVaultType = new(); public SimpleMediaTypeRegistry() { @@ -76,6 +79,9 @@ public class SimpleMediaTypeRegistry : IMediaTypeRegistry _paramsTypes[vaultType] = typeof(TParams); _metaDataTypes[vaultType] = typeof(TMetaData); + // Populate reverse mapping + _typeToVaultType[typeof(TBinary)] = vaultType; + if (vaultFactory != null) _vaultFactories[vaultType] = vaultFactory; } @@ -146,4 +152,22 @@ public class SimpleMediaTypeRegistry : IMediaTypeRegistry public Type GetMetaDataType(MediaVaultType vaultType) => _metaDataTypes.TryGetValue(vaultType, out var type) ? type : throw new ArgumentException($"Unknown vault type: {vaultType}"); + + public MediaVaultType GetVaultType(Type binaryType) + { + if (_typeToVaultType.TryGetValue(binaryType, out var vaultType)) + return vaultType; + + // Check inheritance hierarchy for derived types + foreach (var kvp in _typeToVaultType) + { + if (kvp.Key.IsAssignableFrom(binaryType)) + return kvp.Value; + } + + throw new ArgumentException($"Cannot infer MediaVaultType for {binaryType.Name}. Type not registered."); + } + + public MediaVaultType GetVaultType() where T : FileBinary + => GetVaultType(typeof(T)); } diff --git a/DeepDrftContent/Startup.cs b/DeepDrftContent/Startup.cs index 17dbc8f..1f7f93e 100644 --- a/DeepDrftContent/Startup.cs +++ b/DeepDrftContent/Startup.cs @@ -21,10 +21,10 @@ namespace DeepDrftContent private static async Task InitializeTrackVault(FileDatabase.Services.FileDatabase fileDatabase) { - var vaultKey = new EntryKey("tracks", MediaVaultType.Audio); - if (!fileDatabase.HasVault(vaultKey)) + const string vaultId = "tracks"; + if (!fileDatabase.HasVault(vaultId)) { - await fileDatabase.CreateVaultAsync(vaultKey); + await fileDatabase.CreateVaultAsync(vaultId, MediaVaultType.Audio); } } } diff --git a/DeepDrftTests/FileDatabaseTests.cs b/DeepDrftTests/FileDatabaseTests.cs index 0a2c85e..d753e44 100644 --- a/DeepDrftTests/FileDatabaseTests.cs +++ b/DeepDrftTests/FileDatabaseTests.cs @@ -64,12 +64,12 @@ public class FileDatabaseTests Assert.That(_fileDatabase, Is.Not.Null); // Act - await _fileDatabase.CreateVaultAsync(TestData.TestKeys.ImageVaultKey); + await _fileDatabase.CreateVaultAsync(TestData.TestKeys.ImageVaultKey, TestData.TestKeys.ImageVaultType); // Assert Assert.That(_fileDatabase.GetIndexSize(), Is.EqualTo(1), "Index should contain one element"); - var vaultDirectory = Path.Combine(_testDatabasePath, TestData.TestKeys.ImageVaultKey.Key); + var vaultDirectory = Path.Combine(_testDatabasePath, TestData.TestKeys.ImageVaultKey); Assert.That(Directory.Exists(vaultDirectory), Is.True, "Vault directory should exist"); } @@ -80,12 +80,11 @@ public class FileDatabaseTests _fileDatabase = await FileDatabase.FromAsync(_testDatabasePath); Assert.That(_fileDatabase, Is.Not.Null); - await _fileDatabase.CreateVaultAsync(TestData.TestKeys.ImageVaultKey); + await _fileDatabase.CreateVaultAsync(TestData.TestKeys.ImageVaultKey, TestData.TestKeys.ImageVaultType); var testImage = TestData.CreateTestImageBinary(1.0); // Act await _fileDatabase.RegisterResourceAsync( - MediaVaultType.Image, TestData.TestKeys.ImageVaultKey, TestData.TestKeys.TestImageEntry, testImage); @@ -104,18 +103,16 @@ public class FileDatabaseTests _fileDatabase = await FileDatabase.FromAsync(_testDatabasePath); Assert.That(_fileDatabase, Is.Not.Null); - await _fileDatabase.CreateVaultAsync(TestData.TestKeys.ImageVaultKey); + await _fileDatabase.CreateVaultAsync(TestData.TestKeys.ImageVaultKey, TestData.TestKeys.ImageVaultType); var testImage = TestData.CreateTestImageBinary(1.0); await _fileDatabase.RegisterResourceAsync( - MediaVaultType.Image, TestData.TestKeys.ImageVaultKey, TestData.TestKeys.TestImageEntry, testImage); // Act var loadedMedia = await _fileDatabase.LoadResourceAsync( - MediaVaultType.Image, TestData.TestKeys.ImageVaultKey, TestData.TestKeys.TestImageEntry); @@ -139,7 +136,6 @@ public class FileDatabaseTests Assert.DoesNotThrowAsync(async () => { await _fileDatabase.LoadResourceAsync( - MediaVaultType.Image, TestData.TestKeys.NonExistentVaultKey, TestData.TestKeys.NonExistentEntryKey); }, "Should not throw exceptions when accessing nonexistent vault"); @@ -152,13 +148,12 @@ public class FileDatabaseTests _fileDatabase = await FileDatabase.FromAsync(_testDatabasePath); Assert.That(_fileDatabase, Is.Not.Null); - await _fileDatabase.CreateVaultAsync(TestData.TestKeys.ImageVaultKey); + await _fileDatabase.CreateVaultAsync(TestData.TestKeys.ImageVaultKey, TestData.TestKeys.ImageVaultType); // Act & Assert - Should not throw exception when accessing nonexistent resource Assert.DoesNotThrowAsync(async () => { await _fileDatabase.LoadResourceAsync( - MediaVaultType.Image, TestData.TestKeys.ImageVaultKey, TestData.TestKeys.NonExistentEntryKey); }, "Should not throw exceptions when accessing nonexistent resource"); @@ -171,11 +166,10 @@ public class FileDatabaseTests _fileDatabase = await FileDatabase.FromAsync(_testDatabasePath); Assert.That(_fileDatabase, Is.Not.Null); - await _fileDatabase.CreateVaultAsync(TestData.TestKeys.ImageVaultKey); + await _fileDatabase.CreateVaultAsync(TestData.TestKeys.ImageVaultKey, TestData.TestKeys.ImageVaultType); var testImage = TestData.CreateTestImageBinary(1.0); await _fileDatabase.RegisterResourceAsync( - MediaVaultType.Image, TestData.TestKeys.ImageVaultKey, TestData.TestKeys.TestImageEntry, testImage); @@ -198,7 +192,6 @@ public class FileDatabaseTests // Verify resource can be loaded var loadedMedia = await reloadedDatabase.LoadResourceAsync( - MediaVaultType.Image, TestData.TestKeys.ImageVaultKey, TestData.TestKeys.TestImageEntry); diff --git a/DeepDrftTests/IndexSystemTests.cs b/DeepDrftTests/IndexSystemTests.cs index 800095a..9ed8d1c 100644 --- a/DeepDrftTests/IndexSystemTests.cs +++ b/DeepDrftTests/IndexSystemTests.cs @@ -38,10 +38,10 @@ public class IndexSystemTests } /// - /// Helper method to create test entry keys - DRY principle + /// Helper method to create test entry IDs - DRY principle /// - protected static EntryKey CreateTestEntryKey(string key, MediaVaultType type = MediaVaultType.Image) - => new(key, type); + protected static string CreateTestEntryId(string key) + => key; /// /// Helper method to create test metadata - DRY principle @@ -69,7 +69,7 @@ public class IndexSystemTests public async Task CreateIndexAsync_DirectoryType_CreatesDirectoryIndex() { // Act - var index = await _factory.CreateIndexAsync(IndexType.Directory, TestDirectory); + var index = await _factory.CreateDirectoryIndexAsync(TestDirectory); // Assert Assert.That(index, Is.Not.Null, "Index should be created"); @@ -81,7 +81,7 @@ public class IndexSystemTests public async Task CreateIndexAsync_VaultType_CreatesVaultIndex() { // Act - var index = await _factory.CreateIndexAsync(IndexType.Vault, TestDirectory); + var index = await _factory.CreateVaultIndexAsync(TestDirectory, MediaVaultType.Media); // Assert Assert.That(index, Is.Not.Null, "Index should be created"); @@ -89,23 +89,12 @@ public class IndexSystemTests Assert.That(File.Exists(IndexPath), Is.True, "Index file should be created"); } - [Test] - public void CreateIndexAsync_InvalidType_ThrowsArgumentException() - { - // Arrange - var invalidType = (IndexType)999; - - // Act & Assert - Assert.ThrowsAsync(async () => - await _factory.CreateIndexAsync(invalidType, TestDirectory), - "Should throw for invalid index type"); - } [Test] public async Task LoadIndexAsync_ExistingDirectoryIndex_LoadsSuccessfully() { // Arrange - Create an index first - await _factory.CreateIndexAsync(IndexType.Directory, TestDirectory); + await _factory.CreateDirectoryIndexAsync(TestDirectory); // Act var loadedIndex = await _factory.LoadIndexAsync(IndexType.Directory, TestDirectory); @@ -119,7 +108,7 @@ public class IndexSystemTests public async Task LoadIndexAsync_ExistingVaultIndex_LoadsSuccessfully() { // Arrange - Create an index first - await _factory.CreateIndexAsync(IndexType.Vault, TestDirectory); + await _factory.CreateVaultIndexAsync(TestDirectory, MediaVaultType.Media); // Act var loadedIndex = await _factory.LoadIndexAsync(IndexType.Vault, TestDirectory); @@ -142,11 +131,11 @@ public class IndexSystemTests public async Task LoadOrCreateIndexAsync_ExistingIndex_LoadsExisting() { // Arrange - Create an index with data - var originalIndex = await _factory.CreateIndexAsync(IndexType.Directory, TestDirectory); + var originalIndex = await _factory.CreateDirectoryIndexAsync(TestDirectory); Assert.That(originalIndex, Is.TypeOf(), "Should create DirectoryIndex"); var directoryIndex = (DirectoryIndex)originalIndex!; - var testKey = CreateTestEntryKey("test-entry"); + var testKey = CreateTestEntryId("test-entry"); directoryIndex.PutEntry(testKey); // Save the modified index @@ -154,30 +143,28 @@ public class IndexSystemTests await FileUtils.PutObjectAsync(IndexPath, indexData); // Act - var loadedIndex = await _factory.LoadOrCreateIndexAsync(IndexType.Directory, TestDirectory); + 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"); - var queryableIndex = (IEntryQueryable)loadedIndex!; - Assert.That(queryableIndex.GetEntriesSize(), Is.EqualTo(1), "Should preserve existing entries"); + Assert.That(loadedIndex.GetEntriesSize(), Is.EqualTo(1), "Should preserve existing entries"); } [Test] public async Task LoadOrCreateIndexAsync_NonExistentIndex_CreatesNew() { // Act - var index = await _factory.LoadOrCreateIndexAsync(IndexType.Directory, TestDirectory); + 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"); - var queryableIndex = (IEntryQueryable)index!; - Assert.That(queryableIndex.GetEntriesSize(), Is.EqualTo(0), "New index should be empty"); + Assert.That(index.GetEntriesSize(), Is.EqualTo(0), "New index should be empty"); } [Test] @@ -185,7 +172,7 @@ public class IndexSystemTests { // Arrange var directoryIndex = new DirectoryIndex(new DirectoryIndexData("test")); - var testKey = CreateTestEntryKey("test-entry"); + var testKey = CreateTestEntryId("test-entry"); directoryIndex.PutEntry(testKey); // Act @@ -202,7 +189,7 @@ public class IndexSystemTests { // Arrange var vaultIndex = new VaultIndex(new VaultIndexData("test")); - var testKey = CreateTestEntryKey("test-entry"); + var testKey = CreateTestEntryId("test-entry"); var testMetaData = CreateTestMetaData("test-entry"); vaultIndex.PutEntry(testKey, testMetaData); @@ -273,7 +260,7 @@ public class IndexSystemTests public void PutEntry_NewEntry_AddsSuccessfully() { // Arrange - var testKey = CreateTestEntryKey("new-entry"); + var testKey = CreateTestEntryId("new-entry"); // Act _directoryIndex.PutEntry(testKey); @@ -288,7 +275,7 @@ public class IndexSystemTests public void PutEntry_DuplicateEntry_DoesNotDuplicate() { // Arrange - var testKey = CreateTestEntryKey("duplicate-entry"); + var testKey = CreateTestEntryId("duplicate-entry"); _directoryIndex.PutEntry(testKey); // Act @@ -304,9 +291,9 @@ public class IndexSystemTests // Arrange var keys = new[] { - CreateTestEntryKey("entry1"), - CreateTestEntryKey("entry2"), - CreateTestEntryKey("entry3") + CreateTestEntryId("entry1"), + CreateTestEntryId("entry2"), + CreateTestEntryId("entry3") }; // Act @@ -327,7 +314,7 @@ public class IndexSystemTests public void HasEntry_ExistingEntry_ReturnsTrue() { // Arrange - var testKey = CreateTestEntryKey("existing-entry"); + var testKey = CreateTestEntryId("existing-entry"); _directoryIndex.PutEntry(testKey); // Act & Assert @@ -338,7 +325,7 @@ public class IndexSystemTests public void HasEntry_NonExistentEntry_ReturnsFalse() { // Arrange - var testKey = CreateTestEntryKey("non-existent"); + var testKey = CreateTestEntryId("non-existent"); // Act & Assert Assert.That(_directoryIndex.HasEntry(testKey), Is.False, "Should not find non-existent entry"); @@ -350,9 +337,9 @@ public class IndexSystemTests // Arrange var keys = new[] { - CreateTestEntryKey("entry1"), - CreateTestEntryKey("entry2"), - CreateTestEntryKey("entry3") + CreateTestEntryId("entry1"), + CreateTestEntryId("entry2"), + CreateTestEntryId("entry3") }; foreach (var key in keys) @@ -399,7 +386,7 @@ public class IndexSystemTests public void PutEntry_NewEntryWithMetadata_AddsSuccessfully() { // Arrange - var testKey = CreateTestEntryKey("new-entry"); + var testKey = CreateTestEntryId("new-entry"); var testMetaData = CreateTestMetaData("new-entry"); // Act @@ -415,7 +402,7 @@ public class IndexSystemTests public void PutEntry_DuplicateEntry_UpdatesMetadata() { // Arrange - var testKey = CreateTestEntryKey("duplicate-entry"); + var testKey = CreateTestEntryId("duplicate-entry"); var originalMetaData = CreateTestMetaData("original"); var updatedMetaData = CreateTestMetaData("updated"); @@ -433,7 +420,7 @@ public class IndexSystemTests public void GetEntry_ExistingEntry_ReturnsMetadata() { // Arrange - var testKey = CreateTestEntryKey("existing-entry"); + var testKey = CreateTestEntryId("existing-entry"); var testMetaData = CreateTestMetaData("existing-entry"); _vaultIndex.PutEntry(testKey, testMetaData); @@ -448,7 +435,7 @@ public class IndexSystemTests public void GetEntry_NonExistentEntry_ReturnsNull() { // Arrange - var testKey = CreateTestEntryKey("non-existent"); + var testKey = CreateTestEntryId("non-existent"); // Act var retrievedMetaData = _vaultIndex.GetEntry(testKey); @@ -463,9 +450,9 @@ public class IndexSystemTests // Arrange var entries = new[] { - (CreateTestEntryKey("entry1"), CreateTestMetaData("entry1", ".png")), - (CreateTestEntryKey("entry2"), CreateTestMetaData("entry2", ".jpg")), - (CreateTestEntryKey("entry3"), CreateTestMetaData("entry3", ".gif")) + (CreateTestEntryId("entry1"), CreateTestMetaData("entry1", ".png")), + (CreateTestEntryId("entry2"), CreateTestMetaData("entry2", ".jpg")), + (CreateTestEntryId("entry3"), CreateTestMetaData("entry3", ".gif")) }; // Act @@ -484,70 +471,6 @@ public class IndexSystemTests } } - /// - /// Tests for IndexFactory - Single Responsibility Principle - /// - [TestFixture] - public class IndexFactoryTests : IndexTestBase - { - [Test] - public async Task IndexFactory_DirectoryType_BuildsDirectoryIndex() - { - // Arrange - var factory = new IndexFactory(TestDirectory, IndexType.Directory); - - // Act - var index = await factory.BuildIndexAsync(); - - // Assert - Assert.That(index, Is.Not.Null, "Index should be built"); - Assert.That(index, Is.TypeOf(), "Should build DirectoryIndex"); - Assert.That(File.Exists(IndexPath), Is.True, "Index file should be created"); - } - - [Test] - public async Task IndexFactory_VaultType_BuildsVaultIndex() - { - // Arrange - var factory = new IndexFactory(TestDirectory, IndexType.Vault); - - // Act - var index = await factory.BuildIndexAsync(); - - // Assert - Assert.That(index, Is.Not.Null, "Index should be built"); - Assert.That(index, Is.TypeOf(), "Should build VaultIndex"); - Assert.That(File.Exists(IndexPath), Is.True, "Index file should be created"); - } - - [Test] - public async Task IndexFactory_ExistingIndex_LoadsExistingData() - { - // Arrange - Create index with data - var factory = new IndexFactory(TestDirectory, IndexType.Directory); - var originalIndex = await factory.BuildIndexAsync(); - var directoryIndex = (DirectoryIndex)originalIndex!; - var testKey = CreateTestEntryKey("persisted-entry"); - directoryIndex.PutEntry(testKey); - - // Save the index manually - var factoryService = new IndexFactoryService(); - var indexData = factoryService.CreateIndexData(IndexType.Directory, directoryIndex); - await FileUtils.PutObjectAsync(IndexPath, indexData); - - // Act - Create new factory and build - var newFactory = new IndexFactory(TestDirectory, IndexType.Directory); - var loadedIndex = await newFactory.BuildIndexAsync(); - - // Assert - Assert.That(loadedIndex, Is.Not.Null, "Index should be loaded"); - Assert.That(loadedIndex, Is.InstanceOf(), "Should implement IEntryQueryable"); - - var queryableIndex = (IEntryQueryable)loadedIndex!; - Assert.That(queryableIndex.GetEntriesSize(), Is.EqualTo(1), "Should load existing entry"); - Assert.That(queryableIndex.HasEntry(testKey), Is.True, "Should contain persisted entry"); - } - } /// /// Integration tests for IndexDirectory classes - Open/Closed Principle @@ -563,10 +486,10 @@ public class IndexSystemTests // Arrange var database = await FileDatabase.FromAsync(TestDirectory); - var testVaultKey = CreateTestEntryKey("test-vault"); + var testVaultKey = CreateTestEntryId("test-vault"); // Act - This internally uses DirectoryIndexDirectory.AddToIndexAsync - await database!.CreateVaultAsync(testVaultKey); + await database!.CreateVaultAsync(testVaultKey, MediaVaultType.Image); // Assert Assert.That(database.HasIndexEntry(testVaultKey), Is.True, "Should contain added entry"); @@ -586,11 +509,11 @@ public class IndexSystemTests // Arrange var vault = await ImageVault.FromAsync(TestDirectory); - var testKey = CreateTestEntryKey("test-entry"); + var testKey = CreateTestEntryId("test-entry"); var testImage = TestData.CreateTestImageBinary(1.0); // Act - This internally uses VaultIndexDirectory.AddToIndexAsync - await vault!.AddEntryAsync(MediaVaultType.Image, testKey, testImage); + await vault!.AddEntryAsync(testKey, testImage); // Assert Assert.That(vault.HasIndexEntry(testKey), Is.True, "Should contain added entry"); diff --git a/DeepDrftTests/MediaVaultTests.cs b/DeepDrftTests/MediaVaultTests.cs index 137c345..331ac1f 100644 --- a/DeepDrftTests/MediaVaultTests.cs +++ b/DeepDrftTests/MediaVaultTests.cs @@ -38,10 +38,10 @@ public class MediaVaultTests } /// - /// Helper method to create test entry keys - DRY principle + /// Helper method to create test entry IDs - DRY principle /// - protected static EntryKey CreateTestEntryKey(string key, MediaVaultType type = MediaVaultType.Image) - => new(key, type); + protected static string CreateTestEntryId(string key) + => key; /// /// Helper method to create test media files - DRY principle @@ -113,11 +113,11 @@ public class MediaVaultTests public async Task AddEntryAsync_ImageBinary_AddsToIndexAndCreatesFile() { // Arrange - var entryKey = CreateTestEntryKey("test-image"); + var entryKey = CreateTestEntryId("test-image"); var imageBinary = TestData.CreateTestImageBinary(1.5); // Act - await _imageVault.AddEntryAsync(MediaVaultType.Image, entryKey, imageBinary); + await _imageVault.AddEntryAsync(entryKey, imageBinary); // Assert Assert.That(_imageVault.HasIndexEntry(entryKey), Is.True, "Should add to index"); @@ -132,15 +132,15 @@ public class MediaVaultTests // Arrange var entries = new[] { - (CreateTestEntryKey("image1"), TestData.CreateTestImageBinary(1.0)), - (CreateTestEntryKey("image2"), TestData.CreateTestImageBinary(1.5)), - (CreateTestEntryKey("image3"), TestData.CreateTestImageBinary(2.0)) + (CreateTestEntryId("image1"), TestData.CreateTestImageBinary(1.0)), + (CreateTestEntryId("image2"), TestData.CreateTestImageBinary(1.5)), + (CreateTestEntryId("image3"), TestData.CreateTestImageBinary(2.0)) }; // Act foreach (var (key, binary) in entries) { - await _imageVault.AddEntryAsync(MediaVaultType.Image, key, binary); + await _imageVault.AddEntryAsync(key, binary); } // Assert @@ -149,7 +149,7 @@ public class MediaVaultTests foreach (var (key, binary) in entries) { Assert.That(_imageVault.HasIndexEntry(key), Is.True, $"Should contain {key} in index"); - var expectedFilePath = Path.Combine(TestDirectory, $"{key.Key}.png"); + var expectedFilePath = Path.Combine(TestDirectory, $"{key}.png"); AssertMediaFileExists(expectedFilePath, binary.Buffer); } } @@ -158,12 +158,12 @@ public class MediaVaultTests public async Task GetEntryAsync_ExistingImage_ReturnsImageBinary() { // Arrange - var entryKey = CreateTestEntryKey("existing-image"); + var entryKey = CreateTestEntryId("existing-image"); var originalImage = TestData.CreateTestImageBinary(1.77); - await _imageVault.AddEntryAsync(MediaVaultType.Image, entryKey, originalImage); + await _imageVault.AddEntryAsync(entryKey, originalImage); // Act - var retrievedImage = await _imageVault.GetEntryAsync(MediaVaultType.Image, entryKey); + var retrievedImage = await _imageVault.GetEntryAsync(entryKey); // Assert Assert.That(retrievedImage, Is.Not.Null, "Should retrieve image"); @@ -176,10 +176,10 @@ public class MediaVaultTests public async Task GetEntryAsync_NonExistentImage_ReturnsNull() { // Arrange - var nonExistentKey = CreateTestEntryKey("non-existent"); + var nonExistentKey = CreateTestEntryId("non-existent"); // Act - var retrievedImage = await _imageVault.GetEntryAsync(MediaVaultType.Image, nonExistentKey); + var retrievedImage = await _imageVault.GetEntryAsync(nonExistentKey); // Assert Assert.That(retrievedImage, Is.Null, "Should return null for non-existent image"); @@ -189,16 +189,16 @@ public class MediaVaultTests public async Task GetEntryAsync_IndexEntryExistsButFileDeleted_ReturnsNull() { // Arrange - var entryKey = CreateTestEntryKey("deleted-file"); + var entryKey = CreateTestEntryId("deleted-file"); var imageBinary = TestData.CreateTestImageBinary(1.0); - await _imageVault.AddEntryAsync(MediaVaultType.Image, entryKey, imageBinary); + await _imageVault.AddEntryAsync(entryKey, imageBinary); // Delete the physical file but leave index entry var filePath = Path.Combine(TestDirectory, "deleted-file.png"); File.Delete(filePath); // Act - var retrievedImage = await _imageVault.GetEntryAsync(MediaVaultType.Image, entryKey); + var retrievedImage = await _imageVault.GetEntryAsync(entryKey); // Assert Assert.That(retrievedImage, Is.Null, "Should return null when file is missing"); @@ -208,18 +208,18 @@ public class MediaVaultTests public async Task AddEntryAsync_DuplicateKey_UpdatesExistingEntry() { // Arrange - var entryKey = CreateTestEntryKey("duplicate-key"); + var entryKey = CreateTestEntryId("duplicate-key"); var originalImage = TestData.CreateTestImageBinary(1.0); var updatedImage = TestData.CreateTestImageBinary(2.0); // Act - await _imageVault.AddEntryAsync(MediaVaultType.Image, entryKey, originalImage); - await _imageVault.AddEntryAsync(MediaVaultType.Image, entryKey, updatedImage); + await _imageVault.AddEntryAsync(entryKey, originalImage); + await _imageVault.AddEntryAsync(entryKey, updatedImage); // Assert Assert.That(_imageVault.GetIndexSize(), Is.EqualTo(1), "Should still have only one entry"); - var retrievedImage = await _imageVault.GetEntryAsync(MediaVaultType.Image, entryKey); + var retrievedImage = await _imageVault.GetEntryAsync(entryKey); Assert.That(retrievedImage, Is.Not.Null, "Should retrieve updated image"); Assert.That(retrievedImage!.AspectRatio, Is.EqualTo(2.0), "Should have updated aspect ratio"); } @@ -257,11 +257,11 @@ public class MediaVaultTests public async Task AddEntryAsync_AudioBinary_AddsToIndexAndCreatesFile() { // Arrange - var entryKey = CreateTestEntryKey("test-audio", MediaVaultType.Audio); + var entryKey = CreateTestEntryId("test-audio"); var audioBinary = TestData.CreateTestAudioBinary(120.0, 320); // Act - await _audioVault.AddEntryAsync(MediaVaultType.Audio, entryKey, audioBinary); + await _audioVault.AddEntryAsync(entryKey, audioBinary); // Assert Assert.That(_audioVault.HasIndexEntry(entryKey), Is.True, "Should add to index"); @@ -274,12 +274,12 @@ public class MediaVaultTests public async Task GetEntryAsync_ExistingAudio_ReturnsAudioBinary() { // Arrange - var entryKey = CreateTestEntryKey("existing-audio", MediaVaultType.Audio); + var entryKey = CreateTestEntryId("existing-audio"); var originalAudio = TestData.CreateTestAudioBinary(180.5, 256); - await _audioVault.AddEntryAsync(MediaVaultType.Audio, entryKey, originalAudio); + await _audioVault.AddEntryAsync(entryKey, originalAudio); // Act - var retrievedAudio = await _audioVault.GetEntryAsync(MediaVaultType.Audio, entryKey); + var retrievedAudio = await _audioVault.GetEntryAsync(entryKey); // Assert Assert.That(retrievedAudio, Is.Not.Null, "Should retrieve audio"); @@ -337,9 +337,9 @@ public class MediaVaultTests return (string)method!.Invoke(_vault, new object[] { mediaKey })!; } - public bool HasIndexEntry(EntryKey entryKey) => _vault.HasIndexEntry(entryKey); - public Task AddEntryAsync(MediaVaultType vaultType, EntryKey entryKey, object media) => - _vault.AddEntryAsync(vaultType, entryKey, media); + public bool HasIndexEntry(string entryId) => _vault.HasIndexEntry(entryId); + public Task AddEntryAsync(string entryId, FileBinary media) => + _vault.AddEntryAsync(entryId, media); } [Test] @@ -394,19 +394,19 @@ public class MediaVaultTests } [Test] - public async Task AddEntryAsync_UnsupportedMediaType_ThrowsArgumentException() + public async Task AddEntryAsync_BaseFileBinary_ThrowsArgumentException() { // Arrange var vault = await TestMediaVaultWrapper.FromAsync(TestDirectory); Assert.That(vault, Is.Not.Null, "Vault should be created"); - var entryKey = CreateTestEntryKey("test"); - var unsupportedMedia = new object(); // Not a supported media type + var entryKey = CreateTestEntryId("test"); + var baseFileBinary = new FileBinary(new FileBinaryParams(TestData.TestPngBytes, TestData.TestPngBytes.Length)); // Base FileBinary, not a specific media type // Act & Assert Assert.ThrowsAsync(async () => - await vault!.AddEntryAsync(MediaVaultType.Image, entryKey, unsupportedMedia), - "Should throw for unsupported media type"); + await vault!.AddEntryAsync(entryKey, baseFileBinary), + "Should throw for base FileBinary type - must be specific media type"); } [Test] @@ -416,11 +416,11 @@ public class MediaVaultTests var vault = await TestMediaVaultWrapper.FromAsync(TestDirectory); Assert.That(vault, Is.Not.Null, "Vault should be created"); - var entryKey = CreateTestEntryKey("test-media"); + var entryKey = CreateTestEntryId("test-media"); var imageBinary = TestData.CreateTestImageBinary(1.0); // Use existing test data helper // Act - await vault!.AddEntryAsync(MediaVaultType.Image, entryKey, imageBinary); + await vault!.AddEntryAsync(entryKey, imageBinary); // Assert Assert.That(vault.HasIndexEntry(entryKey), Is.True, "Should add entry to index"); @@ -456,10 +456,10 @@ public class MediaVaultTests { // Arrange var vault = await ImageVault.FromAsync(TestDirectory); - var entryKey = CreateTestEntryKey("corrupted-file"); + var entryKey = CreateTestEntryId("corrupted-file"); var imageBinary = TestData.CreateTestImageBinary(1.0); - await vault!.AddEntryAsync(MediaVaultType.Image, entryKey, imageBinary); + await vault!.AddEntryAsync(entryKey, imageBinary); // Corrupt the media file var filePath = Path.Combine(TestDirectory, "corrupted-file.png"); @@ -468,7 +468,7 @@ public class MediaVaultTests // Act & Assert - Should not throw, but behavior may vary Assert.DoesNotThrowAsync(async () => { - await vault.GetEntryAsync(MediaVaultType.Image, entryKey); + await vault.GetEntryAsync(entryKey); }, "Should handle corrupted files gracefully"); } @@ -480,7 +480,7 @@ public class MediaVaultTests // Arrange var vault = await ImageVault.FromAsync(TestDirectory); - var entryKey = CreateTestEntryKey("large-file"); + var entryKey = CreateTestEntryId("large-file"); // Create a reasonably large buffer (not too large to cause test issues) var largeBuffer = new byte[1024 * 1024]; // 1MB @@ -491,7 +491,7 @@ public class MediaVaultTests // Act & Assert - Should not throw exceptions Assert.DoesNotThrowAsync(async () => { - await vault!.AddEntryAsync(MediaVaultType.Image, entryKey, largeBinary); + await vault!.AddEntryAsync(entryKey, largeBinary); }, "Should handle large files gracefully"); } @@ -500,16 +500,16 @@ public class MediaVaultTests { // Arrange var vault = await ImageVault.FromAsync(TestDirectory); - var entryKey = CreateTestEntryKey("concurrent-test"); + var entryKey = CreateTestEntryId("concurrent-test"); var imageBinary = TestData.CreateTestImageBinary(1.0); - await vault!.AddEntryAsync(MediaVaultType.Image, entryKey, imageBinary); + await vault!.AddEntryAsync(entryKey, imageBinary); // Act - Multiple concurrent reads var tasks = new List>(); for (int i = 0; i < 10; i++) { - tasks.Add(vault.GetEntryAsync(MediaVaultType.Image, entryKey)); + tasks.Add(vault.GetEntryAsync(entryKey)); } var results = await Task.WhenAll(tasks); @@ -537,14 +537,14 @@ public class MediaVaultTests // Arrange var database = await FileDatabase.FromAsync(TestDirectory); - var vaultKey = new EntryKey("test-vault", MediaVaultType.Image); - var entryKey = new EntryKey("test-image", MediaVaultType.Image); + var vaultKey = "test-vault"; + var entryKey = "test-image"; var imageBinary = TestData.CreateTestImageBinary(1.5); // Act - await database!.CreateVaultAsync(vaultKey); - await database.RegisterResourceAsync(MediaVaultType.Image, vaultKey, entryKey, imageBinary); - var retrievedImage = await database.LoadResourceAsync(MediaVaultType.Image, vaultKey, entryKey); + await database!.CreateVaultAsync(vaultKey, MediaVaultType.Image); + await database.RegisterResourceAsync(vaultKey, entryKey, imageBinary); + var retrievedImage = await database.LoadResourceAsync(vaultKey, entryKey); // Assert Assert.That(retrievedImage, Is.Not.Null, "Should retrieve image through database"); @@ -564,16 +564,16 @@ public class MediaVaultTests // Arrange - Create and populate vault var database1 = await FileDatabase.FromAsync(TestDirectory); - var vaultKey = new EntryKey("persistent-vault", MediaVaultType.Image); - var entryKey = new EntryKey("persistent-image", MediaVaultType.Image); + var vaultKey = "persistent-vault"; + var entryKey = "persistent-image"; var imageBinary = TestData.CreateTestImageBinary(2.0); - await database1!.CreateVaultAsync(vaultKey); - await database1.RegisterResourceAsync(MediaVaultType.Image, vaultKey, entryKey, imageBinary); + await database1!.CreateVaultAsync(vaultKey, MediaVaultType.Image); + await database1.RegisterResourceAsync(vaultKey, entryKey, imageBinary); // Act - Reload database var database2 = await FileDatabase.FromAsync(TestDirectory); - var retrievedImage = await database2!.LoadResourceAsync(MediaVaultType.Image, vaultKey, entryKey); + var retrievedImage = await database2!.LoadResourceAsync(vaultKey, entryKey); // Assert Assert.That(retrievedImage, Is.Not.Null, "Should retrieve image after database reload"); diff --git a/DeepDrftTests/ModelTests.cs b/DeepDrftTests/ModelTests.cs index 1c2828f..5afd8da 100644 --- a/DeepDrftTests/ModelTests.cs +++ b/DeepDrftTests/ModelTests.cs @@ -8,38 +8,6 @@ namespace DeepDrftTests; [TestFixture] public class ModelTests { - [TestFixture] - public class EntryKeyTests - { - [Test] - public void EntryKey_CanBeCreated() - { - // Arrange - var key = "test-key"; - var type = MediaVaultType.Image; - - // Act - var entryKey = new EntryKey(key, type); - - // Assert - Assert.That(entryKey.Key, Is.EqualTo(key), "Key should match"); - Assert.That(entryKey.Type, Is.EqualTo(type), "Type should match"); - } - - [Test] - public void EntryKey_SupportsStructuralEquality() - { - // Arrange - var key1 = new EntryKey("test", MediaVaultType.Image); - var key2 = new EntryKey("test", MediaVaultType.Image); - var key3 = new EntryKey("different", MediaVaultType.Image); - - // Act & Assert - Assert.That(key1, Is.EqualTo(key2), "Structurally equal keys should be equal"); - Assert.That(key1, Is.Not.EqualTo(key3), "Different keys should not be equal"); - Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()), "Equal keys should have same hash code"); - } - } [TestFixture] public class MediaModelTests diff --git a/DeepDrftTests/TestData.cs b/DeepDrftTests/TestData.cs index b994719..3bc37bd 100644 --- a/DeepDrftTests/TestData.cs +++ b/DeepDrftTests/TestData.cs @@ -51,14 +51,17 @@ public static class TestData } /// - /// Test entry keys used across tests + /// Test keys used across tests - now using simple strings and separate vault types /// public static class TestKeys { - public static readonly EntryKey TestImageEntry = new("test", MediaVaultType.Image); - public static readonly EntryKey ImageVaultKey = new("img", MediaVaultType.Image); - public static readonly EntryKey NonExistentVaultKey = new("i-do-not-exist", MediaVaultType.Image); - public static readonly EntryKey NonExistentEntryKey = new("i-do-not-exist", MediaVaultType.Image); + public const string TestImageEntry = "test"; + public const string ImageVaultKey = "img"; + public const string NonExistentVaultKey = "i-do-not-exist"; + public const string NonExistentEntryKey = "i-do-not-exist"; + + // Vault type for image-related tests + public const MediaVaultType ImageVaultType = MediaVaultType.Image; } /// diff --git a/DeepDrftTests/UtilityTests.cs b/DeepDrftTests/UtilityTests.cs index 03a79c8..65511f1 100644 --- a/DeepDrftTests/UtilityTests.cs +++ b/DeepDrftTests/UtilityTests.cs @@ -16,8 +16,8 @@ public class UtilityTests public void StructuralMap_CanAddAndRetrieveEntries() { // Arrange - var map = new StructuralMap(); - var key = new EntryKey("test", MediaVaultType.Image); + var map = new StructuralMap(); + var key = "test"; var value = "test-value"; // Act @@ -33,9 +33,9 @@ public class UtilityTests public void StructuralMap_HandlesStructuralEquality() { // Arrange - var map = new StructuralMap(); - var key1 = new EntryKey("test", MediaVaultType.Image); - var key2 = new EntryKey("test", MediaVaultType.Image); // Same values, different instance + var map = new StructuralMap(); + var key1 = "test"; + var key2 = "test"; // Same values var value = "test-value"; // Act @@ -50,8 +50,8 @@ public class UtilityTests public void StructuralMap_CanRemoveEntries() { // Arrange - var map = new StructuralMap(); - var key = new EntryKey("test", MediaVaultType.Image); + var map = new StructuralMap(); + var key = "test"; var value = "test-value"; map.Set(key, value); @@ -68,12 +68,12 @@ public class UtilityTests public void StructuralMap_CanEnumerateEntries() { // Arrange - var map = new StructuralMap(); + var map = new StructuralMap(); var entries = new[] { - (new EntryKey("key1", MediaVaultType.Image), "value1"), - (new EntryKey("key2", MediaVaultType.Media), "value2"), - (new EntryKey("key3", MediaVaultType.Image), "value3") + ("key1", "value1"), + ("key2", "value2"), + ("key3", "value3") }; foreach (var (key, value) in entries) @@ -102,8 +102,8 @@ public class UtilityTests public void StructuralSet_CanAddAndContainEntries() { // Arrange - var set = new StructuralSet(); - var key = new EntryKey("test", MediaVaultType.Image); + var set = new StructuralSet(); + var key = "test"; // Act set.Add(key); @@ -117,9 +117,9 @@ public class UtilityTests public void StructuralSet_HandlesStructuralEquality() { // Arrange - var set = new StructuralSet(); - var key1 = new EntryKey("test", MediaVaultType.Image); - var key2 = new EntryKey("test", MediaVaultType.Image); // Same values, different instance + var set = new StructuralSet(); + var key1 = "test"; + var key2 = "test"; // Same values // Act set.Add(key1); @@ -134,8 +134,8 @@ public class UtilityTests public void StructuralSet_CanRemoveEntries() { // Arrange - var set = new StructuralSet(); - var key = new EntryKey("test", MediaVaultType.Image); + var set = new StructuralSet(); + var key = "test"; set.Add(key); // Act @@ -151,12 +151,12 @@ public class UtilityTests public void StructuralSet_CanEnumerateEntries() { // Arrange - var set = new StructuralSet(); + var set = new StructuralSet(); var keys = new[] { - new EntryKey("key1", MediaVaultType.Image), - new EntryKey("key2", MediaVaultType.Media), - new EntryKey("key3", MediaVaultType.Image) + "key1", + "key2", + "key3" }; foreach (var key in keys)