33using KitStack . Abstractions . Models ;
44using KitStack . Abstractions . Utilities ;
55using System . Runtime . InteropServices ;
6- using System . IO ;
7- using System . Linq ;
86using KitStack . Fakes . Contracts ;
97using KitStack . Fakes . Models ;
108using KitStack . Fakes . Options ;
@@ -22,7 +20,16 @@ public class InMemoryFileStorageManager(IOptions<FakeOptions>? options = null) :
2220 private readonly ConcurrentDictionary < string , FakeStoredFile > _store = new ( ) ;
2321 private readonly FakeOptions _options = options ? . Value ?? new FakeOptions ( ) ;
2422
25- // High-level convenience to create/store an IFormFile for an entity T (in-memory)
23+ /// <summary>
24+ /// Create and store an uploaded <see cref="IFormFile"/> for the specified entity type <typeparamref name="T"/>.
25+ /// The file content is preserved in memory and a populated <see cref="IFileEntry"/> is returned.
26+ /// The returned entry's <see cref="IFileEntry.FileLocation"/> is a provider-relative path (URL-safe).
27+ /// </summary>
28+ /// <typeparam name="T">Entity type the file is associated with.</typeparam>
29+ /// <param name="file">Uploaded form file to store (required).</param>
30+ /// <param name="category">Logical category or module name used to organize storage (required).</param>
31+ /// <param name="cancellationToken">Cancellation token.</param>
32+ /// <returns>A task that resolves to the stored <see cref="IFileEntry"/>.</returns>
2633 public async Task < IFileEntry > CreateAsync < T > ( IFormFile file , string ? category , CancellationToken cancellationToken = default )
2734 where T : class
2835 {
@@ -69,6 +76,37 @@ public async Task<IFileEntry> CreateAsync<T>(IFormFile file, string? category, C
6976 return fileEntry ;
7077 }
7178
79+ /// <summary>
80+ /// Create and store an uploaded file and associate it with the provided <paramref name="entity"/>.
81+ /// If the entity implements <see cref="IFileAttachable"/>, this method will call
82+ /// <see cref="IFileAttachable.AddFileAttachment"/> to attach the created file entry.
83+ /// </summary>
84+ /// <typeparam name="T">Entity type which implements <see cref="IFileAttachable"/>.</typeparam>
85+ /// <param name="entity">Entity instance to attach the file entry to (required).</param>
86+ /// <param name="file">Uploaded form file to store (required).</param>
87+ /// <param name="category">Logical category or module name used to organize storage (required).</param>
88+ /// <param name="cancellationToken">Cancellation token.</param>
89+ /// <returns>The created primary <see cref="IFileEntry"/>.</returns>
90+ public async Task < IFileEntry > CreateAsync < T > ( T entity , IFormFile file , string ? category , CancellationToken cancellationToken )
91+ where T : class , IFileAttachable
92+ {
93+ var primary = await CreateAsync < T > ( file , category , cancellationToken ) . ConfigureAwait ( false ) ;
94+
95+ entity . AddFileAttachment ( primary ) ;
96+ return primary ;
97+ }
98+
99+ /// <summary>
100+ /// Create the primary/original file and in-memory image variants (if the uploaded file is an image).
101+ /// Returns the primary <see cref="IFileEntry"/> and a list of created variant entries. Variants are
102+ /// stored in the in-memory fake store so tests can inspect them via <see cref="ListFiles"/> or
103+ /// <see cref="TryGetFile"/>.
104+ /// </summary>
105+ /// <typeparam name="T">Entity type the file is associated with.</typeparam>
106+ /// <param name="file">Uploaded form file to store (required).</param>
107+ /// <param name="category">Logical category or module name used to organize storage (required).</param>
108+ /// <param name="cancellationToken">Cancellation token.</param>
109+ /// <returns>A task that resolves to a tuple containing the primary entry and created variants.</returns>
72110 public async Task < ( IFileEntry Primary , List < IFileEntry > Variants ) > CreateWithVariantsAsync < T > ( IFormFile file , string ? category , CancellationToken cancellationToken = default )
73111 where T : class
74112 {
@@ -138,6 +176,13 @@ public async Task<IFileEntry> CreateAsync<T>(IFormFile file, string? category, C
138176 return ( primary , variants ) ;
139177 }
140178
179+ /// <summary>
180+ /// Build a simple <see cref="FileEntry"/> describing an in-memory variant file.
181+ /// The <paramref name="relativePath"/> should be a provider-relative path (URL-safe).
182+ /// </summary>
183+ /// <param name="relativePath">Provider-relative path for the variant.</param>
184+ /// <param name="content">Byte content of the variant (used to set Size).</param>
185+ /// <returns>A new <see cref="FileEntry"/> instance for the variant.</returns>
141186 private static FileEntry BuildVariantFileEntryInMemory ( string relativePath , byte [ ] content )
142187 {
143188 // Derive VariantType from the containing folder name
@@ -159,16 +204,34 @@ private static FileEntry BuildVariantFileEntryInMemory(string relativePath, byte
159204 return entry ;
160205 }
161206
207+ /// <summary>
208+ /// Simulate a small operation delay when configured via <see cref="FakeOptions.OperationDelayMs"/>.
209+ /// This helps tests exercise timeouts and concurrency scenarios without hitting real IO.
210+ /// </summary>
211+ /// <param name="cancellationToken">Cancellation token.</param>
162212 private async Task SimulateDelayAsync ( CancellationToken cancellationToken )
163213 {
164214 if ( _options . OperationDelayMs > 0 )
165215 await Task . Delay ( _options . OperationDelayMs , cancellationToken ) . ConfigureAwait ( false ) ;
166216 }
167217
168218 // IFakeFileStore
219+ /// <summary>
220+ /// Return a read-only snapshot of files currently stored in the fake in-memory store.
221+ /// Useful for assertions in unit tests.
222+ /// </summary>
169223 public IReadOnlyCollection < FakeStoredFile > ListFiles ( ) => _store . Values . ToList ( ) . AsReadOnly ( ) ;
170224
225+ /// <summary>
226+ /// Attempt to retrieve a stored file by its provider-relative location.
227+ /// </summary>
228+ /// <param name="fileLocation">Provider-relative file location to lookup.</param>
229+ /// <param name="file">Out parameter set to the stored file when found; otherwise null.</param>
230+ /// <returns>True if the file was found, false otherwise.</returns>
171231 public bool TryGetFile ( string fileLocation , out FakeStoredFile ? file ) => _store . TryGetValue ( fileLocation , out file ) ;
172232
233+ /// <summary>
234+ /// Clear all stored files from the in-memory fake store. Useful for test teardown.
235+ /// </summary>
173236 public void Clear ( ) => _store . Clear ( ) ;
174237}
0 commit comments