diff --git a/.editorconfig b/.editorconfig index 64ad963b34..f2ea6175b6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -37,22 +37,37 @@ trim_trailing_whitespace = true tab_width = 4 # New line preferences csharp_new_line_before_open_brace = all +csharp_new_line_before_open_brace_for_methods = true +csharp_new_line_before_open_brace_for_control_blocks = true +csharp_preserve_single_line_statements = false +csharp_style_allow_embedded_statements_on_same_line_experimental = false csharp_new_line_before_else = true csharp_new_line_before_catch = true csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true csharp_new_line_within_query_expression_clauses = true csharp_wrap_before_eq = false place_attribute_on_same_line = "never" # csharp_style_namespace_declarations = file_scoped:warning +# https://dotnettips.wordpress.com/2023/06/27/microsoft-net-code-analysis-always-add-braces-in-c/ +dotnet_diagnostic.IDE0011.severity = warning +csharp_prefer_braces = true:warning +dotnet_diagnostic.SA1500.severity = warning +dotnet_diagnostic.SA1503.severity = warning +dotnet_diagnostic.SA1520.severity = warning + + # Indentation preferences csharp_indent_block_contents = true csharp_indent_braces = false csharp_indent_case_contents = true csharp_indent_switch_labels = true csharp_indent_labels = one_less_than_current +csharp_indent_case_contents_when_block = false +csharp_space_after_cast = true # Prefer "var" everywhere csharp_style_var_for_built_in_types = true:suggestion diff --git a/starsky/starsky.foundation.database/Query/Query.cs b/starsky/starsky.foundation.database/Query/Query.cs index d27ab6be29..f0fe289d76 100644 --- a/starsky/starsky.foundation.database/Query/Query.cs +++ b/starsky/starsky.foundation.database/Query/Query.cs @@ -49,12 +49,19 @@ public Query(ApplicationDbContext context, /// FileIndex-objects with database data public FileIndexItem? GetObjectByFilePath(string filePath) { - if ( filePath != "/" ) filePath = PathHelper.RemoveLatestSlash(filePath); + if ( filePath != "/" ) + { + filePath = PathHelper.RemoveLatestSlash(filePath); + } FileIndexItem? LocalQuery(ApplicationDbContext context) { var item = context.FileIndex.FirstOrDefault(p => p.FilePath == filePath); - if ( item != null ) item.Status = FileIndexItem.ExifStatus.Ok; + if ( item != null ) + { + item.Status = FileIndexItem.ExifStatus.Ok; + } + return item; } @@ -85,7 +92,11 @@ public Query(ApplicationDbContext context, _cache.TryGetValue( GetObjectByFilePathAsyncCacheName(filePath), out var data) ) { - if ( !( data is FileIndexItem fileIndexItem ) ) return null; + if ( !( data is FileIndexItem fileIndexItem ) ) + { + return null; + } + fileIndexItem.Status = FileIndexItem.ExifStatus.OkAndSame; return fileIndexItem; } @@ -95,7 +106,9 @@ public Query(ApplicationDbContext context, // cache code: if ( cacheTime == null || _appSettings.AddMemoryCache != true || result == null ) + { return result; + } SetGetObjectByFilePathCache(filePath, result.Clone(), cacheTime); @@ -120,19 +133,25 @@ public void SetGetObjectByFilePathCache(string filePath, public async Task GetSubPathByHashAsync(string fileHash) { // The CLI programs uses no cache - if ( !IsCacheEnabled() || _cache == null ) return await QueryGetItemByHashAsync(fileHash); + if ( !IsCacheEnabled() || _cache == null ) + { + return await QueryGetItemByHashAsync(fileHash); + } // Return values from IMemoryCache var queryHashListCacheName = CachingDbName("hashList", fileHash); // if result is not null return cached value if ( _cache.TryGetValue(queryHashListCacheName, out var cachedSubPath) - && !string.IsNullOrEmpty(( string? )cachedSubPath) ) return ( string )cachedSubPath; + && !string.IsNullOrEmpty(( string? ) cachedSubPath) ) + { + return ( string ) cachedSubPath; + } cachedSubPath = await QueryGetItemByHashAsync(fileHash); _cache.Set(queryHashListCacheName, cachedSubPath, new TimeSpan(48, 0, 0)); - return ( string? )cachedSubPath; + return ( string? ) cachedSubPath; } /// @@ -141,11 +160,17 @@ public void SetGetObjectByFilePathCache(string filePath, /// base32 fileHash public void ResetItemByHash(string? fileHash) { - if ( _cache == null || _appSettings.AddMemoryCache == false ) return; + if ( _cache == null || _appSettings.AddMemoryCache == false ) + { + return; + } var queryCacheName = CachingDbName("hashList", fileHash); - if ( _cache.TryGetValue(queryCacheName, out _) ) _cache.Remove(queryCacheName); + if ( _cache.TryGetValue(queryCacheName, out _) ) + { + _cache.Remove(queryCacheName); + } } /// @@ -165,7 +190,9 @@ async Task LocalQuery(ApplicationDbContext context, FileIndexItem fileIndexItem) CacheUpdateItem(new List { updateStatusContent.Clone() }); if ( _appSettings.Verbose == true ) // Ef core changes debug + { _logger.LogDebug(context.ChangeTracker.DebugView.LongView); + } // object cache path is used to avoid updates SetGetObjectByFilePathCache(fileIndexItem.FilePath!, updateStatusContent, @@ -204,7 +231,11 @@ await RetryQueryUpdateSaveChangesAsync(updateStatusContent, e, { // Skip if Duplicate entry // MySqlConnector.MySqlException (0x80004005): Duplicate entry for key 'PRIMARY' - if ( !exception.Message.Contains("Duplicate") ) throw; + if ( !exception.Message.Contains("Duplicate") ) + { + throw; + } + _logger.LogError(exception, $"[UpdateItemAsync] Skipped MySqlException Duplicate entry for key {updateStatusContent.FilePath}"); } @@ -221,12 +252,16 @@ await RetryQueryUpdateSaveChangesAsync(updateStatusContent, e, public async Task> UpdateItemAsync( List updateStatusContentList) { - if ( updateStatusContentList.Count == 0 ) return new List(); + if ( updateStatusContentList.Count == 0 ) + { + return new List(); + } async Task> LocalQuery(DbContext context, List fileIndexItems) { foreach ( var item in fileIndexItems ) + { try { context.Attach(item).State = EntityState.Modified; @@ -236,11 +271,14 @@ async Task> LocalQuery(DbContext context, // System.InvalidOperationException: The property 'FileIndexItem.Id' has a temporary value while attempting to change the entity's state to 'Modified' // Issue #994 } + } await context.SaveChangesAsync(); foreach ( var item in fileIndexItems ) + { context.Attach(item).State = EntityState.Detached; + } CacheUpdateItem(fileIndexItems); return fileIndexItems; @@ -288,14 +326,19 @@ async Task> LocalQuery(DbContext context, /// items to update public void CacheUpdateItem(List updateStatusContent) { - if ( _cache == null || _appSettings.AddMemoryCache == false ) return; + if ( _cache == null || _appSettings.AddMemoryCache == false ) + { + return; + } var skippedCacheItems = new HashSet(); foreach ( var item in updateStatusContent.ToList() ) { if ( item.Status == FileIndexItem.ExifStatus.OkAndSame || item.Status == FileIndexItem.ExifStatus.Default ) + { item.Status = FileIndexItem.ExifStatus.Ok; + } // ToList() > Collection was modified; enumeration operation may not execute. var queryCacheName = CachingDbName(nameof(FileIndexItem), @@ -309,7 +352,7 @@ public void CacheUpdateItem(List updateStatusContent) } objectFileFolders ??= new List(); - var displayFileFolders = ( List )objectFileFolders; + var displayFileFolders = ( List ) objectFileFolders; // make it a list to avoid enum errors displayFileFolders = displayFileFolders.ToList(); @@ -317,12 +360,16 @@ public void CacheUpdateItem(List updateStatusContent) var obj = displayFileFolders.Find(p => p.FilePath == item.FilePath); if ( obj != null ) // remove add again + { displayFileFolders.Remove(obj); + } if ( item.Status == FileIndexItem.ExifStatus.Ok ) // ExifStatus.default is already changed // Add here item to cached index + { displayFileFolders.Add(item); + } // make it a list to avoid enum errors displayFileFolders = displayFileFolders.ToList(); @@ -334,8 +381,10 @@ public void CacheUpdateItem(List updateStatusContent) } if ( skippedCacheItems.Count >= 1 && _appSettings.Verbose == true ) + { _logger.LogInformation( $"[CacheUpdateItem] skipped: {string.Join(", ", skippedCacheItems)}"); + } } /// @@ -343,11 +392,19 @@ public void CacheUpdateItem(List updateStatusContent) /// This Does remove a SINGLE item from the cache NOT from the database /// /// + [SuppressMessage("ReSharper", + "S4136: All 'RemoveCacheItem' method overloads should be adjacent.")] public void RemoveCacheItem(List updateStatusContent) { - if ( _cache == null || _appSettings.AddMemoryCache == false ) return; + if ( _cache == null || _appSettings.AddMemoryCache == false ) + { + return; + } - foreach ( var item in updateStatusContent.ToList() ) RemoveCacheItem(item); + foreach ( var item in updateStatusContent.ToList() ) + { + RemoveCacheItem(item); + } } /// @@ -357,11 +414,17 @@ public void RemoveCacheItem(List updateStatusContent) public bool RemoveCacheParentItem(string directoryName) { // Add protection for disabled caching - if ( _cache == null || _appSettings.AddMemoryCache == false ) return false; + if ( _cache == null || _appSettings.AddMemoryCache == false ) + { + return false; + } var queryCacheName = CachingDbName(nameof(FileIndexItem), PathHelper.RemoveLatestSlash(directoryName.Clone().ToString()!)); - if ( !_cache.TryGetValue(queryCacheName, out _) ) return false; + if ( !_cache.TryGetValue(queryCacheName, out _) ) + { + return false; + } _cache.Remove(queryCacheName); return true; @@ -375,7 +438,10 @@ public bool RemoveCacheParentItem(string directoryName) public bool AddCacheParentItem(string directoryName, List items) { // Add protection for disabled caching - if ( _cache == null || _appSettings.AddMemoryCache == false ) return false; + if ( _cache == null || _appSettings.AddMemoryCache == false ) + { + return false; + } var queryCacheName = CachingDbName(nameof(FileIndexItem), PathHelper.RemoveLatestSlash(directoryName.Clone().ToString()!)); @@ -402,7 +468,11 @@ async Task LocalDefaultQuery() async Task LocalQuery(ApplicationDbContext context) { // only in test case fileIndex is null - if ( context.FileIndex != null ) await context.FileIndex.AddAsync(fileIndexItem); + if ( context.FileIndex != null ) + { + await context.FileIndex.AddAsync(fileIndexItem); + } + await context.SaveChangesAsync(); // Fix for: The instance of entity type 'Item' cannot be tracked because // another instance with the same key value for {'Id'} is already being tracked @@ -462,7 +532,9 @@ public async Task> AddParentItemsAsync(string subPath) var toAddList = new List(); // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator foreach ( var pathShouldExist in pathListShouldExist ) + { if ( !indexItems.Select(p => p.FilePath).Contains(pathShouldExist) ) + { toAddList.Add(new FileIndexItem(pathShouldExist) { IsDirectory = true, @@ -471,6 +543,8 @@ public async Task> AddParentItemsAsync(string subPath) Software = pathShouldExist == "/" ? "Root object" : string.Empty, Status = FileIndexItem.ExifStatus.Ok }); + } + } await AddRangeAsync(toAddList); return toAddList; @@ -492,7 +566,11 @@ internal static string GetObjectByFilePathAsyncCacheName(string subPath) private async Task GetObjectByFilePathQueryAsync( string filePath) { - if ( filePath != "/" ) filePath = PathHelper.RemoveLatestSlash(filePath); + if ( filePath != "/" ) + { + filePath = PathHelper.RemoveLatestSlash(filePath); + } + var paths = new List { filePath }; return ( await GetObjectsByFilePathQueryAsync(paths) ) .FirstOrDefault(); @@ -528,7 +606,11 @@ internal static string GetObjectByFilePathAsyncCacheName(string subPath) internal static string CachingDbName(string functionName, string? singleItemDbPath) { // when is nothing assume its the home item - if ( string.IsNullOrWhiteSpace(singleItemDbPath) ) singleItemDbPath = "/"; + if ( string.IsNullOrWhiteSpace(singleItemDbPath) ) + { + singleItemDbPath = "/"; + } + // For creating an unique name: DetailView_/2018/01/1.jpg var uniqueSingleDbCacheNameBuilder = new StringBuilder(); uniqueSingleDbCacheNameBuilder.Append(functionName + "_" + singleItemDbPath); @@ -560,7 +642,11 @@ async Task LocalRetrySaveChangesAsyncQuery() // https://go.microsoft.com/fwlink/?linkid=2097913 await Task.Delay(delay); var context = new InjectServiceScope(_scopeFactory).Context(); - if ( context == null! ) throw new AggregateException("Query Context is null"); + if ( context == null! ) + { + throw new AggregateException("Query Context is null"); + } + context.Attach(updateStatusContent).State = EntityState.Modified; await context.SaveChangesAsync(); context.Attach(updateStatusContent).State = EntityState.Unchanged; @@ -605,7 +691,11 @@ async Task LocalRetrySaveChangesAsyncQuery() /// true when enabled internal bool IsCacheEnabled() { - if ( _cache == null || _appSettings.AddMemoryCache == false ) return false; + if ( _cache == null || _appSettings.AddMemoryCache == false ) + { + return false; + } + return true; } @@ -619,17 +709,26 @@ internal bool IsCacheEnabled() internal void AddCacheItem(FileIndexItem updateStatusContent) { // If cache is turned of - if ( _cache == null || _appSettings.AddMemoryCache == false ) return; + if ( _cache == null || _appSettings.AddMemoryCache == false ) + { + return; + } var queryCacheName = CachingDbName(nameof(FileIndexItem), updateStatusContent.ParentDirectory!); - if ( !_cache.TryGetValue(queryCacheName, out var objectFileFolders) ) return; + if ( !_cache.TryGetValue(queryCacheName, out var objectFileFolders) ) + { + return; + } objectFileFolders ??= new List(); - var displayFileFolders = ( List )objectFileFolders; + var displayFileFolders = ( List ) objectFileFolders; - if ( updateStatusContent.FilePath == "/" ) return; + if ( updateStatusContent.FilePath == "/" ) + { + return; + } displayFileFolders.Add(updateStatusContent); // Order by filename @@ -647,15 +746,21 @@ internal void AddCacheItem(FileIndexItem updateStatusContent) public void RemoveCacheItem(FileIndexItem updateStatusContent) { // Add protection for disabled caching - if ( _cache == null || _appSettings.AddMemoryCache == false ) return; + if ( _cache == null || _appSettings.AddMemoryCache == false ) + { + return; + } var queryCacheName = CachingDbName(nameof(FileIndexItem), updateStatusContent.ParentDirectory!); - if ( !_cache.TryGetValue(queryCacheName, out var objectFileFolders) ) return; + if ( !_cache.TryGetValue(queryCacheName, out var objectFileFolders) ) + { + return; + } objectFileFolders ??= new List(); - var displayFileFolders = ( List )objectFileFolders; + var displayFileFolders = ( List ) objectFileFolders; // Order by filename displayFileFolders = displayFileFolders diff --git a/starsky/starsky/Controllers/AllowedTypesController.cs b/starsky/starsky/Controllers/AllowedTypesController.cs index b60eb91e9f..0882b6ccd4 100644 --- a/starsky/starsky/Controllers/AllowedTypesController.cs +++ b/starsky/starsky/Controllers/AllowedTypesController.cs @@ -5,61 +5,69 @@ using starsky.foundation.platform.Helpers; using starsky.project.web.Helpers; -namespace starsky.Controllers +namespace starsky.Controllers; + +[Authorize] +public sealed class AllowedTypesController : Controller { - [Authorize] - public sealed class AllowedTypesController : Controller + /// + /// A (string) list of allowed MIME-types ExtensionSyncSupportedList + /// + /// Json list + /// list + /// please login first + [HttpGet("/api/allowed-types/mimetype/sync")] + [ProducesResponseType(typeof(HashSet), 200)] + [Produces("application/json")] + public IActionResult AllowedTypesMimetypeSync() { - /// - /// A (string) list of allowed MIME-types ExtensionSyncSupportedList - /// - /// Json list - /// list - /// please login first - [HttpGet("/api/allowed-types/mimetype/sync")] - [ProducesResponseType(typeof(HashSet), 200)] - [Produces("application/json")] - public IActionResult AllowedTypesMimetypeSync() - { - var mimeTypes = ExtensionRolesHelper.ExtensionSyncSupportedList - .Select(MimeHelper.GetMimeType).ToHashSet(); - return Json(mimeTypes); - } + var mimeTypes = ExtensionRolesHelper.ExtensionSyncSupportedList + .Select(MimeHelper.GetMimeType).ToHashSet(); + return Json(mimeTypes); + } - /// - /// A (string) list of allowed ExtensionThumbSupportedList MimeTypes - /// - /// Json list - /// list - /// please login first - [HttpGet("/api/allowed-types/mimetype/thumb")] - [ProducesResponseType(typeof(HashSet), 200)] - [Produces("application/json")] - public IActionResult AllowedTypesMimetypeSyncThumb() + /// + /// A (string) list of allowed ExtensionThumbSupportedList MimeTypes + /// + /// Json list + /// list + /// please login first + [HttpGet("/api/allowed-types/mimetype/thumb")] + [ProducesResponseType(typeof(HashSet), 200)] + [Produces("application/json")] + public IActionResult AllowedTypesMimetypeSyncThumb() + { + var mimeTypes = ExtensionRolesHelper.ExtensionThumbSupportedList + .Select(MimeHelper.GetMimeType).ToHashSet(); + return Json(mimeTypes); + } + + /// + /// Check if IsExtensionThumbnailSupported + /// + /// Json list + /// the name with extension and no parent path + /// is supported + /// the extenstion from the filename is not supported to generate thumbnails + /// please log in first + [HttpGet("/api/allowed-types/thumb")] + [ProducesResponseType(typeof(bool), 200)] + [ProducesResponseType(typeof(bool), 415)] + [Produces("application/json")] + public IActionResult AllowedTypesThumb(string f) + { + if ( !ModelState.IsValid ) { - var mimeTypes = ExtensionRolesHelper.ExtensionThumbSupportedList - .Select(MimeHelper.GetMimeType).ToHashSet(); - return Json(mimeTypes); + return BadRequest("ModelState is not valid"); } - /// - /// Check if IsExtensionThumbnailSupported - /// - /// Json list - /// the name with extension and no parent path - /// is supported - /// the extenstion from the filename is not supported to generate thumbnails - /// please login first - [HttpGet("/api/allowed-types/thumb")] - [ProducesResponseType(typeof(bool), 200)] - [ProducesResponseType(typeof(bool), 415)] - [Produces("application/json")] - public IActionResult AllowedTypesThumb(string f) + var result = ExtensionRolesHelper.IsExtensionThumbnailSupported(f); + if ( !result ) { - var result = ExtensionRolesHelper.IsExtensionThumbnailSupported(f); - if ( !result ) Response.StatusCode = 415; - return Json(result); + Response.StatusCode = 415; } + + return Json(result); } } diff --git a/starsky/starsky/Controllers/AppSettingsController.cs b/starsky/starsky/Controllers/AppSettingsController.cs index ad18e0b25b..d077a49ed2 100644 --- a/starsky/starsky/Controllers/AppSettingsController.cs +++ b/starsky/starsky/Controllers/AppSettingsController.cs @@ -8,69 +8,73 @@ using starsky.foundation.accountmanagement.Services; using starsky.foundation.platform.Models; -namespace starsky.Controllers +namespace starsky.Controllers; + +[Authorize] +public sealed class AppSettingsController : Controller { - [Authorize] - public sealed class AppSettingsController : Controller + private readonly AppSettings _appSettings; + private readonly IUpdateAppSettingsByPath _updateAppSettingsByPath; + + public AppSettingsController(AppSettings appSettings, + IUpdateAppSettingsByPath updateAppSettingsByPath) { - private readonly AppSettings _appSettings; - private readonly IUpdateAppSettingsByPath _updateAppSettingsByPath; + _appSettings = appSettings; + _updateAppSettingsByPath = updateAppSettingsByPath; + } - public AppSettingsController(AppSettings appSettings, - IUpdateAppSettingsByPath updateAppSettingsByPath) - { - _appSettings = appSettings; - _updateAppSettingsByPath = updateAppSettingsByPath; - } + /// + /// Show the runtime settings (dont allow AllowAnonymous) + /// + /// config data, except connection strings + /// returns the runtime settings of Starsky + [HttpHead("/api/env")] + [HttpGet("/api/env")] + [Produces("application/json")] + [ProducesResponseType(typeof(AppSettings), 200)] + [ProducesResponseType(typeof(AppSettings), 401)] + [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse", + Justification = "Request in tests")] + public IActionResult Env() + { + var appSettings = _appSettings.CloneToDisplay(); - /// - /// Show the runtime settings (dont allow AllowAnonymous) - /// - /// config data, except connection strings - /// returns the runtime settings of Starsky - [HttpHead("/api/env")] - [HttpGet("/api/env")] - [Produces("application/json")] - [ProducesResponseType(typeof(AppSettings), 200)] - [ProducesResponseType(typeof(AppSettings), 401)] - [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse", - Justification = "Request in tests")] - public IActionResult Env() + // For end-to-end testing + if ( Request != null! && Request.Headers.Any(p => p.Key == "x-force-html") ) { - var appSettings = _appSettings.CloneToDisplay(); - - // For end-to-end testing - if ( Request != null! && Request.Headers.Any(p => p.Key == "x-force-html") ) - { - Response.Headers.ContentType = "text/html; charset=utf-8"; - } - - return Json(appSettings); + Response.Headers.ContentType = "text/html; charset=utf-8"; } - /// - /// Show the runtime settings (dont allow AllowAnonymous) - /// - /// config data, except connection strings - /// returns the runtime settings of Starsky - [HttpPost("/api/env")] - [Produces("application/json")] - [ProducesResponseType(typeof(AppSettings), 200)] - [ProducesResponseType(typeof(AppSettings), 401)] - [Permission(UserManager.AppPermissions.AppSettingsWrite)] - public async Task UpdateAppSettings( - AppSettingsTransferObject appSettingTransferObject) + return Json(appSettings); + } + + /// + /// Show the runtime settings (dont allow AllowAnonymous) + /// + /// config data, except connection strings + /// returns the runtime settings of Starsky + [HttpPost("/api/env")] + [Produces("application/json")] + [ProducesResponseType(typeof(AppSettings), 200)] + [ProducesResponseType(typeof(AppSettings), 401)] + [Permission(UserManager.AppPermissions.AppSettingsWrite)] + public async Task UpdateAppSettings( + AppSettingsTransferObject appSettingTransferObject) + { + if ( !ModelState.IsValid ) { - var result = await _updateAppSettingsByPath.UpdateAppSettingsAsync( - appSettingTransferObject); + return BadRequest("ModelState is not valid"); + } - if ( !result.IsError ) - { - return Env(); - } + var result = await _updateAppSettingsByPath.UpdateAppSettingsAsync( + appSettingTransferObject); - Response.StatusCode = result.StatusCode; - return Content(result.Message); + if ( !result.IsError ) + { + return Env(); } + + Response.StatusCode = result.StatusCode; + return Content(result.Message); } } diff --git a/starsky/starsky/Controllers/CacheIndexController.cs b/starsky/starsky/Controllers/CacheIndexController.cs index 65c0d1d79a..21e3597301 100644 --- a/starsky/starsky/Controllers/CacheIndexController.cs +++ b/starsky/starsky/Controllers/CacheIndexController.cs @@ -4,80 +4,100 @@ using starsky.foundation.database.Interfaces; using starsky.foundation.platform.Models; -namespace starsky.Controllers +namespace starsky.Controllers; + +[Authorize] +public sealed class CacheIndexController : Controller { - [Authorize] - public sealed class CacheIndexController : Controller + private readonly AppSettings _appSettings; + private readonly IQuery _query; + + public CacheIndexController( + IQuery query, AppSettings appSettings) { - private readonly IQuery _query; - private readonly AppSettings _appSettings; + _appSettings = appSettings; + _query = query; + } - public CacheIndexController( - IQuery query, AppSettings appSettings) + /// + /// Get Database Cache (only the cache) + /// + /// subPath (only direct so no dot;comma list) + /// redirect or if json enabled a status + /// when json" + /// "cache disabled in config" + /// + /// ignored, please check if the 'f' path exist or use a folder string to clear + /// the cache + /// + /// User unauthorized + [HttpGet("/api/cache/list")] + public IActionResult ListCache(string f = "/") + { + if ( !ModelState.IsValid ) { - _appSettings = appSettings; - _query = query; + return BadRequest("ModelState is not valid"); } - /// - /// Get Database Cache (only the cache) - /// - /// subPath (only direct so no dot;comma list) - /// redirect or if json enabled a status - /// when json" - /// "cache disabled in config" - /// ignored, please check if the 'f' path exist or use a folder string to clear the cache - /// User unauthorized - [HttpGet("/api/cache/list")] - public IActionResult ListCache(string f = "/") + //For folder paths only + if ( _appSettings.AddMemoryCache == false ) { - //For folder paths only - if ( _appSettings.AddMemoryCache == false ) - { - Response.StatusCode = 412; - return Json("cache disabled in config"); - } - - var (success, singleItem) = _query.CacheGetParentFolder(f); - if ( !success || singleItem == null ) - return BadRequest( - "ignored, please check if the 'f' path exist or use a folder string to get the cache"); + Response.StatusCode = 412; + return Json("cache disabled in config"); + } - return Json(singleItem); + var (success, singleItem) = _query.CacheGetParentFolder(f); + if ( !success || singleItem == null ) + { + return BadRequest( + "ignored, please check if the 'f' path exist or use a folder string to get the cache"); } - /// - /// Delete Database Cache (only the cache) - /// - /// subPath (only direct so no dot;comma list) - /// redirect or if json enabled a status - /// when json is true, "cache successful cleared" - /// "cache disabled in config" - /// ignored, please check if the 'f' path exist or use a folder string to clear the cache - /// redirect back to the url - /// User unauthorized - [HttpGet("/api/remove-cache")] - [HttpPost("/api/remove-cache")] - [ProducesResponseType(200)] // "cache successful cleared" - [ProducesResponseType(412)] // "cache disabled in config" - [ProducesResponseType(400)] // "ignored, please check if the 'f' path exist or use a folder string to clear the cache" - [ProducesResponseType(302)] // redirect back to the url - public IActionResult RemoveCache(string f = "/") + return Json(singleItem); + } + + /// + /// Delete Database Cache (only the cache) + /// + /// subPath (only direct so no dot;comma list) + /// redirect or if json enabled a status + /// when json is true, "cache successful cleared" + /// "cache disabled in config" + /// + /// ignored, please check if the 'f' path exist or use a folder string to clear + /// the cache + /// + /// redirect back to the url + /// User unauthorized + [HttpGet("/api/remove-cache")] + [HttpPost("/api/remove-cache")] + [ProducesResponseType(200)] // "cache successful cleared" + [ProducesResponseType(412)] // "cache disabled in config" + [ProducesResponseType(400)] // "ignored, please check if the 'f' path exist or use a folder string to clear the cache" + [ProducesResponseType(302)] // redirect back to the url + public IActionResult RemoveCache(string f = "/") + { + if ( !ModelState.IsValid ) { - //For folder paths only - if ( _appSettings.AddMemoryCache == false ) - { - Response.StatusCode = 412; - return Json("cache disabled in config"); - } + return BadRequest("ModelState is not valid"); + } - var singleItem = _query.SingleItem(f); - if ( singleItem == null || !singleItem.IsDirectory ) - return BadRequest( - "ignored, please check if the 'f' path exist or use a folder string to clear the cache"); + //For folder paths only + if ( _appSettings.AddMemoryCache == false ) + { + Response.StatusCode = 412; + return Json("cache disabled in config"); + } - return Json(_query.RemoveCacheParentItem(f) ? "cache successful cleared" : "cache did not exist"); + var singleItem = _query.SingleItem(f); + if ( singleItem == null || !singleItem.IsDirectory ) + { + return BadRequest( + "ignored, please check if the 'f' path exist or use a folder string to clear the cache"); } + return Json(_query.RemoveCacheParentItem(f) + ? "cache successful cleared" + : "cache did not exist"); } } diff --git a/starsky/starsky/Controllers/DeleteController.cs b/starsky/starsky/Controllers/DeleteController.cs index 9257b03242..e7b8cf45b9 100644 --- a/starsky/starsky/Controllers/DeleteController.cs +++ b/starsky/starsky/Controllers/DeleteController.cs @@ -5,44 +5,52 @@ using starsky.feature.metaupdate.Interfaces; using starsky.foundation.database.Models; -namespace starsky.Controllers +namespace starsky.Controllers; + +[Authorize] +public sealed class DeleteController : Controller { - [Authorize] - public sealed class DeleteController : Controller + private readonly IDeleteItem _deleteItem; + + public DeleteController(IDeleteItem deleteItem) { - private readonly IDeleteItem _deleteItem; + _deleteItem = deleteItem; + } - public DeleteController(IDeleteItem deleteItem) + /// + /// Remove files from the disk, but the file must contain the !delete! + /// (TrashKeyword.TrashKeywordString) tag + /// + /// subPaths, separated by dot comma + /// true is to update files with the same name before the extenstion + /// list of deleted files + /// file is gone + /// + /// item not found on disk or !delete! (TrashKeyword.TrashKeywordString) tag is + /// missing + /// + /// User unauthorized + [HttpDelete("/api/delete")] + [ProducesResponseType(typeof(List), 200)] + [ProducesResponseType(typeof(List), 404)] + [Produces("application/json")] + public async Task Delete(string f, bool collections = false) + { + if ( !ModelState.IsValid ) { - _deleteItem = deleteItem; + return BadRequest("ModelState is not valid"); } - /// - /// Remove files from the disk, but the file must contain the !delete! (TrashKeyword.TrashKeywordString) tag - /// - /// subPaths, separated by dot comma - /// true is to update files with the same name before the extenstion - /// list of deleted files - /// file is gone - /// item not found on disk or !delete! (TrashKeyword.TrashKeywordString) tag is missing - /// User unauthorized - [HttpDelete("/api/delete")] - [ProducesResponseType(typeof(List), 200)] - [ProducesResponseType(typeof(List), 404)] - [Produces("application/json")] - public async Task Delete(string f, bool collections = false) + var fileIndexResultsList = await _deleteItem.DeleteAsync(f, collections); + // When all items are not found + // ok = file is deleted + if ( fileIndexResultsList.TrueForAll(p => + p.Status != FileIndexItem.ExifStatus.Ok) ) { - var fileIndexResultsList = await _deleteItem.DeleteAsync(f, collections); - // When all items are not found - // ok = file is deleted - if ( fileIndexResultsList.TrueForAll(p => - p.Status != FileIndexItem.ExifStatus.Ok) ) - { - return NotFound(fileIndexResultsList); - } + return NotFound(fileIndexResultsList); + } - return Json(fileIndexResultsList); - } + return Json(fileIndexResultsList); } } diff --git a/starsky/starsky/Controllers/DesktopEditorController.cs b/starsky/starsky/Controllers/DesktopEditorController.cs index 0cfdbbd1ce..9dc2cd584f 100644 --- a/starsky/starsky/Controllers/DesktopEditorController.cs +++ b/starsky/starsky/Controllers/DesktopEditorController.cs @@ -18,13 +18,13 @@ public DesktopEditorController(IOpenEditorDesktopService openEditorDesktopServic } /// - /// Open a file in the default editor or a specific editor on the desktop + /// Open a file in the default editor or a specific editor on the desktop /// /// single or multiple subPaths /// to combine files with the same name before the extension /// /// returns a list of items from the database - /// list with no content + /// list with no content /// subPath not found in the database /// User unauthorized [HttpPost("/api/desktop-editor/open")] @@ -37,6 +37,11 @@ public async Task OpenAsync( string f = "", bool collections = true) { + if ( !ModelState.IsValid ) + { + return BadRequest("ModelState is not valid"); + } + var (success, status, list) = await _openEditorDesktopService.OpenAsync(f, collections); @@ -54,7 +59,7 @@ public async Task OpenAsync( /// - /// Check the amount of files to open before + /// Check the amount of files to open before /// /// single or multiple subPaths /// @@ -64,8 +69,14 @@ public async Task OpenAsync( [Produces("application/json")] [ProducesResponseType(typeof(bool), 200)] [ProducesResponseType(401)] + [ProducesResponseType(400)] public IActionResult OpenAmountConfirmationChecker(string f) { + if ( !ModelState.IsValid ) + { + return BadRequest("ModelState is not valid"); + } + var result = _openEditorDesktopService.OpenAmountConfirmationChecker(f); return Json(result); } diff --git a/starsky/starsky/Controllers/DiskController.cs b/starsky/starsky/Controllers/DiskController.cs index e8c8666dc4..7f61dddfea 100644 --- a/starsky/starsky/Controllers/DiskController.cs +++ b/starsky/starsky/Controllers/DiskController.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -15,146 +16,149 @@ using starsky.foundation.storage.Storage; using starsky.project.web.ViewModels; -namespace starsky.Controllers +namespace starsky.Controllers; + +[Authorize] +public sealed class DiskController : Controller { - [Authorize] - public sealed class DiskController : Controller + private readonly IWebSocketConnectionsService _connectionsService; + private readonly IStorage _iStorage; + private readonly INotificationQuery _notificationQuery; + private readonly IQuery _query; + + public DiskController(IQuery query, ISelectorStorage selectorStorage, + IWebSocketConnectionsService connectionsService, INotificationQuery notificationQuery) { - private readonly IQuery _query; - private readonly IStorage _iStorage; - private readonly IWebSocketConnectionsService _connectionsService; - private readonly INotificationQuery _notificationQuery; + _query = query; + _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath); + _connectionsService = connectionsService; + _notificationQuery = notificationQuery; + } - public DiskController(IQuery query, ISelectorStorage selectorStorage, - IWebSocketConnectionsService connectionsService, INotificationQuery notificationQuery) + /// + /// Make a directory (-p) + /// + /// subPaths split by dot comma + /// list of changed files IActionResult Mkdir + /// create the item on disk and in db + /// A conflict, Directory already exist + /// missing path + /// User unauthorized + [HttpPost("/api/disk/mkdir")] + [ProducesResponseType(typeof(List), 200)] + [ProducesResponseType(typeof(List), 409)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(typeof(string), 401)] + [Produces("application/json")] + public async Task Mkdir(string f) + { + var inputFilePaths = PathHelper.SplitInputFilePaths(f).ToList(); + if ( inputFilePaths.Count == 0 ) { - _query = query; - _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath); - _connectionsService = connectionsService; - _notificationQuery = notificationQuery; + Response.StatusCode = 400; + return Json(new List()); } - /// - /// Make a directory (-p) - /// - /// subPaths split by dot comma - /// list of changed files IActionResult Mkdir - /// create the item on disk and in db - /// A conflict, Directory already exist - /// missing path - /// User unauthorized - [HttpPost("/api/disk/mkdir")] - [ProducesResponseType(typeof(List), 200)] - [ProducesResponseType(typeof(List), 409)] - [ProducesResponseType(typeof(List), 400)] - [ProducesResponseType(typeof(string), 401)] - [Produces("application/json")] - public async Task Mkdir(string f) + var syncResultsList = new List(); + + foreach ( var subPath in inputFilePaths.Select(PathHelper.RemoveLatestSlash) ) { - var inputFilePaths = PathHelper.SplitInputFilePaths(f).ToList(); - if ( inputFilePaths.Count == 0 ) + var toAddStatus = new SyncViewModel { - Response.StatusCode = 400; - return Json(new List()); - } + FilePath = subPath, Status = FileIndexItem.ExifStatus.Ok + }; - var syncResultsList = new List(); - - foreach ( var subPath in inputFilePaths.Select(PathHelper.RemoveLatestSlash) ) + if ( _iStorage.ExistFolder(subPath) ) { - var toAddStatus = new SyncViewModel - { - FilePath = subPath, Status = FileIndexItem.ExifStatus.Ok - }; - - if ( _iStorage.ExistFolder(subPath) ) - { - toAddStatus.Status = FileIndexItem.ExifStatus.OperationNotSupported; - syncResultsList.Add(toAddStatus); - continue; - } - - await _query.AddItemAsync(new FileIndexItem(subPath) - { - IsDirectory = true, ImageFormat = ExtensionRolesHelper.ImageFormat.directory - }); - - // add to fs - _iStorage.CreateDirectory(subPath); - + toAddStatus.Status = FileIndexItem.ExifStatus.OperationNotSupported; syncResultsList.Add(toAddStatus); + continue; } - // When all items are not found - if ( syncResultsList.TrueForAll(p => p.Status != FileIndexItem.ExifStatus.Ok) ) - Response.StatusCode = 409; // A conflict, Directory already exist + await _query.AddItemAsync(new FileIndexItem(subPath) + { + IsDirectory = true, ImageFormat = ExtensionRolesHelper.ImageFormat.directory + }); - await SyncMessageToSocket(syncResultsList, ApiNotificationType.Mkdir); + // add to fs + _iStorage.CreateDirectory(subPath); - return Json(syncResultsList); + syncResultsList.Add(toAddStatus); } - /// - /// Update other users with a message from SyncViewModel - /// - /// SyncViewModel - /// optional debug name - /// Completed send of Socket SendToAllAsync - private async Task SyncMessageToSocket(IEnumerable syncResultsList, - ApiNotificationType type = ApiNotificationType.Unknown) + // When all items are not found + if ( syncResultsList.TrueForAll(p => p.Status != FileIndexItem.ExifStatus.Ok) ) { - var list = syncResultsList.Select(t => new FileIndexItem(t.FilePath) - { - Status = t.Status, IsDirectory = true - }).ToList(); + Response.StatusCode = 409; // A conflict, Directory already exist + } - var webSocketResponse = new ApiNotificationResponseModel< - List>(list, type); + await SyncMessageToSocket(syncResultsList, ApiNotificationType.Mkdir); - await _notificationQuery.AddNotification(webSocketResponse); - await _connectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None); - } + return Json(syncResultsList); + } - /// - /// Rename file/folder and update it in the database - /// - /// from subPath - /// to subPath - /// is collections bool - /// default is to not included files that are removed in result - /// list of details form changed files (IActionResult Rename) - /// the item including the updated content - /// item not found in the database or on disk - /// User unauthorized - [ProducesResponseType(typeof(List), 200)] - [ProducesResponseType(typeof(List), 404)] - [HttpPost("/api/disk/rename")] - [Produces("application/json")] - public async Task Rename(string f, string to, bool collections = true, - bool currentStatus = true) + /// + /// Update other users with a message from SyncViewModel + /// + /// SyncViewModel + /// optional debug name + /// Completed send of Socket SendToAllAsync + private async Task SyncMessageToSocket(IEnumerable syncResultsList, + ApiNotificationType type = ApiNotificationType.Unknown) + { + var list = syncResultsList.Select(t => new FileIndexItem(t.FilePath) { - if ( string.IsNullOrEmpty(f) ) - { - return BadRequest("No input files"); - } + Status = t.Status, IsDirectory = true + }).ToList(); - var rename = await new RenameService(_query, _iStorage).Rename(f, to, collections); + var webSocketResponse = new ApiNotificationResponseModel< + List>(list, type); - // When all items are not found - if ( rename.TrueForAll(p => p.Status != FileIndexItem.ExifStatus.Ok) ) - return NotFound(rename); + await _notificationQuery.AddNotification(webSocketResponse); + await _connectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None); + } - var webSocketResponse = - new ApiNotificationResponseModel>(rename, - ApiNotificationType.Rename); + /// + /// Rename file/folder and update it in the database + /// + /// from subPath + /// to subPath + /// is collections bool + /// default is to not included files that are removed in result + /// list of details form changed files (IActionResult Rename) + /// the item including the updated content + /// item not found in the database or on disk + /// User unauthorized + [ProducesResponseType(typeof(List), 200)] + [ProducesResponseType(typeof(List), 404)] + [HttpPost("/api/disk/rename")] + [Produces("application/json")] + public async Task Rename([Required] string f, string to, bool collections = true, + bool currentStatus = true) + { + if ( string.IsNullOrEmpty(f) || !ModelState.IsValid ) + { + return BadRequest("No input files"); + } - await _notificationQuery.AddNotification(webSocketResponse); - await _connectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None); + var rename = await new RenameService(_query, _iStorage).Rename(f, to, collections); - return Json(currentStatus - ? rename.Where(p => p.Status - != FileIndexItem.ExifStatus.NotFoundSourceMissing).ToList() - : rename); + // When all items are not found + if ( rename.TrueForAll(p => p.Status != FileIndexItem.ExifStatus.Ok) ) + { + return NotFound(rename); } + + var webSocketResponse = + new ApiNotificationResponseModel>(rename, + ApiNotificationType.Rename); + + await _notificationQuery.AddNotification(webSocketResponse); + await _connectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None); + + return Json(currentStatus + ? rename.Where(p => p.Status + != FileIndexItem.ExifStatus.NotFoundSourceMissing).ToList() + : rename); } } diff --git a/starsky/starsky/Controllers/DownloadPhotoController.cs b/starsky/starsky/Controllers/DownloadPhotoController.cs index 6593541b9f..57bf7b5af9 100644 --- a/starsky/starsky/Controllers/DownloadPhotoController.cs +++ b/starsky/starsky/Controllers/DownloadPhotoController.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -12,129 +13,147 @@ using starsky.Helpers; using starsky.project.web.Helpers; -namespace starsky.Controllers +namespace starsky.Controllers; + +[Authorize] +public sealed class DownloadPhotoController : Controller { - [Authorize] - public sealed class DownloadPhotoController : Controller + private readonly IStorage _iStorage; + private readonly IWebLogger _logger; + private readonly IQuery _query; + private readonly IThumbnailService _thumbnailService; + private readonly IStorage _thumbnailStorage; + + public DownloadPhotoController(IQuery query, ISelectorStorage selectorStorage, + IWebLogger logger, IThumbnailService thumbnailService) + { + _query = query; + _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath); + _thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail); + _thumbnailService = thumbnailService; + _logger = logger; + } + + /// + /// Download sidecar file for example image.xmp + /// + /// string, subPath to find the file + /// FileStream with image + /// returns content of the file + /// source image missing + /// User unauthorized + [HttpGet("/api/download-sidecar")] + [ProducesResponseType(200)] // file + [ProducesResponseType(404)] // not found + [ProducesResponseType(400)] + public IActionResult DownloadSidecar([Required] string f) { - private readonly IQuery _query; - private readonly IStorage _iStorage; - private readonly IStorage _thumbnailStorage; - private readonly IWebLogger _logger; - private readonly IThumbnailService _thumbnailService; - - public DownloadPhotoController(IQuery query, ISelectorStorage selectorStorage, - IWebLogger logger, IThumbnailService thumbnailService) + if ( !ModelState.IsValid ) { - _query = query; - _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath); - _thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail); - _thumbnailService = thumbnailService; - _logger = logger; + return BadRequest("ModelState is not valid"); } - /// - /// Download sidecar file for example image.xmp - /// - /// string, subPath to find the file - /// FileStream with image - /// returns content of the file - /// source image missing - /// User unauthorized - [HttpGet("/api/download-sidecar")] - [ProducesResponseType(200)] // file - [ProducesResponseType(404)] // not found - public IActionResult DownloadSidecar(string f) + if ( !ExtensionRolesHelper.IsExtensionSidecar(f) ) { - if ( !ExtensionRolesHelper.IsExtensionSidecar(f) ) - { - return NotFound("FileName is not a sidecar"); - } + return NotFound("FileName is not a sidecar"); + } - if ( !_iStorage.ExistFile(f) ) - return NotFound($"source image missing {f}"); + if ( !_iStorage.ExistFile(f) ) + { + return NotFound($"source image missing {f}"); + } - var fs = _iStorage.ReadStream(f); - return File(fs, MimeHelper.GetMimeTypeByFileName(f)); + var fs = _iStorage.ReadStream(f); + return File(fs, MimeHelper.GetMimeTypeByFileName(f)); + } + + /// + /// Select manually the original or thumbnail + /// + /// string, 'sub path' to find the file + /// true = 1000px thumb (if supported) + /// true = send client headers to cache + /// FileStream with image + /// returns content of the file + /// source image missing + /// "Thumbnail generation failed" + /// User unauthorized + [HttpGet("/api/download-photo")] + [ProducesResponseType(200)] // file + [ProducesResponseType(404)] // not found + [ProducesResponseType(500)] // "Thumbnail generation failed" + public async Task DownloadPhoto(string f, bool isThumbnail = true, + bool cache = true) + { + if ( !ModelState.IsValid ) + { + return BadRequest("ModelState is not valid"); } - /// - /// Select manually the original or thumbnail - /// - /// string, 'sub path' to find the file - /// true = 1000px thumb (if supported) - /// true = send client headers to cache - /// FileStream with image - /// returns content of the file - /// source image missing - /// "Thumbnail generation failed" - /// User unauthorized - [HttpGet("/api/download-photo")] - [ProducesResponseType(200)] // file - [ProducesResponseType(404)] // not found - [ProducesResponseType(500)] // "Thumbnail generation failed" - public async Task DownloadPhoto(string f, bool isThumbnail = true, - bool cache = true) + if ( f.Contains("?isthumbnail") ) { // f = subpath/filepath - if ( f.Contains("?isthumbnail") ) - { - return NotFound("please use &isthumbnail = instead of ?isthumbnail= "); - } + return NotFound("please use &isthumbnail = instead of ?isthumbnail= "); + } - var fileIndexItem = await _query.GetObjectByFilePathAsync(f); - if ( fileIndexItem == null ) + var fileIndexItem = await _query.GetObjectByFilePathAsync(f); + if ( fileIndexItem == null ) + { + return NotFound("not in index " + f); + } + + if ( !_iStorage.ExistFile(fileIndexItem.FilePath!) ) + { + return NotFound($"source image missing {fileIndexItem.FilePath}"); + } + + // Return full image + if ( !isThumbnail ) + { + if ( cache ) { - return NotFound("not in index " + f); + CacheControlOverwrite.SetExpiresResponseHeaders(Request); } - if ( !_iStorage.ExistFile(fileIndexItem.FilePath!) ) - return NotFound($"source image missing {fileIndexItem.FilePath}"); + var fileStream = _iStorage.ReadStream(fileIndexItem.FilePath!); - // Return full image - if ( !isThumbnail ) - { - if ( cache ) CacheControlOverwrite.SetExpiresResponseHeaders(Request); - var fileStream = _iStorage.ReadStream(fileIndexItem.FilePath!); + // Return the right mime type (enableRangeProcessing = needed for safari and mp4) + return File(fileStream, MimeHelper.GetMimeTypeByFileName(fileIndexItem.FilePath!), + true); + } - // Return the right mime type (enableRangeProcessing = needed for safari and mp4) - return File(fileStream, MimeHelper.GetMimeTypeByFileName(fileIndexItem.FilePath!), - true); - } + if ( !_thumbnailStorage.ExistFolder("/") ) + { + return NotFound("ThumbnailTempFolder not found"); + } - if ( !_thumbnailStorage.ExistFolder("/") ) - { - return NotFound("ThumbnailTempFolder not found"); - } + var data = new ThumbnailSizesExistStatusModel + { + Small = _thumbnailStorage.ExistFile( + ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Small)), + Large = _thumbnailStorage.ExistFile( + ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Large)), + ExtraLarge = _thumbnailStorage.ExistFile( + ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.ExtraLarge)) + }; - var data = new ThumbnailSizesExistStatusModel - { - Small = _thumbnailStorage.ExistFile( - ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Small)), - Large = _thumbnailStorage.ExistFile( - ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Large)), - ExtraLarge = _thumbnailStorage.ExistFile( - ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.ExtraLarge)) - }; - - if ( !data.Small || !data.Large || !data.ExtraLarge ) + if ( !data.Small || !data.Large || !data.ExtraLarge ) + { + _logger.LogDebug("Thumbnail generation started"); + await _thumbnailService.CreateThumbAsync(fileIndexItem.FilePath!, + fileIndexItem.FileHash!); + + if ( !_thumbnailStorage.ExistFile( + ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, + ThumbnailSize.Large)) ) { - _logger.LogDebug("Thumbnail generation started"); - await _thumbnailService.CreateThumbAsync(fileIndexItem.FilePath!, - fileIndexItem.FileHash!); - - if ( !_thumbnailStorage.ExistFile( - ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, - ThumbnailSize.Large)) ) - { - Response.StatusCode = 500; - return Json("Thumbnail generation failed"); - } + Response.StatusCode = 500; + return Json("Thumbnail generation failed"); } - - var thumbnailFileStream = _thumbnailStorage.ReadStream( - ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Large)); - return File(thumbnailFileStream, "image/jpeg"); } + + var thumbnailFileStream = _thumbnailStorage.ReadStream( + ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Large)); + return File(thumbnailFileStream, "image/jpeg"); } } diff --git a/starsky/starsky/Controllers/ErrorController.cs b/starsky/starsky/Controllers/ErrorController.cs index 5ad82af93d..57689595a5 100644 --- a/starsky/starsky/Controllers/ErrorController.cs +++ b/starsky/starsky/Controllers/ErrorController.cs @@ -3,37 +3,40 @@ using Microsoft.AspNetCore.Mvc; using starsky.foundation.platform.Models; -namespace starsky.Controllers +namespace starsky.Controllers; + +[AllowAnonymous] +public sealed class ErrorController : Controller { - [AllowAnonymous] - public sealed class ErrorController : Controller + private readonly string _clientApp; + + public ErrorController(AppSettings appSettings) { - private readonly string _clientApp; + _clientApp = Path.Combine(appSettings.BaseDirectoryProject, + "clientapp", "build", "index.html"); + } - public ErrorController(AppSettings appSettings) + /// + /// Return Error page (HTML) + /// + /// to add the status code to the response + /// Any Error html page + [HttpGet("/error")] + [Produces("text/html")] + public IActionResult Error(int? statusCode = null) + { + if ( !ModelState.IsValid ) { - _clientApp = Path.Combine(appSettings.BaseDirectoryProject, - "clientapp", "build", "index.html"); + return BadRequest("Model is invalid"); } - /// - /// Return Error page (HTML) - /// - /// to add the status code to the response - /// Any Error html page - [HttpGet("/error")] - [Produces("text/html")] - public IActionResult Error(int? statusCode = null) + if ( statusCode.HasValue ) { - if ( statusCode.HasValue ) - { - // here is the trick - HttpContext.Response.StatusCode = statusCode.Value; - } - - // or "~/error/${statusCode}.html" - return PhysicalFile(_clientApp, "text/html"); + // here is the trick + HttpContext.Response.StatusCode = statusCode.Value; } - } + // or "~/error/${statusCode}.html" + return PhysicalFile(_clientApp, "text/html"); + } } diff --git a/starsky/starsky/Controllers/UploadController.cs b/starsky/starsky/Controllers/UploadController.cs index 61840c98a3..e5ec1643ba 100644 --- a/starsky/starsky/Controllers/UploadController.cs +++ b/starsky/starsky/Controllers/UploadController.cs @@ -15,7 +15,6 @@ using starsky.foundation.database.Interfaces; using starsky.foundation.database.Models; using starsky.foundation.http.Streaming; -using starsky.foundation.thumbnailmeta.Interfaces; using starsky.foundation.platform.Enums; using starsky.foundation.platform.Helpers; using starsky.foundation.platform.Interfaces; @@ -24,301 +23,324 @@ using starsky.foundation.storage.Interfaces; using starsky.foundation.storage.Storage; using starsky.foundation.sync.SyncServices; +using starsky.foundation.thumbnailmeta.Interfaces; + +namespace starsky.Controllers; -namespace starsky.Controllers +[Authorize] // <- should be logged in! +[SuppressMessage("Usage", "S5693:Make sure the content " + + "length limit is safe here", Justification = "Is checked")] +public sealed class UploadController : Controller { - [Authorize] // <- should be logged in! - [SuppressMessage("Usage", "S5693:Make sure the content " + - "length limit is safe here", Justification = "Is checked")] - public sealed class UploadController : Controller + private readonly AppSettings _appSettings; + private readonly IStorage _iHostStorage; + private readonly IImport _import; + private readonly IStorage _iStorage; + private readonly IWebLogger _logger; + private readonly IMetaExifThumbnailService _metaExifThumbnailService; + private readonly IMetaUpdateStatusThumbnailService _metaUpdateStatusThumbnailService; + private readonly IQuery _query; + private readonly IRealtimeConnectionsService _realtimeService; + private readonly ISelectorStorage _selectorStorage; + + [SuppressMessage("Usage", + "S107: Constructor has 8 parameters, which is greater than the 7 authorized")] + public UploadController(IImport import, AppSettings appSettings, + ISelectorStorage selectorStorage, IQuery query, + IRealtimeConnectionsService realtimeService, IWebLogger logger, + IMetaExifThumbnailService metaExifThumbnailService, + IMetaUpdateStatusThumbnailService metaUpdateStatusThumbnailService) { - private readonly AppSettings _appSettings; - private readonly IImport _import; - private readonly IStorage _iStorage; - private readonly IStorage _iHostStorage; - private readonly IQuery _query; - private readonly ISelectorStorage _selectorStorage; - private readonly IRealtimeConnectionsService _realtimeService; - private readonly IWebLogger _logger; - private readonly IMetaExifThumbnailService _metaExifThumbnailService; - private readonly IMetaUpdateStatusThumbnailService _metaUpdateStatusThumbnailService; - - [SuppressMessage("Usage", - "S107: Constructor has 8 parameters, which is greater than the 7 authorized")] - public UploadController(IImport import, AppSettings appSettings, - ISelectorStorage selectorStorage, IQuery query, - IRealtimeConnectionsService realtimeService, IWebLogger logger, - IMetaExifThumbnailService metaExifThumbnailService, - IMetaUpdateStatusThumbnailService metaUpdateStatusThumbnailService) + _appSettings = appSettings; + _import = import; + _query = query; + _selectorStorage = selectorStorage; + _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath); + _iHostStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem); + _realtimeService = realtimeService; + _logger = logger; + _metaExifThumbnailService = metaExifThumbnailService; + _metaUpdateStatusThumbnailService = + metaUpdateStatusThumbnailService; + } + + /// + /// Upload to specific folder (does not check if already has been imported) + /// Use the header 'to' to determine the location to where to upload + /// Add header 'filename' when uploading direct without form + /// (ActionResult UploadToFolder) + /// + /// done + /// folder not found + /// Wrong input (e.g. wrong extenstion type) + /// missing 'to' header + /// the ImportIndexItem of the imported files + [HttpPost("/api/upload")] + [DisableFormValueModelBinding] + [RequestFormLimits(MultipartBodyLengthLimit = 320_000_000)] + [RequestSizeLimit(320_000_000)] // in bytes, 305MB + [ProducesResponseType(typeof(List), 200)] // yes + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(List), 404)] + [ProducesResponseType(typeof(List), + 415)] // Wrong input (e.g. wrong extenstion type) + [Produces("application/json")] + [SuppressMessage("Usage", "S6932: Use model binding instead of accessing the raw request data")] + public async Task UploadToFolder() + { + var to = Request.Headers["to"].ToString(); + if ( string.IsNullOrWhiteSpace(to) ) { - _appSettings = appSettings; - _import = import; - _query = query; - _selectorStorage = selectorStorage; - _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath); - _iHostStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem); - _realtimeService = realtimeService; - _logger = logger; - _metaExifThumbnailService = metaExifThumbnailService; - _metaUpdateStatusThumbnailService = - metaUpdateStatusThumbnailService; + return BadRequest("missing 'to' header"); } - /// - /// Upload to specific folder (does not check if already has been imported) - /// Use the header 'to' to determine the location to where to upload - /// Add header 'filename' when uploading direct without form - /// (ActionResult UploadToFolder) - /// - /// done - /// folder not found - /// Wrong input (e.g. wrong extenstion type) - /// missing 'to' header - /// the ImportIndexItem of the imported files - [HttpPost("/api/upload")] - [DisableFormValueModelBinding] - [RequestFormLimits(MultipartBodyLengthLimit = 320_000_000)] - [RequestSizeLimit(320_000_000)] // in bytes, 305MB - [ProducesResponseType(typeof(List), 200)] // yes - [ProducesResponseType(typeof(string), 400)] - [ProducesResponseType(typeof(List), 404)] - [ProducesResponseType(typeof(List), - 415)] // Wrong input (e.g. wrong extenstion type) - [Produces("application/json")] - public async Task UploadToFolder() + var parentDirectory = GetParentDirectoryFromRequestHeader(); + if ( parentDirectory == null ) { - var to = Request.Headers["to"].ToString(); - if ( string.IsNullOrWhiteSpace(to) ) return BadRequest("missing 'to' header"); + return NotFound(new ImportIndexItem { Status = ImportStatus.ParentDirectoryNotFound }); + } + + var tempImportPaths = await Request.StreamFile(_appSettings, _selectorStorage); + + var fileIndexResultsList = await _import.Preflight(tempImportPaths, + new ImportSettingsModel { IndexMode = false }); + // fail/pass, right type, string=subPath, string?2= error reason + var metaResultsList = new List<(bool, bool, string, string?)>(); - var parentDirectory = GetParentDirectoryFromRequestHeader(); - if ( parentDirectory == null ) + for ( var i = 0; i < fileIndexResultsList.Count; i++ ) + { + if ( fileIndexResultsList[i].Status != ImportStatus.Ok ) { - return NotFound(new ImportIndexItem - { - Status = ImportStatus.ParentDirectoryNotFound - }); + continue; } - var tempImportPaths = await Request.StreamFile(_appSettings, _selectorStorage); + var tempFileStream = _iHostStorage.ReadStream(tempImportPaths[i]); - var fileIndexResultsList = await _import.Preflight(tempImportPaths, - new ImportSettingsModel { IndexMode = false }); - // fail/pass, right type, string=subPath, string?2= error reason - var metaResultsList = new List<(bool, bool, string, string?)>(); + var fileName = Path.GetFileName(tempImportPaths[i]); - for ( var i = 0; i < fileIndexResultsList.Count; i++ ) + // subPath is always unix style + var subPath = PathHelper.AddSlash(parentDirectory) + fileName; + if ( parentDirectory == "/" ) { - if ( fileIndexResultsList[i].Status != ImportStatus.Ok ) - { - continue; - } - - var tempFileStream = _iHostStorage.ReadStream(tempImportPaths[i]); - - var fileName = Path.GetFileName(tempImportPaths[i]); - - // subPath is always unix style - var subPath = PathHelper.AddSlash(parentDirectory) + fileName; - if ( parentDirectory == "/" ) subPath = parentDirectory + fileName; - - // to get the output in the result right - fileIndexResultsList[i].FileIndexItem!.FileName = fileName; - fileIndexResultsList[i].FileIndexItem!.ParentDirectory = parentDirectory; - fileIndexResultsList[i].FilePath = subPath; - // Do sync action before writing it down - fileIndexResultsList[i].FileIndexItem = - await SyncItem(fileIndexResultsList[i].FileIndexItem!); - - var writeStatus = - await _iStorage.WriteStreamAsync(tempFileStream, subPath + ".tmp"); - // Is already flushed / disposed when the stream is written - - // to avoid partly written stream to be read by an other application - _iStorage.FileDelete(subPath); - _iStorage.FileMove(subPath + ".tmp", subPath); - _logger.LogInformation($"[UploadController] write {subPath} is {writeStatus}"); - - // clear directory cache - _query.RemoveCacheParentItem(subPath); - - var deleteStatus = _iHostStorage.FileDelete(tempImportPaths[i]); - _logger.LogInformation( - $"[UploadController] delete {tempImportPaths[i]} is {deleteStatus}"); - - var parentPath = Directory.GetParent(tempImportPaths[i])?.FullName; - if ( !string.IsNullOrEmpty(parentPath) && parentPath != _appSettings.TempFolder ) - { - _iHostStorage.FolderDelete(parentPath); - } - - metaResultsList.Add(( await _metaExifThumbnailService.AddMetaThumbnail(subPath, - fileIndexResultsList[i].FileIndexItem!.FileHash!) )); + subPath = parentDirectory + fileName; } - // send all uploads as list - var socketResult = fileIndexResultsList - .Where(p => p.Status == ImportStatus.Ok) - .Select(item => item.FileIndexItem).Cast().ToList(); + // to get the output in the result right + fileIndexResultsList[i].FileIndexItem!.FileName = fileName; + fileIndexResultsList[i].FileIndexItem!.ParentDirectory = parentDirectory; + fileIndexResultsList[i].FilePath = subPath; + // Do sync action before writing it down + fileIndexResultsList[i].FileIndexItem = + await SyncItem(fileIndexResultsList[i].FileIndexItem!); + + var writeStatus = + await _iStorage.WriteStreamAsync(tempFileStream, subPath + ".tmp"); + // Is already flushed / disposed when the stream is written - var webSocketResponse = new ApiNotificationResponseModel>( - socketResult, ApiNotificationType.UploadFile); - await _realtimeService.NotificationToAllAsync(webSocketResponse, - CancellationToken.None); + // to avoid partly written stream to be read by an other application + _iStorage.FileDelete(subPath); + _iStorage.FileMove(subPath + ".tmp", subPath); + _logger.LogInformation($"[UploadController] write {subPath} is {writeStatus}"); - await _metaUpdateStatusThumbnailService.UpdateStatusThumbnail(metaResultsList); + // clear directory cache + _query.RemoveCacheParentItem(subPath); - // Wrong input (extension is not allowed) - if ( fileIndexResultsList.TrueForAll(p => p.Status == ImportStatus.FileError) ) + var deleteStatus = _iHostStorage.FileDelete(tempImportPaths[i]); + _logger.LogInformation( + $"[UploadController] delete {tempImportPaths[i]} is {deleteStatus}"); + + var parentPath = Directory.GetParent(tempImportPaths[i])?.FullName; + if ( !string.IsNullOrEmpty(parentPath) && parentPath != _appSettings.TempFolder ) { - _logger.LogInformation($"Wrong input extension is not allowed" + - $" {string.Join(",", fileIndexResultsList.Select(p => p.FilePath))}"); - Response.StatusCode = 415; + _iHostStorage.FolderDelete(parentPath); } - return Json(fileIndexResultsList); + metaResultsList.Add(await _metaExifThumbnailService.AddMetaThumbnail(subPath, + fileIndexResultsList[i].FileIndexItem!.FileHash!)); } - /// - /// Perform database updates - /// - /// to update to - /// updated item - private async Task SyncItem(FileIndexItem metaDataItem) + // send all uploads as list + var socketResult = fileIndexResultsList + .Where(p => p.Status == ImportStatus.Ok) + .Select(item => item.FileIndexItem).Cast().ToList(); + + var webSocketResponse = new ApiNotificationResponseModel>( + socketResult, ApiNotificationType.UploadFile); + await _realtimeService.NotificationToAllAsync(webSocketResponse, + CancellationToken.None); + + await _metaUpdateStatusThumbnailService.UpdateStatusThumbnail(metaResultsList); + + // Wrong input (extension is not allowed) + if ( fileIndexResultsList.TrueForAll(p => p.Status == ImportStatus.FileError) ) { - var itemFromDatabase = await _query.GetObjectByFilePathAsync(metaDataItem.FilePath!); - if ( itemFromDatabase == null ) - { - AddOrRemoveXmpSidecarFileToDatabase(metaDataItem); - await _query.AddItemAsync(metaDataItem); - return metaDataItem; - } + _logger.LogInformation($"Wrong input extension is not allowed" + + $" {string.Join(",", fileIndexResultsList.Select(p => p.FilePath))}"); + Response.StatusCode = 415; + } - FileIndexCompareHelper.Compare(itemFromDatabase, metaDataItem); - AddOrRemoveXmpSidecarFileToDatabase(metaDataItem); + return Json(fileIndexResultsList); + } - await _query.UpdateItemAsync(itemFromDatabase); - return itemFromDatabase; + /// + /// Perform database updates + /// + /// to update to + /// updated item + private async Task SyncItem(FileIndexItem metaDataItem) + { + var itemFromDatabase = await _query.GetObjectByFilePathAsync(metaDataItem.FilePath!); + if ( itemFromDatabase == null ) + { + AddOrRemoveXmpSidecarFileToDatabase(metaDataItem); + await _query.AddItemAsync(metaDataItem); + return metaDataItem; } - private void AddOrRemoveXmpSidecarFileToDatabase(FileIndexItem metaDataItem) + FileIndexCompareHelper.Compare(itemFromDatabase, metaDataItem); + AddOrRemoveXmpSidecarFileToDatabase(metaDataItem); + + await _query.UpdateItemAsync(itemFromDatabase); + return itemFromDatabase; + } + + private void AddOrRemoveXmpSidecarFileToDatabase(FileIndexItem metaDataItem) + { + if ( _iStorage.ExistFile(ExtensionRolesHelper.ReplaceExtensionWithXmp(metaDataItem + .FilePath)) ) { - if ( _iStorage.ExistFile(ExtensionRolesHelper.ReplaceExtensionWithXmp(metaDataItem - .FilePath)) ) - { - metaDataItem.AddSidecarExtension("xmp"); - return; - } + metaDataItem.AddSidecarExtension("xmp"); + return; + } + + metaDataItem.RemoveSidecarExtension("xmp"); + } - metaDataItem.RemoveSidecarExtension("xmp"); + /// + /// Check if xml can be parsed + /// Used by sidecar upload + /// + /// string with xml + /// true when parsed + private bool IsValidXml(string xml) + { + try + { + // ReSharper disable once ReturnValueOfPureMethodIsNotUsed + XDocument.Parse(xml); + return true; + } + catch + { + _logger.LogInformation("[IsValidXml] non valid xml"); + return false; } + } - /// - /// Check if xml can be parsed - /// Used by sidecar upload - /// - /// string with xml - /// true when parsed - private bool IsValidXml(string xml) + /// + /// Upload sidecar file to specific folder (does not check if already has been imported) + /// Use the header 'to' to determine the location to where to upload + /// Add header 'filename' when uploading direct without form + /// (ActionResult UploadToFolderSidecarFile) + /// + /// done + /// parent folder not found + /// Wrong input (e.g. wrong extenstion type) + /// missing 'to' header + /// the ImportIndexItem of the imported files + [HttpPost("/api/upload-sidecar")] + [DisableFormValueModelBinding] + [RequestFormLimits(MultipartBodyLengthLimit = 3_000_000)] + [RequestSizeLimit(3_000_000)] // in bytes, 3 MB + [ProducesResponseType(typeof(List), 200)] // yes + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(List), 404)] // parent dir not found + [ProducesResponseType(typeof(List), + 415)] // Wrong input (e.g. wrong extenstion type) + [Produces("application/json")] + [SuppressMessage("Usage", "S6932: Use model binding instead of accessing the raw request data")] + public async Task UploadToFolderSidecarFile() + { + var to = Request.Headers["to"].ToString(); + if ( string.IsNullOrWhiteSpace(to) ) { - try - { - // ReSharper disable once ReturnValueOfPureMethodIsNotUsed - XDocument.Parse(xml); - return true; - } - catch - { - _logger.LogInformation("[IsValidXml] non valid xml"); - return false; - } + return BadRequest("missing 'to' header"); } - /// - /// Upload sidecar file to specific folder (does not check if already has been imported) - /// Use the header 'to' to determine the location to where to upload - /// Add header 'filename' when uploading direct without form - /// (ActionResult UploadToFolderSidecarFile) - /// - /// done - /// parent folder not found - /// Wrong input (e.g. wrong extenstion type) - /// missing 'to' header - /// the ImportIndexItem of the imported files - [HttpPost("/api/upload-sidecar")] - [DisableFormValueModelBinding] - [RequestFormLimits(MultipartBodyLengthLimit = 3_000_000)] - [RequestSizeLimit(3_000_000)] // in bytes, 3 MB - [ProducesResponseType(typeof(List), 200)] // yes - [ProducesResponseType(typeof(string), 400)] - [ProducesResponseType(typeof(List), 404)] // parent dir not found - [ProducesResponseType(typeof(List), - 415)] // Wrong input (e.g. wrong extenstion type) - [Produces("application/json")] - public async Task UploadToFolderSidecarFile() + _logger.LogInformation($"[UploadToFolderSidecarFile] to:{to}"); + + var parentDirectory = GetParentDirectoryFromRequestHeader(); + if ( parentDirectory == null ) { - var to = Request.Headers["to"].ToString(); - if ( string.IsNullOrWhiteSpace(to) ) return BadRequest("missing 'to' header"); - _logger.LogInformation($"[UploadToFolderSidecarFile] to:{to}"); + return NotFound(new ImportIndexItem()); + } + + var tempImportPaths = await Request.StreamFile(_appSettings, _selectorStorage); - var parentDirectory = GetParentDirectoryFromRequestHeader(); - if ( parentDirectory == null ) + var importedList = new List(); + foreach ( var tempImportSinglePath in tempImportPaths ) + { + var data = await StreamToStringHelper.StreamToStringAsync( + _iHostStorage.ReadStream(tempImportSinglePath)); + if ( !IsValidXml(data) ) { - return NotFound(new ImportIndexItem()); + continue; } - var tempImportPaths = await Request.StreamFile(_appSettings, _selectorStorage); + var tempFileStream = _iHostStorage.ReadStream(tempImportSinglePath); + var fileName = Path.GetFileName(tempImportSinglePath); - var importedList = new List(); - foreach ( var tempImportSinglePath in tempImportPaths ) + var subPath = PathHelper.AddSlash(parentDirectory) + fileName; + if ( parentDirectory == "/" ) { - var data = await StreamToStringHelper.StreamToStringAsync( - _iHostStorage.ReadStream(tempImportSinglePath)); - if ( !IsValidXml(data) ) continue; - - var tempFileStream = _iHostStorage.ReadStream(tempImportSinglePath); - var fileName = Path.GetFileName(tempImportSinglePath); - - var subPath = PathHelper.AddSlash(parentDirectory) + fileName; - if ( parentDirectory == "/" ) subPath = parentDirectory + fileName; - - if ( _appSettings.UseDiskWatcher == false ) - { - await new SyncSingleFile(_appSettings, _query, - _iStorage, null!, _logger).UpdateSidecarFile(subPath); - } - - await _iStorage.WriteStreamAsync(tempFileStream, subPath); - await tempFileStream.DisposeAsync(); - importedList.Add(subPath); - - var deleteStatus = _iHostStorage.FileDelete(tempImportSinglePath); - _logger.LogInformation($"delete {tempImportSinglePath} is {deleteStatus}"); + subPath = parentDirectory + fileName; } - if ( importedList.Count == 0 ) + if ( _appSettings.UseDiskWatcher == false ) { - Response.StatusCode = 415; + await new SyncSingleFile(_appSettings, _query, + _iStorage, null!, _logger).UpdateSidecarFile(subPath); } - return Json(importedList); + await _iStorage.WriteStreamAsync(tempFileStream, subPath); + await tempFileStream.DisposeAsync(); + importedList.Add(subPath); + + var deleteStatus = _iHostStorage.FileDelete(tempImportSinglePath); + _logger.LogInformation($"delete {tempImportSinglePath} is {deleteStatus}"); } - internal string? GetParentDirectoryFromRequestHeader() + if ( importedList.Count == 0 ) { - var to = Request.Headers["to"].ToString(); - if ( to == "/" ) return "/"; + Response.StatusCode = 415; + } - // only used for direct import - if ( _iStorage.ExistFolder(FilenamesHelper.GetParentPath(to)) && - FilenamesHelper.IsValidFileName(FilenamesHelper.GetFileName(to)) ) - { - Request.Headers["filename"] = FilenamesHelper.GetFileName(to); - return FilenamesHelper.GetParentPath(PathHelper.RemoveLatestSlash(to)); - } + return Json(importedList); + } + + [SuppressMessage("Usage", "S6932: Use model binding instead of accessing the raw request data")] + internal string? GetParentDirectoryFromRequestHeader() + { + var to = Request.Headers["to"].ToString(); + if ( to == "/" ) + { + return "/"; + } + + // only used for direct import + if ( _iStorage.ExistFolder(FilenamesHelper.GetParentPath(to)) && + FilenamesHelper.IsValidFileName(FilenamesHelper.GetFileName(to)) ) + { + Request.Headers["filename"] = FilenamesHelper.GetFileName(to); + return FilenamesHelper.GetParentPath(PathHelper.RemoveLatestSlash(to)); + } - // ReSharper disable once ConvertIfStatementToReturnStatement - if ( !_iStorage.ExistFolder(PathHelper.RemoveLatestSlash(to)) ) return null; - return PathHelper.RemoveLatestSlash(to); + // ReSharper disable once ConvertIfStatementToReturnStatement + if ( !_iStorage.ExistFolder(PathHelper.RemoveLatestSlash(to)) ) + { + return null; } + + return PathHelper.RemoveLatestSlash(to); } } diff --git a/starsky/starskytest/Controllers/AllowedTypesControllerTest.cs b/starsky/starskytest/Controllers/AllowedTypesControllerTest.cs index 20540ca6a1..5fd86c048e 100644 --- a/starsky/starskytest/Controllers/AllowedTypesControllerTest.cs +++ b/starsky/starskytest/Controllers/AllowedTypesControllerTest.cs @@ -4,43 +4,62 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using starsky.Controllers; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public sealed class AllowedTypesControllerTest { - [TestClass] - public sealed class AllowedTypesControllerTest + private readonly HttpContext _httpContext = new DefaultHttpContext(); + + [TestMethod] + public void AllowedTypesController_MimetypeSync() + { + var jsonResult = new AllowedTypesController().AllowedTypesMimetypeSync() as JsonResult; + var allowedApiResult = jsonResult?.Value as HashSet; + Assert.IsTrue(allowedApiResult?.Contains("image/jpeg")); + } + + [TestMethod] + public void AllowedTypesController_MimetypeSyncThumb() { - private readonly HttpContext _httpContext = new DefaultHttpContext(); - - [TestMethod] - public void AllowedTypesController_MimetypeSync() - { - var jsonResult = new AllowedTypesController().AllowedTypesMimetypeSync() as JsonResult; - var allowedApiResult = jsonResult?.Value as HashSet; - Assert.IsTrue(allowedApiResult?.Contains("image/jpeg")); - } - - [TestMethod] - public void AllowedTypesController_MimetypeSyncThumb() - { - var jsonResult = new AllowedTypesController().AllowedTypesMimetypeSyncThumb() as JsonResult; - var allowedApiResult = jsonResult?.Value as HashSet; - Assert.IsTrue(allowedApiResult?.Contains("image/jpeg")); - } - - [TestMethod] - public void AllowedTypesController_AllowedTypesThumb_NoInput() - { - var jsonResult = new AllowedTypesController{ ControllerContext = {HttpContext = _httpContext}}.AllowedTypesThumb("") as JsonResult; - var allowedApiResult = bool.Parse(jsonResult?.Value?.ToString()!); - Assert.IsFalse(allowedApiResult); - } - - [TestMethod] - public void AllowedTypesController_AllowedTypesThumb_Example() - { - var jsonResult = new AllowedTypesController{ ControllerContext = {HttpContext = _httpContext}}.AllowedTypesThumb("test.jpg") as JsonResult; - var allowedApiResult = bool.Parse(jsonResult?.Value?.ToString()!); - Assert.IsTrue(allowedApiResult); - } + var jsonResult = new AllowedTypesController().AllowedTypesMimetypeSyncThumb() as JsonResult; + var allowedApiResult = jsonResult?.Value as HashSet; + Assert.IsTrue(allowedApiResult?.Contains("image/jpeg")); + } + + [TestMethod] + public void AllowedTypesController_AllowedTypesThumb_NoInput() + { + var jsonResult = + new AllowedTypesController { ControllerContext = { HttpContext = _httpContext } } + .AllowedTypesThumb("") as JsonResult; + var allowedApiResult = bool.Parse(jsonResult?.Value?.ToString()!); + Assert.IsFalse(allowedApiResult); + } + + [TestMethod] + public void AllowedTypesController_AllowedTypesThumb_Example() + { + var jsonResult = + new AllowedTypesController { ControllerContext = { HttpContext = _httpContext } } + .AllowedTypesThumb("test.jpg") as JsonResult; + var allowedApiResult = bool.Parse(jsonResult?.Value?.ToString()!); + Assert.IsTrue(allowedApiResult); + } + + [TestMethod] + public void AllowedTypesController_AllowedTypesThumb_ReturnsBadRequest() + { + // Arrange + var controller = + new AllowedTypesController { ControllerContext = { HttpContext = _httpContext } }; + + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + // Act + var result = controller.AllowedTypesThumb("test.jpg"); + + // Assert + Assert.IsInstanceOfType(result); } } diff --git a/starsky/starskytest/Controllers/AppSettingsControllerTest.cs b/starsky/starskytest/Controllers/AppSettingsControllerTest.cs index d16a7bc7a4..7f6b488097 100644 --- a/starsky/starskytest/Controllers/AppSettingsControllerTest.cs +++ b/starsky/starskytest/Controllers/AppSettingsControllerTest.cs @@ -13,173 +13,189 @@ using starsky.foundation.storage.Helpers; using starskytest.FakeMocks; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public sealed class AppSettingsControllerTest { - [TestClass] - public sealed class AppSettingsControllerTest + [TestMethod] + public void ENV_StarskyTestEnv() { - [TestMethod] - public void ENV_StarskyTestEnv() - { - var controller = - new AppSettingsController(new AppSettings(), new FakeIUpdateAppSettingsByPath()); - var actionResult = controller.Env() as JsonResult; - var resultAppSettings = actionResult?.Value as AppSettings; - Assert.AreEqual("Starsky", resultAppSettings?.Name); - } - - [TestMethod] - public void ENV_StarskyTestEnv_ForceHtml() - { - var controller = new AppSettingsController(new AppSettings(), - new FakeIUpdateAppSettingsByPath()); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); - controller.ControllerContext.HttpContext.Request.Headers.Append("x-force-html", "true"); - var actionResult = controller.Env() as JsonResult; - var resultAppSettings = actionResult?.Value as AppSettings; - Assert.AreEqual("Starsky", resultAppSettings?.Name); - Assert.AreEqual("text/html; charset=utf-8", - controller.ControllerContext.HttpContext.Response.Headers.ContentType.ToString()); - } - - [TestMethod] - public async Task UpdateAppSettings_Verbose() - { - var appSettings = new AppSettings(); - var storage = new FakeIStorage(new List { "/" }); - var controller = new AppSettingsController(appSettings, - new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage))); - - var actionResult = - await controller.UpdateAppSettings(new AppSettingsTransferObject { Verbose = true }) - as JsonResult; - var result = actionResult?.Value as AppSettings; - Assert.IsTrue(result?.Verbose); - } - - [TestMethod] - public async Task UpdateAppSettings_StorageFolder() - { - var appSettings = new AppSettings(); - var controller = new AppSettingsController(appSettings, new UpdateAppSettingsByPath( - appSettings, - new FakeSelectorStorage( - new FakeIStorage(new List { $"{Path.DirectorySeparatorChar}test" })))); - - controller.ControllerContext.HttpContext = new DefaultHttpContext(); - - var actionResult = await controller.UpdateAppSettings(new AppSettingsTransferObject - { - Verbose = true, StorageFolder = $"{Path.DirectorySeparatorChar}test" - }) as JsonResult; - - var result = actionResult?.Value as AppSettings; - Assert.IsTrue(result?.Verbose); - Assert.AreEqual(Path.DirectorySeparatorChar + PathHelper.AddBackslash("test"), - result?.StorageFolder); - } - - [TestMethod] - public async Task UpdateAppSettingsTest_IgnoreWhenEnvIsSet() + var controller = + new AppSettingsController(new AppSettings(), new FakeIUpdateAppSettingsByPath()); + var actionResult = controller.Env() as JsonResult; + var resultAppSettings = actionResult?.Value as AppSettings; + Assert.AreEqual("Starsky", resultAppSettings?.Name); + } + + [TestMethod] + public void ENV_StarskyTestEnv_ForceHtml() + { + var controller = new AppSettingsController(new AppSettings(), + new FakeIUpdateAppSettingsByPath()); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + controller.ControllerContext.HttpContext.Request.Headers.Append("x-force-html", "true"); + var actionResult = controller.Env() as JsonResult; + var resultAppSettings = actionResult?.Value as AppSettings; + Assert.AreEqual("Starsky", resultAppSettings?.Name); + Assert.AreEqual("text/html; charset=utf-8", + controller.ControllerContext.HttpContext.Response.Headers.ContentType.ToString()); + } + + [TestMethod] + public async Task UpdateAppSettings_Verbose() + { + var appSettings = new AppSettings(); + var storage = new FakeIStorage(new List { "/" }); + var controller = new AppSettingsController(appSettings, + new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage))); + + var actionResult = + await controller.UpdateAppSettings(new AppSettingsTransferObject { Verbose = true }) + as JsonResult; + var result = actionResult?.Value as AppSettings; + Assert.IsTrue(result?.Verbose); + } + + [TestMethod] + public async Task UpdateAppSettings_StorageFolder() + { + var appSettings = new AppSettings(); + var controller = new AppSettingsController(appSettings, new UpdateAppSettingsByPath( + appSettings, + new FakeSelectorStorage( + new FakeIStorage(new List { $"{Path.DirectorySeparatorChar}test" })))); + + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + + var actionResult = await controller.UpdateAppSettings(new AppSettingsTransferObject { - Environment.SetEnvironmentVariable("app__storageFolder", - "any_value"); - - var appSettings = new AppSettings(); - var controller = new AppSettingsController(appSettings, - new FakeIUpdateAppSettingsByPath( - new UpdateAppSettingsStatusModel { StatusCode = 403 })); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); - await controller.UpdateAppSettings( - new AppSettingsTransferObject { StorageFolder = "test" }); + Verbose = true, StorageFolder = $"{Path.DirectorySeparatorChar}test" + }) as JsonResult; + + var result = actionResult?.Value as AppSettings; + Assert.IsTrue(result?.Verbose); + Assert.AreEqual(Path.DirectorySeparatorChar + PathHelper.AddBackslash("test"), + result?.StorageFolder); + } + + [TestMethod] + public async Task UpdateAppSettingsTest_IgnoreWhenEnvIsSet() + { + Environment.SetEnvironmentVariable("app__storageFolder", + "any_value"); + + var appSettings = new AppSettings(); + var controller = new AppSettingsController(appSettings, + new FakeIUpdateAppSettingsByPath( + new UpdateAppSettingsStatusModel { StatusCode = 403 })); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + await controller.UpdateAppSettings( + new AppSettingsTransferObject { StorageFolder = "test" }); + + Assert.AreEqual(403, controller.Response.StatusCode); + } + + [TestMethod] + public async Task UpdateAppSettingsTest_DirNotFound() + { + var appSettings = new AppSettings(); + var controller = new AppSettingsController(appSettings, + new FakeIUpdateAppSettingsByPath( + new UpdateAppSettingsStatusModel { StatusCode = 404 })); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); - Assert.AreEqual(403, controller.Response.StatusCode); - } + await controller.UpdateAppSettings( + new AppSettingsTransferObject { StorageFolder = "not_found" }); - [TestMethod] - public async Task UpdateAppSettingsTest_DirNotFound() + Assert.AreEqual(404, controller.Response.StatusCode); + } + + [TestMethod] + public async Task UpdateAppSettingsTest_StorageFolder_JsonCheck() + { + var storage = new FakeIStorage(new List { "test" }); + Environment.SetEnvironmentVariable("app__storageFolder", string.Empty); + + var appSettings = new AppSettings { - var appSettings = new AppSettings(); - var controller = new AppSettingsController(appSettings, - new FakeIUpdateAppSettingsByPath( - new UpdateAppSettingsStatusModel { StatusCode = 404 })); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); + AppSettingsPath = + $"{Path.DirectorySeparatorChar}temp{Path.DirectorySeparatorChar}appsettings.json" + }; + var controller = new AppSettingsController(appSettings, + new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage))); + await controller.UpdateAppSettings( + new AppSettingsTransferObject { Verbose = true, StorageFolder = "test" }); + + Assert.IsTrue(storage.ExistFile(appSettings.AppSettingsPath)); + + var jsonContent = await StreamToStringHelper.StreamToStringAsync( + storage.ReadStream(appSettings.AppSettingsPath)); + Assert.IsTrue(jsonContent.Contains("app\": {")); + Assert.IsTrue(jsonContent.Contains("\"StorageFolder\": \"")); + } + + [TestMethod] + public async Task UpdateAppSettings_UseLocalDesktop() + { + var appSettings = new AppSettings(); + var storage = new FakeIStorage(new List { "/" }); + var controller = new AppSettingsController(appSettings, + new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage))); + + var actionResult = await controller.UpdateAppSettings( - new AppSettingsTransferObject { StorageFolder = "not_found" }); + new AppSettingsTransferObject { UseLocalDesktop = true }) as JsonResult; + var result = actionResult?.Value as AppSettings; + Assert.IsTrue(result?.UseLocalDesktop); + } - Assert.AreEqual(404, controller.Response.StatusCode); - } + [TestMethod] + public async Task UpdateAppSettings_UseSystemTrash() + { + var appSettings = new AppSettings(); + var storage = new FakeIStorage(new List { "/" }); + var controller = new AppSettingsController(appSettings, + new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage))); - [TestMethod] - public async Task UpdateAppSettingsTest_StorageFolder_JsonCheck() - { - var storage = new FakeIStorage(new List { "test" }); - Environment.SetEnvironmentVariable("app__storageFolder", string.Empty); - - var appSettings = new AppSettings - { - AppSettingsPath = - $"{Path.DirectorySeparatorChar}temp{Path.DirectorySeparatorChar}appsettings.json" - }; - var controller = new AppSettingsController(appSettings, - new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage))); + var actionResult = await controller.UpdateAppSettings( - new AppSettingsTransferObject { Verbose = true, StorageFolder = "test" }); + new AppSettingsTransferObject { UseSystemTrash = true }) as JsonResult; + var result = actionResult?.Value as AppSettings; + Assert.IsTrue(result?.UseSystemTrash); + } - Assert.IsTrue(storage.ExistFile(appSettings.AppSettingsPath)); + [TestMethod] + public async Task UpdateAppSettings_Verbose_IgnoreSystemTrashValue() + { + var appSettings = new AppSettings(); + var storage = new FakeIStorage(new List { "/" }); + var controller = new AppSettingsController(appSettings, + new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage))); - var jsonContent = await StreamToStringHelper.StreamToStringAsync( - storage.ReadStream(appSettings.AppSettingsPath)); + var actionResult = + await controller.UpdateAppSettings(new AppSettingsTransferObject { Verbose = true }) + as JsonResult; + var result = actionResult?.Value as AppSettings; - Assert.IsTrue(jsonContent.Contains("app\": {")); - Assert.IsTrue(jsonContent.Contains("\"StorageFolder\": \"")); - } + Assert.AreEqual(appSettings.UseSystemTrash, result?.UseSystemTrash); + } - [TestMethod] - public async Task UpdateAppSettings_UseLocalDesktop() - { - var appSettings = new AppSettings(); - var storage = new FakeIStorage(new List { "/" }); - var controller = new AppSettingsController(appSettings, - new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage))); - - var actionResult = - await controller.UpdateAppSettings( - new AppSettingsTransferObject { UseLocalDesktop = true }) as JsonResult; - var result = actionResult?.Value as AppSettings; - Assert.IsTrue(result?.UseLocalDesktop); - } - - [TestMethod] - public async Task UpdateAppSettings_UseSystemTrash() - { - var appSettings = new AppSettings(); - var storage = new FakeIStorage(new List { "/" }); - var controller = new AppSettingsController(appSettings, - new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage))); - - var actionResult = - await controller.UpdateAppSettings( - new AppSettingsTransferObject { UseSystemTrash = true }) as JsonResult; - var result = actionResult?.Value as AppSettings; - Assert.IsTrue(result?.UseSystemTrash); - } - - [TestMethod] - public async Task UpdateAppSettings_Verbose_IgnoreSystemTrashValue() - { - var appSettings = new AppSettings(); - var storage = new FakeIStorage(new List { "/" }); - var controller = new AppSettingsController(appSettings, - new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage))); - - var actionResult = - await controller.UpdateAppSettings(new AppSettingsTransferObject { Verbose = true }) - as JsonResult; - var result = actionResult?.Value as AppSettings; - - Assert.AreEqual(appSettings.UseSystemTrash, result?.UseSystemTrash); - } + [TestMethod] + public async Task UpdateAppSettings_AllowedTypesThumb_ReturnsBadRequest() + { + // Arrange + var appSettings = new AppSettings(); + var controller = new AppSettingsController(appSettings, + new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(new FakeIStorage()))); + + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + // Act + var result = await controller.UpdateAppSettings(null!); + + // Assert + Assert.IsInstanceOfType(result); } } diff --git a/starsky/starskytest/Controllers/CacheIndexControllerTest.cs b/starsky/starskytest/Controllers/CacheIndexControllerTest.cs index 841639ca04..b303ebcf1d 100644 --- a/starsky/starskytest/Controllers/CacheIndexControllerTest.cs +++ b/starsky/starskytest/Controllers/CacheIndexControllerTest.cs @@ -21,197 +21,225 @@ using starskytest.FakeCreateAn; using starskytest.FakeMocks; +namespace starskytest.Controllers; -namespace starskytest.Controllers +[TestClass] +public sealed class CacheIndexControllerTest { - [TestClass] - public sealed class CacheIndexControllerTest + private readonly AppSettings _appSettings; + private readonly ApplicationDbContext _context; + private readonly Query _query; + + public CacheIndexControllerTest() + { + var provider = new ServiceCollection() + .AddMemoryCache() + .BuildServiceProvider(); + var memoryCache = provider.GetService(); + + var builderDb = new DbContextOptionsBuilder(); + builderDb.UseInMemoryDatabase("test1234"); + var options = builderDb.Options; + _context = new ApplicationDbContext(options); + _query = new Query(_context, new AppSettings(), null!, new FakeIWebLogger(), + memoryCache); + + // Inject Fake ExifTool; dependency injection + var services = new ServiceCollection(); + + // Fake the readMeta output + services.AddSingleton(); + + // Inject Config helper + services.AddSingleton(new ConfigurationBuilder().Build()); + // random config + var createAnImage = new CreateAnImage(); + var dict = new Dictionary + { + { "App:StorageFolder", createAnImage.BasePath }, + { "App:ThumbnailTempFolder", createAnImage.BasePath }, + { "App:Verbose", "true" } + }; + // Start using dependency injection + var builder = new ConfigurationBuilder(); + // Add random config to dependency injection + builder.AddInMemoryCollection(dict); + // build config + var configuration = builder.Build(); + // inject config as object to a service + services.ConfigurePoCo(configuration.GetSection("App")); + + // Add Background services + services.AddSingleton(); + services.AddSingleton(); + + // build the service + var serviceProvider = services.BuildServiceProvider(); + // get the service + _appSettings = serviceProvider.GetRequiredService(); + } + + [TestMethod] + public async Task CacheIndexController_CheckIfCacheIsRemoved_CleanCache() { - private readonly Query _query; - private readonly AppSettings _appSettings; - private readonly ApplicationDbContext _context; + // Act + var controller = new CacheIndexController(_query, _appSettings); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); - public CacheIndexControllerTest() + await _query.AddItemAsync(new FileIndexItem { - var provider = new ServiceCollection() - .AddMemoryCache() - .BuildServiceProvider(); - var memoryCache = provider.GetService(); - - var builderDb = new DbContextOptionsBuilder(); - builderDb.UseInMemoryDatabase("test1234"); - var options = builderDb.Options; - _context = new ApplicationDbContext(options); - _query = new Query(_context, new AppSettings(), null!, new FakeIWebLogger(), - memoryCache); - - // Inject Fake ExifTool; dependency injection - var services = new ServiceCollection(); - - // Fake the readMeta output - services.AddSingleton(); - - // Inject Config helper - services.AddSingleton(new ConfigurationBuilder().Build()); - // random config - var createAnImage = new CreateAnImage(); - var dict = new Dictionary - { - { "App:StorageFolder", createAnImage.BasePath }, - { "App:ThumbnailTempFolder", createAnImage.BasePath }, - { "App:Verbose", "true" } - }; - // Start using dependency injection - var builder = new ConfigurationBuilder(); - // Add random config to dependency injection - builder.AddInMemoryCollection(dict); - // build config - var configuration = builder.Build(); - // inject config as object to a service - services.ConfigurePoCo(configuration.GetSection("App")); - - // Add Background services - services.AddSingleton(); - services.AddSingleton(); - - // build the service - var serviceProvider = services.BuildServiceProvider(); - // get the service - _appSettings = serviceProvider.GetRequiredService(); - } - - [TestMethod] - public async Task CacheIndexController_CheckIfCacheIsRemoved_CleanCache() + FileName = "cacheDeleteTest", ParentDirectory = "/", IsDirectory = true + }); + + await _query.AddItemAsync(new FileIndexItem { - // Act - var controller = new CacheIndexController(_query, _appSettings); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); + FileName = "file.jpg", ParentDirectory = "/cacheDeleteTest", IsDirectory = false + }); - await _query.AddItemAsync(new FileIndexItem - { - FileName = "cacheDeleteTest", ParentDirectory = "/", IsDirectory = true - }); + Assert.IsTrue(_query.DisplayFileFolders("/cacheDeleteTest").Any()); - await _query.AddItemAsync(new FileIndexItem - { - FileName = "file.jpg", ParentDirectory = "/cacheDeleteTest", IsDirectory = false - }); + // Ask the cache + _query.DisplayFileFolders("/cacheDeleteTest"); - Assert.IsTrue(_query.DisplayFileFolders("/cacheDeleteTest").Any()); + // Don't notify the cache that there is an update + var newItem = new FileIndexItem + { + FileName = "file2.jpg", ParentDirectory = "/cacheDeleteTest", IsDirectory = false + }; + _context.FileIndex.Add(newItem); + _context.SaveChanges(); + // Write changes to database + + // Check if there is one item in the cache + var beforeQuery = _query.DisplayFileFolders("/cacheDeleteTest"); + Assert.AreEqual(1, beforeQuery.Count()); + + // Act, remove content from cache + var actionResult = controller.RemoveCache("/cacheDeleteTest") as JsonResult; + Assert.AreEqual("cache successful cleared", actionResult?.Value); + + // Check if there are now two items in the cache + var newQuery = _query.DisplayFileFolders("/cacheDeleteTest"); + Assert.AreEqual(2, newQuery.Count()); + } - // Ask the cache - _query.DisplayFileFolders("/cacheDeleteTest"); + [TestMethod] + public async Task RemoveCache_CacheDidNotExist() + { + // Act + var controller = new CacheIndexController(_query, _appSettings); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); - // Don't notify the cache that there is an update - var newItem = new FileIndexItem - { - FileName = "file2.jpg", - ParentDirectory = "/cacheDeleteTest", - IsDirectory = false - }; - _context.FileIndex.Add(newItem); - _context.SaveChanges(); - // Write changes to database - - // Check if there is one item in the cache - var beforeQuery = _query.DisplayFileFolders("/cacheDeleteTest"); - Assert.AreEqual(1, beforeQuery.Count()); - - // Act, remove content from cache - var actionResult = controller.RemoveCache("/cacheDeleteTest") as JsonResult; - Assert.AreEqual("cache successful cleared", actionResult?.Value); - - // Check if there are now two items in the cache - var newQuery = _query.DisplayFileFolders("/cacheDeleteTest"); - Assert.AreEqual(2, newQuery.Count()); - } - - [TestMethod] - public async Task RemoveCache_CacheDidNotExist() + await _query.AddItemAsync(new FileIndexItem { - // Act - var controller = new CacheIndexController(_query, _appSettings); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); + FileName = "cacheDeleteTest2", ParentDirectory = "/", IsDirectory = true + }); - await _query.AddItemAsync(new FileIndexItem - { - FileName = "cacheDeleteTest2", ParentDirectory = "/", IsDirectory = true - }); + // Act, remove content from cache + var actionResult = controller.RemoveCache("/cacheDeleteTest2") as JsonResult; + Assert.AreEqual("cache did not exist", actionResult?.Value); + } - // Act, remove content from cache - var actionResult = controller.RemoveCache("/cacheDeleteTest2") as JsonResult; - Assert.AreEqual("cache did not exist", actionResult?.Value); - } + [TestMethod] + public void RemoveCache_ReturnsBadRequest() + { + // Arrange + var controller = new CacheIndexController(_query, _appSettings); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); - [TestMethod] - public void CacheIndexController_NonExistingCacheRemove() - { - // Act - var controller = new CacheIndexController(_query, _appSettings); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); + controller.ModelState.AddModelError("Key", "ErrorMessage"); - var actionResult = controller.RemoveCache("/404page") as BadRequestObjectResult; - Assert.AreEqual(400, actionResult?.StatusCode); - } + // Act + var result = controller.RemoveCache(null!); - [TestMethod] - public void CacheIndexController_CacheDisabled() - { - var controller = - new CacheIndexController(_query, new AppSettings { AddMemoryCache = false }); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); + // Assert + Assert.IsInstanceOfType(result); + } - var actionResult = controller.RemoveCache("/404page") as JsonResult; - Assert.AreEqual("cache disabled in config", actionResult?.Value); - } + [TestMethod] + public void CacheIndexController_NonExistingCacheRemove() + { + // Act + var controller = new CacheIndexController(_query, _appSettings); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); - [TestMethod] - public void ListCache_CacheDidNotExist() - { - // Act - var controller = new CacheIndexController(_query, _appSettings); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); - - // Act, remove content from cache - var actionResult = controller.ListCache("/cacheDeleteTest2") as BadRequestObjectResult; - Assert.AreEqual("ignored, please check if the 'f' path " + - "exist or use a folder string to get the cache", actionResult?.Value); - } - - [TestMethod] - public void ListCache_CacheDisabled() - { - var controller = - new CacheIndexController(_query, new AppSettings { AddMemoryCache = false }); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); + var actionResult = controller.RemoveCache("/404page") as BadRequestObjectResult; + Assert.AreEqual(400, actionResult?.StatusCode); + } + + [TestMethod] + public void CacheIndexController_CacheDisabled() + { + var controller = + new CacheIndexController(_query, new AppSettings { AddMemoryCache = false }); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); - var actionResult = controller.ListCache("/404page") as JsonResult; - Assert.AreEqual("cache disabled in config", actionResult?.Value); - } + var actionResult = controller.RemoveCache("/404page") as JsonResult; + Assert.AreEqual("cache disabled in config", actionResult?.Value); + } - [TestMethod] - public void ListCache_GetCache() - { - // Act - var controller = new CacheIndexController(_query, _appSettings); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); + [TestMethod] + public void ListCache_ReturnsBadRequest() + { + // Arrange + var controller = new CacheIndexController(_query, _appSettings); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + // Act + var result = controller.ListCache(null!); + + // Assert + Assert.IsInstanceOfType(result); + } + + [TestMethod] + public void ListCache_CacheDidNotExist() + { + // Act + var controller = new CacheIndexController(_query, _appSettings); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + + // Act, remove content from cache + var actionResult = controller.ListCache("/cacheDeleteTest2") as BadRequestObjectResult; + Assert.AreEqual("ignored, please check if the 'f' path " + + "exist or use a folder string to get the cache", actionResult?.Value); + } + + [TestMethod] + public void ListCache_CacheDisabled() + { + var controller = + new CacheIndexController(_query, new AppSettings { AddMemoryCache = false }); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + + var actionResult = controller.ListCache("/404page") as JsonResult; + Assert.AreEqual("cache disabled in config", actionResult?.Value); + } - _query.AddCacheParentItem("/list-cache", - new List + [TestMethod] + public void ListCache_GetCache() + { + // Act + var controller = new CacheIndexController(_query, _appSettings); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + + _query.AddCacheParentItem("/list-cache", + new List + { + new() { - new FileIndexItem - { - FileName = "cacheDeleteTest2", - ParentDirectory = "/list-cache", - IsDirectory = true - } - }); - - // Act, remove content from cache - var actionResult = controller.ListCache("/list-cache") as JsonResult; - - Assert.IsNotNull(actionResult); - Assert.IsNotNull(actionResult.Value); - } + FileName = "cacheDeleteTest2", + ParentDirectory = "/list-cache", + IsDirectory = true + } + }); + + // Act, remove content from cache + var actionResult = controller.ListCache("/list-cache") as JsonResult; + + Assert.IsNotNull(actionResult); + Assert.IsNotNull(actionResult.Value); } } diff --git a/starsky/starskytest/Controllers/DeleteControllerTest.cs b/starsky/starskytest/Controllers/DeleteControllerTest.cs index e855bfd09a..8627599d4f 100644 --- a/starsky/starskytest/Controllers/DeleteControllerTest.cs +++ b/starsky/starskytest/Controllers/DeleteControllerTest.cs @@ -26,169 +26,195 @@ using starskytest.FakeCreateAn; using starskytest.FakeMocks; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public sealed class DeleteControllerTest { - [TestClass] - public sealed class DeleteControllerTest + private readonly AppSettings _appSettings; + private readonly CreateAnImage _createAnImage; + private readonly IStorage _iStorage; + + private readonly Query _query; + + public DeleteControllerTest() { - - private readonly Query _query; - private readonly AppSettings _appSettings; - private readonly CreateAnImage _createAnImage; - private readonly IStorage _iStorage; + var provider = new ServiceCollection() + .AddMemoryCache() + .BuildServiceProvider(); + var memoryCache = provider.GetService(); + + var builderDb = new DbContextOptionsBuilder(); + builderDb.UseInMemoryDatabase("test1234"); + var options = builderDb.Options; + var context = new ApplicationDbContext(options); + _query = new Query(context, + new AppSettings(), null, new FakeIWebLogger(), memoryCache); - public DeleteControllerTest() + // Inject Fake ExifTool; dependency injection + var services = new ServiceCollection(); + + // Fake the readMeta output + services.AddSingleton(); + + // Inject Config helper + services.AddSingleton(new ConfigurationBuilder().Build()); + // random config + _createAnImage = new CreateAnImage(); + var dict = new Dictionary { - var provider = new ServiceCollection() - .AddMemoryCache() - .BuildServiceProvider(); - var memoryCache = provider.GetService(); - - var builderDb = new DbContextOptionsBuilder(); - builderDb.UseInMemoryDatabase("test1234"); - var options = builderDb.Options; - var context = new ApplicationDbContext(options); - _query = new Query(context, - new AppSettings(), null, new FakeIWebLogger(),memoryCache); - - // Inject Fake ExifTool; dependency injection - var services = new ServiceCollection(); - - // Fake the readMeta output - services.AddSingleton(); - - // Inject Config helper - services.AddSingleton(new ConfigurationBuilder().Build()); - // random config - _createAnImage = new CreateAnImage(); - var dict = new Dictionary - { - { "App:StorageFolder", _createAnImage.BasePath }, - { "App:ThumbnailTempFolder",_createAnImage.BasePath }, - { "App:Verbose", "true" } - }; - // Start using dependency injection - var builder = new ConfigurationBuilder(); - // Add random config to dependency injection - builder.AddInMemoryCollection(dict); - // build config - var configuration = builder.Build(); - // inject config as object to a service - services.ConfigurePoCo(configuration.GetSection("App")); - - // Add Background services - services.AddSingleton(); - services.AddSingleton(); - - // build the service - var serviceProvider = services.BuildServiceProvider(); - // get the service - _appSettings = serviceProvider.GetRequiredService(); - - _iStorage = new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()); - } - - private async Task InsertSearchData(bool delete = false) + { "App:StorageFolder", _createAnImage.BasePath }, + { "App:ThumbnailTempFolder", _createAnImage.BasePath }, + { "App:Verbose", "true" } + }; + // Start using dependency injection + var builder = new ConfigurationBuilder(); + // Add random config to dependency injection + builder.AddInMemoryCollection(dict); + // build config + var configuration = builder.Build(); + // inject config as object to a service + services.ConfigurePoCo(configuration.GetSection("App")); + + // Add Background services + services.AddSingleton(); + services.AddSingleton(); + + // build the service + var serviceProvider = services.BuildServiceProvider(); + // get the service + _appSettings = serviceProvider.GetRequiredService(); + + _iStorage = new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()); + } + + private async Task InsertSearchData(bool delete = false) + { + var fileHashCode = new FileHash(_iStorage).GetHashCode(_createAnImage.DbPath).Key; + + if ( string.IsNullOrEmpty(await _query.GetSubPathByHashAsync(fileHashCode)) ) { - var fileHashCode = new FileHash(_iStorage).GetHashCode(_createAnImage.DbPath).Key; - - if (string.IsNullOrEmpty(await _query.GetSubPathByHashAsync(fileHashCode))) + var isDelete = string.Empty; + if ( delete ) { - var isDelete = string.Empty; - if (delete) isDelete = TrashKeyword.TrashKeywordString; - await _query.AddItemAsync(new FileIndexItem - { - FileName = _createAnImage.FileName, - ParentDirectory = "/", - FileHash = fileHashCode, - ColorClass = ColorClassParser.Color.Winner, // 1 - Tags = isDelete - }); + isDelete = TrashKeyword.TrashKeywordString; } - return _query.GetObjectByFilePath(_createAnImage.DbPath); - } - - [TestMethod] - public async Task ApiController_Delete_API_HappyFlow_Test() - { - var createAnImage = await InsertSearchData(true); - _appSettings.DatabaseType = AppSettings.DatabaseTypeList.InMemoryDatabase; - - // RealFs Storage - var selectorStorage = new FakeSelectorStorage(new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger())); - - var deleteItem = new DeleteItem(_query,_appSettings,selectorStorage); - var controller = new DeleteController(deleteItem); - - Console.WriteLine("createAnImage.FilePath"); - Console.WriteLine("@#~ "+ createAnImage?.FilePath); - - // create an image - var createAnImage1 = new CreateAnImage(); - Assert.IsNotNull(createAnImage1); - - var actionResult = await controller.Delete(createAnImage?.FilePath!) as JsonResult; - Assert.AreNotEqual(null,actionResult); - var jsonCollection = actionResult?.Value as List; - Assert.AreEqual(createAnImage?.FilePath,jsonCollection?.FirstOrDefault()?.FilePath); - - var createAnImage2 = new CreateAnImage(); //restore afterwards - Assert.IsNotNull(createAnImage2); - } - - [TestMethod] - public async Task ApiController_Delete_API_RemoveNotAllowedFile_Test() - { - // re add data - var createAnImage = await InsertSearchData(); - Assert.IsNotNull(createAnImage?.FilePath); - - // Clean existing items to avoid errors - var itemByHash = _query.SingleItem(createAnImage.FilePath); - Assert.IsNotNull(itemByHash); - Assert.IsNotNull(itemByHash.FileIndexItem); - - itemByHash.FileIndexItem.Tags = string.Empty; - await _query.UpdateItemAsync(itemByHash.FileIndexItem); - - _appSettings.DatabaseType = AppSettings.DatabaseTypeList.InMemoryDatabase; - - var selectorStorage = - new FakeSelectorStorage(new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger())); - - var deleteItem = new DeleteItem(_query,_appSettings,selectorStorage); - var controller = new DeleteController(deleteItem); - - var notFoundResult = await controller.Delete(createAnImage.FilePath) as NotFoundObjectResult; - Assert.AreEqual(404,notFoundResult?.StatusCode); - var jsonCollection = notFoundResult?.Value as List; - - Assert.AreEqual(FileIndexItem.ExifStatus.OperationNotSupported, - jsonCollection?.FirstOrDefault()?.Status); - - await _query.RemoveItemAsync(_query.SingleItem(createAnImage.FilePath)?.FileIndexItem!); - } - - - [TestMethod] - public async Task ApiController_Delete_SourceImageMissingOnDisk_WithFakeExiftool() - { await _query.AddItemAsync(new FileIndexItem { - FileName = "345678765434567.jpg", + FileName = _createAnImage.FileName, ParentDirectory = "/", - FileHash = "345678765434567" + FileHash = fileHashCode, + ColorClass = ColorClassParser.Color.Winner, // 1 + Tags = isDelete }); - - var selectorStorage = new FakeSelectorStorage(new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger())); - var deleteItem = new DeleteItem(_query,_appSettings,selectorStorage); - var controller = new DeleteController(deleteItem); - var notFoundResult = await controller.Delete("/345678765434567.jpg") as NotFoundObjectResult; - Assert.AreEqual(404,notFoundResult?.StatusCode); - - await _query.RemoveItemAsync(_query.SingleItem("/345678765434567.jpg")?.FileIndexItem!); } + return _query.GetObjectByFilePath(_createAnImage.DbPath); + } + + + [TestMethod] + public async Task ApiController_Delete_API_HappyFlow_Test() + { + var createAnImage = await InsertSearchData(true); + _appSettings.DatabaseType = AppSettings.DatabaseTypeList.InMemoryDatabase; + + // RealFs Storage + var selectorStorage = + new FakeSelectorStorage( + new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger())); + + var deleteItem = new DeleteItem(_query, _appSettings, selectorStorage); + var controller = new DeleteController(deleteItem); + + Console.WriteLine("createAnImage.FilePath"); + Console.WriteLine("@#~ " + createAnImage?.FilePath); + + // create an image + var createAnImage1 = new CreateAnImage(); + Assert.IsNotNull(createAnImage1); + + var actionResult = await controller.Delete(createAnImage?.FilePath!) as JsonResult; + Assert.AreNotEqual(null, actionResult); + var jsonCollection = actionResult?.Value as List; + Assert.AreEqual(createAnImage?.FilePath, jsonCollection?.FirstOrDefault()?.FilePath); + + var createAnImage2 = new CreateAnImage(); //restore afterwards + Assert.IsNotNull(createAnImage2); + } + + [TestMethod] + public async Task Delete_ReturnsBadRequest() + { + // Arrange + var deleteItem = new DeleteItem(_query, _appSettings, new FakeSelectorStorage()); + var controller = new DeleteController(deleteItem); + + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + // Act + var result = await controller.Delete(null!); + + // Assert + Assert.IsInstanceOfType(result); + } + + [TestMethod] + public async Task ApiController_Delete_API_RemoveNotAllowedFile_Test() + { + // re add data + var createAnImage = await InsertSearchData(); + Assert.IsNotNull(createAnImage?.FilePath); + + // Clean existing items to avoid errors + var itemByHash = _query.SingleItem(createAnImage.FilePath); + Assert.IsNotNull(itemByHash); + Assert.IsNotNull(itemByHash.FileIndexItem); + + itemByHash.FileIndexItem.Tags = string.Empty; + await _query.UpdateItemAsync(itemByHash.FileIndexItem); + + _appSettings.DatabaseType = AppSettings.DatabaseTypeList.InMemoryDatabase; + + var selectorStorage = + new FakeSelectorStorage( + new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger())); + + var deleteItem = new DeleteItem(_query, _appSettings, selectorStorage); + var controller = new DeleteController(deleteItem); + + var notFoundResult = + await controller.Delete(createAnImage.FilePath) as NotFoundObjectResult; + Assert.AreEqual(404, notFoundResult?.StatusCode); + var jsonCollection = notFoundResult?.Value as List; + + Assert.AreEqual(FileIndexItem.ExifStatus.OperationNotSupported, + jsonCollection?.FirstOrDefault()?.Status); + + await _query.RemoveItemAsync(_query.SingleItem(createAnImage.FilePath)?.FileIndexItem!); + } + + + [TestMethod] + public async Task ApiController_Delete_SourceImageMissingOnDisk_WithFakeExiftool() + { + await _query.AddItemAsync(new FileIndexItem + { + FileName = "345678765434567.jpg", + ParentDirectory = "/", + FileHash = "345678765434567" + }); + + var selectorStorage = + new FakeSelectorStorage( + new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger())); + var deleteItem = new DeleteItem(_query, _appSettings, selectorStorage); + var controller = new DeleteController(deleteItem); + var notFoundResult = + await controller.Delete("/345678765434567.jpg") as NotFoundObjectResult; + Assert.AreEqual(404, notFoundResult?.StatusCode); + + await _query.RemoveItemAsync(_query.SingleItem("/345678765434567.jpg")?.FileIndexItem!); } } diff --git a/starsky/starskytest/Controllers/DesktopEditorControllerTest.cs b/starsky/starskytest/Controllers/DesktopEditorControllerTest.cs index ef09835d47..12468292ed 100644 --- a/starsky/starskytest/Controllers/DesktopEditorControllerTest.cs +++ b/starsky/starskytest/Controllers/DesktopEditorControllerTest.cs @@ -31,13 +31,30 @@ public void OpenAmountConfirmationChecker_FeatureToggleEnabled() var result = controller.OpenAmountConfirmationChecker("/test.jpg;/test2.jpg"); - var castedResult = ( JsonResult )result; - var boolValue = ( bool? )castedResult.Value; + var castedResult = ( JsonResult ) result; + var boolValue = ( bool? ) castedResult.Value; // mock is always true Assert.IsTrue(boolValue); } + [TestMethod] + public void OpenAmountConfirmationChecker_ReturnsBadRequest() + { + // Arrange + var controller = new DesktopEditorController(new OpenEditorDesktopService(new AppSettings(), + new FakeIOpenApplicationNativeService(new List(), "test"), + new FakeIOpenEditorPreflight(new List()))); + + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + // Act + var result = controller.OpenAmountConfirmationChecker(null!); + + // Assert + Assert.IsInstanceOfType(result); + } + [TestMethod] public async Task OpenAsync_FeatureToggleDisabled() { @@ -51,11 +68,28 @@ public async Task OpenAsync_FeatureToggleDisabled() }; var result = await controller.OpenAsync("/test.jpg;/test2.jpg"); - var castedResult = ( BadRequestObjectResult )result; + var castedResult = ( BadRequestObjectResult ) result; Assert.AreEqual(400, castedResult.StatusCode); } + [TestMethod] + public async Task OpenAsync_ReturnsBadRequest() + { + // Arrange + var controller = new DesktopEditorController(new OpenEditorDesktopService(new AppSettings(), + new FakeIOpenApplicationNativeService(new List(), "test"), + new FakeIOpenEditorPreflight(new List()))); + + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + // Act + var result = await controller.OpenAsync(null!); + + // Assert + Assert.IsInstanceOfType(result); + } + [TestMethod] public async Task OpenAsync_NoResultsBack() { @@ -72,8 +106,8 @@ public async Task OpenAsync_NoResultsBack() var result = await controller.OpenAsync("/test.jpg;/test2.jpg"); Assert.AreEqual(206, controller.HttpContext.Response.StatusCode); - var castedResult = ( JsonResult )result; - var arrayValues = ( List? )castedResult.Value; + var castedResult = ( JsonResult ) result; + var arrayValues = ( List? ) castedResult.Value; Assert.AreEqual(0, arrayValues?.Count); } @@ -83,7 +117,7 @@ public async Task OpenAsync_HappyFlow() { var preflight = new FakeIOpenEditorPreflight(new List { - new PathImageFormatExistsAppPathModel + new() { AppPath = "test", Status = FileIndexItem.ExifStatus.Ok, @@ -105,8 +139,8 @@ public async Task OpenAsync_HappyFlow() var result = await controller.OpenAsync("/test.jpg;/test2.jpg"); Assert.AreEqual(200, controller.HttpContext.Response.StatusCode); - var castedResult = ( JsonResult )result; - var arrayValues = ( List? )castedResult.Value; + var castedResult = ( JsonResult ) result; + var arrayValues = ( List? ) castedResult.Value; Assert.AreEqual(1, arrayValues?.Count); } diff --git a/starsky/starskytest/Controllers/DiskControllerTest.cs b/starsky/starskytest/Controllers/DiskControllerTest.cs index 72e2471710..b6c34d5593 100644 --- a/starsky/starskytest/Controllers/DiskControllerTest.cs +++ b/starsky/starskytest/Controllers/DiskControllerTest.cs @@ -26,297 +26,315 @@ using starskytest.FakeCreateAn; using starskytest.FakeMocks; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public sealed class DiskControllerTest { - [TestClass] - public sealed class DiskControllerTest + private readonly CreateAnImage _createAnImage; + private readonly Query _query; + private IStorage? _iStorage; + + public DiskControllerTest() { - private readonly Query _query; - private readonly CreateAnImage _createAnImage; - private IStorage? _iStorage; + var provider = new ServiceCollection() + .AddMemoryCache() + .BuildServiceProvider(); + var memoryCache = provider.GetService(); - public DiskControllerTest() - { - var provider = new ServiceCollection() - .AddMemoryCache() - .BuildServiceProvider(); - var memoryCache = provider.GetService(); + var builderDb = new DbContextOptionsBuilder(); + builderDb.UseInMemoryDatabase("SyncControllerTest"); + var options = builderDb.Options; + var context = new ApplicationDbContext(options); - var builderDb = new DbContextOptionsBuilder(); - builderDb.UseInMemoryDatabase("SyncControllerTest"); - var options = builderDb.Options; - var context = new ApplicationDbContext(options); + // Inject Fake Exiftool; dependency injection + var services = new ServiceCollection(); + services.AddSingleton(); - // Inject Fake Exiftool; dependency injection - var services = new ServiceCollection(); - services.AddSingleton(); + // Fake the readmeta output + services.AddSingleton(); - // Fake the readmeta output - services.AddSingleton(); + // Inject Config helper + services.AddSingleton(new ConfigurationBuilder().Build()); + // random config + _createAnImage = new CreateAnImage(); + var dict = new Dictionary + { + { "App:StorageFolder", _createAnImage.BasePath }, + { "App:ThumbnailTempFolder", _createAnImage.BasePath }, + { "App:Verbose", "true" } + }; + // Start using dependency injection + var builder = new ConfigurationBuilder(); + // Add random config to dependency injection + builder.AddInMemoryCollection(dict); + // build config + var configuration = builder.Build(); + // inject config as object to a service + services.ConfigurePoCo(configuration.GetSection("App")); + + // Add Background services + services.AddSingleton(); + services.AddSingleton(); + + // build the service + var serviceProvider = services.BuildServiceProvider(); + // get the service + var appSettings = serviceProvider.GetRequiredService(); + + var scopeFactory = serviceProvider.GetRequiredService(); + _query = new Query(context, appSettings, scopeFactory, new FakeIWebLogger(), + memoryCache); + } - // Inject Config helper - services.AddSingleton(new ConfigurationBuilder().Build()); - // random config - _createAnImage = new CreateAnImage(); - var dict = new Dictionary - { - { "App:StorageFolder", _createAnImage.BasePath }, - { "App:ThumbnailTempFolder", _createAnImage.BasePath }, - { "App:Verbose", "true" } - }; - // Start using dependency injection - var builder = new ConfigurationBuilder(); - // Add random config to dependency injection - builder.AddInMemoryCollection(dict!); - // build config - var configuration = builder.Build(); - // inject config as object to a service - services.ConfigurePoCo(configuration.GetSection("App")); - - // Add Background services - services.AddSingleton(); - services.AddSingleton(); - - // build the service - var serviceProvider = services.BuildServiceProvider(); - // get the service - var appSettings = serviceProvider.GetRequiredService(); - - var scopeFactory = serviceProvider.GetRequiredService(); - _query = new Query(context, appSettings, scopeFactory, new FakeIWebLogger(), - memoryCache); - } + private async Task InsertSearchData() + { + _iStorage = new FakeIStorage(new List { "/" }, + new List { _createAnImage.DbPath }); + var fileHashCode = + ( await new FileHash(_iStorage).GetHashCodeAsync(_createAnImage.DbPath) ).Key; - private async Task InsertSearchData() + if ( string.IsNullOrEmpty(await _query.GetSubPathByHashAsync(fileHashCode)) ) { - _iStorage = new FakeIStorage(new List { "/" }, - new List { _createAnImage.DbPath }); - var fileHashCode = - ( await new FileHash(_iStorage).GetHashCodeAsync(_createAnImage.DbPath) ).Key; + await _query.AddItemAsync(new FileIndexItem + { + FileName = "/", ParentDirectory = "/", IsDirectory = true + }); - if ( string.IsNullOrEmpty(await _query.GetSubPathByHashAsync(fileHashCode)) ) + await _query.AddItemAsync(new FileIndexItem { - await _query.AddItemAsync(new FileIndexItem - { - FileName = "/", ParentDirectory = "/", IsDirectory = true - }); - - await _query.AddItemAsync(new FileIndexItem - { - FileName = _createAnImage.FileName, - ParentDirectory = "/", - FileHash = fileHashCode, - ColorClass = ColorClassParser.Color.Winner, // 1 - }); - } - - _query.GetObjectByFilePath(_createAnImage.DbPath); + FileName = _createAnImage.FileName, + ParentDirectory = "/", + FileHash = fileHashCode, + ColorClass = ColorClassParser.Color.Winner // 1 + }); } + await _query.GetObjectByFilePathAsync(_createAnImage.DbPath); + } - [TestMethod] - public async Task SyncControllerTest_Rename_NotFoundInIndex() - { - var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; - var fakeStorage = new FakeIStorage(); - var storageSelector = new FakeSelectorStorage(fakeStorage); - var controller = new DiskController(_query, storageSelector, - new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()); - controller.ControllerContext = context; + [TestMethod] + public async Task SyncControllerTest_Rename_NotFoundInIndex() + { + var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; + var fakeStorage = new FakeIStorage(); + var storageSelector = new FakeSelectorStorage(fakeStorage); - var result = - await controller.Rename("/notfound-image.jpg", "/test.jpg") as NotFoundObjectResult; + var controller = new DiskController(_query, storageSelector, + new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()); + controller.ControllerContext = context; - Assert.AreEqual(404, result?.StatusCode); - } + var result = + await controller.Rename("/notfound-image.jpg", "/test.jpg") as NotFoundObjectResult; - [TestMethod] - public async Task SyncControllerTest_BadRequest() - { - var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; - var fakeStorage = new FakeIStorage(); - var storageSelector = new FakeSelectorStorage(fakeStorage); + Assert.AreEqual(404, result?.StatusCode); + } - var controller = new DiskController(_query, storageSelector, - new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()); - controller.ControllerContext = context; + [TestMethod] + public async Task Rename_BadRequest() + { + var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; + var fakeStorage = new FakeIStorage(); + var storageSelector = new FakeSelectorStorage(fakeStorage); - var result = - await controller.Rename(string.Empty, "/test.jpg") as BadRequestObjectResult; + var controller = new DiskController(_query, storageSelector, + new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()); + controller.ControllerContext = context; - Assert.AreEqual(400, result?.StatusCode); - } + var result = + await controller.Rename(string.Empty, "/test.jpg") as BadRequestObjectResult; - [TestMethod] - public async Task SyncControllerTest_Rename_Good() - { - await InsertSearchData(); + Assert.AreEqual(400, result?.StatusCode); + } - var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; + [TestMethod] + public async Task Rename_ReturnsBadRequest() + { + var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; + var fakeStorage = new FakeIStorage(); + var storageSelector = new FakeSelectorStorage(fakeStorage); - var fakeStorage = new FakeIStorage(new List { "/" }, - new List { _createAnImage.DbPath }); - var storageSelector = new FakeSelectorStorage(fakeStorage); + var controller = new DiskController(_query, storageSelector, + new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()); + controller.ControllerContext = context; + controller.ModelState.AddModelError("Key", "ErrorMessage"); - var controller = - new DiskController(_query, storageSelector, - new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()) - { - ControllerContext = context - }; + var result = + await controller.Rename(string.Empty, "/test.jpg") as BadRequestObjectResult; - var result = await controller.Rename(_createAnImage.DbPath, "/test.jpg") as JsonResult; - var list = result?.Value as List; + Assert.AreEqual(400, result?.StatusCode); + Assert.IsInstanceOfType(result); + } - Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?.FirstOrDefault()?.Status); + [TestMethod] + public async Task SyncControllerTest_Rename_Good() + { + await InsertSearchData(); - await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!); - } + var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; - [TestMethod] - public async Task SyncControllerTest_Rename_WithCurrentStatusDisabled() - { - await InsertSearchData(); + var fakeStorage = new FakeIStorage(new List { "/" }, + new List { _createAnImage.DbPath }); + var storageSelector = new FakeSelectorStorage(fakeStorage); - var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; + var controller = + new DiskController(_query, storageSelector, + new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()) + { + ControllerContext = context + }; - var fakeStorage = new FakeIStorage(new List { "/" }, - new List { _createAnImage.DbPath }); - var storageSelector = new FakeSelectorStorage(fakeStorage); + var result = await controller.Rename(_createAnImage.DbPath, "/test.jpg") as JsonResult; + var list = result?.Value as List; - var controller = - new DiskController(_query, storageSelector, - new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()) - { - ControllerContext = context - }; + Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?.FirstOrDefault()?.Status); - var result = - await controller.Rename(_createAnImage.DbPath, "/test.jpg", true, false) as - JsonResult; - var list = result?.Value as List; + await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!); + } - Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?[0].Status); - Assert.AreEqual(FileIndexItem.ExifStatus.NotFoundSourceMissing, list?[1].Status); + [TestMethod] + public async Task SyncControllerTest_Rename_WithCurrentStatusDisabled() + { + await InsertSearchData(); - await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!); - } + var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; - [TestMethod] - public async Task SyncControllerTest_Rename_Good_SocketUpdate() - { - await InsertSearchData(); + var fakeStorage = new FakeIStorage(new List { "/" }, + new List { _createAnImage.DbPath }); + var storageSelector = new FakeSelectorStorage(fakeStorage); - var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; - var socket = new FakeIWebSocketConnectionsService(); + var controller = + new DiskController(_query, storageSelector, + new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()) + { + ControllerContext = context + }; - var fakeStorage = new FakeIStorage(new List { "/" }, - new List { _createAnImage.DbPath }); - var storageSelector = new FakeSelectorStorage(fakeStorage); + var result = + await controller.Rename(_createAnImage.DbPath, "/test.jpg", true, false) as + JsonResult; + var list = result?.Value as List; - var controller = - new DiskController(_query, storageSelector, - socket, new FakeINotificationQuery()) { ControllerContext = context }; + Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?[0].Status); + Assert.AreEqual(FileIndexItem.ExifStatus.NotFoundSourceMissing, list?[1].Status); - await controller.Rename(_createAnImage.DbPath, "/test.jpg"); + await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!); + } - Assert.AreEqual(1, socket.FakeSendToAllAsync.Count(p => !p.Contains("[system]"))); - Assert.IsTrue(socket.FakeSendToAllAsync[0].Contains("/test.jpg")); + [TestMethod] + public async Task SyncControllerTest_Rename_Good_SocketUpdate() + { + await InsertSearchData(); - await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!); - } + var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; + var socket = new FakeIWebSocketConnectionsService(); - [TestMethod] - public async Task SyncControllerTest_Mkdir_Good() - { - await InsertSearchData(); - var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; - - var fakeStorage = new FakeIStorage(new List { "/" }, - new List { _createAnImage.DbPath }); - var storageSelector = new FakeSelectorStorage(fakeStorage); - - var controller = - new DiskController(_query, storageSelector, - new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()) - { - ControllerContext = context - }; - - var result = await controller.Mkdir("/test_dir") as JsonResult; - var list = result?.Value as List; - Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?.FirstOrDefault()?.Status); - } + var fakeStorage = new FakeIStorage(new List { "/" }, + new List { _createAnImage.DbPath }); + var storageSelector = new FakeSelectorStorage(fakeStorage); - [TestMethod] - public async Task SyncControllerTest_Mkdir_Good_SocketUpdate() - { - await InsertSearchData(); - var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; + var controller = + new DiskController(_query, storageSelector, + socket, new FakeINotificationQuery()) { ControllerContext = context }; - var socket = new FakeIWebSocketConnectionsService(); - var fakeStorage = new FakeIStorage(new List { "/" }, - new List { _createAnImage.DbPath }); - var storageSelector = new FakeSelectorStorage(fakeStorage); + await controller.Rename(_createAnImage.DbPath, "/test.jpg"); - var controller = - new DiskController(_query, storageSelector, - socket, new FakeINotificationQuery()) { ControllerContext = context }; + Assert.AreEqual(1, socket.FakeSendToAllAsync.Count(p => !p.Contains("[system]"))); + Assert.IsTrue(socket.FakeSendToAllAsync[0].Contains("/test.jpg")); - await controller.Mkdir("/test_dir"); + await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!); + } - var value = socket.FakeSendToAllAsync.Find(p => - !p.StartsWith("[system]")); + [TestMethod] + public async Task SyncControllerTest_Mkdir_Good() + { + await InsertSearchData(); + var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; - Assert.IsNotNull(value); - Assert.IsTrue(value.Contains("/test_dir")); - } + var fakeStorage = new FakeIStorage(new List { "/" }, + new List { _createAnImage.DbPath }); + var storageSelector = new FakeSelectorStorage(fakeStorage); - [TestMethod] - public async Task SyncControllerTest_Mkdir_Exist() - { - await InsertSearchData(); - var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; - - var fakeStorage = new FakeIStorage(new List { "/", "/test_dir" }, - new List { _createAnImage.DbPath }); - var storageSelector = new FakeSelectorStorage(fakeStorage); - - var controller = - new DiskController(_query, storageSelector, - new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()) - { - ControllerContext = context - }; - - var result = await controller.Mkdir("/test_dir") as JsonResult; - var list = result?.Value as List; - Assert.AreEqual(FileIndexItem.ExifStatus.OperationNotSupported, - list?.FirstOrDefault()?.Status); - } + var controller = + new DiskController(_query, storageSelector, + new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()) + { + ControllerContext = context + }; + var result = await controller.Mkdir("/test_dir") as JsonResult; + var list = result?.Value as List; + Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?.FirstOrDefault()?.Status); + } - [TestMethod] - public async Task Mkdir_BadRequest() - { - var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; + [TestMethod] + public async Task SyncControllerTest_Mkdir_Good_SocketUpdate() + { + await InsertSearchData(); + var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; - var fakeStorage = new FakeIStorage(new List { "/", "/test_dir" }, - new List { _createAnImage.DbPath }); - var storageSelector = new FakeSelectorStorage(fakeStorage); + var socket = new FakeIWebSocketConnectionsService(); + var fakeStorage = new FakeIStorage(new List { "/" }, + new List { _createAnImage.DbPath }); + var storageSelector = new FakeSelectorStorage(fakeStorage); - var controller = - new DiskController(_query, storageSelector, - new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()) - { - ControllerContext = context - }; + var controller = + new DiskController(_query, storageSelector, + socket, new FakeINotificationQuery()) { ControllerContext = context }; - await controller.Mkdir(string.Empty); + await controller.Mkdir("/test_dir"); - Assert.AreEqual(400, context.HttpContext.Response.StatusCode); - } + var value = socket.FakeSendToAllAsync.Find(p => + !p.StartsWith("[system]")); + + Assert.IsNotNull(value); + Assert.IsTrue(value.Contains("/test_dir")); + } + + [TestMethod] + public async Task SyncControllerTest_Mkdir_Exist() + { + await InsertSearchData(); + var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; + + var fakeStorage = new FakeIStorage(new List { "/", "/test_dir" }, + new List { _createAnImage.DbPath }); + var storageSelector = new FakeSelectorStorage(fakeStorage); + + var controller = + new DiskController(_query, storageSelector, + new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()) + { + ControllerContext = context + }; + + var result = await controller.Mkdir("/test_dir") as JsonResult; + var list = result?.Value as List; + Assert.AreEqual(FileIndexItem.ExifStatus.OperationNotSupported, + list?.FirstOrDefault()?.Status); + } + + + [TestMethod] + public async Task Mkdir_BadRequest() + { + var context = new ControllerContext { HttpContext = new DefaultHttpContext() }; + + var fakeStorage = new FakeIStorage(new List { "/", "/test_dir" }, + new List { _createAnImage.DbPath }); + var storageSelector = new FakeSelectorStorage(fakeStorage); + + var controller = + new DiskController(_query, storageSelector, + new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()) + { + ControllerContext = context + }; + + await controller.Mkdir(string.Empty); + + Assert.AreEqual(400, context.HttpContext.Response.StatusCode); } } diff --git a/starsky/starskytest/Controllers/DownloadPhotoControllerTest.cs b/starsky/starskytest/Controllers/DownloadPhotoControllerTest.cs index f11005c120..a0eddcfdf7 100644 --- a/starsky/starskytest/Controllers/DownloadPhotoControllerTest.cs +++ b/starsky/starskytest/Controllers/DownloadPhotoControllerTest.cs @@ -14,261 +14,322 @@ using starsky.foundation.database.Query; using starsky.foundation.platform.Helpers; using starsky.foundation.platform.Models; +using starskytest.FakeCreateAn; using starskytest.FakeMocks; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public sealed class DownloadPhotoControllerTest { - [TestClass] - public sealed class DownloadPhotoControllerTest + private readonly Query _query; + + public DownloadPhotoControllerTest() { - private readonly Query _query; + var provider = new ServiceCollection() + .AddMemoryCache() + .BuildServiceProvider(); + var memoryCache = provider.GetService(); + + var builderDb = new DbContextOptionsBuilder(); + builderDb.UseInMemoryDatabase(nameof(DownloadPhotoControllerTest)); + var options = builderDb.Options; + var context = new ApplicationDbContext(options); + var scopeFactory = provider.GetService(); + _query = new Query(context, new AppSettings(), scopeFactory, new FakeIWebLogger(), + memoryCache); + } - public DownloadPhotoControllerTest() + private async Task InsertSearchData() + { + var item = new FileIndexItem { - var provider = new ServiceCollection() - .AddMemoryCache() - .BuildServiceProvider(); - var memoryCache = provider.GetService(); - - var builderDb = new DbContextOptionsBuilder(); - builderDb.UseInMemoryDatabase(nameof(DownloadPhotoControllerTest)); - var options = builderDb.Options; - var context = new ApplicationDbContext(options); - var scopeFactory = provider.GetService(); - _query = new Query(context, new AppSettings(), scopeFactory, new FakeIWebLogger(), memoryCache); - } + FileName = "test.jpg", + ParentDirectory = "/", + FileHash = "/home0012304590.jpg", + ColorClass = ColorClassParser.Color.Winner // 1 + }; - private async Task InsertSearchData() + if ( string.IsNullOrEmpty(await _query.GetSubPathByHashAsync("home0012304590")) ) { - var item = new FileIndexItem + await _query.AddItemAsync(item); + } + + return item; + } + + private static FakeIStorage ArrangeStorage() + { + var folderPaths = new List { "/" }; + var inputSubPaths = new List { "/test.jpg", "/test.xmp", "/corrupt.jpg" }; + var storage = + new FakeIStorage(folderPaths, inputSubPaths, + new List + { + CreateAnImage.Bytes.ToArray(), + CreateAnXmp.Bytes.ToArray(), + Array.Empty() + }); + return storage; + } + + [TestMethod] + public void DownloadSidecar_Ok() + { + // Arrange + var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); + + // Act + var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService()); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + var actionResult = controller.DownloadSidecar("/test.xmp") as FileStreamResult; + Assert.AreNotEqual(null, actionResult); + + actionResult?.FileStream.Dispose(); + } + + [TestMethod] + public void DownloadSidecar_ReturnsBadRequest() + { + // Arrange + var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); + + // Act + var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService()); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var actionResult = controller.DownloadSidecar(null!); + + // Assert + Assert.IsInstanceOfType(actionResult, typeof(BadRequestObjectResult)); + } + + [TestMethod] + public void DownloadSidecar_TryToGetJpeg() + { + // Arrange + var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); + + // Act + var controller = + new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService()) { - FileName = "test.jpg", - ParentDirectory = "/", - FileHash = "/home0012304590.jpg", - ColorClass = ColorClassParser.Color.Winner // 1 + ControllerContext = { HttpContext = new DefaultHttpContext() } }; + var actionResult = controller.DownloadSidecar("/test.jpg") as NotFoundObjectResult; - if ( string.IsNullOrEmpty(await _query.GetSubPathByHashAsync("home0012304590")) ) - { - await _query.AddItemAsync(item); - } - return item; - } + Assert.AreNotEqual(null, actionResult); + Assert.AreEqual(404, actionResult?.StatusCode); + } - private static FakeIStorage ArrangeStorage() - { - var folderPaths = new List{"/"}; - var inputSubPaths = new List{"/test.jpg","/test.xmp", "/corrupt.jpg"}; - var storage = - new FakeIStorage(folderPaths, inputSubPaths, - new List{FakeCreateAn.CreateAnImage.Bytes.ToArray(), - FakeCreateAn.CreateAnXmp.Bytes.ToArray(), Array.Empty()}); - return storage; - } + [TestMethod] + public void DownloadSidecar_NotFound() + { + // Arrange + var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - [TestMethod] - public void DownloadSidecar_Ok() - { - // Arrange - var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - - // Act - var controller = new DownloadPhotoController(_query,selectorStorage, new FakeIWebLogger(), new FakeIThumbnailService()); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); - var actionResult = controller.DownloadSidecar("/test.xmp") as FileStreamResult; - Assert.AreNotEqual(null,actionResult); - - actionResult?.FileStream.Dispose(); - } - - [TestMethod] - public void DownloadSidecar_TryToGetJpeg() - { - // Arrange - var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - - // Act - var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), new FakeIThumbnailService()) + // Act + var controller = + new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService()) { - ControllerContext = {HttpContext = new DefaultHttpContext()} + ControllerContext = { HttpContext = new DefaultHttpContext() } }; - var actionResult = controller.DownloadSidecar("/test.jpg") as NotFoundObjectResult; - - Assert.AreNotEqual(null,actionResult); - Assert.AreEqual(404,actionResult?.StatusCode); - } - - [TestMethod] - public void DownloadSidecar_NotFound() - { - // Arrange - var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - - // Act - var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), new FakeIThumbnailService()) + var actionResult = controller.DownloadSidecar("/not-found.xmp") as NotFoundObjectResult; + + Assert.AreNotEqual(null, actionResult); + Assert.AreEqual(404, actionResult?.StatusCode); + } + + [TestMethod] + public async Task DownloadPhoto_isThumbnailTrue_CreateThumb_ReturnFileStream_Test() + { + // Arrange + var fileIndexItem = await InsertSearchData(); + var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); + + // Act + var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService(selectorStorage)); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + var actionResult = + await controller.DownloadPhoto(fileIndexItem.FilePath!) as FileStreamResult; + Assert.AreNotEqual(null, actionResult); + + await actionResult!.FileStream.DisposeAsync(); + } + + [TestMethod] + public async Task DownloadPhoto_WrongInputNotFound() + { + // Arrange + var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); + + // Act + var controller = + new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService()) { - ControllerContext = {HttpContext = new DefaultHttpContext()} + ControllerContext = { HttpContext = new DefaultHttpContext() } }; - var actionResult = controller.DownloadSidecar("/not-found.xmp") as NotFoundObjectResult; - - Assert.AreNotEqual(null,actionResult); - Assert.AreEqual(404,actionResult?.StatusCode); - } - - [TestMethod] - public async Task DownloadPhoto_isThumbnailTrue_CreateThumb_ReturnFileStream_Test() - { - // Arrange - var fileIndexItem = await InsertSearchData(); - var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - - // Act - var controller = new DownloadPhotoController(_query,selectorStorage, new FakeIWebLogger(), new FakeIThumbnailService(selectorStorage)); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); - var actionResult = await controller.DownloadPhoto(fileIndexItem.FilePath!) as FileStreamResult; - Assert.AreNotEqual(null,actionResult); - - await actionResult!.FileStream.DisposeAsync(); - } - - [TestMethod] - public async Task DownloadPhoto_WrongInputNotFound() - { - // Arrange - var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - - // Act - var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), new FakeIThumbnailService()) + var actionResult = await controller.DownloadPhoto("?isthumbnail") as NotFoundObjectResult; + + Assert.AreNotEqual(null, actionResult); + Assert.AreEqual(404, actionResult?.StatusCode); + } + + [TestMethod] + public async Task DownloadPhoto_ReturnsBadRequest() + { + // Arrange + var controller = + new DownloadPhotoController(_query, new FakeSelectorStorage(), new FakeIWebLogger(), + new FakeIThumbnailService()) { - ControllerContext = {HttpContext = new DefaultHttpContext()} + ControllerContext = { HttpContext = new DefaultHttpContext() } }; - var actionResult = await controller.DownloadPhoto("?isthumbnail") as NotFoundObjectResult; - - Assert.AreNotEqual(null,actionResult); - Assert.AreEqual(404,actionResult?.StatusCode); - } - - [TestMethod] - public async Task DownloadPhotoCorrupt() + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var actionResult = await controller.DownloadPhoto(null!); + + // Assert + Assert.IsInstanceOfType(actionResult, typeof(BadRequestObjectResult)); + } + + [TestMethod] + public async Task DownloadPhotoCorrupt() + { + // Arrange + var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); + + var item = await _query.AddItemAsync(new FileIndexItem { - // Arrange - var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - - var item = await _query.AddItemAsync(new FileIndexItem - { - FileName = "corrupt.jpg", - ParentDirectory = "/", - FileHash = "hash" - }); - - // Act - var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), new FakeIThumbnailService()) + FileName = "corrupt.jpg", ParentDirectory = "/", FileHash = "hash" + }); + + // Act + var controller = + new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService()) { - ControllerContext = {HttpContext = new DefaultHttpContext()} + ControllerContext = { HttpContext = new DefaultHttpContext() } }; - - var actionResult = await controller.DownloadPhoto("/corrupt.jpg") as JsonResult; - Assert.AreNotEqual(null,actionResult); - Assert.AreEqual(500,controller.Response.StatusCode); + var actionResult = await controller.DownloadPhoto("/corrupt.jpg") as JsonResult; + Assert.AreNotEqual(null, actionResult); - await _query.RemoveItemAsync(item); - } - - - [TestMethod] - public async Task DownloadPhoto_NotFound() - { - // Arrange - var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - - // Act - var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), new FakeIThumbnailService()) + Assert.AreEqual(500, controller.Response.StatusCode); + + await _query.RemoveItemAsync(item); + } + + + [TestMethod] + public async Task DownloadPhoto_NotFound() + { + // Arrange + var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); + + // Act + var controller = + new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService()) { - ControllerContext = {HttpContext = new DefaultHttpContext()} + ControllerContext = { HttpContext = new DefaultHttpContext() } }; - var actionResult = await controller.DownloadPhoto("/not-found.jpg") as NotFoundObjectResult; - - Assert.AreNotEqual(null,actionResult); - Assert.AreEqual(404,actionResult?.StatusCode); - } - - [TestMethod] - public async Task DownloadPhoto_isThumbnailFalse_ReturnFileStream_Test() - { - // Arrange - var fileIndexItem = await InsertSearchData(); - var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - - // Act - var controller = new DownloadPhotoController(_query,selectorStorage, new FakeIWebLogger(), new FakeIThumbnailService()); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); - var actionResult = await controller.DownloadPhoto(fileIndexItem.FilePath!,false) as FileStreamResult; - Assert.AreNotEqual(null,actionResult); - - await actionResult!.FileStream.DisposeAsync(); - } + var actionResult = await controller.DownloadPhoto("/not-found.jpg") as NotFoundObjectResult; - [TestMethod] - public async Task DownloadPhoto_isThumbnailTrue_ReturnAThumb_ReturnFileStream_Test() - { - // Arrange - var fileIndexItem = await InsertSearchData(); - var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); + Assert.AreNotEqual(null, actionResult); + Assert.AreEqual(404, actionResult?.StatusCode); + } - // Act - var controller = new DownloadPhotoController(_query,selectorStorage, new FakeIWebLogger(), new FakeIThumbnailService(selectorStorage)); - controller.ControllerContext.HttpContext = new DefaultHttpContext(); + [TestMethod] + public async Task DownloadPhoto_isThumbnailFalse_ReturnFileStream_Test() + { + // Arrange + var fileIndexItem = await InsertSearchData(); + var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - // Run once - var actionResult1 = await controller.DownloadPhoto(fileIndexItem.FilePath!) as FileStreamResult; - await actionResult1!.FileStream.DisposeAsync(); + // Act + var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService()); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + var actionResult = + await controller.DownloadPhoto(fileIndexItem.FilePath!, false) as FileStreamResult; + Assert.AreNotEqual(null, actionResult); - // Run twice - var actionResult2 = await controller.DownloadPhoto(fileIndexItem.FilePath!) as FileStreamResult; - Assert.AreNotEqual(null,actionResult2); + await actionResult!.FileStream.DisposeAsync(); + } - await actionResult2!.FileStream.DisposeAsync(); - } - - [TestMethod] - public async Task ApiController_DownloadPhoto_SourceImageIsMissing_Test() - { - // Arrange - var fileIndexItem = await InsertSearchData(); - - // so the item does not exist on disk - var selectorStorage = new FakeSelectorStorage(); - - // Act - var controller = new DownloadPhotoController(_query,selectorStorage, new FakeIWebLogger(), new FakeIThumbnailService()); - var actionResult = await controller.DownloadPhoto(fileIndexItem.FilePath!) as NotFoundObjectResult; - Assert.AreNotEqual(null,actionResult); - Assert.AreEqual(404,actionResult?.StatusCode); - Assert.AreEqual("source image missing /test.jpg",actionResult?.Value); - } + [TestMethod] + public async Task DownloadPhoto_isThumbnailTrue_ReturnAThumb_ReturnFileStream_Test() + { + // Arrange + var fileIndexItem = await InsertSearchData(); + var selectorStorage = new FakeSelectorStorage(ArrangeStorage()); - [TestMethod] - public async Task DownloadPhoto_Thumb_base_folder_not_found_Test() - { - // Arrange - var fileIndexItem = await InsertSearchData(); - var storage = - new FakeIStorage(null!, new List{"/test.jpg"}, - new List{FakeCreateAn.CreateAnImage.Bytes.ToArray()}); - var selectorStorage = new FakeSelectorStorage(storage); - - - // Act - var controller = new DownloadPhotoController(_query,selectorStorage, - new FakeIWebLogger(), new FakeIThumbnailService()); - - controller.ControllerContext.HttpContext = new DefaultHttpContext(); - var actionResult = await controller.DownloadPhoto(fileIndexItem.FilePath!) as NotFoundObjectResult; - - Assert.AreNotEqual(null,actionResult); - Assert.AreEqual(404,actionResult?.StatusCode); - Assert.AreEqual("ThumbnailTempFolder not found",actionResult?.Value); - } + // Act + var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService(selectorStorage)); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + + // Run once + var actionResult1 = + await controller.DownloadPhoto(fileIndexItem.FilePath!) as FileStreamResult; + await actionResult1!.FileStream.DisposeAsync(); + + // Run twice + var actionResult2 = + await controller.DownloadPhoto(fileIndexItem.FilePath!) as FileStreamResult; + Assert.AreNotEqual(null, actionResult2); + + await actionResult2!.FileStream.DisposeAsync(); + } + + [TestMethod] + public async Task ApiController_DownloadPhoto_SourceImageIsMissing_Test() + { + // Arrange + var fileIndexItem = await InsertSearchData(); + + // so the item does not exist on disk + var selectorStorage = new FakeSelectorStorage(); + + // Act + var controller = new DownloadPhotoController(_query, selectorStorage, new FakeIWebLogger(), + new FakeIThumbnailService()); + var actionResult = + await controller.DownloadPhoto(fileIndexItem.FilePath!) as NotFoundObjectResult; + Assert.AreNotEqual(null, actionResult); + Assert.AreEqual(404, actionResult?.StatusCode); + Assert.AreEqual("source image missing /test.jpg", actionResult?.Value); + } + + [TestMethod] + public async Task DownloadPhoto_Thumb_base_folder_not_found_Test() + { + // Arrange + var fileIndexItem = await InsertSearchData(); + var storage = + new FakeIStorage(null!, new List { "/test.jpg" }, + new List { CreateAnImage.Bytes.ToArray() }); + var selectorStorage = new FakeSelectorStorage(storage); + + + // Act + var controller = new DownloadPhotoController(_query, selectorStorage, + new FakeIWebLogger(), new FakeIThumbnailService()); + + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + var actionResult = + await controller.DownloadPhoto(fileIndexItem.FilePath!) as NotFoundObjectResult; + + Assert.AreNotEqual(null, actionResult); + Assert.AreEqual(404, actionResult?.StatusCode); + Assert.AreEqual("ThumbnailTempFolder not found", actionResult?.Value); } } diff --git a/starsky/starskytest/Controllers/ErrorControllerTest.cs b/starsky/starskytest/Controllers/ErrorControllerTest.cs index 8bf9595612..d58c434114 100644 --- a/starsky/starskytest/Controllers/ErrorControllerTest.cs +++ b/starsky/starskytest/Controllers/ErrorControllerTest.cs @@ -4,22 +4,37 @@ using starsky.Controllers; using starsky.foundation.platform.Models; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public sealed class ErrorControllerTest { - [TestClass] - public sealed class ErrorControllerTest + [TestMethod] + public void ErrorControllerTest_Error() { - - [TestMethod] - public void ErrorControllerTest_Error() + var controller = new ErrorController(new AppSettings()) { - var controller = new ErrorController(new AppSettings()) - { - ControllerContext = {HttpContext = new DefaultHttpContext()} - }; + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + + var actionResult = controller.Error(404) as PhysicalFileResult; + Assert.AreEqual("text/html", actionResult?.ContentType); + } + + [TestMethod] + public void Error_ReturnsBadRequest() + { + // Arrange + var controller = new ErrorController(new AppSettings()) + { + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + // Act + var actionResult = controller.Error(); - var actionResult = controller.Error(404) as PhysicalFileResult; - Assert.AreEqual("text/html",actionResult?.ContentType); - } + // Assert + Assert.IsInstanceOfType(actionResult, typeof(BadRequestObjectResult)); } }