From d08db8ab0eafc752a2e1bff2fc53e3f68e4a1432 Mon Sep 17 00:00:00 2001 From: Dion Date: Tue, 10 Sep 2024 17:06:48 +0200 Subject: [PATCH 1/5] fix code smells --- .../JsonConverter/EnumListConverter.cs | 5 +- .../ArchiveFormats/TarBal.cs | 16 +- .../Storage/StorageSubPathFilesystem.cs | 536 ++++++------- .../Services/OffsetDataMetaExifThumbnail.cs | 233 +++--- starsky/starsky/Controllers/DiskController.cs | 16 +- .../starsky/Controllers/ExportController.cs | 158 ++-- starsky/starsky/Controllers/GeoController.cs | 150 ++-- .../HealthCheckForUpdatesController.cs | 117 +-- .../starsky/Controllers/HealthController.cs | 314 ++++---- starsky/starsky/Controllers/HomeController.cs | 357 +++++---- .../starsky/Controllers/ImportController.cs | 8 +- .../starsky/Controllers/IndexController.cs | 5 + .../starsky/Controllers/MetaInfoController.cs | 5 + .../Controllers/DiskControllerTest.cs | 18 + .../Controllers/ExportControllerTest.cs | 21 + .../Controllers/GeoControllerTest.cs | 331 ++++---- .../HealthCheckForUpdatesControllerTest.cs | 336 +++++---- .../Controllers/HealthControllerTest.cs | 423 ++++++----- .../Controllers/HomeControllerTest.cs | 711 ++++++++++-------- .../Controllers/ImportControllerTest.cs | 14 + .../Controllers/IndexControllerTest.cs | 12 + .../Controllers/MetaInfoControllerTest.cs | 11 + 22 files changed, 2049 insertions(+), 1748 deletions(-) diff --git a/starsky/starsky.foundation.platform/JsonConverter/EnumListConverter.cs b/starsky/starsky.foundation.platform/JsonConverter/EnumListConverter.cs index 420645d15b..cd85ab382c 100644 --- a/starsky/starsky.foundation.platform/JsonConverter/EnumListConverter.cs +++ b/starsky/starsky.foundation.platform/JsonConverter/EnumListConverter.cs @@ -1,14 +1,17 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; namespace starsky.foundation.platform.JsonConverter; /// -/// Enum converter for Lists with Enum into Json +/// Enum converter for Lists with Enum into Json /// /// Enum +[SuppressMessage("ReSharper", "S6966: Await ReadAsync instead.", + Justification = "There is no Async jet")] public class EnumListConverter : JsonConverter> where T : struct, Enum { public override List Read(ref Utf8JsonReader reader, Type typeToConvert, diff --git a/starsky/starsky.foundation.storage/ArchiveFormats/TarBal.cs b/starsky/starsky.foundation.storage/ArchiveFormats/TarBal.cs index 288a2ce2fb..a82b2b72f1 100644 --- a/starsky/starsky.foundation.storage/ArchiveFormats/TarBal.cs +++ b/starsky/starsky.foundation.storage/ArchiveFormats/TarBal.cs @@ -123,13 +123,18 @@ public async Task ExtractTar(Stream stream, string outputDir, } } - private static async Task CreateLongFileName(long size, Stream stream, + private async Task CreateLongFileName(long size, Stream stream, CancellationToken cancellationToken) { //If Type Tag is 'L' we have a filename that is longer than the 100 bytes reserved for it in the header. //We read it here and save it temporarily as it will be the file name of the next block where the actual data is var buf = new byte[size]; - await stream.ReadAsync(buf, 0, buf.Length, cancellationToken); + var actualSize = await stream.ReadAsync(buf, 0, buf.Length, cancellationToken); + if ( actualSize != size ) + { + _logger.LogError("[CreateLongFileName] less than {size} bytes read", size); + } + return Encoding.ASCII.GetString(buf).Trim('\0'); } @@ -147,7 +152,12 @@ private async Task CreateFileOrDirectory(string outputDir, string name, long siz { var str = new MemoryStream(); var buf = new byte[size]; - await stream.ReadAsync(buf, 0, buf.Length, cancellationToken); + var actualSize = await stream.ReadAsync(buf, 0, buf.Length, cancellationToken); + if ( actualSize != size ) + { + _logger.LogError("[CreateFileOrDirectory] less than {size} bytes read", size); + } + await str.WriteAsync(buf, 0, buf.Length, cancellationToken); _storage.WriteStreamOpenOrCreate(str, output); } diff --git a/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs b/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs index 25583bfdec..d975556b7a 100644 --- a/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs +++ b/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs @@ -10,328 +10,332 @@ using starsky.foundation.storage.Interfaces; using starsky.foundation.storage.Models; -namespace starsky.foundation.storage.Storage +namespace starsky.foundation.storage.Storage; + +[Service(typeof(IStorage), InjectionLifetime = InjectionLifetime.Scoped)] +public sealed class StorageSubPathFilesystem : IStorage { - [Service(typeof(IStorage), InjectionLifetime = InjectionLifetime.Scoped)] - public sealed class StorageSubPathFilesystem : IStorage - { - private readonly AppSettings _appSettings; - private readonly IWebLogger _logger; + private readonly AppSettings _appSettings; + private readonly IWebLogger _logger; - public StorageSubPathFilesystem(AppSettings appSettings, IWebLogger logger) - { - _appSettings = appSettings; - _logger = logger; - } + public StorageSubPathFilesystem(AppSettings appSettings, IWebLogger logger) + { + _appSettings = appSettings; + _logger = logger; + } - /// - /// Checks if a file is ready - /// - /// - /// - public bool IsFileReady(string path) - { - var fullPath = _appSettings.DatabasePathToFilePath(path); - return new StorageHostFullPathFilesystem(_logger).IsFileReady(fullPath); - } + /// + /// Checks if a file is ready + /// + /// + /// + public bool IsFileReady(string path) + { + var fullPath = _appSettings.DatabasePathToFilePath(path); + return new StorageHostFullPathFilesystem(_logger).IsFileReady(fullPath); + } - /// - /// Get the storage info - /// - /// full File Path - /// StorageInfo object - public StorageInfo Info(string path) - { - var fullPath = _appSettings.DatabasePathToFilePath(path); + /// + /// Get the storage info + /// + /// full File Path + /// StorageInfo object + public StorageInfo Info(string path) + { + var fullPath = _appSettings.DatabasePathToFilePath(path); - return new StorageHostFullPathFilesystem(_logger).Info(fullPath); - } + return new StorageHostFullPathFilesystem(_logger).Info(fullPath); + } - public DateTime SetLastWriteTime(string path, DateTime? dateTime = null) + /// + /// Does file exist (true == exist) + /// + /// subPath + /// bool true = exist + public bool ExistFile(string path) + { + if ( string.IsNullOrEmpty(path) ) { - var fullPath = _appSettings.DatabasePathToFilePath(path); - - return new StorageHostFullPathFilesystem(_logger).SetLastWriteTime(fullPath, dateTime); + return false; } - /// - /// Does file exist (true == exist) - /// - /// subPath - /// bool true = exist - public bool ExistFile(string path) - { - if ( string.IsNullOrEmpty(path) ) - { - return false; - } + var isFolderOrFile = IsFolderOrFile(path); + return isFolderOrFile == FolderOrFileModel.FolderOrFileTypeList.File; + } - var isFolderOrFile = IsFolderOrFile(path); - return isFolderOrFile == FolderOrFileModel.FolderOrFileTypeList.File; - } + /// + /// Check if folder exist + /// + /// subPath style + /// true if exist + public bool ExistFolder(string path) + { + var isFolderOrFile = IsFolderOrFile(path); + return isFolderOrFile == FolderOrFileModel.FolderOrFileTypeList.Folder; + } - /// - /// Check if folder exist - /// - /// subPath style - /// true if exist - public bool ExistFolder(string path) - { - var isFolderOrFile = IsFolderOrFile(path); - return isFolderOrFile == FolderOrFileModel.FolderOrFileTypeList.Folder; - } + /// + /// is the subPath a folder or file, or deleted (FolderOrFileModel.FolderOrFileTypeList.Deleted) + /// + /// path of the database + /// is file, folder or deleted + public FolderOrFileModel.FolderOrFileTypeList IsFolderOrFile(string path) + { + var fullFilePath = _appSettings.DatabasePathToFilePath(path); + return new StorageHostFullPathFilesystem(_logger).IsFolderOrFile(fullFilePath); + } - /// - /// is the subPath a folder or file, or deleted (FolderOrFileModel.FolderOrFileTypeList.Deleted) - /// - /// path of the database - /// is file, folder or deleted - public FolderOrFileModel.FolderOrFileTypeList IsFolderOrFile(string path) - { - var fullFilePath = _appSettings.DatabasePathToFilePath(path); - return new StorageHostFullPathFilesystem(_logger).IsFolderOrFile(fullFilePath); - } + /// + /// Move a entire folder + /// + /// inputSubPath + /// toSubPath + public void FolderMove(string fromPath, string toPath) + { + var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath); + var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath); + new StorageHostFullPathFilesystem(_logger).FolderMove(inputFileFullPath, + toFileFullPath); + } - /// - /// Move a entire folder - /// - /// inputSubPath - /// toSubPath - public void FolderMove(string fromPath, string toPath) - { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath); - var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath); - new StorageHostFullPathFilesystem(_logger).FolderMove(inputFileFullPath, - toFileFullPath); - } + /// + /// Move a file + /// + /// inputSubPath + /// toSubPath + public bool FileMove(string fromPath, string toPath) + { + var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath); + var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath); + return new StorageHostFullPathFilesystem(_logger).FileMove(inputFileFullPath, + toFileFullPath); + } - /// - /// Move a file - /// - /// inputSubPath - /// toSubPath - public bool FileMove(string fromPath, string toPath) - { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath); - var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath); - return new StorageHostFullPathFilesystem(_logger).FileMove(inputFileFullPath, - toFileFullPath); - } + /// + /// Copy a single file + /// + /// inputSubPath + /// toSubPath + public void FileCopy(string fromPath, string toPath) + { + var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath); + var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath); + new StorageHostFullPathFilesystem(_logger).FileCopy(inputFileFullPath, toFileFullPath); + } - /// - /// Copy a single file - /// - /// inputSubPath - /// toSubPath - public void FileCopy(string fromPath, string toPath) - { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath); - var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath); - new StorageHostFullPathFilesystem(_logger).FileCopy(inputFileFullPath, toFileFullPath); - } + /// + /// Delete a file + /// + /// subPath + /// bool + public bool FileDelete(string path) + { + var inputFileFullPath = _appSettings.DatabasePathToFilePath(path); + return new StorageHostFullPathFilesystem(_logger).FileDelete(inputFileFullPath); + } - /// - /// Delete a file - /// - /// subPath - /// bool - public bool FileDelete(string path) - { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(path); - return new StorageHostFullPathFilesystem(_logger).FileDelete(inputFileFullPath); - } + /// + /// Create an Directory + /// + /// subPath location + public void CreateDirectory(string path) + { + var inputFileFullPath = _appSettings.DatabasePathToFilePath(path); + Directory.CreateDirectory(inputFileFullPath); + } - /// - /// Create an Directory - /// - /// subPath location - public void CreateDirectory(string path) - { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(path); - Directory.CreateDirectory(inputFileFullPath); - } + /// + /// Delete folder and child items of that folder + /// + /// subPath + /// bool + public bool FolderDelete(string path) + { + var inputFileFullPath = _appSettings.DatabasePathToFilePath(path); + return new StorageHostFullPathFilesystem(_logger).FolderDelete(inputFileFullPath); + } - /// - /// Delete folder and child items of that folder - /// - /// subPath - /// bool - public bool FolderDelete(string path) - { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(path); - return new StorageHostFullPathFilesystem(_logger).FolderDelete(inputFileFullPath); - } + /// + /// Returns a list of Files in a directory (non-Recursive) + /// to filter use: + /// ..etAllFilesInDirectory(subPath) + /// .Where(ExtensionRolesHelper.IsExtensionExifToolSupported) + /// + /// subPath path relative to the database + /// + public IEnumerable GetAllFilesInDirectory(string path) + { + var storage = new StorageHostFullPathFilesystem(_logger); + var fullFilePath = _appSettings.DatabasePathToFilePath(path); - /// - /// Returns a list of Files in a directory (non-Recursive) - /// to filter use: - /// ..etAllFilesInDirectory(subPath) - /// .Where(ExtensionRolesHelper.IsExtensionExifToolSupported) - /// - /// subPath path relative to the database - /// - public IEnumerable GetAllFilesInDirectory(string path) + if ( !storage.ExistFolder(fullFilePath) ) { - var storage = new StorageHostFullPathFilesystem(_logger); - var fullFilePath = _appSettings.DatabasePathToFilePath(path); + return Enumerable.Empty(); + } - if ( !storage.ExistFolder(fullFilePath) ) - { - return Enumerable.Empty(); - } + var imageFilesList = storage.GetAllFilesInDirectory(fullFilePath); - var imageFilesList = storage.GetAllFilesInDirectory(fullFilePath); + // to filter use: + // ..etAllFilesInDirectory(subPath) + // .Where(ExtensionRolesHelper.IsExtensionExifToolSupported) - // to filter use: - // ..etAllFilesInDirectory(subPath) - // .Where(ExtensionRolesHelper.IsExtensionExifToolSupported) + // convert back to subPath style + return _appSettings.RenameListItemsToDbStyle(imageFilesList.ToList()); + } - // convert back to subPath style - return _appSettings.RenameListItemsToDbStyle(imageFilesList.ToList()); - } + /// + /// Returns a list of Files in a directory (Recursive) + /// to filter use: + /// ..etAllFilesInDirectory(subPath) + /// .Where(ExtensionRolesHelper.IsExtensionExifToolSupported) + /// + /// subPath, path relative to the database + /// list of files + public IEnumerable GetAllFilesInDirectoryRecursive(string path) + { + var fullFilePath = _appSettings.DatabasePathToFilePath(path); + var storage = new StorageHostFullPathFilesystem(_logger); - /// - /// Returns a list of Files in a directory (Recursive) - /// to filter use: - /// ..etAllFilesInDirectory(subPath) - /// .Where(ExtensionRolesHelper.IsExtensionExifToolSupported) - /// - /// subPath, path relative to the database - /// list of files - public IEnumerable GetAllFilesInDirectoryRecursive(string path) + if ( !storage.ExistFolder(fullFilePath) ) { - var fullFilePath = _appSettings.DatabasePathToFilePath(path); - var storage = new StorageHostFullPathFilesystem(_logger); + return Enumerable.Empty(); + } - if ( !storage.ExistFolder(fullFilePath) ) - { - return Enumerable.Empty(); - } + var imageFilesList = storage.GetAllFilesInDirectoryRecursive(fullFilePath); - var imageFilesList = storage.GetAllFilesInDirectoryRecursive(fullFilePath); + // to filter use: + // ..etAllFilesInDirectory(subPath) + // .Where(ExtensionRolesHelper.IsExtensionExifToolSupported) + // OR: + // .Where(ExtensionRolesHelper.IsExtensionSyncSupported) - // to filter use: - // ..etAllFilesInDirectory(subPath) - // .Where(ExtensionRolesHelper.IsExtensionExifToolSupported) - // OR: - // .Where(ExtensionRolesHelper.IsExtensionSyncSupported) + // convert back to subPath style + return _appSettings.RenameListItemsToDbStyle(imageFilesList.ToList()); + } - // convert back to subPath style - return _appSettings.RenameListItemsToDbStyle(imageFilesList.ToList()); - } + /// + /// Gets a non-Recursive list of child directories + /// + /// subPath + /// list of Directories (example: /2020_01_01/test) + public IEnumerable GetDirectories(string path) + { + var fullFilePath = _appSettings.DatabasePathToFilePath(path); + var storage = new StorageHostFullPathFilesystem(_logger); - /// - /// Gets a non-Recursive list of child directories - /// - /// subPath - /// list of Directories (example: /2020_01_01/test) - public IEnumerable GetDirectories(string path) + if ( !storage.ExistFolder(fullFilePath) ) { - var fullFilePath = _appSettings.DatabasePathToFilePath(path); - var storage = new StorageHostFullPathFilesystem(_logger); + return Enumerable.Empty(); + } - if ( !storage.ExistFolder(fullFilePath) ) - { - return Enumerable.Empty(); - } + var folders = storage.GetDirectories(fullFilePath); - var folders = storage.GetDirectories(fullFilePath); + // Used For subfolders + // convert back to subPath style + return _appSettings.RenameListItemsToDbStyle(folders.ToList()); + } - // Used For subfolders - // convert back to subPath style - return _appSettings.RenameListItemsToDbStyle(folders.ToList()); - } + /// + /// Returns a list of directories // Get list of child folders + /// + /// subPath in dir + /// list of paths + public IEnumerable> GetDirectoryRecursive(string path) + { + var storage = new StorageHostFullPathFilesystem(_logger); - /// - /// Returns a list of directories // Get list of child folders - /// - /// subPath in dir - /// list of paths - public IEnumerable> GetDirectoryRecursive(string path) + var fullFilePath = _appSettings.DatabasePathToFilePath(path); + if ( !storage.ExistFolder(fullFilePath) ) { - var storage = new StorageHostFullPathFilesystem(_logger); + return Enumerable.Empty>(); + } - var fullFilePath = _appSettings.DatabasePathToFilePath(path); - if ( !storage.ExistFolder(fullFilePath) ) - { - return Enumerable.Empty>(); - } + var folders = storage.GetDirectoryRecursive(fullFilePath); - var folders = storage.GetDirectoryRecursive(fullFilePath); + // Used For subfolders + // convert back to subPath style + return _appSettings.RenameListItemsToDbStyle(folders.ToList()); + } - // Used For subfolders - // convert back to subPath style - return _appSettings.RenameListItemsToDbStyle(folders.ToList()); + /// + /// Get the file using a stream (don't forget to dispose this) + /// + /// subPath + /// number of bytes to read (default -1 = all) + /// FileStream or Stream.Null when file dont exist + [SuppressMessage("ReSharper", "MustUseReturnValue")] + public Stream ReadStream(string path, int maxRead = -1) + { + if ( !ExistFile(path) ) + { + return Stream.Null; } - /// - /// Get the file using a stream (don't forget to dispose this) - /// - /// subPath - /// number of bytes to read (default -1 = all) - /// FileStream or Stream.Null when file dont exist - [SuppressMessage("ReSharper", "MustUseReturnValue")] - public Stream ReadStream(string path, int maxRead = -1) + if ( _appSettings.IsVerbose() ) { - if ( !ExistFile(path) ) - { - return Stream.Null; - } + Console.WriteLine(path); + } - if ( _appSettings.IsVerbose() ) + Stream LocalGet() + { + var fullFilePath = _appSettings.DatabasePathToFilePath(path); + var fileStream = new FileStream(fullFilePath, FileMode.Open, FileAccess.Read); + if ( maxRead <= 1 ) { - Console.WriteLine(path); + // read all + return fileStream; } - Stream LocalGet() + // read only the first number of bytes + var buffer = new byte[maxRead]; + var actualRead = fileStream.Read(buffer, 0, maxRead); + if ( actualRead != maxRead ) { - var fullFilePath = _appSettings.DatabasePathToFilePath(path); - var fileStream = new FileStream(fullFilePath, FileMode.Open, FileAccess.Read); - if ( maxRead <= 1 ) - { - // read all - return fileStream; - } - - // read only the first number of bytes - var buffer = new byte[maxRead]; - fileStream.Read(buffer, 0, maxRead); - fileStream.Flush(); - fileStream.Close(); - fileStream.Dispose(); // also flush - return new MemoryStream(buffer); + _logger.LogError("[StorageSubPathFileSystem] ReadStream: actualRead != maxRead"); } - return new RetryStream().Retry(LocalGet); + fileStream.Flush(); + fileStream.Close(); + fileStream.Dispose(); // also flush + return new MemoryStream(buffer); } + return new RetryStream().Retry(LocalGet); + } - /// - /// Write fileStream to disk - /// - /// some stream - /// location - /// - /// - public bool WriteStream(Stream stream, string path) - { - // should be able to write files that are not exist yet - var fullFilePath = _appSettings.DatabasePathToFilePath(path); - return new StorageHostFullPathFilesystem(_logger).WriteStream(stream, fullFilePath); - } + /// + /// Write fileStream to disk + /// + /// some stream + /// location + /// + /// + public bool WriteStream(Stream stream, string path) + { + // should be able to write files that are not exist yet + var fullFilePath = _appSettings.DatabasePathToFilePath(path); - public bool WriteStreamOpenOrCreate(Stream stream, string path) - { - throw new NotImplementedException(); - } + return new StorageHostFullPathFilesystem(_logger).WriteStream(stream, fullFilePath); + } - public Task WriteStreamAsync(Stream stream, string path) - { - var fullFilePath = _appSettings.DatabasePathToFilePath(path); - var service = new StorageHostFullPathFilesystem(_logger); - return service.WriteStreamAsync(stream, fullFilePath); - } + public bool WriteStreamOpenOrCreate(Stream stream, string path) + { + throw new NotImplementedException(); + } + + public Task WriteStreamAsync(Stream stream, string path) + { + var fullFilePath = _appSettings.DatabasePathToFilePath(path); + var service = new StorageHostFullPathFilesystem(_logger); + return service.WriteStreamAsync(stream, fullFilePath); + } + + public DateTime SetLastWriteTime(string path, DateTime? dateTime = null) + { + var fullPath = _appSettings.DatabasePathToFilePath(path); + + return new StorageHostFullPathFilesystem(_logger).SetLastWriteTime(fullPath, dateTime); } } diff --git a/starsky/starsky.foundation.thumbnailmeta/Services/OffsetDataMetaExifThumbnail.cs b/starsky/starsky.foundation.thumbnailmeta/Services/OffsetDataMetaExifThumbnail.cs index 12fa583072..b37c56dc02 100644 --- a/starsky/starsky.foundation.thumbnailmeta/Services/OffsetDataMetaExifThumbnail.cs +++ b/starsky/starsky.foundation.thumbnailmeta/Services/OffsetDataMetaExifThumbnail.cs @@ -8,160 +8,163 @@ using starsky.foundation.database.Models; using starsky.foundation.injection; using starsky.foundation.platform.Helpers; -using starsky.foundation.thumbnailmeta.Interfaces; -using starsky.foundation.thumbnailmeta.Models; using starsky.foundation.platform.Interfaces; using starsky.foundation.readmeta.ReadMetaHelpers; using starsky.foundation.storage.Interfaces; using starsky.foundation.storage.Storage; +using starsky.foundation.thumbnailmeta.Interfaces; +using starsky.foundation.thumbnailmeta.Models; using Directory = MetadataExtractor.Directory; [assembly: InternalsVisibleTo("starskytest")] -namespace starsky.foundation.thumbnailmeta.Services +namespace starsky.foundation.thumbnailmeta.Services; + +[Service(typeof(IOffsetDataMetaExifThumbnail), InjectionLifetime = InjectionLifetime.Scoped)] +public sealed class OffsetDataMetaExifThumbnail : IOffsetDataMetaExifThumbnail { - [Service(typeof(IOffsetDataMetaExifThumbnail), InjectionLifetime = InjectionLifetime.Scoped)] - public sealed class OffsetDataMetaExifThumbnail : IOffsetDataMetaExifThumbnail + private readonly IStorage _iStorage; + private readonly IWebLogger _logger; + + public OffsetDataMetaExifThumbnail(ISelectorStorage selectorStorage, IWebLogger logger) { - private readonly IStorage _iStorage; - private readonly IWebLogger _logger; + _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath); + _logger = logger; + } - public OffsetDataMetaExifThumbnail(ISelectorStorage selectorStorage, IWebLogger logger) - { - _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath); - _logger = logger; - } + public (ExifThumbnailDirectory?, int, int, FileIndexItem.Rotation) + GetExifMetaDirectories(string subPath) + { + var (allExifItems, exifThumbnailDir) = ReadExifMetaDirectories(subPath); + return ParseMetaThumbnail(allExifItems, exifThumbnailDir, subPath); + } - public (ExifThumbnailDirectory?, int, int, FileIndexItem.Rotation) - GetExifMetaDirectories(string subPath) + public OffsetModel ParseOffsetData(ExifThumbnailDirectory? exifThumbnailDir, string subPath) + { + if ( exifThumbnailDir == null ) { - var (allExifItems, exifThumbnailDir) = ReadExifMetaDirectories(subPath); - return ParseMetaThumbnail(allExifItems, exifThumbnailDir, subPath); + return new OffsetModel + { + Success = false, + Reason = + $"{FilenamesHelper.GetFileName(subPath)} ExifThumbnailDirectory null" + }; } - internal (List?, ExifThumbnailDirectory?) ReadExifMetaDirectories(string subPath) + var thumbnailOffset = long.Parse(exifThumbnailDir!.GetDescription( + ExifThumbnailDirectory.TagThumbnailOffset)!.Split(' ')[0]); + const int maxIssue35Offset = 12; + var thumbnailLength = int.Parse(exifThumbnailDir.GetDescription( + ExifThumbnailDirectory.TagThumbnailLength)!.Split(' ')[0]) + maxIssue35Offset; + var thumbnail = new byte[thumbnailLength]; + + using ( var imageStream = _iStorage.ReadStream(subPath) ) { - using ( var stream = _iStorage.ReadStream(subPath) ) - { - var allExifItems = - ImageMetadataReader.ReadMetadata(stream).ToList(); - var exifThumbnailDir = - allExifItems.Find(p => - p.Name == "Exif Thumbnail") as ExifThumbnailDirectory; + imageStream.Seek(thumbnailOffset, SeekOrigin.Begin); - return (allExifItems, exifThumbnailDir); + var actualRead = imageStream.Read(thumbnail, 0, thumbnailLength); + if ( actualRead != thumbnailLength ) + { + _logger.LogError("[ParseOffsetData] ReadStream: actualRead != maxRead"); } } - internal (ExifThumbnailDirectory?, int, int, FileIndexItem.Rotation) ParseMetaThumbnail( - List? allExifItems, - ExifThumbnailDirectory? exifThumbnailDir, string? reference = null) + // work around Metadata Extractor issue #35 + if ( thumbnailLength <= maxIssue35Offset + 1 ) { - if ( exifThumbnailDir == null || allExifItems == null ) + _logger.LogInformation( + $"[ParseOffsetData] thumbnailLength : {thumbnailLength} {maxIssue35Offset + 1}"); + return new OffsetModel { - return (null, 0, 0, FileIndexItem.Rotation.DoNotChange); - } - - var jpegTags = allExifItems.OfType().FirstOrDefault()?.Tags; - - var heightPixels = jpegTags?.FirstOrDefault(p => p.Type == JpegDirectory.TagImageHeight) - ?.Description; - var widthPixels = jpegTags?.FirstOrDefault(p => p.Type == JpegDirectory.TagImageWidth) - ?.Description; + Success = false, Reason = $"{FilenamesHelper.GetFileName(subPath)} offsetLength" + }; + } - if ( string.IsNullOrEmpty(heightPixels) && string.IsNullOrEmpty(widthPixels) ) + var issue35Offset = 0; + for ( var offset = 0; offset <= maxIssue35Offset; ++offset ) + { + // 0xffd8 is the JFIF start of image segment indicator + if ( thumbnail[offset] == 0xff && thumbnail[offset + 1] == 0xd8 ) { - var exifSubIfdDirectories = allExifItems.OfType().ToList(); - foreach ( var exifSubIfdDirectoryTags in exifSubIfdDirectories.Select(p => p.Tags) ) - { - var heightValue = exifSubIfdDirectoryTags - .FirstOrDefault(p => p.Type == ExifDirectoryBase.TagImageHeight) - ?.Description; - var widthValue = exifSubIfdDirectoryTags - .FirstOrDefault(p => p.Type == ExifDirectoryBase.TagImageWidth) - ?.Description; - if ( heightValue == null || widthValue == null ) - { - continue; - } - - heightPixels = heightValue; - widthPixels = widthValue; - } + issue35Offset = offset; + break; } + } - var rotation = ReadMetaExif.GetOrientationFromExifItem(allExifItems); - - var heightParseResult = int.TryParse(heightPixels?.Replace( - " pixels", string.Empty), out var height); - - var widthParseResult = - int.TryParse(widthPixels?.Replace(" pixels", string.Empty), out var width); + return new OffsetModel + { + Success = true, + Index = issue35Offset, + Count = thumbnailLength - issue35Offset, + Data = thumbnail // byte array + }; + } - if ( !heightParseResult || !widthParseResult || height == 0 || width == 0 ) - { - _logger.LogInformation( - $"[ParseMetaThumbnail] ${reference} has no height or width {width}x{height} "); - } + internal (List?, ExifThumbnailDirectory?) ReadExifMetaDirectories(string subPath) + { + using ( var stream = _iStorage.ReadStream(subPath) ) + { + var allExifItems = + ImageMetadataReader.ReadMetadata(stream).ToList(); + var exifThumbnailDir = + allExifItems.Find(p => + p.Name == "Exif Thumbnail") as ExifThumbnailDirectory; - return (exifThumbnailDir, width, height, rotation); + return ( allExifItems, exifThumbnailDir ); } + } - public OffsetModel ParseOffsetData(ExifThumbnailDirectory? exifThumbnailDir, string subPath) + internal (ExifThumbnailDirectory?, int, int, FileIndexItem.Rotation) ParseMetaThumbnail( + List? allExifItems, + ExifThumbnailDirectory? exifThumbnailDir, string? reference = null) + { + if ( exifThumbnailDir == null || allExifItems == null ) { - if ( exifThumbnailDir == null ) - { - return new OffsetModel - { - Success = false, - Reason = - $"{FilenamesHelper.GetFileName(subPath)} ExifThumbnailDirectory null" - }; - } + return ( null, 0, 0, FileIndexItem.Rotation.DoNotChange ); + } - long thumbnailOffset = long.Parse(exifThumbnailDir!.GetDescription( - ExifThumbnailDirectory.TagThumbnailOffset)!.Split(' ')[0]); - const int maxIssue35Offset = 12; - int thumbnailLength = int.Parse(exifThumbnailDir.GetDescription( - ExifThumbnailDirectory.TagThumbnailLength)!.Split(' ')[0]) + maxIssue35Offset; - byte[] thumbnail = new byte[thumbnailLength]; + var jpegTags = allExifItems.OfType().FirstOrDefault()?.Tags; - using ( var imageStream = _iStorage.ReadStream(subPath) ) - { - imageStream.Seek(thumbnailOffset, SeekOrigin.Begin); - imageStream.Read(thumbnail, 0, thumbnailLength); - } + var heightPixels = jpegTags?.FirstOrDefault(p => p.Type == JpegDirectory.TagImageHeight) + ?.Description; + var widthPixels = jpegTags?.FirstOrDefault(p => p.Type == JpegDirectory.TagImageWidth) + ?.Description; - // work around Metadata Extractor issue #35 - if ( thumbnailLength <= maxIssue35Offset + 1 ) - { - _logger.LogInformation( - $"[ParseOffsetData] thumbnailLength : {thumbnailLength} {maxIssue35Offset + 1}"); - return new OffsetModel - { - Success = false, - Reason = $"{FilenamesHelper.GetFileName(subPath)} offsetLength" - }; - } - - int issue35Offset = 0; - for ( int offset = 0; offset <= maxIssue35Offset; ++offset ) + if ( string.IsNullOrEmpty(heightPixels) && string.IsNullOrEmpty(widthPixels) ) + { + var exifSubIfdDirectories = allExifItems.OfType().ToList(); + foreach ( var exifSubIfdDirectoryTags in exifSubIfdDirectories.Select(p => p.Tags) ) { - // 0xffd8 is the JFIF start of image segment indicator - if ( ( thumbnail[offset] == 0xff ) && ( thumbnail[offset + 1] == 0xd8 ) ) + var heightValue = exifSubIfdDirectoryTags + .FirstOrDefault(p => p.Type == ExifDirectoryBase.TagImageHeight) + ?.Description; + var widthValue = exifSubIfdDirectoryTags + .FirstOrDefault(p => p.Type == ExifDirectoryBase.TagImageWidth) + ?.Description; + if ( heightValue == null || widthValue == null ) { - issue35Offset = offset; - break; + continue; } + + heightPixels = heightValue; + widthPixels = widthValue; } + } - return new OffsetModel - { - Success = true, - Index = issue35Offset, - Count = thumbnailLength - issue35Offset, - Data = thumbnail, // byte array - }; + var rotation = ReadMetaExif.GetOrientationFromExifItem(allExifItems); + + var heightParseResult = int.TryParse(heightPixels?.Replace( + " pixels", string.Empty), out var height); + + var widthParseResult = + int.TryParse(widthPixels?.Replace(" pixels", string.Empty), out var width); + + if ( !heightParseResult || !widthParseResult || height == 0 || width == 0 ) + { + _logger.LogInformation( + $"[ParseMetaThumbnail] ${reference} has no height or width {width}x{height} "); } + + return ( exifThumbnailDir, width, height, rotation ); } } diff --git a/starsky/starsky/Controllers/DiskController.cs b/starsky/starsky/Controllers/DiskController.cs index 4b3f422960..bfc7495007 100644 --- a/starsky/starsky/Controllers/DiskController.cs +++ b/starsky/starsky/Controllers/DiskController.cs @@ -52,6 +52,11 @@ public DiskController(IQuery query, ISelectorStorage selectorStorage, [Produces("application/json")] public async Task Mkdir(string f) { + if ( !ModelState.IsValid ) + { + return BadRequest("Model invalid"); + } + var inputFilePaths = PathHelper.SplitInputFilePaths(f).ToList(); if ( inputFilePaths.Count == 0 ) { @@ -65,8 +70,7 @@ public async Task Mkdir(string f) { var toAddStatus = new SyncViewModel { - FilePath = subPath, - Status = FileIndexItem.ExifStatus.Ok + FilePath = subPath, Status = FileIndexItem.ExifStatus.Ok }; if ( _iStorage.ExistFolder(subPath) ) @@ -78,8 +82,7 @@ public async Task Mkdir(string f) await _query.AddItemAsync(new FileIndexItem(subPath) { - IsDirectory = true, - ImageFormat = ExtensionRolesHelper.ImageFormat.directory + IsDirectory = true, ImageFormat = ExtensionRolesHelper.ImageFormat.directory }); // add to fs @@ -110,8 +113,7 @@ private async Task SyncMessageToSocket(IEnumerable syncResultsLis { var list = syncResultsList.Select(t => new FileIndexItem(t.FilePath) { - Status = t.Status, - IsDirectory = true + Status = t.Status, IsDirectory = true }).ToList(); var webSocketResponse = new ApiNotificationResponseModel< @@ -161,7 +163,7 @@ public async Task Rename([Required] string f, string to, bool col return Json(currentStatus ? rename.Where(p => p.Status - != FileIndexItem.ExifStatus.NotFoundSourceMissing).ToList() + != FileIndexItem.ExifStatus.NotFoundSourceMissing).ToList() : rename); } } diff --git a/starsky/starsky/Controllers/ExportController.cs b/starsky/starsky/Controllers/ExportController.cs index 119aec353c..92db0b7665 100644 --- a/starsky/starsky/Controllers/ExportController.cs +++ b/starsky/starsky/Controllers/ExportController.cs @@ -12,97 +12,99 @@ using starsky.foundation.worker.Interfaces; using starsky.project.web.Helpers; -namespace starsky.Controllers +namespace starsky.Controllers; + +[Authorize] +public sealed class ExportController : Controller { - [Authorize] - public sealed class ExportController : Controller + private readonly IUpdateBackgroundTaskQueue _bgTaskQueue; + private readonly IExport _export; + private readonly IStorage _hostFileSystemStorage; + + public ExportController(IUpdateBackgroundTaskQueue queue, + ISelectorStorage selectorStorage, IExport export) { - private readonly IUpdateBackgroundTaskQueue _bgTaskQueue; - private readonly IStorage _hostFileSystemStorage; - private readonly IExport _export; + _bgTaskQueue = queue; + _hostFileSystemStorage = + selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem); + _export = export; + } - public ExportController(IUpdateBackgroundTaskQueue queue, - ISelectorStorage selectorStorage, IExport export) - { - _bgTaskQueue = queue; - _hostFileSystemStorage = - selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem); - _export = export; - } + /// + /// Export source files to an zip archive + /// + /// subPath to files + /// enable files with the same name (before the extension) + /// export thumbnails + /// name of a to generate zip file + /// the name of the to generated zip file + /// files not found + [HttpPost("/api/export/create-zip")] + [ProducesResponseType(typeof(string), 200)] // "zipHash" + [ProducesResponseType(typeof(List), 404)] // "Not found" + [Produces("application/json")] + public async Task CreateZip(string f, bool collections = true, + bool thumbnail = false) + { + var inputFilePaths = PathHelper.SplitInputFilePaths(f); + var (zipOutputName, fileIndexResultsList) = + await _export.PreflightAsync(inputFilePaths, collections, thumbnail); - /// - /// Export source files to an zip archive - /// - /// subPath to files - /// enable files with the same name (before the extension) - /// export thumbnails - /// name of a to generate zip file - /// the name of the to generated zip file - /// files not found - [HttpPost("/api/export/create-zip")] - [ProducesResponseType(typeof(string), 200)] // "zipHash" - [ProducesResponseType(typeof(List), 404)] // "Not found" - [Produces("application/json")] - public async Task CreateZip(string f, bool collections = true, - bool thumbnail = false) + // When all items are not found + // allow read only + if ( fileIndexResultsList.TrueForAll(p => p.Status != FileIndexItem.ExifStatus.Ok) ) { - var inputFilePaths = PathHelper.SplitInputFilePaths(f); - var (zipOutputName, fileIndexResultsList) = - await _export.PreflightAsync(inputFilePaths, collections, thumbnail); + return NotFound(fileIndexResultsList); + } - // When all items are not found - // allow read only - if ( fileIndexResultsList.TrueForAll(p => p.Status != FileIndexItem.ExifStatus.Ok) ) - { - return NotFound(fileIndexResultsList); - } + // NOT covered: when try to export for example image thumbnails of xml file - // NOT covered: when try to export for example image thumbnails of xml file + // Creating a zip is a background task + await _bgTaskQueue.QueueBackgroundWorkItemAsync( + async _ => { await _export.CreateZip(fileIndexResultsList, thumbnail, zipOutputName); }, + zipOutputName, Activity.Current?.Id); - // Creating a zip is a background task - await _bgTaskQueue.QueueBackgroundWorkItemAsync( - async _ => - { - await _export.CreateZip(fileIndexResultsList, thumbnail, zipOutputName); - }, zipOutputName, Activity.Current?.Id); + // for the rest api + return Json(zipOutputName); + } - // for the rest api - return Json(zipOutputName); + /// + /// Get the exported zip, but first call 'createZip' + /// use for example this url: /export/zip/TNA995920129.zip + /// TNA995920129 is from 'createZip' + /// + /// zip hash e.g. TNA995920129 + /// true to get OK instead of a zip file + /// Not ready or the zip-file + /// if json is true return 'OK', else the zip file + /// Not ready generating the zip, please wait + [HttpGet("/api/export/zip/{f}.zip")] + [ProducesResponseType(200)] // "zip file" + [ProducesResponseType(206)] // "Not Ready" + public ActionResult Status(string f, bool json = false) + { + if ( !ModelState.IsValid ) + { + return BadRequest("Model invalid"); } - /// - /// Get the exported zip, but first call 'createZip' - /// use for example this url: /export/zip/TNA995920129.zip - /// TNA995920129 is from 'createZip' - /// - /// zip hash e.g. TNA995920129 - /// true to get OK instead of a zip file - /// Not ready or the zip-file - /// if json is true return 'OK', else the zip file - /// Not ready generating the zip, please wait - [HttpGet("/api/export/zip/{f}.zip")] - [ProducesResponseType(200)] // "zip file" - [ProducesResponseType(206)] // "Not Ready" - public ActionResult Status(string f, bool json = false) + var (status, sourceFullPath) = _export.StatusIsReady(f); + switch ( status ) { - var (status, sourceFullPath) = _export.StatusIsReady(f); - switch ( status ) - { - case null: - return NotFound("Path is not found"); - case false: - Response.StatusCode = 206; - return Json("Not Ready"); - } - - if ( json ) - { - return Json("OK"); - } + case null: + return NotFound("Path is not found"); + case false: + Response.StatusCode = 206; + return Json("Not Ready"); + } - var fs = _hostFileSystemStorage.ReadStream(sourceFullPath!); - // Return the right mime type - return File(fs, MimeHelper.GetMimeTypeByFileName(sourceFullPath)); + if ( json ) + { + return Json("OK"); } + + var fs = _hostFileSystemStorage.ReadStream(sourceFullPath!); + // Return the right mime type + return File(fs, MimeHelper.GetMimeTypeByFileName(sourceFullPath)); } } diff --git a/starsky/starsky/Controllers/GeoController.cs b/starsky/starsky/Controllers/GeoController.cs index 5f6b61a433..f0dbcf0134 100644 --- a/starsky/starsky/Controllers/GeoController.cs +++ b/starsky/starsky/Controllers/GeoController.cs @@ -14,91 +14,101 @@ using starsky.foundation.storage.Storage; using starsky.foundation.worker.Interfaces; -namespace starsky.Controllers +namespace starsky.Controllers; + +[Authorize] +public sealed class GeoController : Controller { - [Authorize] - public sealed class GeoController : Controller + private readonly IUpdateBackgroundTaskQueue _bgTaskQueue; + private readonly IMemoryCache? _cache; + private readonly IStorage _iStorage; + private readonly IWebLogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + + public GeoController(IUpdateBackgroundTaskQueue queue, + ISelectorStorage selectorStorage, + IMemoryCache? memoryCache, IWebLogger logger, IServiceScopeFactory serviceScopeFactory) { - private readonly IUpdateBackgroundTaskQueue _bgTaskQueue; - private readonly IMemoryCache? _cache; - private readonly IStorage _iStorage; - private readonly IServiceScopeFactory _serviceScopeFactory; - private readonly IWebLogger _logger; + _bgTaskQueue = queue; + _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath); + _cache = memoryCache; + _serviceScopeFactory = serviceScopeFactory; + _logger = logger; + } - public GeoController(IUpdateBackgroundTaskQueue queue, - ISelectorStorage selectorStorage, - IMemoryCache? memoryCache, IWebLogger logger, IServiceScopeFactory serviceScopeFactory) + /// + /// Get Geo sync status + /// + /// sub path folders + /// status of geo sync + /// the current status + /// cache service is missing + [HttpGet("/api/geo/status")] + [ProducesResponseType(typeof(GeoCacheStatus), 200)] // "cache service is missing" + [ProducesResponseType(typeof(string), 404)] // "Not found" + [Produces("application/json")] + public IActionResult Status( + string f = "/") + { + if ( !ModelState.IsValid ) { - _bgTaskQueue = queue; - _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath); - _cache = memoryCache; - _serviceScopeFactory = serviceScopeFactory; - _logger = logger; + return BadRequest("Model invalid"); } - /// - /// Get Geo sync status - /// - /// sub path folders - /// status of geo sync - /// the current status - /// cache service is missing - [HttpGet("/api/geo/status")] - [ProducesResponseType(typeof(GeoCacheStatus), 200)] // "cache service is missing" - [ProducesResponseType(typeof(string), 404)] // "Not found" - [Produces("application/json")] - public IActionResult Status( - string f = "/") + if ( _cache == null ) { - if ( _cache == null ) - { - return NotFound("cache service is missing"); - } - - return Json(new GeoCacheStatusService(_cache).Status(f)); + return NotFound("cache service is missing"); } + return Json(new GeoCacheStatusService(_cache).Status(f)); + } + - /// - /// Reverse lookup for Geo Information and/or add Geo location based on a GPX file within the same directory - /// - /// subPath only folders - /// -i in cli - /// -a in cli - /// - /// event is fired - /// sub path not found in the database - /// User unauthorized - [HttpPost("/api/geo/sync")] - [Produces("application/json")] - [ProducesResponseType(typeof(string), 404)] // event is fired - [ProducesResponseType(typeof(string), 200)] // "Not found" - public async Task GeoSyncFolder( - string f = "/", - bool index = true, - bool overwriteLocationNames = false - ) + /// + /// Reverse lookup for Geo Information and/or add Geo location based on a GPX file within the same + /// directory + /// + /// subPath only folders + /// -i in cli + /// -a in cli + /// + /// event is fired + /// sub path not found in the database + /// User unauthorized + [HttpPost("/api/geo/sync")] + [Produces("application/json")] + [ProducesResponseType(typeof(string), 404)] // event is fired + [ProducesResponseType(typeof(string), 200)] // "Not found" + public async Task GeoSyncFolder( + string f = "/", + bool index = true, + bool overwriteLocationNames = false + ) + { + if ( !ModelState.IsValid ) { - if ( _iStorage.IsFolderOrFile(f) == FolderOrFileModel.FolderOrFileTypeList.Deleted ) - { - return NotFound("Folder location is not found"); - } + return BadRequest("Model invalid"); + } + if ( _iStorage.IsFolderOrFile(f) == FolderOrFileModel.FolderOrFileTypeList.Deleted ) + { + return NotFound("Folder location is not found"); + } - await _bgTaskQueue.QueueBackgroundWorkItemAsync(async _ => - { - _logger.LogInformation( - $"{nameof(GeoSyncFolder)} started {f} {DateTime.UtcNow.ToShortTimeString()}"); - var geoBackgroundTask = _serviceScopeFactory.CreateScope().ServiceProvider - .GetRequiredService(); - var result = await geoBackgroundTask.GeoBackgroundTaskAsync(f, index, - overwriteLocationNames); + await _bgTaskQueue.QueueBackgroundWorkItemAsync(async _ => + { + _logger.LogInformation( + $"{nameof(GeoSyncFolder)} started {f} {DateTime.UtcNow.ToShortTimeString()}"); - _logger.LogInformation($"{nameof(GeoSyncFolder)} end {f} {result.Count}"); - }, f, Activity.Current?.Id); + var geoBackgroundTask = _serviceScopeFactory.CreateScope().ServiceProvider + .GetRequiredService(); + var result = await geoBackgroundTask.GeoBackgroundTaskAsync(f, index, + overwriteLocationNames); - return Json("job started"); - } + _logger.LogInformation($"{nameof(GeoSyncFolder)} end {f} {result.Count}"); + }, f, Activity.Current?.Id); + + return Json("job started"); } } diff --git a/starsky/starsky/Controllers/HealthCheckForUpdatesController.cs b/starsky/starsky/Controllers/HealthCheckForUpdatesController.cs index 4016c94a9a..686b881c4f 100644 --- a/starsky/starsky/Controllers/HealthCheckForUpdatesController.cs +++ b/starsky/starsky/Controllers/HealthCheckForUpdatesController.cs @@ -7,70 +7,79 @@ using starsky.feature.health.UpdateCheck.Models; using starsky.Helpers; -namespace starsky.Controllers +namespace starsky.Controllers; + +[AllowAnonymous] +public sealed class HealthCheckForUpdatesController : Controller { - [AllowAnonymous] - public sealed class HealthCheckForUpdatesController : Controller + private readonly ICheckForUpdates _checkForUpdates; + private readonly ISpecificVersionReleaseInfo _specificVersionReleaseInfo; + + public HealthCheckForUpdatesController(ICheckForUpdates checkForUpdates, + ISpecificVersionReleaseInfo specificVersionReleaseInfo) { - private readonly ICheckForUpdates _checkForUpdates; - private readonly ISpecificVersionReleaseInfo _specificVersionReleaseInfo; + _checkForUpdates = checkForUpdates; + _specificVersionReleaseInfo = specificVersionReleaseInfo; + } - public HealthCheckForUpdatesController(ICheckForUpdates checkForUpdates, - ISpecificVersionReleaseInfo specificVersionReleaseInfo) + /// + /// Check if Client/App version has a match with the API-version + /// + /// status if you need to update + /// Feature disabled + /// http request error or version number is not valid + /// There are no releases found + /// Need To Update + /// Current Version Is Latest + [HttpGet("/api/health/check-for-updates")] + [AllowAnonymous] + [ResponseCache(Duration = 7257600, Location = ResponseCacheLocation.Client)] + [Produces("application/json")] + public async Task CheckForUpdates(string currentVersion = "") + { + if ( !ModelState.IsValid ) { - _checkForUpdates = checkForUpdates; - _specificVersionReleaseInfo = specificVersionReleaseInfo; + return BadRequest("Model invalid"); } - /// - /// Check if Client/App version has a match with the API-version - /// - /// status if you need to update - /// Feature disabled - /// http request error or version number is not valid - /// There are no releases found - /// Need To Update - /// Current Version Is Latest - [HttpGet("/api/health/check-for-updates")] - [AllowAnonymous] - [ResponseCache(Duration = 7257600, Location = ResponseCacheLocation.Client)] - [Produces("application/json")] - public async Task CheckForUpdates(string currentVersion = "") + var (key, value) = await _checkForUpdates.IsUpdateNeeded(currentVersion); + return key switch { - var (key, value) = await _checkForUpdates.IsUpdateNeeded(currentVersion); - return key switch - { - UpdateStatus.Disabled => StatusCode(StatusCodes.Status208AlreadyReported, - $"feature is disabled"), - UpdateStatus.HttpError => BadRequest("something went wrong (http)"), - UpdateStatus.NoReleasesFound => StatusCode(StatusCodes.Status206PartialContent, - $"There are no releases found"), - UpdateStatus.NeedToUpdate => StatusCode(StatusCodes.Status202Accepted, value), - UpdateStatus.CurrentVersionIsLatest => StatusCode(StatusCodes.Status200OK, value), - UpdateStatus.InputNotValid => BadRequest("something went wrong (version)"), - _ => throw new NotSupportedException( - "IsUpdateNeeded didn't pass any valid selection") - }; - } + UpdateStatus.Disabled => StatusCode(StatusCodes.Status208AlreadyReported, + "feature is disabled"), + UpdateStatus.HttpError => BadRequest("something went wrong (http)"), + UpdateStatus.NoReleasesFound => StatusCode(StatusCodes.Status206PartialContent, + "There are no releases found"), + UpdateStatus.NeedToUpdate => StatusCode(StatusCodes.Status202Accepted, value), + UpdateStatus.CurrentVersionIsLatest => StatusCode(StatusCodes.Status200OK, value), + UpdateStatus.InputNotValid => BadRequest("something went wrong (version)"), + _ => throw new NotSupportedException( + "IsUpdateNeeded didn't pass any valid selection") + }; + } - /// - /// Get more info to show about the release - /// - /// status if you need to update - /// result - [HttpGet("/api/health/release-info")] - [AllowAnonymous] - [Produces("application/json")] - public async Task SpecificVersionReleaseInfo(string v = "") + /// + /// Get more info to show about the release + /// + /// status if you need to update + /// result + [HttpGet("/api/health/release-info")] + [AllowAnonymous] + [Produces("application/json")] + public async Task SpecificVersionReleaseInfo(string v = "") + { + if ( !ModelState.IsValid ) { - if ( !string.IsNullOrWhiteSpace(v) ) - { - CacheControlOverwrite.SetExpiresResponseHeaders(Request, 604800); // 1 week - } + return BadRequest("Model invalid"); + } - var result = - await _specificVersionReleaseInfo.SpecificVersionMessage(v); - return Json(result); + if ( !string.IsNullOrWhiteSpace(v) ) + { + CacheControlOverwrite.SetExpiresResponseHeaders(Request, 604800); // 1 week } + + var result = + await _specificVersionReleaseInfo.SpecificVersionMessage(v); + return Json(result); } } diff --git a/starsky/starsky/Controllers/HealthController.cs b/starsky/starsky/Controllers/HealthController.cs index 6d16cde3cd..9ab688e1be 100644 --- a/starsky/starsky/Controllers/HealthController.cs +++ b/starsky/starsky/Controllers/HealthController.cs @@ -14,194 +14,198 @@ [assembly: InternalsVisibleTo("starskytest")] -namespace starsky.Controllers +namespace starsky.Controllers; + +public sealed class HealthController : Controller { - public sealed class HealthController : Controller + /// + /// Check if min version is matching + /// keywords: MinVersion or Version( or SemVersion( + /// + internal const string MinimumVersion = "0.5"; // only insert 0.5 or 0.6 + + /// + /// Name of the header for api version + /// + private const string ApiVersionHeaderName = "x-api-version"; + + private readonly IMemoryCache? _cache; + private readonly HealthCheckService _service; + + public HealthController(HealthCheckService service, + IMemoryCache? memoryCache = null) { - private readonly HealthCheckService _service; - private readonly IMemoryCache? _cache; + _service = service; + _cache = memoryCache; + } - public HealthController(HealthCheckService service, - IMemoryCache? memoryCache = null) + /// + /// Check if the service has any known errors and return only a string + /// Public API + /// + /// + /// Ok + /// 503 Service Unavailable + [HttpGet("/api/health")] + [HttpHead("/api/health")] + [Produces("application/json")] + [ProducesResponseType(typeof(string), 200)] + [ProducesResponseType(typeof(string), 503)] + [AllowAnonymous] + public async Task Index() + { + var result = await CheckHealthAsyncWithTimeout(10000); + if ( result.Status == HealthStatus.Healthy ) { - _service = service; - _cache = memoryCache; + return Content(result.Status.ToString()); } - /// - /// Check if the service has any known errors and return only a string - /// Public API - /// - /// - /// Ok - /// 503 Service Unavailable - [HttpGet("/api/health")] - [HttpHead("/api/health")] - [Produces("application/json")] - [ProducesResponseType(typeof(string), 200)] - [ProducesResponseType(typeof(string), 503)] - [AllowAnonymous] - public async Task Index() + Response.StatusCode = 503; + return Content(result.Status.ToString()); + } + + /// + /// With timeout after 15 seconds + /// + /// in milliseconds, defaults to 15 seconds + /// report + internal async Task CheckHealthAsyncWithTimeout(int timeoutTime = 15000) + { + const string healthControllerCacheKey = "health"; + try { - var result = await CheckHealthAsyncWithTimeout(10000); - if ( result.Status == HealthStatus.Healthy ) + if ( _cache != null && + _cache.TryGetValue(healthControllerCacheKey, out var objectHealthStatus) && + objectHealthStatus is HealthReport healthStatus && + healthStatus.Status == HealthStatus.Healthy ) { - return Content(result.Status.ToString()); + return healthStatus; } + var result = await _service.CheckHealthAsync().TimeoutAfter(timeoutTime); + if ( _cache != null && result.Status == HealthStatus.Healthy ) + { + _cache.Set(healthControllerCacheKey, result, new TimeSpan(0, 1, 30)); + } + + return result; + } + catch ( TimeoutException exception ) + { + var entry = new HealthReportEntry( + HealthStatus.Unhealthy, + "timeout", + TimeSpan.FromMilliseconds(timeoutTime), + exception, + null); + + return new HealthReport( + new Dictionary { { "timeout", entry } }, + TimeSpan.FromMilliseconds(timeoutTime)); + } + } + + + /// + /// Check if the service has any known errors + /// For Authorized Users only + /// + /// + /// Ok + /// 503 Service Unavailable + /// Login first + [HttpGet("/api/health/details")] + [Authorize] // <-------------- + [Produces("application/json")] + [ProducesResponseType(typeof(HealthView), 200)] + [ProducesResponseType(typeof(HealthView), 503)] + [ProducesResponseType(401)] + public async Task Details() + { + var result = await CheckHealthAsyncWithTimeout(); + + var health = CreateHealthEntryLog(result); + if ( !health.IsHealthy ) + { Response.StatusCode = 503; - return Content(result.Status.ToString()); } - /// - /// With timeout after 15 seconds - /// - /// in milliseconds, defaults to 15 seconds - /// report - internal async Task CheckHealthAsyncWithTimeout(int timeoutTime = 15000) + return Json(health); + } + + private static HealthView CreateHealthEntryLog(HealthReport result) + { + var health = new HealthView { - const string healthControllerCacheKey = "health"; - try - { - if ( _cache != null && - _cache.TryGetValue(healthControllerCacheKey, out var objectHealthStatus) && - objectHealthStatus is HealthReport healthStatus && - healthStatus.Status == HealthStatus.Healthy ) - { - return healthStatus; - } + IsHealthy = result.Status == HealthStatus.Healthy, + TotalDuration = result.TotalDuration + }; - var result = await _service.CheckHealthAsync().TimeoutAfter(timeoutTime); - if ( _cache != null && result.Status == HealthStatus.Healthy ) + foreach ( var (key, value) in result.Entries ) + { + health.Entries.Add( + new HealthEntry { - _cache.Set(healthControllerCacheKey, result, new TimeSpan(0, 1, 30)); + Duration = value.Duration, + Name = key, + IsHealthy = value.Status == HealthStatus.Healthy, + Description = value.Description + value.Exception?.Message + + value.Exception?.StackTrace } - - return result; - } - catch ( TimeoutException exception ) - { - var entry = new HealthReportEntry( - HealthStatus.Unhealthy, - "timeout", - TimeSpan.FromMilliseconds(timeoutTime), - exception, - null); - - return new HealthReport( - new Dictionary { { "timeout", entry } }, - TimeSpan.FromMilliseconds(timeoutTime)); - } + ); } + return health; + } - /// - /// Check if the service has any known errors - /// For Authorized Users only - /// - /// - /// Ok - /// 503 Service Unavailable - /// Login first - [HttpGet("/api/health/details")] - [Authorize] // <-------------- - [Produces("application/json")] - [ProducesResponseType(typeof(HealthView), 200)] - [ProducesResponseType(typeof(HealthView), 503)] - [ProducesResponseType(401)] - public async Task Details() + /// + /// Check if Client/App version has a match with the API-version + /// the parameter 'version' is checked first, and if missing the x-api-version header is used + /// + /// status + /// Ok + /// Version mismatch + /// Missing x-api-version header OR bad formatted version in header + [HttpPost("/api/health/version")] + [AllowAnonymous] + public IActionResult Version(string? version = null) + { + if ( !ModelState.IsValid ) { - var result = await CheckHealthAsyncWithTimeout(); - - var health = CreateHealthEntryLog(result); - if ( !health.IsHealthy ) - { - Response.StatusCode = 503; - } - - return Json(health); + return BadRequest("Model invalid"); } - private static HealthView CreateHealthEntryLog(HealthReport result) + if ( string.IsNullOrEmpty(version) ) { - var health = new HealthView - { - IsHealthy = result.Status == HealthStatus.Healthy, - TotalDuration = result.TotalDuration - }; - - foreach ( var (key, value) in result.Entries ) + var headerVersion = + Request.Headers.FirstOrDefault(p => + p.Key == ApiVersionHeaderName).Value; + if ( !string.IsNullOrEmpty(headerVersion) ) { - health.Entries.Add( - new HealthEntry - { - Duration = value.Duration, - Name = key, - IsHealthy = value.Status == HealthStatus.Healthy, - Description = value.Description + value.Exception?.Message + - value.Exception?.StackTrace - } - ); + version = headerVersion; } - - return health; } - /// - /// Check if min version is matching - /// keywords: MinVersion or Version( or SemVersion( - /// - internal const string MinimumVersion = "0.5"; // only insert 0.5 or 0.6 - - /// - /// Name of the header for api version - /// - private const string ApiVersionHeaderName = "x-api-version"; - - /// - /// Check if Client/App version has a match with the API-version - /// the parameter 'version' is checked first, and if missing the x-api-version header is used - /// - /// status - /// Ok - /// Version mismatch - /// Missing x-api-version header OR bad formatted version in header - [HttpPost("/api/health/version")] - [AllowAnonymous] - public IActionResult Version(string? version = null) + if ( string.IsNullOrEmpty(version) ) { - if ( string.IsNullOrEmpty(version) ) - { - var headerVersion = - Request.Headers.FirstOrDefault(p => - p.Key == ApiVersionHeaderName).Value; - if ( !string.IsNullOrEmpty(headerVersion) ) - { - version = headerVersion; - } - } + return BadRequest( + $"Missing version data, Add {ApiVersionHeaderName} header with value"); + } - if ( string.IsNullOrEmpty(version) ) + try + { + if ( SemVersion.Parse(version) >= SemVersion.Parse(MinimumVersion) ) { - return BadRequest( - $"Missing version data, Add {ApiVersionHeaderName} header with value"); + return Ok(version); } - try - { - if ( SemVersion.Parse(version) >= SemVersion.Parse(MinimumVersion) ) - { - return Ok(version); - } - - return StatusCode(StatusCodes.Status202Accepted, - $"Please Upgrade ClientApp to {MinimumVersion} or newer"); - } - catch ( ArgumentException ) - { - return StatusCode(StatusCodes.Status400BadRequest, - $"Parsing failed {version}"); - } + return StatusCode(StatusCodes.Status202Accepted, + $"Please Upgrade ClientApp to {MinimumVersion} or newer"); + } + catch ( ArgumentException ) + { + return StatusCode(StatusCodes.Status400BadRequest, + $"Parsing failed {version}"); } } } diff --git a/starsky/starsky/Controllers/HomeController.cs b/starsky/starsky/Controllers/HomeController.cs index fe14d792a4..57249d6d0e 100644 --- a/starsky/starsky/Controllers/HomeController.cs +++ b/starsky/starsky/Controllers/HomeController.cs @@ -1,215 +1,238 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Antiforgery; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using starsky.foundation.platform.Models; using starsky.Helpers; [assembly: InternalsVisibleTo("starskytest")] -namespace starsky.Controllers +namespace starsky.Controllers; + +[Authorize] +public sealed class HomeController : Controller { - [Authorize] - public sealed class HomeController : Controller + private const string TextHtmlMimeType = "text/html"; + private readonly IAntiforgery _antiForgery; + private readonly string _clientApp; + + public HomeController(AppSettings appSettings, IAntiforgery antiForgery) { - private readonly string _clientApp; - private readonly IAntiforgery _antiForgery; - private const string TextHtmlMimeType = "text/html"; + _antiForgery = antiForgery; + _clientApp = Path.Combine(appSettings.BaseDirectoryProject, + "clientapp", "build", "index.html"); + } - public HomeController(AppSettings appSettings, IAntiforgery antiForgery) + /// + /// Home page (HTML) + /// + /// subPath + /// client app html + /// client app html + /// Login first + [Produces("text/html")] + [ProducesResponseType(200)] + [ProducesResponseType(401)] + [SuppressMessage("Usage", "IDE0060:Remove unused parameter")] + public IActionResult Index(string f = "") + { + if ( !ModelState.IsValid ) { - _antiForgery = antiForgery; - _clientApp = Path.Combine(appSettings.BaseDirectoryProject, - "clientapp", "build", "index.html"); + return BadRequest("Model invalid"); } - /// - /// Home page (HTML) - /// - /// subPath - /// client app html - /// client app html - /// Login first - [Produces("text/html")] - [ProducesResponseType(200)] - [ProducesResponseType(401)] - [SuppressMessage("Usage", "IDE0060:Remove unused parameter")] - public IActionResult Index(string f = "") + new AntiForgeryCookie(_antiForgery).SetAntiForgeryCookie(HttpContext); + return PhysicalFile(_clientApp, TextHtmlMimeType); + } + + /// + /// Redirect to search GET page (HTML) + /// + /// search query + /// page number + /// client app html + /// redirect to get page + /// Login first + [Produces("text/html")] + [ProducesResponseType(301)] + [ProducesResponseType(401)] + [HttpPost("/search")] + public IActionResult SearchPost(string t = "", int p = 0) + { + if ( !ModelState.IsValid ) { - new AntiForgeryCookie(_antiForgery).SetAntiForgeryCookie(HttpContext); - return PhysicalFile(_clientApp, TextHtmlMimeType); + return BadRequest("Model invalid"); } - /// - /// Redirect to search GET page (HTML) - /// - /// search query - /// page number - /// client app html - /// redirect to get page - /// Login first - [Produces("text/html")] - [ProducesResponseType(301)] - [ProducesResponseType(401)] - [HttpPost("/search")] - public IActionResult SearchPost(string t = "", int p = 0) + if ( string.IsNullOrEmpty(t) ) { - if ( string.IsNullOrEmpty(t) ) - { - return Redirect($"/search"); - } - - // Added filter to prevent redirects based on tainted, user-controlled data - // unescaped: ^[a-zA-Z0-9_\-+"'/=:,\.>< ]+$ - if ( !Regex.IsMatch(t, "^[a-zA-Z0-9_\\-+\"'/=:,\\.>< ]+$", - RegexOptions.None, TimeSpan.FromMilliseconds(100)) ) - { - return BadRequest("`t` is not allowed"); - } - - return Redirect(AppendPathBasePrefix(Request.PathBase.Value, $"/search?t={t}&p={p}")); + return Redirect("/search"); } - /// - /// Search GET page (HTML) - /// - /// search query - /// page number - /// client app html - /// client app html - /// Is Case Sensitive Redirect - /// input not allowed - /// Login first - [Produces("text/html")] - [ProducesResponseType(200)] - [ProducesResponseType(301)] - [ProducesResponseType(400)] - [ProducesResponseType(401)] - [HttpGet("/search")] - public IActionResult Search(string t = "", int p = 0) + // Added filter to prevent redirects based on tainted, user-controlled data + // unescaped: ^[a-zA-Z0-9_\-+"'/=:,\.>< ]+$ + if ( !Regex.IsMatch(t, "^[a-zA-Z0-9_\\-+\"'/=:,\\.>< ]+$", + RegexOptions.None, TimeSpan.FromMilliseconds(100)) ) { - new AntiForgeryCookie(_antiForgery).SetAntiForgeryCookie(HttpContext); - - if ( !IsCaseSensitiveRedirect("/search", Request.Path.Value) ) - { - return PhysicalFile(_clientApp, TextHtmlMimeType); - } - - // if not case sensitive is already served - if ( string.IsNullOrEmpty(t) ) - { - return Redirect(AppendPathBasePrefix(Request.PathBase.Value, $"/search")); - } - - // Added filter to prevent redirects based on tainted, user-controlled data - // unescaped: ^[a-zA-Z0-9_\-+"'/=:>< ]+$ - if ( !Regex.IsMatch(t, "^[a-zA-Z0-9_\\-+\"'/=:>< ]+$", - RegexOptions.None, TimeSpan.FromMilliseconds(100)) ) - { - return BadRequest("`t` is not allowed"); - } - - return Redirect(AppendPathBasePrefix(Request.PathBase.Value, $"/search?t={t}&p={p}")); + return BadRequest("`t` is not allowed"); } - /// - /// Trash page (HTML) - /// - /// page number - /// client app html - /// client app html - /// Is Case Sensitive Redirect - /// Login first - [Produces("text/html")] - [ProducesResponseType(200)] - [ProducesResponseType(301)] - [ProducesResponseType(401)] - [HttpGet("/trash")] - public IActionResult Trash(int p = 0) + return Redirect(AppendPathBasePrefix(Request.PathBase.Value, $"/search?t={t}&p={p}")); + } + + /// + /// Search GET page (HTML) + /// + /// search query + /// page number + /// client app html + /// client app html + /// Is Case Sensitive Redirect + /// input not allowed + /// Login first + [Produces("text/html")] + [ProducesResponseType(200)] + [ProducesResponseType(301)] + [ProducesResponseType(400)] + [ProducesResponseType(401)] + [HttpGet("/search")] + public IActionResult Search(string t = "", int p = 0) + { + if ( !ModelState.IsValid ) { - if ( IsCaseSensitiveRedirect("/trash", Request.Path.Value) ) - { - return Redirect(AppendPathBasePrefix(Request.PathBase.Value, $"/trash?p={p}")); - } + return BadRequest("Model invalid"); + } + + new AntiForgeryCookie(_antiForgery).SetAntiForgeryCookie(HttpContext); + if ( !IsCaseSensitiveRedirect("/search", Request.Path.Value) ) + { return PhysicalFile(_clientApp, TextHtmlMimeType); } - /// - /// Import page (HTML) - /// - /// client app html - /// client app html - /// Is Case Sensitive Redirect - [Produces("text/html")] - [ProducesResponseType(200)] - [ProducesResponseType(301)] - [HttpGet("/import")] - public IActionResult Import() + // if not case sensitive is already served + if ( string.IsNullOrEmpty(t) ) { - if ( IsCaseSensitiveRedirect("/import", Request.Path.Value) ) - { - return Redirect(AppendPathBasePrefix(Request.PathBase.Value, $"/import")); - } - - return PhysicalFile(_clientApp, TextHtmlMimeType); + return Redirect(AppendPathBasePrefix(Request.PathBase.Value, "/search")); } - /// - /// Preferences page (HTML) - /// - /// client app html - /// client app html - /// Is Case Sensitive Redirect - [Produces("text/html")] - [ProducesResponseType(200)] - [ProducesResponseType(301)] - [HttpGet("/preferences")] - public IActionResult Preferences() + // Added filter to prevent redirects based on tainted, user-controlled data + // unescaped: ^[a-zA-Z0-9_\-+"'/=:>< ]+$ + if ( !Regex.IsMatch(t, "^[a-zA-Z0-9_\\-+\"'/=:>< ]+$", + RegexOptions.None, TimeSpan.FromMilliseconds(100)) ) { - if ( IsCaseSensitiveRedirect("/preferences", Request.Path.Value) ) - { - return Redirect(AppendPathBasePrefix(Request.PathBase.Value, $"/preferences")); - } + return BadRequest("`t` is not allowed"); + } - return PhysicalFile(_clientApp, TextHtmlMimeType); + return Redirect(AppendPathBasePrefix(Request.PathBase.Value, $"/search?t={t}&p={p}")); + } + + /// + /// Trash page (HTML) + /// + /// page number + /// client app html + /// client app html + /// Is Case Sensitive Redirect + /// Login first + [Produces("text/html")] + [ProducesResponseType(200)] + [ProducesResponseType(301)] + [ProducesResponseType(401)] + [HttpGet("/trash")] + public IActionResult Trash(int p = 0) + { + if ( !ModelState.IsValid ) + { + return BadRequest("Model invalid"); + } + + if ( IsCaseSensitiveRedirect("/trash", Request.Path.Value) ) + { + return Redirect(AppendPathBasePrefix(Request.PathBase.Value, $"/trash?p={p}")); } - /// - /// View the Register form (HTML) - /// - /// when successful continue - /// client app html - /// successful Register-page - [HttpGet("/account/register")] - [AllowAnonymous] - [ProducesResponseType(200)] - [Produces("text/html")] - [ProducesResponseType(200)] - [SuppressMessage("ReSharper", "UnusedParameter.Global")] - [SuppressMessage("Usage", "IDE0060:Remove unused parameter")] - public IActionResult Register(string? returnUrl = null) + return PhysicalFile(_clientApp, TextHtmlMimeType); + } + + /// + /// Import page (HTML) + /// + /// client app html + /// client app html + /// Is Case Sensitive Redirect + [Produces("text/html")] + [ProducesResponseType(200)] + [ProducesResponseType(301)] + [HttpGet("/import")] + public IActionResult Import() + { + if ( IsCaseSensitiveRedirect("/import", Request.Path.Value) ) { - new AntiForgeryCookie(_antiForgery).SetAntiForgeryCookie(HttpContext); - return PhysicalFile(_clientApp, TextHtmlMimeType); + return Redirect(AppendPathBasePrefix(Request.PathBase.Value, "/import")); } - internal static string AppendPathBasePrefix(string? requestPathBase, string url) + return PhysicalFile(_clientApp, TextHtmlMimeType); + } + + /// + /// Preferences page (HTML) + /// + /// client app html + /// client app html + /// Is Case Sensitive Redirect + [Produces("text/html")] + [ProducesResponseType(200)] + [ProducesResponseType(301)] + [HttpGet("/preferences")] + public IActionResult Preferences() + { + if ( IsCaseSensitiveRedirect("/preferences", Request.Path.Value) ) { - return requestPathBase?.Equals("/starsky", - StringComparison.InvariantCultureIgnoreCase) == true - ? $"/starsky{url}" - : url; + return Redirect(AppendPathBasePrefix(Request.PathBase.Value, "/preferences")); } - internal static bool IsCaseSensitiveRedirect(string? expectedRequestPath, - string? requestPathValue) + return PhysicalFile(_clientApp, TextHtmlMimeType); + } + + /// + /// View the Register form (HTML) + /// + /// when successful continue + /// client app html + /// successful Register-page + [HttpGet("/account/register")] + [AllowAnonymous] + [ProducesResponseType(200)] + [Produces("text/html")] + [ProducesResponseType(200)] + [SuppressMessage("ReSharper", "UnusedParameter.Global")] + [SuppressMessage("Usage", "IDE0060:Remove unused parameter")] + public IActionResult Register(string? returnUrl = null) + { + if ( !ModelState.IsValid ) { - return expectedRequestPath != requestPathValue; + return BadRequest("Model invalid"); } + new AntiForgeryCookie(_antiForgery).SetAntiForgeryCookie(HttpContext); + return PhysicalFile(_clientApp, TextHtmlMimeType); + } + + internal static string AppendPathBasePrefix(string? requestPathBase, string url) + { + return requestPathBase?.Equals("/starsky", + StringComparison.InvariantCultureIgnoreCase) == true + ? $"/starsky{url}" + : url; + } + + internal static bool IsCaseSensitiveRedirect(string? expectedRequestPath, + string? requestPathValue) + { + return expectedRequestPath != requestPathValue; } } diff --git a/starsky/starsky/Controllers/ImportController.cs b/starsky/starsky/Controllers/ImportController.cs index 6e4bd2a22e..eefbb9f717 100644 --- a/starsky/starsky/Controllers/ImportController.cs +++ b/starsky/starsky/Controllers/ImportController.cs @@ -168,12 +168,14 @@ internal async Task> ImportPostBackgroundTask( [ProducesResponseType(typeof(List), 206)] // file already imported [ProducesResponseType(404)] // url 404 [Produces("application/json")] - public async Task FromUrl(string fileUrl, string filename, string structure) + public async Task FromUrl(string fileUrl, string? filename, string structure) { - if ( filename == null ) + if ( !ModelState.IsValid ) { - filename = Base32.Encode(FileHash.GenerateRandomBytes(8)) + ".unknown"; + return BadRequest("Model invalid"); } + + filename ??= Base32.Encode(FileHash.GenerateRandomBytes(8)) + ".unknown"; // I/O function calls should not be vulnerable to path injection attacks if ( !Regex.IsMatch(filename, "^[a-zA-Z0-9_\\s\\.]+$", diff --git a/starsky/starsky/Controllers/IndexController.cs b/starsky/starsky/Controllers/IndexController.cs index 6530fdfc74..8a57c61cba 100644 --- a/starsky/starsky/Controllers/IndexController.cs +++ b/starsky/starsky/Controllers/IndexController.cs @@ -46,6 +46,11 @@ public IActionResult Index( bool hideDelete = true, SortType sort = SortType.FileName) { + if ( !ModelState.IsValid ) + { + return BadRequest("Model invalid"); + } + // Used in Detail and Index View => does not hide this single item var colorClassActiveList = FileIndexItem.GetColorClassList(colorClass); diff --git a/starsky/starsky/Controllers/MetaInfoController.cs b/starsky/starsky/Controllers/MetaInfoController.cs index 14f71761ff..188c6acd36 100644 --- a/starsky/starsky/Controllers/MetaInfoController.cs +++ b/starsky/starsky/Controllers/MetaInfoController.cs @@ -36,6 +36,11 @@ public MetaInfoController(IMetaInfo metaInfo) [Produces("application/json")] public async Task InfoAsync(string f, bool collections = true) { + if ( !ModelState.IsValid ) + { + return BadRequest("Model invalid"); + } + var inputFilePaths = PathHelper.SplitInputFilePaths(f).ToList(); var fileIndexResultsList = await _metaInfo.GetInfoAsync(inputFilePaths, collections); diff --git a/starsky/starskytest/Controllers/DiskControllerTest.cs b/starsky/starskytest/Controllers/DiskControllerTest.cs index b6c34d5593..0aa0bbc1dc 100644 --- a/starsky/starskytest/Controllers/DiskControllerTest.cs +++ b/starsky/starskytest/Controllers/DiskControllerTest.cs @@ -269,6 +269,24 @@ public async Task SyncControllerTest_Mkdir_Good() Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?.FirstOrDefault()?.Status); } + [TestMethod] + public async Task Mkdir_ReturnsBadRequest() + { + // Arrange + var controller = + new DiskController(_query, new FakeSelectorStorage(), + new FakeIWebSocketConnectionsService(), new FakeINotificationQuery()); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + // Act + var result = await controller.Mkdir(null!); + + // Assert + Assert.IsInstanceOfType(result); + } + [TestMethod] public async Task SyncControllerTest_Mkdir_Good_SocketUpdate() { diff --git a/starsky/starskytest/Controllers/ExportControllerTest.cs b/starsky/starskytest/Controllers/ExportControllerTest.cs index 7616ce662c..d521031229 100644 --- a/starsky/starskytest/Controllers/ExportControllerTest.cs +++ b/starsky/starskytest/Controllers/ExportControllerTest.cs @@ -414,6 +414,27 @@ public void ExportController_ZipNotFound() Assert.AreEqual(404, actionResult?.StatusCode); } + [TestMethod] + public void Mkdir_ReturnsBadRequest() + { + // Arrange + var storage = new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()); + var selectorStorage = new FakeSelectorStorage(storage); + var export = new ExportService(_query, _appSettings, selectorStorage, + new FakeIWebLogger(), + new FakeIThumbnailService(selectorStorage)); + var controller = new ExportController(_bgTaskQueue, selectorStorage, export); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + // Act + var result = controller.Status(null!); + + // Assert + Assert.IsInstanceOfType(result); + } + [TestMethod] public void Status_Returns_NotReady_When_StatusIsFalse() { diff --git a/starsky/starskytest/Controllers/GeoControllerTest.cs b/starsky/starskytest/Controllers/GeoControllerTest.cs index 00b35f3421..ed239ba646 100644 --- a/starsky/starskytest/Controllers/GeoControllerTest.cs +++ b/starsky/starskytest/Controllers/GeoControllerTest.cs @@ -24,164 +24,195 @@ using starskytest.FakeCreateAn; using starskytest.FakeMocks; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public sealed class GeoControllerTest { - [TestClass] - public sealed class GeoControllerTest + private readonly IUpdateBackgroundTaskQueue _bgTaskQueue; + private readonly IMemoryCache _memoryCache; + private readonly IServiceScopeFactory _scopeFactory; + + public GeoControllerTest() { - private readonly IUpdateBackgroundTaskQueue _bgTaskQueue; - private readonly IServiceScopeFactory _scopeFactory; - private readonly IMemoryCache _memoryCache; + var provider = new ServiceCollection() + .AddMemoryCache() + .BuildServiceProvider(); + _memoryCache = provider.GetRequiredService(); + + var builderDb = new DbContextOptionsBuilder(); + builderDb.UseInMemoryDatabase(nameof(ExportControllerTest)); + + // Inject Fake Exiftool; dependency injection + var services = new ServiceCollection(); + services.AddSingleton(); + + // 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(); + + // for in bg test + services.AddSingleton(); + + // metrics + services.AddSingleton(); + services.AddSingleton(); + + // build the service + var serviceProvider = services.BuildServiceProvider(); + // get the service + + _scopeFactory = serviceProvider.GetRequiredService(); + + // get the background helper + _bgTaskQueue = serviceProvider.GetRequiredService(); + } + - public GeoControllerTest() + [TestMethod] + public async Task FolderExist() + { + var fakeIStorage = new FakeIStorage(new List { "/" }, + new List { "/test.jpg" }); + + var controller = new GeoController(_bgTaskQueue, + new FakeSelectorStorage(fakeIStorage), null!, new FakeIWebLogger(), _scopeFactory) { - var provider = new ServiceCollection() - .AddMemoryCache() - .BuildServiceProvider(); - _memoryCache = provider.GetRequiredService(); - - var builderDb = new DbContextOptionsBuilder(); - builderDb.UseInMemoryDatabase(nameof(ExportControllerTest)); - - // Inject Fake Exiftool; dependency injection - var services = new ServiceCollection(); - services.AddSingleton(); - - // 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(); - - // for in bg test - services.AddSingleton(); - - // metrics - services.AddSingleton(); - services.AddSingleton(); - - // build the service - var serviceProvider = services.BuildServiceProvider(); - // get the service - - _scopeFactory = serviceProvider.GetRequiredService(); - - // get the background helper - _bgTaskQueue = serviceProvider.GetRequiredService(); - } - - - [TestMethod] - public async Task FolderExist() + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + var result = await controller.GeoSyncFolder() as JsonResult; + Assert.AreEqual("job started", result?.Value); + } + + [TestMethod] + public async Task FolderNotExist() + { + var fakeIStorage = new FakeIStorage(new List { "/" }, + new List { "/test.jpg" }); + + var controller = new GeoController(_bgTaskQueue, new FakeSelectorStorage(fakeIStorage), + _memoryCache, new FakeIWebLogger(), _scopeFactory) { - var fakeIStorage = new FakeIStorage(new List { "/" }, - new List { "/test.jpg" }); - - var controller = new GeoController(_bgTaskQueue, - new FakeSelectorStorage(fakeIStorage), null!, new FakeIWebLogger(), _scopeFactory) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - var result = await controller.GeoSyncFolder() as JsonResult; - Assert.AreEqual("job started", result?.Value); - } - - [TestMethod] - public async Task FolderNotExist() + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + var result = await controller.GeoSyncFolder("/not-found") as NotFoundObjectResult; + Assert.AreEqual(404, result?.StatusCode); + } + + [TestMethod] + public async Task GeoSyncFolder_BadRequestObjectResult() + { + var storage = new FakeIStorage(); + var controller = new GeoController(_bgTaskQueue, + new FakeSelectorStorage(storage), null!, new FakeIWebLogger(), _scopeFactory) { - var fakeIStorage = new FakeIStorage(new List { "/" }, - new List { "/test.jpg" }); - - var controller = new GeoController(_bgTaskQueue, new FakeSelectorStorage(fakeIStorage), - _memoryCache, new FakeIWebLogger(), _scopeFactory) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - var result = await controller.GeoSyncFolder("/not-found") as NotFoundObjectResult; - Assert.AreEqual(404, result?.StatusCode); - } - - [TestMethod] - public void StatusCheck_CachedItemExist() + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = await + controller.GeoSyncFolder(null!); + Assert.IsInstanceOfType(result); + } + + [TestMethod] + public void StatusCheck_CachedItemExist() + { + // set startup status aka 50% + new GeoCacheStatusService(_memoryCache).StatusUpdate("/StatusCheck_CachedItemExist", + 1, StatusType.Current); + new GeoCacheStatusService(_memoryCache).StatusUpdate("/StatusCheck_CachedItemExist", + 2, StatusType.Total); + + var storage = new FakeIStorage(); + + var controller = new GeoController(_bgTaskQueue, new FakeSelectorStorage(storage), + _memoryCache, new FakeIWebLogger(), _scopeFactory) { - // set startup status aka 50% - new GeoCacheStatusService(_memoryCache).StatusUpdate("/StatusCheck_CachedItemExist", - 1, StatusType.Current); - new GeoCacheStatusService(_memoryCache).StatusUpdate("/StatusCheck_CachedItemExist", - 2, StatusType.Total); - - var storage = new FakeIStorage(); - - var controller = new GeoController(_bgTaskQueue, new FakeSelectorStorage(storage), - _memoryCache, new FakeIWebLogger(), _scopeFactory) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - - var statusJson = controller.Status("/StatusCheck_CachedItemExist") as JsonResult; - var status = statusJson!.Value as GeoCacheStatus; - Assert.AreEqual(1, status?.Current); - Assert.AreEqual(2, status?.Total); - } - - [TestMethod] - public void StatusCheck_CacheServiceMissing_ItemNotExist() + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + + var statusJson = controller.Status("/StatusCheck_CachedItemExist") as JsonResult; + var status = statusJson!.Value as GeoCacheStatus; + Assert.AreEqual(1, status?.Current); + Assert.AreEqual(2, status?.Total); + } + + [TestMethod] + public void StatusCheck_CacheServiceMissing_ItemNotExist() + { + var storage = new FakeIStorage(); + var controller = new GeoController(_bgTaskQueue, + new FakeSelectorStorage(storage), null!, new FakeIWebLogger(), _scopeFactory) { - var storage = new FakeIStorage(); - var controller = new GeoController(_bgTaskQueue, - new FakeSelectorStorage(storage), null!, new FakeIWebLogger(), _scopeFactory) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - - var status = - controller.Status("/StatusCheck_CachedItemNotExist") as NotFoundObjectResult; - Assert.AreEqual(404, status?.StatusCode); - } - - [TestMethod] - public async Task QueueBackgroundWorkItemAsync() + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + + var status = + controller.Status("/StatusCheck_CachedItemNotExist") as NotFoundObjectResult; + Assert.AreEqual(404, status?.StatusCode); + } + + [TestMethod] + public void StatusCheck_BadRequestObjectResult() + { + var storage = new FakeIStorage(); + var controller = new GeoController(_bgTaskQueue, + new FakeSelectorStorage(storage), null!, new FakeIWebLogger(), _scopeFactory) + { + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = + controller.Status(null!); + Assert.IsInstanceOfType(result); + } + + [TestMethod] + public async Task QueueBackgroundWorkItemAsync() + { + // reset + var geoBackgroundTaskBefore = _scopeFactory.CreateScope().ServiceProvider + .GetRequiredService() as FakeIGeoBackgroundTask; + Assert.IsNotNull(geoBackgroundTaskBefore); + geoBackgroundTaskBefore.Count = 0; + // end reset + + var storage = new FakeIStorage(new List { "/" }); + var controller = new GeoController(new FakeIUpdateBackgroundTaskQueue(), + new FakeSelectorStorage(storage), null!, new FakeIWebLogger(), _scopeFactory) { - // reset - var geoBackgroundTaskBefore = _scopeFactory.CreateScope().ServiceProvider - .GetRequiredService() as FakeIGeoBackgroundTask; - Assert.IsNotNull(geoBackgroundTaskBefore); - geoBackgroundTaskBefore.Count = 0; - // end reset - - var storage = new FakeIStorage(new List { "/" }); - var controller = new GeoController(new FakeIUpdateBackgroundTaskQueue(), - new FakeSelectorStorage(storage), null!, new FakeIWebLogger(), _scopeFactory) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - - await controller.GeoSyncFolder(); - - var geoBackgroundTask = _scopeFactory.CreateScope().ServiceProvider - .GetRequiredService() as FakeIGeoBackgroundTask; - Assert.IsNotNull(geoBackgroundTask); - Assert.AreEqual(1, geoBackgroundTask.Count); - } + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + + await controller.GeoSyncFolder(); + + var geoBackgroundTask = _scopeFactory.CreateScope().ServiceProvider + .GetRequiredService() as FakeIGeoBackgroundTask; + Assert.IsNotNull(geoBackgroundTask); + Assert.AreEqual(1, geoBackgroundTask.Count); } } diff --git a/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs b/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs index c13af11bf0..7bb96bceab 100644 --- a/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs +++ b/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs @@ -8,171 +8,207 @@ using starsky.feature.health.UpdateCheck.Models; using starskytest.FakeMocks; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public sealed class HealthCheckForUpdatesControllerTest { - [TestClass] - public sealed class HealthCheckForUpdatesControllerTest - { - // or CheckForUpdatesTest + // or CheckForUpdatesTest + + // Disabled, + // HttpError, + // NoReleasesFound, + // NeedToUpdate, + // CurrentVersionIsLatest - // Disabled, - // HttpError, - // NoReleasesFound, - // NeedToUpdate, - // CurrentVersionIsLatest + [TestMethod] + public async Task CheckForUpdates_Disabled() + { + var fakeService = new FakeICheckForUpdates( + new KeyValuePair(UpdateStatus.Disabled, string.Empty)); - [TestMethod] - public async Task CheckForUpdates_Disabled() - { - var fakeService = new FakeICheckForUpdates( - new KeyValuePair(UpdateStatus.Disabled, string.Empty)); + var actionResult = + await new HealthCheckForUpdatesController(fakeService, + new FakeISpecificVersionReleaseInfo()).CheckForUpdates() as ObjectResult; + Assert.AreEqual(208, actionResult?.StatusCode); + } - var actionResult = - await new HealthCheckForUpdatesController(fakeService, - new FakeISpecificVersionReleaseInfo()).CheckForUpdates() as ObjectResult; - Assert.AreEqual(208, actionResult?.StatusCode); - } - private class TestOverWriteEnumModel - { - public UpdateStatus Value { get; set; } - } + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public async Task CheckForUpdates_NotSupportedException() + { + var input = new TestOverWriteEnumModel { Value = UpdateStatus.Disabled }; + // Use reflection to set the updateStatus field to UpdateAvailable + // overwrite enum value + var propertyInfo = input.GetType().GetProperty("Value"); + Assert.IsNotNull(propertyInfo); + propertyInfo.SetValue(input, 44, null); // <-- this could not happen - [TestMethod] - [ExpectedException(typeof(NotSupportedException))] - public async Task CheckForUpdates_NotSupportedException() - { - var input = new TestOverWriteEnumModel { Value = UpdateStatus.Disabled }; + var fakeService = new FakeICheckForUpdates( + new KeyValuePair(input.Value, string.Empty)); - // Use reflection to set the updateStatus field to UpdateAvailable - // overwrite enum value - var propertyInfo = input.GetType().GetProperty("Value"); - Assert.IsNotNull(propertyInfo); - propertyInfo.SetValue(input, 44, null); // <-- this could not happen + Assert.IsNotNull(input.Value); - var fakeService = new FakeICheckForUpdates( - new KeyValuePair(input.Value, string.Empty)); + await new HealthCheckForUpdatesController(fakeService, + new FakeISpecificVersionReleaseInfo()).CheckForUpdates(); + } - Assert.IsNotNull(input.Value); + [TestMethod] + public async Task CheckForUpdates_HttpError() + { + var fakeService = new FakeICheckForUpdates( + new KeyValuePair(UpdateStatus.HttpError, string.Empty)); + var actionResult = await new HealthCheckForUpdatesController(fakeService, - new FakeISpecificVersionReleaseInfo()).CheckForUpdates(); - } - - [TestMethod] - public async Task CheckForUpdates_HttpError() - { - var fakeService = new FakeICheckForUpdates( - new KeyValuePair(UpdateStatus.HttpError, string.Empty)); - - var actionResult = - await new HealthCheckForUpdatesController(fakeService, - new FakeISpecificVersionReleaseInfo()).CheckForUpdates() as ObjectResult; - Assert.AreEqual(400, actionResult?.StatusCode); - } - - [TestMethod] - public async Task CheckForUpdates_InputNotValid() - { - var fakeService = new FakeICheckForUpdates( - new KeyValuePair(UpdateStatus.InputNotValid, string.Empty)); - - var service2 = new FakeISpecificVersionReleaseInfo(); - var actionResult = - await new HealthCheckForUpdatesController(fakeService, service2) - .CheckForUpdates() as - ObjectResult; - Assert.AreEqual(400, actionResult?.StatusCode); - } - - [TestMethod] - public async Task CheckForUpdates_NoReleasesFound() - { - var fakeService = new FakeICheckForUpdates( - new KeyValuePair(UpdateStatus.NoReleasesFound, - string.Empty)); - - var service2 = new FakeISpecificVersionReleaseInfo(); - - var actionResult = - await new HealthCheckForUpdatesController(fakeService, service2) - .CheckForUpdates() as - ObjectResult; - Assert.AreEqual(206, actionResult?.StatusCode); - } - - // NeedToUpdate - [TestMethod] - public async Task CheckForUpdates_NeedToUpdate() - { - var service2 = new FakeISpecificVersionReleaseInfo(); - var fakeService = new FakeICheckForUpdates( - new KeyValuePair(UpdateStatus.NeedToUpdate, string.Empty)); - - var actionResult = - await new HealthCheckForUpdatesController(fakeService, service2) - .CheckForUpdates() as - ObjectResult; - Assert.AreEqual(202, actionResult?.StatusCode); - } - - [TestMethod] - public async Task CheckForUpdates_CurrentVersionIsLatest() - { - var fakeService = new FakeICheckForUpdates( - new KeyValuePair(UpdateStatus.CurrentVersionIsLatest, - string.Empty)); - var service2 = new FakeISpecificVersionReleaseInfo(); - - var actionResult = - await new HealthCheckForUpdatesController(fakeService, service2) - .CheckForUpdates() as - ObjectResult; - Assert.AreEqual(200, actionResult?.StatusCode); - } - - [TestMethod] - public async Task SpecificVersionReleaseInfo_GivesResult() - { - var fakeService = new FakeICheckForUpdates(new KeyValuePair()); - var service2 = new FakeISpecificVersionReleaseInfo( - new Dictionary> + new FakeISpecificVersionReleaseInfo()).CheckForUpdates() as ObjectResult; + Assert.AreEqual(400, actionResult?.StatusCode); + } + + [TestMethod] + public async Task CheckForUpdates_InputNotValid() + { + var fakeService = new FakeICheckForUpdates( + new KeyValuePair(UpdateStatus.InputNotValid, string.Empty)); + + var service2 = new FakeISpecificVersionReleaseInfo(); + var actionResult = + await new HealthCheckForUpdatesController(fakeService, service2) + .CheckForUpdates() as + ObjectResult; + Assert.AreEqual(400, actionResult?.StatusCode); + } + + [TestMethod] + public async Task CheckForUpdates_NoReleasesFound() + { + var fakeService = new FakeICheckForUpdates( + new KeyValuePair(UpdateStatus.NoReleasesFound, + string.Empty)); + + var service2 = new FakeISpecificVersionReleaseInfo(); + + var actionResult = + await new HealthCheckForUpdatesController(fakeService, service2) + .CheckForUpdates() as + ObjectResult; + Assert.AreEqual(206, actionResult?.StatusCode); + } + + // NeedToUpdate + [TestMethod] + public async Task CheckForUpdates_NeedToUpdate() + { + var service2 = new FakeISpecificVersionReleaseInfo(); + var fakeService = new FakeICheckForUpdates( + new KeyValuePair(UpdateStatus.NeedToUpdate, string.Empty)); + + var actionResult = + await new HealthCheckForUpdatesController(fakeService, service2) + .CheckForUpdates() as + ObjectResult; + Assert.AreEqual(202, actionResult?.StatusCode); + } + + [TestMethod] + public async Task CheckForUpdates_CurrentVersionIsLatest() + { + var fakeService = new FakeICheckForUpdates( + new KeyValuePair(UpdateStatus.CurrentVersionIsLatest, + string.Empty)); + var service2 = new FakeISpecificVersionReleaseInfo(); + + var actionResult = + await new HealthCheckForUpdatesController(fakeService, service2) + .CheckForUpdates() as + ObjectResult; + Assert.AreEqual(200, actionResult?.StatusCode); + } + + [TestMethod] + public async Task CheckForUpdates_BadRequest() + { + var fakeService = new FakeICheckForUpdates( + new KeyValuePair(UpdateStatus.CurrentVersionIsLatest, + string.Empty)); + var service2 = new FakeISpecificVersionReleaseInfo(); + + var controller = + new HealthCheckForUpdatesController(fakeService, service2); + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + // Act + var result = await controller.CheckForUpdates(); + + // Assert + Assert.IsInstanceOfType(result); + } + + [TestMethod] + public async Task SpecificVersionReleaseInfo_GivesResult() + { + var fakeService = new FakeICheckForUpdates(new KeyValuePair()); + var service2 = new FakeISpecificVersionReleaseInfo( + new Dictionary> + { { + "1.0.0", + new Dictionary { - "1.0.0", - new Dictionary - { - { "en", " # 1.0.0\n\n- [x] test\n- [ ] test2\n\n" } - } + { "en", " # 1.0.0\n\n- [x] test\n- [ ] test2\n\n" } } } - ); - - var controller = new HealthCheckForUpdatesController(fakeService, - service2) { ControllerContext = { HttpContext = new DefaultHttpContext() } }; - var actionResult = await controller - .SpecificVersionReleaseInfo("1.0.0") as JsonResult; - - Assert.AreEqual(" # 1.0.0\n\n- [x] test\n- [ ] test2\n\n", - actionResult?.Value); - } - - [TestMethod] - public async Task SpecificVersionReleaseInfo_NoContent() - { - var fakeService = new FakeICheckForUpdates(new KeyValuePair()); - var service2 = new FakeISpecificVersionReleaseInfo( - new Dictionary>() - ); - - var controller = new HealthCheckForUpdatesController(fakeService, - service2) { ControllerContext = { HttpContext = new DefaultHttpContext() } }; - var actionResult = await controller - .SpecificVersionReleaseInfo() as JsonResult; - - Assert.AreEqual(string.Empty, - actionResult?.Value); - } + } + ); + + var controller = new HealthCheckForUpdatesController(fakeService, + service2) { ControllerContext = { HttpContext = new DefaultHttpContext() } }; + var actionResult = await controller + .SpecificVersionReleaseInfo("1.0.0") as JsonResult; + + Assert.AreEqual(" # 1.0.0\n\n- [x] test\n- [ ] test2\n\n", + actionResult?.Value); + } + + [TestMethod] + public async Task SpecificVersionReleaseInfo_NoContent() + { + var fakeService = new FakeICheckForUpdates(new KeyValuePair()); + var service2 = new FakeISpecificVersionReleaseInfo( + new Dictionary>() + ); + + var controller = new HealthCheckForUpdatesController(fakeService, + service2) { ControllerContext = { HttpContext = new DefaultHttpContext() } }; + var actionResult = await controller + .SpecificVersionReleaseInfo() as JsonResult; + + Assert.AreEqual(string.Empty, + actionResult?.Value); + } + + [TestMethod] + public async Task SpecificVersionReleaseInfo_BadRequest() + { + var fakeService = new FakeICheckForUpdates(new KeyValuePair()); + var service2 = new FakeISpecificVersionReleaseInfo( + new Dictionary>() + ); + + var controller = new HealthCheckForUpdatesController(fakeService, + service2) { ControllerContext = { HttpContext = new DefaultHttpContext() } }; + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = await controller + .SpecificVersionReleaseInfo(null!); + + Assert.IsInstanceOfType(result); + } + + private class TestOverWriteEnumModel + { + public UpdateStatus Value { get; set; } } } diff --git a/starsky/starskytest/Controllers/HealthControllerTest.cs b/starsky/starskytest/Controllers/HealthControllerTest.cs index 055103a9d6..d1e09c2df5 100644 --- a/starsky/starskytest/Controllers/HealthControllerTest.cs +++ b/starsky/starskytest/Controllers/HealthControllerTest.cs @@ -10,250 +10,263 @@ using starsky.project.web.ViewModels; using starskytest.FakeMocks; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public sealed class HealthControllerTest { - [TestClass] - public sealed class HealthControllerTest + [TestMethod] + public async Task HealthControllerTest_Details_True() { - [TestMethod] - public async Task HealthControllerTest_Details_True() - { - var fakeHealthCheckService = new FakeHealthCheckService(true); - var controller = - new HealthController(fakeHealthCheckService) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - - var actionResult = await controller.Details() as JsonResult; - var castedResult = actionResult?.Value as HealthView; - - Assert.IsTrue(castedResult?.IsHealthy); - Assert.IsTrue(castedResult?.Entries.FirstOrDefault()?.IsHealthy); - Assert.AreEqual("test", castedResult?.Entries.FirstOrDefault()?.Name); - Assert.IsTrue(castedResult?.Entries.FirstOrDefault()?.Duration == TimeSpan.Zero); - Assert.IsTrue(castedResult.Entries.Count != 0); - Assert.IsTrue(castedResult.TotalDuration == TimeSpan.Zero); - } - - [TestMethod] - public async Task HealthControllerTest_Details_False() - { - var fakeHealthCheckService = new FakeHealthCheckService(false); - var controller = - new HealthController(fakeHealthCheckService) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - - var actionResult = await controller.Details() as JsonResult; - var castedResult = actionResult?.Value as HealthView; - - Assert.IsFalse(castedResult?.IsHealthy); - Assert.IsFalse(castedResult?.Entries.FirstOrDefault()?.IsHealthy); - Assert.AreEqual("test", castedResult?.Entries?.FirstOrDefault()?.Name); - Assert.IsTrue(castedResult?.Entries?.FirstOrDefault()?.Duration == TimeSpan.Zero); - Assert.IsTrue(castedResult.Entries.Count != 0); - Assert.IsTrue(castedResult.TotalDuration == TimeSpan.Zero); - } - - [TestMethod] - public async Task HealthControllerTest_Index_True() - { - var fakeHealthCheckService = new FakeHealthCheckService(true); - var controller = - new HealthController(fakeHealthCheckService) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - - var actionResult = await controller.Index() as ContentResult; - - Assert.AreEqual("Healthy", actionResult?.Content); - } - - - [TestMethod] - public async Task HealthControllerTest_Index_False() - { - var fakeHealthCheckService = new FakeHealthCheckService(false); - var controller = - new HealthController(fakeHealthCheckService) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; + var fakeHealthCheckService = new FakeHealthCheckService(true); + var controller = + new HealthController(fakeHealthCheckService) + { + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; - var actionResult = await controller.Index() as ContentResult; + var actionResult = await controller.Details() as JsonResult; + var castedResult = actionResult?.Value as HealthView; - Assert.AreEqual("Unhealthy", actionResult?.Content); - } + Assert.IsTrue(castedResult?.IsHealthy); + Assert.IsTrue(castedResult?.Entries.FirstOrDefault()?.IsHealthy); + Assert.AreEqual("test", castedResult?.Entries.FirstOrDefault()?.Name); + Assert.IsTrue(castedResult?.Entries.FirstOrDefault()?.Duration == TimeSpan.Zero); + Assert.IsTrue(castedResult.Entries.Count != 0); + Assert.IsTrue(castedResult.TotalDuration == TimeSpan.Zero); + } - [TestMethod] - public void Version_NoVersion() - { - var controller = new HealthController(null!) + [TestMethod] + public async Task HealthControllerTest_Details_False() + { + var fakeHealthCheckService = new FakeHealthCheckService(false); + var controller = + new HealthController(fakeHealthCheckService) { ControllerContext = { HttpContext = new DefaultHttpContext() } }; - var noVersion = controller.Version() as BadRequestObjectResult; - Assert.AreEqual(400, noVersion?.StatusCode); - } - [TestMethod] - public void Version_Version_newer() - { - var controller = new HealthController(null!) + var actionResult = await controller.Details() as JsonResult; + var castedResult = actionResult?.Value as HealthView; + + Assert.IsFalse(castedResult?.IsHealthy); + Assert.IsFalse(castedResult?.Entries.FirstOrDefault()?.IsHealthy); + Assert.AreEqual("test", castedResult?.Entries?.FirstOrDefault()?.Name); + Assert.IsTrue(castedResult?.Entries?.FirstOrDefault()?.Duration == TimeSpan.Zero); + Assert.IsTrue(castedResult.Entries.Count != 0); + Assert.IsTrue(castedResult.TotalDuration == TimeSpan.Zero); + } + + [TestMethod] + public async Task HealthControllerTest_Index_True() + { + var fakeHealthCheckService = new FakeHealthCheckService(true); + var controller = + new HealthController(fakeHealthCheckService) { ControllerContext = { HttpContext = new DefaultHttpContext() } }; - controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = "1.0"; - var noVersion = controller.Version() as OkObjectResult; - Assert.AreEqual(200, noVersion?.StatusCode); - } - [TestMethod] - public void Version_Version_older() - { - var controller = new HealthController(null!) + var actionResult = await controller.Index() as ContentResult; + + Assert.AreEqual("Healthy", actionResult?.Content); + } + + + [TestMethod] + public async Task HealthControllerTest_Index_False() + { + var fakeHealthCheckService = new FakeHealthCheckService(false); + var controller = + new HealthController(fakeHealthCheckService) { ControllerContext = { HttpContext = new DefaultHttpContext() } }; - controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = "0.1"; - var noVersion = controller.Version() as ObjectResult; - Assert.AreEqual(202, noVersion?.StatusCode); - } - [TestMethod] - public void Version_Version_AsParam_older() + var actionResult = await controller.Index() as ContentResult; + + Assert.AreEqual("Unhealthy", actionResult?.Content); + } + + [TestMethod] + public void Version_NoVersion() + { + var controller = new HealthController(null!) { - var controller = new HealthController(null!) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - var noVersion = controller.Version("0.1") as ObjectResult; - Assert.AreEqual(202, noVersion?.StatusCode); - } + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + var noVersion = controller.Version() as BadRequestObjectResult; + Assert.AreEqual(400, noVersion?.StatusCode); + } - [TestMethod] - public void Version_Version_beta1_isBefore() + [TestMethod] + public void Version_NoVersion_BadRequest() + { + var controller = new HealthController(null!) { - var controller = new HealthController(null!) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + controller.ModelState.AddModelError("Key", "ErrorMessage"); - const string beta = HealthController.MinimumVersion + "-beta.1"; - // the beta is before the 0.3 release - controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = beta; - var noVersion = controller.Version() as ObjectResult; - Assert.AreEqual(202, noVersion?.StatusCode); - } + var result = controller.Version() as BadRequestObjectResult; + Assert.AreEqual(400, result?.StatusCode); + Assert.IsInstanceOfType(result); + } - [TestMethod] - public void Version_Version_eq() + [TestMethod] + public void Version_Version_newer() + { + var controller = new HealthController(null!) { - var controller = new HealthController(null!) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = "1.0"; + var noVersion = controller.Version() as OkObjectResult; + Assert.AreEqual(200, noVersion?.StatusCode); + } - controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = - HealthController.MinimumVersion; - var noVersion = controller.Version() as OkObjectResult; - Assert.AreEqual(200, noVersion?.StatusCode); - } + [TestMethod] + public void Version_Version_older() + { + var controller = new HealthController(null!) + { + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = "0.1"; + var noVersion = controller.Version() as ObjectResult; + Assert.AreEqual(202, noVersion?.StatusCode); + } - [TestMethod] - public void Version_Version_NonValidInput() + [TestMethod] + public void Version_Version_AsParam_older() + { + var controller = new HealthController(null!) { - var controller = new HealthController(null!) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = - "0.bad-input"; - var noVersion = controller.Version() as ObjectResult; - Assert.AreEqual(400, noVersion?.StatusCode); - } + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + var noVersion = controller.Version("0.1") as ObjectResult; + Assert.AreEqual(202, noVersion?.StatusCode); + } + [TestMethod] + public void Version_Version_beta1_isBefore() + { + var controller = new HealthController(null!) + { + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + + const string beta = HealthController.MinimumVersion + "-beta.1"; + // the beta is before the 0.3 release + controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = beta; + var noVersion = controller.Version() as ObjectResult; + Assert.AreEqual(202, noVersion?.StatusCode); + } - [TestMethod] - public void Version_Version_0() + [TestMethod] + public void Version_Version_eq() + { + var controller = new HealthController(null!) { - var controller = new HealthController(null!) - { - ControllerContext = { HttpContext = new DefaultHttpContext() } - }; - controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = "0"; - var noVersion = controller.Version() as ObjectResult; - Assert.AreEqual(202, noVersion?.StatusCode); - } + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; - [TestMethod] - public async Task CheckHealthAsyncWithTimeout_ShouldTimeout() + controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = + HealthController.MinimumVersion; + var noVersion = controller.Version() as OkObjectResult; + Assert.AreEqual(200, noVersion?.StatusCode); + } + + [TestMethod] + public void Version_Version_NonValidInput() + { + var controller = new HealthController(null!) { - var result = await new HealthController( - new FakeHealthCheckService(true)) - .CheckHealthAsyncWithTimeout(-1); - Assert.AreEqual(HealthStatus.Unhealthy, result.Status); - } - - [TestMethod] - public async Task CheckHealthAsyncWithTimeout_ShouldSucceed() + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = + "0.bad-input"; + var noVersion = controller.Version() as ObjectResult; + Assert.AreEqual(400, noVersion?.StatusCode); + } + + + [TestMethod] + public void Version_Version_0() + { + var controller = new HealthController(null!) { - var result = await new HealthController(new FakeHealthCheckService(true)) - .CheckHealthAsyncWithTimeout(); - Assert.AreEqual(HealthStatus.Healthy, result.Status); - } + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + controller.ControllerContext.HttpContext.Request.Headers["x-api-version"] = "0"; + var noVersion = controller.Version() as ObjectResult; + Assert.AreEqual(202, noVersion?.StatusCode); + } + + [TestMethod] + public async Task CheckHealthAsyncWithTimeout_ShouldTimeout() + { + var result = await new HealthController( + new FakeHealthCheckService(true)) + .CheckHealthAsyncWithTimeout(-1); + Assert.AreEqual(HealthStatus.Unhealthy, result.Status); + } - [TestMethod] - public async Task CheckHealthAsyncWithTimeout_IgnoreCachedUnHealthyInput() + [TestMethod] + public async Task CheckHealthAsyncWithTimeout_ShouldSucceed() + { + var result = await new HealthController(new FakeHealthCheckService(true)) + .CheckHealthAsyncWithTimeout(); + Assert.AreEqual(HealthStatus.Healthy, result.Status); + } + + [TestMethod] + public async Task CheckHealthAsyncWithTimeout_IgnoreCachedUnHealthyInput() + { + var entry = new HealthReportEntry( + HealthStatus.Unhealthy, + "timeout", + TimeSpan.FromMilliseconds(1), + null, + null); + var cachedItem = new Dictionary { - var entry = new HealthReportEntry( - HealthStatus.Unhealthy, - "timeout", - TimeSpan.FromMilliseconds(1), - null, - null); - var cachedItem = new Dictionary { - { - "health", new HealthReport( - new Dictionary { { "timeout", entry } }, - TimeSpan.FromMilliseconds(0)) - } - }; + "health", new HealthReport( + new Dictionary { { "timeout", entry } }, + TimeSpan.FromMilliseconds(0)) + } + }; - var result = await new HealthController( - new FakeHealthCheckService(true), new FakeMemoryCache(cachedItem)) - .CheckHealthAsyncWithTimeout(); + var result = await new HealthController( + new FakeHealthCheckService(true), new FakeMemoryCache(cachedItem)) + .CheckHealthAsyncWithTimeout(); - Assert.AreEqual(HealthStatus.Healthy, result.Status); - } + Assert.AreEqual(HealthStatus.Healthy, result.Status); + } - [TestMethod] - public async Task CheckHealthAsyncWithTimeout_IgnoreCheckIfCachedInputIsHealthy() + [TestMethod] + public async Task CheckHealthAsyncWithTimeout_IgnoreCheckIfCachedInputIsHealthy() + { + var entry = new HealthReportEntry( + HealthStatus.Healthy, + "timeout", + TimeSpan.FromMilliseconds(1), + null, + null); + + var cachedItem = new Dictionary { - var entry = new HealthReportEntry( - HealthStatus.Healthy, - "timeout", - TimeSpan.FromMilliseconds(1), - null, - null); - - var cachedItem = new Dictionary { - { - "health", new HealthReport( - new Dictionary { { "timeout", entry } }, - TimeSpan.FromMilliseconds(0)) - } - }; - - var result = await new HealthController( - new FakeHealthCheckService(false), new FakeMemoryCache(cachedItem)) - .CheckHealthAsyncWithTimeout(); - Assert.AreEqual(HealthStatus.Healthy, result.Status); - } + "health", new HealthReport( + new Dictionary { { "timeout", entry } }, + TimeSpan.FromMilliseconds(0)) + } + }; + + var result = await new HealthController( + new FakeHealthCheckService(false), new FakeMemoryCache(cachedItem)) + .CheckHealthAsyncWithTimeout(); + Assert.AreEqual(HealthStatus.Healthy, result.Status); } } diff --git a/starsky/starskytest/Controllers/HomeControllerTest.cs b/starsky/starskytest/Controllers/HomeControllerTest.cs index 5c044eddb2..6660d8d344 100644 --- a/starsky/starskytest/Controllers/HomeControllerTest.cs +++ b/starsky/starskytest/Controllers/HomeControllerTest.cs @@ -6,364 +6,427 @@ using starsky.foundation.platform.Models; using starskytest.FakeMocks; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public sealed class HomeControllerTest { - [TestClass] - public sealed class HomeControllerTest - { - private readonly IAntiforgery _antiForgery; - private readonly HttpContext _httpContext; - private readonly HomeController _controller; + private readonly IAntiforgery _antiForgery; + private readonly HomeController _controller; + private readonly HttpContext _httpContext; - public HomeControllerTest() - { - _antiForgery = new FakeAntiforgery(); - _httpContext = new DefaultHttpContext(); - _controller = new HomeController(new AppSettings(),_antiForgery) - { - ControllerContext = {HttpContext = _httpContext} - }; - } - - [TestMethod] - public void HomeController_Index() - { - var controller = new HomeController(new AppSettings(), _antiForgery) - { - ControllerContext = {HttpContext = _httpContext} - }; - var actionResult = controller.Index() as PhysicalFileResult; - Assert.AreEqual("text/html", actionResult?.ContentType); - } - - [TestMethod] - public void HomeController_IsCaseSensitiveRedirect_true() - { - var caseSensitive = HomeController.IsCaseSensitiveRedirect( - "/Search","/search"); - Assert.IsTrue(caseSensitive); - } - - [TestMethod] - public void HomeController_IsCaseSensitiveRedirect_false() + public HomeControllerTest() + { + _antiForgery = new FakeAntiforgery(); + _httpContext = new DefaultHttpContext(); + _controller = new HomeController(new AppSettings(), _antiForgery) { - var caseSensitive = HomeController.IsCaseSensitiveRedirect( - "/search","/search"); - Assert.IsFalse(caseSensitive); - } - - [TestMethod] - public void HomeController_SearchPost_Controller_CaseSensitive_Redirect() + ControllerContext = { HttpContext = _httpContext } + }; + } + + [TestMethod] + public void HomeController_Index() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - var controller = new HomeController(new AppSettings(),_antiForgery) - { - ControllerContext = {HttpContext = _httpContext} - }; - controller.ControllerContext.HttpContext.Request.Path = new PathString("/Search"); - controller.ControllerContext.HttpContext.Request.QueryString = new QueryString("?T=1"); - var actionResult = controller.SearchPost("1") as RedirectResult; - Assert.AreEqual("/search?t=1&p=0", actionResult?.Url); - } - - [TestMethod] - public void HomeController_SearchGet_Controller_CaseSensitive_Redirect() + ControllerContext = { HttpContext = _httpContext } + }; + var actionResult = controller.Index() as PhysicalFileResult; + Assert.AreEqual("text/html", actionResult?.ContentType); + } + + [TestMethod] + public void HomeController_BadRequest() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - var controller = new HomeController(new AppSettings(),_antiForgery) - { - ControllerContext = {HttpContext = _httpContext} - }; - controller.ControllerContext.HttpContext.Request.Path = new PathString("/Search"); - controller.ControllerContext.HttpContext.Request.QueryString = new QueryString("?T=1"); - var actionResult = controller.Search("1") as RedirectResult; - Assert.AreEqual("/search?t=1&p=0", actionResult?.Url); - } - - - [TestMethod] - public void HomeController_Trash_Controller_CaseSensitive_Redirect() + ControllerContext = { HttpContext = _httpContext } + }; + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = controller.Index(); + + Assert.IsInstanceOfType(result); + } + + [TestMethod] + public void HomeController_IsCaseSensitiveRedirect_true() + { + var caseSensitive = HomeController.IsCaseSensitiveRedirect( + "/Search", "/search"); + Assert.IsTrue(caseSensitive); + } + + [TestMethod] + public void HomeController_IsCaseSensitiveRedirect_false() + { + var caseSensitive = HomeController.IsCaseSensitiveRedirect( + "/search", "/search"); + Assert.IsFalse(caseSensitive); + } + + [TestMethod] + public void HomeController_SearchPost_Controller_CaseSensitive_Redirect() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - var controller = new HomeController(new AppSettings(),_antiForgery) - { - ControllerContext = {HttpContext = _httpContext} - }; - controller.ControllerContext.HttpContext.Request.Path = new PathString("/Trash"); - var actionResult = controller.Trash() as RedirectResult; - Assert.AreEqual("/trash?p=0", actionResult?.Url); - } - - [TestMethod] - public void HomeController_Import_Controller_CaseSensitive_Redirect() + ControllerContext = { HttpContext = _httpContext } + }; + controller.ControllerContext.HttpContext.Request.Path = new PathString("/Search"); + controller.ControllerContext.HttpContext.Request.QueryString = new QueryString("?T=1"); + var actionResult = controller.SearchPost("1") as RedirectResult; + Assert.AreEqual("/search?t=1&p=0", actionResult?.Url); + } + + [TestMethod] + public void HomeController_SearchGet_Controller_CaseSensitive_Redirect() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - var controller = new HomeController(new AppSettings(), _antiForgery) - { - ControllerContext = {HttpContext = _httpContext} - }; - controller.ControllerContext.HttpContext.Request.Path = new PathString("/Import"); - var actionResult = controller.Import() as RedirectResult; - Assert.AreEqual("/import", actionResult?.Url); - } - - [TestMethod] - public void AccountController_RegisterGet() + ControllerContext = { HttpContext = _httpContext } + }; + controller.ControllerContext.HttpContext.Request.Path = new PathString("/Search"); + controller.ControllerContext.HttpContext.Request.QueryString = new QueryString("?T=1"); + var actionResult = controller.Search("1") as RedirectResult; + Assert.AreEqual("/search?t=1&p=0", actionResult?.Url); + } + + + [TestMethod] + public void HomeController_Trash_Controller_CaseSensitive_Redirect() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - var controller = new HomeController(new AppSettings(), _antiForgery) - { - ControllerContext = - { - HttpContext = new DefaultHttpContext() - } - }; - var result = controller.Register(); - Assert.IsNotNull(result); - } - - [TestMethod] - public void Preferences() + ControllerContext = { HttpContext = _httpContext } + }; + controller.ControllerContext.HttpContext.Request.Path = new PathString("/Trash"); + var actionResult = controller.Trash() as RedirectResult; + Assert.AreEqual("/trash?p=0", actionResult?.Url); + } + + [TestMethod] + public void HomeController_Import_Controller_CaseSensitive_Redirect() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - var controller = new HomeController(new AppSettings(),_antiForgery) - { - ControllerContext = {HttpContext = _httpContext} - }; - controller.ControllerContext.HttpContext.Request.Path = new PathString("/preferences"); - - var actionResult = controller.Preferences() as PhysicalFileResult; - Assert.AreEqual("text/html", actionResult?.ContentType); - } - - - [TestMethod] - public void Preferences_Expect_Capital() + ControllerContext = { HttpContext = _httpContext } + }; + controller.ControllerContext.HttpContext.Request.Path = new PathString("/Import"); + var actionResult = controller.Import() as RedirectResult; + Assert.AreEqual("/import", actionResult?.Url); + } + + [TestMethod] + public void AccountController_RegisterGet() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - var controller = new HomeController(new AppSettings(),_antiForgery) - { - ControllerContext = {HttpContext = _httpContext} - }; - controller.ControllerContext.HttpContext.Request.Path = new PathString("/Preferences"); - - var actionResult = controller.Preferences() as RedirectResult; - Assert.AreEqual("/preferences", actionResult?.Url); - } - - [TestMethod] - public void SearchPost_ReturnsRedirectToSearch_WhenTIsEmpty() + ControllerContext = { HttpContext = new DefaultHttpContext() } + }; + var result = controller.Register(); + Assert.IsNotNull(result); + } + + [TestMethod] + public void Register_BadRequest() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - // Arrange - string t = ""; - int p = 0; + ControllerContext = { HttpContext = _httpContext } + }; + controller.ModelState.AddModelError("Key", "ErrorMessage"); - // Act - var result = _controller.SearchPost(t, p); + var result = controller.Register(); - // Assert - Assert.IsInstanceOfType(result, typeof(RedirectResult)); - var redirectResult = (RedirectResult)result; - Assert.AreEqual("/search", redirectResult.Url); - } + Assert.IsInstanceOfType(result); + } - [TestMethod] - public void SearchPost_ReturnsBadRequest_WhenTContainsInvalidCharacters() + [TestMethod] + public void Preferences() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - // Arrange - string t = "abc$"; - int p = 0; + ControllerContext = { HttpContext = _httpContext } + }; + controller.ControllerContext.HttpContext.Request.Path = new PathString("/preferences"); - // Act - var result = _controller.SearchPost(t, p); + var actionResult = controller.Preferences() as PhysicalFileResult; + Assert.AreEqual("text/html", actionResult?.ContentType); + } - // Assert - Assert.IsInstanceOfType(result, typeof(BadRequestObjectResult)); - } - [TestMethod] - public void SearchPost_ReturnsRedirectToSearch_WhenTIsValid() - { - // Arrange - string t = "abc"; - int p = 0; - - // Act - var result = _controller.SearchPost(t, p); - - // Assert - Assert.IsInstanceOfType(result, typeof(RedirectResult)); - var redirectResult = (RedirectResult)result; - Assert.AreEqual($"/search?t={t}&p={p}", redirectResult.Url); - } - - [TestMethod] - public void Search_ReturnsPhysicalFile_WhenRequestPathIsNotCaseSensitiveRedirect() - { - // Arrange - string t = ""; - int p = 0; - var httpContext = new DefaultHttpContext(); - httpContext.Request.Path = "/search"; - httpContext.Request.Method = "GET"; - _controller.ControllerContext = new ControllerContext - { - HttpContext = httpContext - }; - - // Act - var result = _controller.Search(t, p); - - // Assert - Assert.IsInstanceOfType(result, typeof(PhysicalFileResult)); - var physicalFileResult = (PhysicalFileResult)result; - Assert.AreEqual("text/html", physicalFileResult.ContentType); - } - - [TestMethod] - public void Search_ReturnsRedirectToSearch_WhenTIsEmpty() + [TestMethod] + public void Preferences_Expect_Capital() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - // Arrange - string t = ""; - int p = 0; + ControllerContext = { HttpContext = _httpContext } + }; + controller.ControllerContext.HttpContext.Request.Path = new PathString("/Preferences"); - // Act - var result = _controller.Search(t, p); + var actionResult = controller.Preferences() as RedirectResult; + Assert.AreEqual("/preferences", actionResult?.Url); + } - // Assert - Assert.IsInstanceOfType(result, typeof(RedirectResult)); - var redirectResult = (RedirectResult)result; - Assert.AreEqual("/search", redirectResult.Url); - } + [TestMethod] + public void SearchPost_ReturnsRedirectToSearch_WhenTIsEmpty() + { + // Arrange + var t = ""; + var p = 0; - [TestMethod] - public void Search_ReturnsBadRequest_WhenTContainsInvalidCharacters() - { - // Arrange - string t = "abc$"; - int p = 0; + // Act + var result = _controller.SearchPost(t, p); - // Act - var result = _controller.Search(t, p); + // Assert + Assert.IsInstanceOfType(result, typeof(RedirectResult)); + var redirectResult = ( RedirectResult ) result; + Assert.AreEqual("/search", redirectResult.Url); + } - // Assert - Assert.IsInstanceOfType(result, typeof(BadRequestObjectResult)); - } + [TestMethod] + public void SearchPost_ReturnsBadRequest_WhenTContainsInvalidCharacters() + { + // Arrange + var t = "abc$"; + var p = 0; - [TestMethod] - public void Search_ReturnsRedirectToSearch_WhenTIsValid() - { - // Arrange - string t = "abc"; - int p = 0; - - // Act - var result = _controller.Search(t, p); - - // Assert - Assert.IsInstanceOfType(result, typeof(RedirectResult)); - var redirectResult = (RedirectResult)result; - Assert.AreEqual($"/search?t={t}&p={p}", redirectResult.Url); - } - - [TestMethod] - public void Trash_ReturnsPhysicalFile() - { - // Arrange - int p = 0; + // Act + var result = _controller.SearchPost(t, p); - // Act - _controller.Request.Path = "/trash"; - var result = _controller.Trash(p); + // Assert + Assert.IsInstanceOfType(result, typeof(BadRequestObjectResult)); + } - // Assert - Assert.IsInstanceOfType(result, typeof(PhysicalFileResult)); - var physicalFileResult = (PhysicalFileResult)result; - Assert.AreEqual("text/html", physicalFileResult.ContentType); - } + [TestMethod] + public void SearchPost_ReturnsRedirectToSearch_WhenTIsValid() + { + // Arrange + var t = "abc"; + var p = 0; - [TestMethod] - public void Trash_RedirectsToLowerCaseUrl_WhenRequestedUrlIsCaseSensitive() - { - // Arrange - int p = 0; - var request = new DefaultHttpContext().Request; - request.Scheme = "http"; - request.Host = new HostString("localhost"); - request.Path = "/Trash"; - request.PathBase = "/"; - - _controller.ControllerContext = new ControllerContext() - { - HttpContext = new DefaultHttpContext() - }; - - // Act - var result = _controller.Trash(p); - - // Assert - Assert.IsInstanceOfType(result, typeof(RedirectResult)); - var redirectResult = (RedirectResult)result; - Assert.AreEqual("/trash?p=0", redirectResult.Url); - } - - [TestMethod] - public void Trash_ReturnsPhysicalFile_WhenRequestedUrlIsNotCaseSensitive() - { - // Arrange - int p = 0; - // difference is trash vs import - _controller.Request.Path = "/trash"; - - // Act - var result = _controller.Trash(p); - - // Assert - Assert.IsInstanceOfType(result, typeof(PhysicalFileResult)); - var physicalFileResult = (PhysicalFileResult)result; - Assert.AreEqual("text/html", physicalFileResult.ContentType); - } - - [TestMethod] - public void Import_ReturnsPhysicalFile() - { - // Act - _controller.Request.Path = "/import"; - var result = _controller.Import(); - - // Assert - Assert.IsInstanceOfType(result, typeof(PhysicalFileResult)); - var physicalFileResult = (PhysicalFileResult)result; - Assert.AreEqual("text/html", physicalFileResult.ContentType); - } - - [TestMethod] - public void Import_ReturnsPhysicalFile_WhenRequestedUrlIsNotCaseSensitive() + // Act + var result = _controller.SearchPost(t, p); + + // Assert + Assert.IsInstanceOfType(result, typeof(RedirectResult)); + var redirectResult = ( RedirectResult ) result; + Assert.AreEqual($"/search?t={t}&p={p}", redirectResult.Url); + } + + [TestMethod] + public void SearchPost_BadRequest() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - // Arrange - _controller.Request.Path = "/import"; + ControllerContext = { HttpContext = _httpContext } + }; + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = controller.SearchPost(); + + Assert.IsInstanceOfType(result); + } - // Act - var result = _controller.Import(); + [TestMethod] + public void Search_ReturnsPhysicalFile_WhenRequestPathIsNotCaseSensitiveRedirect() + { + // Arrange + var t = ""; + var p = 0; + var httpContext = new DefaultHttpContext(); + httpContext.Request.Path = "/search"; + httpContext.Request.Method = "GET"; + _controller.ControllerContext = new ControllerContext { HttpContext = httpContext }; + + // Act + var result = _controller.Search(t, p); + + // Assert + Assert.IsInstanceOfType(result, typeof(PhysicalFileResult)); + var physicalFileResult = ( PhysicalFileResult ) result; + Assert.AreEqual("text/html", physicalFileResult.ContentType); + } - // Assert - Assert.IsInstanceOfType(result, typeof(PhysicalFileResult)); - var physicalFileResult = (PhysicalFileResult)result; - Assert.AreEqual("text/html", physicalFileResult.ContentType); - } + [TestMethod] + public void Search_ReturnsRedirectToSearch_WhenTIsEmpty() + { + // Arrange + var t = ""; + var p = 0; - [TestMethod] - public void AppendPathBasePrefix_1() + // Act + var result = _controller.Search(t, p); + + // Assert + Assert.IsInstanceOfType(result, typeof(RedirectResult)); + var redirectResult = ( RedirectResult ) result; + Assert.AreEqual("/search", redirectResult.Url); + } + + [TestMethod] + public void Search_ReturnsBadRequest_WhenTContainsInvalidCharacters() + { + // Arrange + var t = "abc$"; + var p = 0; + + // Act + var result = _controller.Search(t, p); + + // Assert + Assert.IsInstanceOfType(result, typeof(BadRequestObjectResult)); + } + + [TestMethod] + public void Search_ReturnsRedirectToSearch_WhenTIsValid() + { + // Arrange + var t = "abc"; + var p = 0; + + // Act + var result = _controller.Search(t, p); + + // Assert + Assert.IsInstanceOfType(result, typeof(RedirectResult)); + var redirectResult = ( RedirectResult ) result; + Assert.AreEqual($"/search?t={t}&p={p}", redirectResult.Url); + } + + [TestMethod] + public void Search_BadRequest() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - var result = HomeController.AppendPathBasePrefix("", $"/search"); - Assert.AreEqual("/search",result); - } - - [TestMethod] - public void AppendPathBasePrefix_2() + ControllerContext = { HttpContext = _httpContext } + }; + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = controller.Search(); + + Assert.IsInstanceOfType(result); + } + + [TestMethod] + public void Trash_ReturnsPhysicalFile() + { + // Arrange + var p = 0; + + // Act + _controller.Request.Path = "/trash"; + var result = _controller.Trash(p); + + // Assert + Assert.IsInstanceOfType(result, typeof(PhysicalFileResult)); + var physicalFileResult = ( PhysicalFileResult ) result; + Assert.AreEqual("text/html", physicalFileResult.ContentType); + } + + [TestMethod] + public void Trash_RedirectsToLowerCaseUrl_WhenRequestedUrlIsCaseSensitive() + { + // Arrange + var p = 0; + var request = new DefaultHttpContext().Request; + request.Scheme = "http"; + request.Host = new HostString("localhost"); + request.Path = "/Trash"; + request.PathBase = "/"; + + _controller.ControllerContext = new ControllerContext { - var result = HomeController.AppendPathBasePrefix("test", $"/search"); - Assert.AreEqual("/search",result); - } - - [TestMethod] - public void AppendPathBasePrefix_3() + HttpContext = new DefaultHttpContext() + }; + + // Act + var result = _controller.Trash(p); + + // Assert + Assert.IsInstanceOfType(result, typeof(RedirectResult)); + var redirectResult = ( RedirectResult ) result; + Assert.AreEqual("/trash?p=0", redirectResult.Url); + } + + [TestMethod] + public void Trash_ReturnsPhysicalFile_WhenRequestedUrlIsNotCaseSensitive() + { + // Arrange + var p = 0; + // difference is trash vs import + _controller.Request.Path = "/trash"; + + // Act + var result = _controller.Trash(p); + + // Assert + Assert.IsInstanceOfType(result, typeof(PhysicalFileResult)); + var physicalFileResult = ( PhysicalFileResult ) result; + Assert.AreEqual("text/html", physicalFileResult.ContentType); + } + + [TestMethod] + public void Trash_BadRequest() + { + var controller = new HomeController(new AppSettings(), _antiForgery) { - var result = HomeController.AppendPathBasePrefix("/starsky", $"/search"); - Assert.AreEqual("/starsky/search",result); - } + ControllerContext = { HttpContext = _httpContext } + }; + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = controller.Trash(); + + Assert.IsInstanceOfType(result); + } + + [TestMethod] + public void Import_ReturnsPhysicalFile() + { + // Act + _controller.Request.Path = "/import"; + var result = _controller.Import(); + + // Assert + Assert.IsInstanceOfType(result, typeof(PhysicalFileResult)); + var physicalFileResult = ( PhysicalFileResult ) result; + Assert.AreEqual("text/html", physicalFileResult.ContentType); + } + + [TestMethod] + public void Import_ReturnsPhysicalFile_WhenRequestedUrlIsNotCaseSensitive() + { + // Arrange + _controller.Request.Path = "/import"; + + // Act + var result = _controller.Import(); + + // Assert + Assert.IsInstanceOfType(result, typeof(PhysicalFileResult)); + var physicalFileResult = ( PhysicalFileResult ) result; + Assert.AreEqual("text/html", physicalFileResult.ContentType); + } + + [TestMethod] + public void AppendPathBasePrefix_1() + { + var result = HomeController.AppendPathBasePrefix("", "/search"); + Assert.AreEqual("/search", result); + } + + [TestMethod] + public void AppendPathBasePrefix_2() + { + var result = HomeController.AppendPathBasePrefix("test", "/search"); + Assert.AreEqual("/search", result); + } + + [TestMethod] + public void AppendPathBasePrefix_3() + { + var result = HomeController.AppendPathBasePrefix("/starsky", "/search"); + Assert.AreEqual("/starsky/search", result); } } diff --git a/starsky/starskytest/Controllers/ImportControllerTest.cs b/starsky/starskytest/Controllers/ImportControllerTest.cs index a1f3a14eca..a5b4770fc3 100644 --- a/starsky/starskytest/Controllers/ImportControllerTest.cs +++ b/starsky/starskytest/Controllers/ImportControllerTest.cs @@ -176,6 +176,20 @@ await importController.FromUrl("", "../../path-injection.dll", null!) as BadRequestResult; Assert.AreEqual(400, actionResult?.StatusCode); } + + [TestMethod] + public async Task FromUrl_BadRequest() + { + var importController = new ImportController(_import, _appSettings, + _bgTaskQueue, null!, new FakeSelectorStorage(new FakeIStorage()), _scopeFactory, + new FakeIWebLogger()) { ControllerContext = RequestWithFile(), }; + importController.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = + await importController.FromUrl(null!, null!, null!); + + Assert.IsInstanceOfType(result); + } [TestMethod] public async Task FromUrl_RequestFromWhiteListedDomain_NotFound() diff --git a/starsky/starskytest/Controllers/IndexControllerTest.cs b/starsky/starskytest/Controllers/IndexControllerTest.cs index 4c9e568afe..87f8d5b046 100644 --- a/starsky/starskytest/Controllers/IndexControllerTest.cs +++ b/starsky/starskytest/Controllers/IndexControllerTest.cs @@ -118,6 +118,18 @@ public void HomeControllerIndexIndexViewModel_EmptyStringPage_Test() var jsonCollection = actionResult?.Value as ArchiveViewModel; Assert.AreEqual("test", jsonCollection?.FileIndexItems.FirstOrDefault()?.FileHash); } + + [TestMethod] + public void Index_BadRequest() + { + var controller = new IndexController(new FakeIQuery(), new AppSettings()); + + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = controller.Index(null!); + + Assert.IsInstanceOfType(result); + } [TestMethod] public void HomeControllerIndex404Test() diff --git a/starsky/starskytest/Controllers/MetaInfoControllerTest.cs b/starsky/starskytest/Controllers/MetaInfoControllerTest.cs index fda2b12d8e..e3711fd42e 100644 --- a/starsky/starskytest/Controllers/MetaInfoControllerTest.cs +++ b/starsky/starskytest/Controllers/MetaInfoControllerTest.cs @@ -55,6 +55,17 @@ public async Task Info_SourceImageMissing_WithFakeExifTool() await controller.InfoAsync("/source_missing.jpg") as NotFoundObjectResult; Assert.AreEqual(404, notFoundResult?.StatusCode); } + + [TestMethod] + public async Task Index_BadRequest() + { + var controller = new MetaInfoController(_metaInfo); + + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = await controller.InfoAsync("/source_missing.jpg"); + Assert.IsInstanceOfType(result); + } [TestMethod] public async Task ReadOnly() From 99d41c5f5630543df3779f3a5b960817fbe4b1f3 Mon Sep 17 00:00:00 2001 From: Dion Date: Tue, 10 Sep 2024 17:24:11 +0200 Subject: [PATCH 2/5] make async --- .../starsky.foundation.database/Helpers/MySqlDatabaseFixes.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starsky/starsky.foundation.database/Helpers/MySqlDatabaseFixes.cs b/starsky/starsky.foundation.database/Helpers/MySqlDatabaseFixes.cs index 52bcf40d9a..b6ae6ab49d 100644 --- a/starsky/starsky.foundation.database/Helpers/MySqlDatabaseFixes.cs +++ b/starsky/starsky.foundation.database/Helpers/MySqlDatabaseFixes.cs @@ -191,12 +191,12 @@ private static async Task> ReadCommand(MySqlCommand command) { if ( command.Connection?.State != ConnectionState.Open ) { - return new List(); + return []; } var tableNames = new List(); await using var reader = await command.ExecuteReaderAsync(); - while ( reader.Read() ) + while ( await reader.ReadAsync() ) { // at least two columns tableNames.Add(reader.GetString(0) + "," + reader.GetString(1)); From 098509084d7188fbf800eb5f436b1d2b5b04d582 Mon Sep 17 00:00:00 2001 From: Dion Date: Wed, 11 Sep 2024 09:23:07 +0200 Subject: [PATCH 3/5] improve tests --- .../starsky/Controllers/ExportController.cs | 5 + starsky/starsky/Controllers/HomeController.cs | 17 +- .../Controllers/ExportControllerTest.cs | 38 +++- .../HealthCheckForUpdatesControllerTest.cs | 9 +- .../DataProtection/SqlXmlRepositoryTest.cs | 206 +++++++++--------- .../ServiceCollectionExtensionsTest.cs | 181 +++++++-------- 6 files changed, 261 insertions(+), 195 deletions(-) diff --git a/starsky/starsky/Controllers/ExportController.cs b/starsky/starsky/Controllers/ExportController.cs index 92db0b7665..222338bf61 100644 --- a/starsky/starsky/Controllers/ExportController.cs +++ b/starsky/starsky/Controllers/ExportController.cs @@ -46,6 +46,11 @@ public ExportController(IUpdateBackgroundTaskQueue queue, public async Task CreateZip(string f, bool collections = true, bool thumbnail = false) { + if ( !ModelState.IsValid ) + { + return BadRequest("Model invalid"); + } + var inputFilePaths = PathHelper.SplitInputFilePaths(f); var (zipOutputName, fileIndexResultsList) = await _export.PreflightAsync(inputFilePaths, collections, thumbnail); diff --git a/starsky/starsky/Controllers/HomeController.cs b/starsky/starsky/Controllers/HomeController.cs index 57249d6d0e..5c6a2315c8 100644 --- a/starsky/starsky/Controllers/HomeController.cs +++ b/starsky/starsky/Controllers/HomeController.cs @@ -17,6 +17,8 @@ namespace starsky.Controllers; public sealed class HomeController : Controller { private const string TextHtmlMimeType = "text/html"; + private const string ModelInvalidText = "Model invalid"; + private readonly IAntiforgery _antiForgery; private readonly string _clientApp; @@ -42,7 +44,7 @@ public IActionResult Index(string f = "") { if ( !ModelState.IsValid ) { - return BadRequest("Model invalid"); + return BadRequest(ModelInvalidText); } new AntiForgeryCookie(_antiForgery).SetAntiForgeryCookie(HttpContext); @@ -65,7 +67,7 @@ public IActionResult SearchPost(string t = "", int p = 0) { if ( !ModelState.IsValid ) { - return BadRequest("Model invalid"); + return BadRequest(ModelInvalidText); } if ( string.IsNullOrEmpty(t) ) @@ -104,9 +106,9 @@ public IActionResult Search(string t = "", int p = 0) { if ( !ModelState.IsValid ) { - return BadRequest("Model invalid"); + return BadRequest(ModelInvalidText); } - + new AntiForgeryCookie(_antiForgery).SetAntiForgeryCookie(HttpContext); if ( !IsCaseSensitiveRedirect("/search", Request.Path.Value) ) @@ -148,9 +150,9 @@ public IActionResult Trash(int p = 0) { if ( !ModelState.IsValid ) { - return BadRequest("Model invalid"); + return BadRequest(ModelInvalidText); } - + if ( IsCaseSensitiveRedirect("/trash", Request.Path.Value) ) { return Redirect(AppendPathBasePrefix(Request.PathBase.Value, $"/trash?p={p}")); @@ -216,8 +218,9 @@ public IActionResult Register(string? returnUrl = null) { if ( !ModelState.IsValid ) { - return BadRequest("Model invalid"); + return BadRequest(ModelInvalidText); } + new AntiForgeryCookie(_antiForgery).SetAntiForgeryCookie(HttpContext); return PhysicalFile(_clientApp, TextHtmlMimeType); } diff --git a/starsky/starskytest/Controllers/ExportControllerTest.cs b/starsky/starskytest/Controllers/ExportControllerTest.cs index d521031229..04d288c9e7 100644 --- a/starsky/starskytest/Controllers/ExportControllerTest.cs +++ b/starsky/starskytest/Controllers/ExportControllerTest.cs @@ -115,8 +115,25 @@ public async Task ExportController_CreateZipNotFound() var controller = new ExportController(_bgTaskQueue, storageSelector, export); controller.ControllerContext.HttpContext = new DefaultHttpContext(); - var actionResult = await controller.CreateZip("/fail") as NotFoundObjectResult; - Assert.AreEqual(404, actionResult?.StatusCode); + var result = await controller.CreateZip("/fail") as NotFoundObjectResult; + Assert.AreEqual(404, result?.StatusCode); + } + + [TestMethod] + public async Task CreateZip_BadRequest() + { + var iStorage = new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()); + var storageSelector = new FakeSelectorStorage(iStorage); + var export = new ExportService(_query, _appSettings, storageSelector, + new FakeIWebLogger(), new FakeIThumbnailService(storageSelector)); + var controller = new ExportController(_bgTaskQueue, storageSelector, export); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = await controller.CreateZip(null!); + + // Assert + Assert.IsInstanceOfType(result); } [TestMethod] @@ -458,6 +475,23 @@ public void Status_Returns_NotReady_When_StatusIsFalse() Assert.AreEqual(206, httpContext.Response.StatusCode); } + [TestMethod] + public void Status_BadRequest() + { + var iStorage = new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()); + var storageSelector = new FakeSelectorStorage(iStorage); + var export = new ExportService(_query, _appSettings, storageSelector, + new FakeIWebLogger(), new FakeIThumbnailService(storageSelector)); + var controller = new ExportController(_bgTaskQueue, storageSelector, export); + controller.ControllerContext.HttpContext = new DefaultHttpContext(); + controller.ModelState.AddModelError("Key", "ErrorMessage"); + + var result = controller.Status(null!); + + // Assert + Assert.IsInstanceOfType(result); + } + [TestMethod] public void Status_Returns_JsonResult_When_JsonIsTrue() { diff --git a/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs b/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs index 7bb96bceab..580726de75 100644 --- a/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs +++ b/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs @@ -35,7 +35,6 @@ public async Task CheckForUpdates_Disabled() [TestMethod] - [ExpectedException(typeof(NotSupportedException))] public async Task CheckForUpdates_NotSupportedException() { var input = new TestOverWriteEnumModel { Value = UpdateStatus.Disabled }; @@ -51,8 +50,12 @@ public async Task CheckForUpdates_NotSupportedException() Assert.IsNotNull(input.Value); - await new HealthCheckForUpdatesController(fakeService, - new FakeISpecificVersionReleaseInfo()).CheckForUpdates(); + var sut = new HealthCheckForUpdatesController(fakeService, + new FakeISpecificVersionReleaseInfo()); + + // ExpectedException + await Assert.ThrowsExceptionAsync(async () => + await sut.CheckForUpdates()); } [TestMethod] diff --git a/starsky/starskytest/starsky.foundation.database/DataProtection/SqlXmlRepositoryTest.cs b/starsky/starskytest/starsky.foundation.database/DataProtection/SqlXmlRepositoryTest.cs index 9ea09b7bde..521d39a33f 100644 --- a/starsky/starskytest/starsky.foundation.database/DataProtection/SqlXmlRepositoryTest.cs +++ b/starsky/starskytest/starsky.foundation.database/DataProtection/SqlXmlRepositoryTest.cs @@ -19,25 +19,26 @@ namespace starskytest.starsky.foundation.database.DataProtection; [TestClass] public class SqlXmlRepositoryTest { - private readonly SqlXmlRepository _repository; private readonly ApplicationDbContext _dbContext; + private readonly SqlXmlRepository _repository; - private IServiceScopeFactory CreateNewScope() - { - var services = new ServiceCollection(); - services.AddDbContext(options => options.UseInMemoryDatabase(nameof(SqlXmlRepositoryTest))); - var serviceProvider = services.BuildServiceProvider(); - return serviceProvider.GetRequiredService(); - } - public SqlXmlRepositoryTest() { var serviceScope = CreateNewScope(); var scope = serviceScope.CreateScope(); _dbContext = scope.ServiceProvider.GetRequiredService(); - _repository = new SqlXmlRepository(_dbContext,serviceScope, new FakeIWebLogger()); + _repository = new SqlXmlRepository(_dbContext, serviceScope, new FakeIWebLogger()); + } + + private IServiceScopeFactory CreateNewScope() + { + var services = new ServiceCollection(); + services.AddDbContext(options => + options.UseInMemoryDatabase(nameof(SqlXmlRepositoryTest))); + var serviceProvider = services.BuildServiceProvider(); + return serviceProvider.GetRequiredService(); } - + [TestMethod] public void SqlXmlRepositoryTest_GetElementNull() { @@ -47,20 +48,20 @@ public void SqlXmlRepositoryTest_GetElementNull() .DataProtectionKeys); _dbContext.DataProtectionKeys.Add(item); _dbContext.SaveChanges(); - + var result = _repository.GetAllElements().ToList(); Assert.AreEqual(0, result.Count); } - + [TestMethod] - [ExpectedException(typeof(NullReferenceException))] public void SqlXmlRepositoryTest_ExpectedException_NullReferenceException() { - new SqlXmlRepository(null!,null!, new FakeIWebLogger()).GetAllElements(); + var sut = new SqlXmlRepository(null!, null!, new FakeIWebLogger()); + Assert.ThrowsException(() => sut.GetAllElements()); // ExpectedException NullReferenceException } - + [SuppressMessage("Usage", "S6602:FirstOrDefault is not Find")] private static MySqlException CreateMySqlException(string message) { @@ -71,67 +72,46 @@ private static MySqlException CreateMySqlException(string message) BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod); // s6602 - var ctor = ctorLIst.FirstOrDefault(p => - p.ToString() == "Void .ctor(MySqlConnector.MySqlErrorCode, System.String, System.String, System.Exception)" ); - + var ctor = ctorLIst.FirstOrDefault(p => + p.ToString() == + "Void .ctor(MySqlConnector.MySqlErrorCode, System.String, System.String, System.Exception)"); + var instance = - ( MySqlException ?) ctor?.Invoke(new object[] + ( MySqlException? ) ctor?.Invoke(new object[] { - MySqlErrorCode.AccessDenied, - "test", - message, - new Exception() + MySqlErrorCode.AccessDenied, "test", message, new Exception() }); return instance!; } - - private class AppDbMySqlException : ApplicationDbContext - { - public AppDbMySqlException(DbContextOptions options) : base(options) - { - } - - public override DbSet DataProtectionKeys => throw CreateMySqlException("0x80004005 DataProtectionKeys"); - public override DatabaseFacade Database => throw CreateMySqlException("EnsureCreated"); - } - [TestMethod] - [ExpectedException(typeof(MySqlException))] public void GetAllElements_SqlXmlRepositoryTest_Exception() { var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "MovieListDatabase") + .UseInMemoryDatabase("MovieListDatabase") .Options; - new SqlXmlRepository(new AppDbMySqlException(options), null!, new FakeIWebLogger()) - .GetAllElements(); + var sut = new SqlXmlRepository(new AppDbMySqlException(options), null!, + new FakeIWebLogger()); + Assert.ThrowsException(() => sut.GetAllElements()); // EnsureCreated is trowed as exception } - - private class GetAllElementsAppDbMySqlException2 : ApplicationDbContext - { - public GetAllElementsAppDbMySqlException2(DbContextOptions options) : base(options) - { - } - public override DbSet DataProtectionKeys => throw CreateMySqlException("general"); - public override DatabaseFacade Database => throw CreateMySqlException("EnsureCreated"); - } - + [TestMethod] public void GetAllElements_Exception2() { var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "MovieListDatabase") + .UseInMemoryDatabase("MovieListDatabase") .Options; - var readOnlyCollection = new SqlXmlRepository(new GetAllElementsAppDbMySqlException2(options), null!, new FakeIWebLogger()) - .GetAllElements(); - Assert.AreEqual(0,readOnlyCollection.Count); + var readOnlyCollection = + new SqlXmlRepository(new GetAllElementsAppDbMySqlException2(options), null!, + new FakeIWebLogger()) + .GetAllElements(); + Assert.AreEqual(0, readOnlyCollection.Count); } - - + [TestMethod] public void SqlXmlRepositoryTest_StoreElement_HappyFlow() { @@ -139,30 +119,22 @@ public void SqlXmlRepositoryTest_StoreElement_HappyFlow() var item = _dbContext.DataProtectionKeys.FirstOrDefault( p => p.FriendlyName == "hi2"); - + Assert.AreEqual("hi2", item!.FriendlyName); } - - private class StoreElementException : ApplicationDbContext - { - public StoreElementException(DbContextOptions options) : base(options) - { - } - public override DbSet DataProtectionKeys => throw new DbUpdateException("general"); - } - + [TestMethod] public void SqlXmlRepositoryTest_StoreElement_Exception() { var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "MovieListDatabase") + .UseInMemoryDatabase("MovieListDatabase") .Options; - var logger = new FakeIWebLogger(); + var logger = new FakeIWebLogger(); var repo = new SqlXmlRepository( new StoreElementException(options), null!, logger); - + repo.StoreElement(new XElement("x3", "x3"), "hi3"); var count = 0; @@ -174,40 +146,32 @@ public void SqlXmlRepositoryTest_StoreElement_Exception() { // do nothing } - + Assert.AreEqual(0, count); - + var error = logger.TrackedExceptions.Find(p => p.Item2?.Contains("AggregateException") == true); - + Assert.IsNotNull(error); } - - private class StoreElementException2RetryLimitExceededException : ApplicationDbContext - { - public StoreElementException2RetryLimitExceededException(DbContextOptions options) : base(options) - { - } - public override DbSet DataProtectionKeys => throw new RetryLimitExceededException("general"); - } - + [TestMethod] public void SqlXmlRepositoryTest_StoreElement_Exception_RetryLimitExceededException() { var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "MovieListDatabase") + .UseInMemoryDatabase("MovieListDatabase") .Options; - var logger = new FakeIWebLogger(); + var logger = new FakeIWebLogger(); var repo = new SqlXmlRepository( new StoreElementException2RetryLimitExceededException(options), null!, logger); - + repo.StoreElement(new XElement("x1", "x1"), "hi3"); var error = logger.TrackedExceptions.Find(p => p.Item2?.Contains("AggregateException") == true); - + var count = 0; try { @@ -217,33 +181,79 @@ public void SqlXmlRepositoryTest_StoreElement_Exception_RetryLimitExceededExcept { // do nothing } - + Assert.AreEqual(0, count); - + Assert.IsNotNull(error); } - - private class StoreElementException2OtherException : ApplicationDbContext - { - public StoreElementException2OtherException(DbContextOptions options) : base(options) - { - } - public override DbSet DataProtectionKeys => throw new NullReferenceException("general"); - } - + [TestMethod] [ExpectedException(typeof(NullReferenceException))] public void SqlXmlRepositoryTest_StoreElement_Exception_Other() { var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "MovieListDatabase") + .UseInMemoryDatabase("MovieListDatabase") .Options; - var logger = new FakeIWebLogger(); + var logger = new FakeIWebLogger(); var repo = new SqlXmlRepository( new StoreElementException2OtherException(options), null!, logger); repo.StoreElement(new XElement("x1", "x1"), "hi"); } - + + private class AppDbMySqlException : ApplicationDbContext + { + public AppDbMySqlException(DbContextOptions options) : base(options) + { + } + + public override DbSet DataProtectionKeys => + throw CreateMySqlException("0x80004005 DataProtectionKeys"); + + public override DatabaseFacade Database => throw CreateMySqlException("EnsureCreated"); + } + + private class GetAllElementsAppDbMySqlException2 : ApplicationDbContext + { + public GetAllElementsAppDbMySqlException2(DbContextOptions options) : base(options) + { + } + + public override DbSet DataProtectionKeys => + throw CreateMySqlException("general"); + + public override DatabaseFacade Database => throw CreateMySqlException("EnsureCreated"); + } + + private class StoreElementException : ApplicationDbContext + { + public StoreElementException(DbContextOptions options) : base(options) + { + } + + public override DbSet DataProtectionKeys => + throw new DbUpdateException("general"); + } + + private class StoreElementException2RetryLimitExceededException : ApplicationDbContext + { + public StoreElementException2RetryLimitExceededException(DbContextOptions options) : + base(options) + { + } + + public override DbSet DataProtectionKeys => + throw new RetryLimitExceededException("general"); + } + + private class StoreElementException2OtherException : ApplicationDbContext + { + public StoreElementException2OtherException(DbContextOptions options) : base(options) + { + } + + public override DbSet DataProtectionKeys => + throw new NullReferenceException("general"); + } } diff --git a/starsky/starskytest/starsky.foundation.injection/ServiceCollectionExtensionsTest.cs b/starsky/starskytest/starsky.foundation.injection/ServiceCollectionExtensionsTest.cs index 0f9c93383a..5c08a4f73d 100644 --- a/starsky/starskytest/starsky.foundation.injection/ServiceCollectionExtensionsTest.cs +++ b/starsky/starskytest/starsky.foundation.injection/ServiceCollectionExtensionsTest.cs @@ -17,31 +17,20 @@ public interface ITestInjectionClass [TestClass] public class ServiceCollectionExtensionsTest { - private class TestInjectionClass : ITestInjectionClass - { - public bool Enabled { get; set; } = true; - } - - private class OverwriteInjectionLifetime - { - [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] - public InjectionLifetime Type { get; set; } - } - [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] public void Add_LifeTimeNull() { var serviceCollection = new ServiceCollection() as IServiceCollection; - serviceCollection.Add(InjectionLifetime.Scoped, null!); + Assert.ThrowsException(() => + serviceCollection.Add(InjectionLifetime.Scoped, null!)); } - + [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] public void Add_LifeTimeNull2() { var serviceCollection = new ServiceCollection() as IServiceCollection; - serviceCollection.Add(InjectionLifetime.Scoped, null!, null!); + Assert.ThrowsException(() => + serviceCollection.Add(InjectionLifetime.Scoped, null!, null!)); } [TestMethod] @@ -50,27 +39,29 @@ public void Add_LifeTime_Scope() var serviceCollection = new ServiceCollection() as IServiceCollection; serviceCollection.Add(InjectionLifetime.Scoped, typeof(TestInjectionClass)); - var serviceDescriptor = serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(TestInjectionClass)); + var serviceDescriptor = + serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(TestInjectionClass)); Assert.AreEqual(ServiceLifetime.Scoped, serviceDescriptor?.Lifetime); - + var serviceProvider = serviceCollection.BuildServiceProvider(); - + var result = serviceProvider.GetService(); Assert.IsTrue(result?.Enabled); } - - + + [TestMethod] public void Add_LifeTime_Singleton() { var serviceCollection = new ServiceCollection() as IServiceCollection; serviceCollection.Add(InjectionLifetime.Singleton, typeof(TestInjectionClass)); - - var serviceDescriptor = serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(TestInjectionClass)); + + var serviceDescriptor = + serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(TestInjectionClass)); Assert.AreEqual(ServiceLifetime.Singleton, serviceDescriptor?.Lifetime); - + var serviceProvider = serviceCollection.BuildServiceProvider(); - + var result = serviceProvider.GetService(); Assert.IsTrue(result?.Enabled); } @@ -81,11 +72,12 @@ public void Add_LifeTime_Transient() var serviceCollection = new ServiceCollection() as IServiceCollection; serviceCollection.Add(InjectionLifetime.Transient, typeof(TestInjectionClass)); - var serviceDescriptor = serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(TestInjectionClass)); + var serviceDescriptor = + serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(TestInjectionClass)); Assert.AreEqual(ServiceLifetime.Transient, serviceDescriptor?.Lifetime); - + var serviceProvider = serviceCollection.BuildServiceProvider(); - + var result = serviceProvider.GetService(); Assert.IsTrue(result?.Enabled); } @@ -97,133 +89,152 @@ public void Add_LifeTime_InvalidType() var overwriteInjectionLifetime = new OverwriteInjectionLifetime(); var propertyObject = overwriteInjectionLifetime.GetType().GetProperty("Type"); propertyObject?.SetValue(overwriteInjectionLifetime, 44, null); // <-- this could not happen - + var serviceCollection = new ServiceCollection() as IServiceCollection; serviceCollection.Add(overwriteInjectionLifetime.Type, typeof(TestInjectionClass)); // expect exception } - - + + [TestMethod] public void Add_implementationType_LifeTime_Scope() { var serviceCollection = new ServiceCollection() as IServiceCollection; - serviceCollection.Add(typeof(ITestInjectionClass), - typeof(TestInjectionClass),InjectionLifetime.Scoped); + serviceCollection.Add(typeof(ITestInjectionClass), + typeof(TestInjectionClass), InjectionLifetime.Scoped); - var serviceDescriptor = serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(ITestInjectionClass)); + var serviceDescriptor = + serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(ITestInjectionClass)); Assert.AreEqual(ServiceLifetime.Scoped, serviceDescriptor?.Lifetime); - + var serviceProvider = serviceCollection.BuildServiceProvider(); - + var result = serviceProvider.GetService(); Assert.IsTrue(result?.Enabled); } - - + + [TestMethod] public void Add_implementationType_LifeTime_Singleton() { var serviceCollection = new ServiceCollection() as IServiceCollection; - serviceCollection.Add(typeof(ITestInjectionClass), - typeof(TestInjectionClass),InjectionLifetime.Singleton); + serviceCollection.Add(typeof(ITestInjectionClass), + typeof(TestInjectionClass), InjectionLifetime.Singleton); - var serviceDescriptor = serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(ITestInjectionClass)); + var serviceDescriptor = + serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(ITestInjectionClass)); Assert.AreEqual(ServiceLifetime.Singleton, serviceDescriptor?.Lifetime); - + var serviceProvider = serviceCollection.BuildServiceProvider(); - + var result = serviceProvider.GetService(); Assert.IsTrue(result?.Enabled); } - + [TestMethod] public void Add_implementationType_LifeTime_Transient() { var serviceCollection = new ServiceCollection() as IServiceCollection; - serviceCollection.Add(typeof(ITestInjectionClass), - typeof(TestInjectionClass),InjectionLifetime.Transient); + serviceCollection.Add(typeof(ITestInjectionClass), + typeof(TestInjectionClass), InjectionLifetime.Transient); - var serviceDescriptor = serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(ITestInjectionClass)); + var serviceDescriptor = + serviceCollection.FirstOrDefault(p => p.ServiceType == typeof(ITestInjectionClass)); Assert.AreEqual(ServiceLifetime.Transient, serviceDescriptor?.Lifetime); - + var serviceProvider = serviceCollection.BuildServiceProvider(); - + var result = serviceProvider.GetService(); Assert.IsTrue(result?.Enabled); } - + [TestMethod] - [ExpectedException(typeof(ArgumentOutOfRangeException))] public void Add_implementationType_LifeTime_InvalidType() { var overwriteInjectionLifetime = new OverwriteInjectionLifetime(); var propertyObject = overwriteInjectionLifetime.GetType().GetProperty("Type"); propertyObject?.SetValue(overwriteInjectionLifetime, 44, null); // <-- this could not happen + + IServiceCollection serviceCollection = new ServiceCollection(); - var serviceCollection = new ServiceCollection() as IServiceCollection; - serviceCollection.Add(typeof(ITestInjectionClass), - typeof(TestInjectionClass),overwriteInjectionLifetime.Type); - // expect exception + Assert.ThrowsException(() => serviceCollection.Add(typeof(ITestInjectionClass), + typeof(TestInjectionClass), overwriteInjectionLifetime.Type)); } [TestMethod] public void GetExportedTypes_Default() { - var result = ServiceCollectionExtensions.GetExportedTypes(typeof(TestInjectionClass).Assembly); + var result = + ServiceCollectionExtensions.GetExportedTypes(typeof(TestInjectionClass).Assembly); var count = result.Count(); Assert.IsTrue(count >= 100); } - - public class AssemblyTestClass : Assembly - { - private readonly Exception _exception; - - public AssemblyTestClass(Exception exception) - { - _exception = exception; - } - public override string FullName { get; } = "test"; - - public override Type[] GetExportedTypes() - { - throw _exception; - } - } - [TestMethod] public void GetExportedTypes_NotSupportedException() { - var result = ServiceCollectionExtensions.GetExportedTypes(new AssemblyTestClass(new NotSupportedException())); - Assert.AreEqual(Type.EmptyTypes,result); + var result = + ServiceCollectionExtensions.GetExportedTypes( + new AssemblyTestClass(new NotSupportedException())); + Assert.AreEqual(Type.EmptyTypes, result); } - + [TestMethod] public void GetExportedTypes_FileLoadException() { - var result = ServiceCollectionExtensions.GetExportedTypes(new AssemblyTestClass(new FileLoadException())); - Assert.AreEqual(Type.EmptyTypes,result); + var result = + ServiceCollectionExtensions.GetExportedTypes( + new AssemblyTestClass(new FileLoadException())); + Assert.AreEqual(Type.EmptyTypes, result); } - + [TestMethod] public void GetExportedTypes_ReflectionTypeLoadException() { - var exception = new ReflectionTypeLoadException([typeof(bool), typeof(byte), null! + var exception = new ReflectionTypeLoadException([ + typeof(bool), typeof(byte), null! ], [null!, new Exception()]); var result = ServiceCollectionExtensions.GetExportedTypes(new AssemblyTestClass(exception)); var count = result.Count(); - Assert.AreEqual(2,count); + Assert.AreEqual(2, count); } - - + [TestMethod] - [ExpectedException(typeof(InvalidOperationException))] public void GetExportedTypes_NullRefException() { var exception = new NullReferenceException(); - ServiceCollectionExtensions.GetExportedTypes(new AssemblyTestClass(exception)); + + Assert.ThrowsException(() => + ServiceCollectionExtensions.GetExportedTypes(new AssemblyTestClass(exception))); + } + + private class TestInjectionClass : ITestInjectionClass + { + public bool Enabled { get; set; } = true; + } + + private class OverwriteInjectionLifetime + { + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] + public InjectionLifetime Type { get; set; } + } + + public class AssemblyTestClass : Assembly + { + private readonly Exception _exception; + + public AssemblyTestClass(Exception exception) + { + _exception = exception; + } + + public override string FullName { get; } = "test"; + + public override Type[] GetExportedTypes() + { + throw _exception; + } } } From a9d3e2872dc0ee17942f69dcacba9155628867c1 Mon Sep 17 00:00:00 2001 From: Dion Date: Wed, 11 Sep 2024 09:35:57 +0200 Subject: [PATCH 4/5] code smells --- .../Helpers/ExifToolTest.cs | 217 +++++++++--------- 1 file changed, 109 insertions(+), 108 deletions(-) diff --git a/starsky/starskytest/starsky.foundation.writemeta/Helpers/ExifToolTest.cs b/starsky/starskytest/starsky.foundation.writemeta/Helpers/ExifToolTest.cs index 4979bf8ecf..40e784fee1 100644 --- a/starsky/starskytest/starsky.foundation.writemeta/Helpers/ExifToolTest.cs +++ b/starsky/starskytest/starsky.foundation.writemeta/Helpers/ExifToolTest.cs @@ -11,142 +11,143 @@ using starskytest.FakeCreateAn; using starskytest.FakeMocks; -namespace starskytest.starsky.foundation.writemeta.Helpers +namespace starskytest.starsky.foundation.writemeta.Helpers; + +[TestClass] +public sealed class ExifToolTest { - [TestClass] - public sealed class ExifToolTest + [TestMethod] + public async Task ExifTool_ArgumentException() { - [TestMethod] - [ExpectedException(typeof(ArgumentException))] - public async Task ExifTool_NotFound_Exception() - { - var appSettings = new AppSettings { ExifToolPath = "Z://Non-exist", }; + var appSettings = new AppSettings { ExifToolPath = "Z://Non-exist" }; - var fakeStorage = new FakeIStorage(new List { "/" }, - new List { "/test.jpg" }, - new List { CreateAnImage.Bytes.ToArray() }); + var fakeStorage = new FakeIStorage(new List { "/" }, + ["/test.jpg"], + new List { CreateAnImage.Bytes.ToArray() }); - await new ExifToolService(new FakeSelectorStorage(fakeStorage), appSettings, - new FakeIWebLogger()) - .WriteTagsAsync("/test.jpg", "-Software=\"Qdraw 2.0\""); - } + var sut = new ExifToolService(new FakeSelectorStorage(fakeStorage), appSettings, + new FakeIWebLogger()); - [TestMethod] - [ExpectedException(typeof(ArgumentException))] - public async Task ExifTool_WriteTagsThumbnailAsync_NotFound_Exception() - { - var appSettings = new AppSettings { ExifToolPath = "Z://Non-exist", }; + await Assert.ThrowsExceptionAsync(async () => + await sut.WriteTagsAsync("/test.jpg", "-Software=\"Qdraw 2.0\"")); + } - var fakeStorage = new FakeIStorage(new List { "/" }, - new List { "/test.jpg" }, - new List { CreateAnImage.Bytes.ToArray() }); + [TestMethod] + public async Task ExifTool_WriteTagsThumbnailAsync_NotFound_Exception() + { + var appSettings = new AppSettings { ExifToolPath = "Z://Non-exist" }; - await new ExifToolService(new FakeSelectorStorage(fakeStorage), appSettings, - new FakeIWebLogger()) - .WriteTagsThumbnailAsync("/test.jpg", "-Software=\"Qdraw 2.0\""); - } + var fakeStorage = new FakeIStorage(["/"], + ["/test.jpg"], + new List { CreateAnImage.Bytes.ToArray() }); - [TestMethod] - public async Task ExifTool_RenameThumbnailByStream_Length26() - { - var appSettings = new AppSettings { ExifToolPath = "Z://Non-exist", }; + var sut = new ExifToolService(new FakeSelectorStorage(fakeStorage), appSettings, + new FakeIWebLogger()); - var fakeStorage = new FakeIStorage(new List { "/" }, - new List { "/test.jpg" }, - new List { CreateAnImage.Bytes.ToArray() }); + await Assert.ThrowsExceptionAsync(async () => + await sut.WriteTagsAsync("/test.jpg", "-Software=\"Qdraw 2.0\"")); + } - var result = - await new ExifTool(fakeStorage, fakeStorage, appSettings, new FakeIWebLogger()) - .RenameThumbnailByStream("OLDHASH", new MemoryStream(), true); + [TestMethod] + public async Task ExifTool_RenameThumbnailByStream_Length26() + { + var appSettings = new AppSettings { ExifToolPath = "Z://Non-exist" }; - Assert.AreEqual(26, result.Length); - } + var fakeStorage = new FakeIStorage(new List { "/" }, + ["/test.jpg"], + new List { CreateAnImage.Bytes.ToArray() }); - [TestMethod] - public async Task ExifTool_RenameThumbnailByStream_Fail() - { - var appSettings = new AppSettings { ExifToolPath = "Z://Non-exist", }; + var result = + await new ExifTool(fakeStorage, fakeStorage, appSettings, new FakeIWebLogger()) + .RenameThumbnailByStream("OLDHASH", new MemoryStream(), true); - var fakeStorage = new FakeIStorage(new List { "/" }, - new List { "/test.jpg" }, - new List { CreateAnImage.Bytes.ToArray() }); + Assert.AreEqual(26, result.Length); + } - var result = - await new ExifTool(fakeStorage, fakeStorage, appSettings, new FakeIWebLogger()) - .RenameThumbnailByStream("OLDHASH", new MemoryStream(), false); + [TestMethod] + public async Task ExifTool_RenameThumbnailByStream_Fail() + { + var appSettings = new AppSettings { ExifToolPath = "Z://Non-exist" }; - Assert.AreEqual(0, result.Length); - } + var fakeStorage = new FakeIStorage(new List { "/" }, + new List { "/test.jpg" }, + new List { CreateAnImage.Bytes.ToArray() }); - [TestMethod] - public async Task ExifTool_RenameThumbnailByStream_NotDisposed_CanWrite() - { - var appSettings = new AppSettings { ExifToolPath = "Z://Non-exist", }; + var result = + await new ExifTool(fakeStorage, fakeStorage, appSettings, new FakeIWebLogger()) + .RenameThumbnailByStream("OLDHASH", new MemoryStream(), false); + + Assert.AreEqual(0, result.Length); + } - var fakeStorage = new FakeIStorage(new List { "/" }, - new List { "/test.jpg" }, - new List { CreateAnImage.Bytes.ToArray() }); + [TestMethod] + public async Task ExifTool_RenameThumbnailByStream_NotDisposed_CanWrite() + { + var appSettings = new AppSettings { ExifToolPath = "Z://Non-exist" }; - var stream = new MemoryStream(); - await new ExifTool(fakeStorage, fakeStorage, appSettings, new FakeIWebLogger()) - .RenameThumbnailByStream("OLDHASH", stream, true); + var fakeStorage = new FakeIStorage(new List { "/" }, + new List { "/test.jpg" }, + new List { CreateAnImage.Bytes.ToArray() }); - Assert.IsTrue(stream.CanWrite); - } + var stream = new MemoryStream(); + await new ExifTool(fakeStorage, fakeStorage, appSettings, new FakeIWebLogger()) + .RenameThumbnailByStream("OLDHASH", stream, true); + + Assert.IsTrue(stream.CanWrite); + } - [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public void StreamToStreamRunner_ArgumentNullException() + [TestMethod] + public void StreamToStreamRunner_ArgumentNullException() + { + Assert.ThrowsException(() => + new StreamToStreamRunner(new AppSettings(), null!, + new FakeIWebLogger())); + } + + [TestMethod] + public async Task RunProcessAsync_RunChildObject_UnixOnly() + { + // Unix only + var appSettings = new AppSettings { Verbose = true, ExifToolPath = "/bin/ls" }; + if ( appSettings.IsWindows || !File.Exists("/bin/ls") ) { - _ = new StreamToStreamRunner(new AppSettings(), null!, - new FakeIWebLogger()); + Assert.Inconclusive("This test if for Unix Only"); + return; } - [TestMethod] - public async Task RunProcessAsync_RunChildObject_UnixOnly() - { - // Unix only - var appSettings = new AppSettings { Verbose = true, ExifToolPath = "/bin/ls" }; - if ( appSettings.IsWindows || !File.Exists("/bin/ls") ) - { - Assert.Inconclusive("This test if for Unix Only"); - return; - } + var runner = new StreamToStreamRunner(appSettings, + new MemoryStream([]), new FakeIWebLogger()); + var result = await runner.RunProcessAsync(string.Empty, "test / unit test"); - var runner = new StreamToStreamRunner(appSettings, - new MemoryStream(Array.Empty()), new FakeIWebLogger()); - var result = await runner.RunProcessAsync(string.Empty, "test / unit test"); + await StreamToStringHelper.StreamToStringAsync(result, false); - await StreamToStringHelper.StreamToStringAsync(result, false); + Assert.AreEqual(0, result.Length); + } - Assert.AreEqual(0, result.Length); - } + [TestMethod] + public async Task WriteTagsAndRenameThumbnailAsync_Disposed() + { + var storage = + new FakeIStorage(new ObjectDisposedException("disposed")); + + var exifTool = new ExifTool(storage, + storage, + new AppSettings(), new FakeIWebLogger()); - [TestMethod] - public async Task WriteTagsAndRenameThumbnailAsync_Disposed() + var exceptionMessage = string.Empty; + try { - var storage = - new FakeIStorage(new ObjectDisposedException("disposed")); - - var exifTool = new ExifTool(storage, - storage, - new AppSettings(), new FakeIWebLogger()); - - var exceptionMessage = string.Empty; - try - { - await exifTool.WriteTagsAndRenameThumbnailAsync("test.jpg", null, ""); - } - catch ( ObjectDisposedException e ) - { - // Expected - exceptionMessage = e.Message; - } - - Assert.IsTrue(exceptionMessage.StartsWith("Cannot access a disposed object.")); - Assert.IsTrue(exceptionMessage.EndsWith("Object name: 'disposed'.")); - - Assert.AreEqual(1, storage.ExceptionCount); + await exifTool.WriteTagsAndRenameThumbnailAsync("test.jpg", null, ""); } + catch ( ObjectDisposedException e ) + { + // Expected + exceptionMessage = e.Message; + } + + Assert.IsTrue(exceptionMessage.StartsWith("Cannot access a disposed object.")); + Assert.IsTrue(exceptionMessage.EndsWith("Object name: 'disposed'.")); + + Assert.AreEqual(1, storage.ExceptionCount); } } From 05b00887b695439d4fe812cd94c54059a09c44be Mon Sep 17 00:00:00 2001 From: Dion Date: Wed, 11 Sep 2024 10:20:06 +0200 Subject: [PATCH 5/5] code smells --- .../HealthCheckForUpdatesControllerTest.cs | 2 - .../ImportThumbnailControllerTest.cs | 386 +++++++++--------- .../Helpers/FileStreamingHelperTest.cs | 282 ++++++------- .../Helpers/MultipartRequestHelperTest.cs | 121 +++--- .../Helpers/SwaggerExportHelperTest.cs | 205 +++++----- starsky/starskytest/root/ProgramTest.cs | 74 ++-- .../Services/GeoIndexGpxTest.cs | 196 +++++---- .../HealthCheck/DiskStorageHealthCheckTest.cs | 107 ++--- .../HealthCheck/PathExistHealthCheckTest.cs | 109 +++-- .../HealthCheck/SetupHealthCheckTest.cs | 253 ++++++------ .../DataProtection/SqlXmlRepositoryTest.cs | 4 +- .../ServiceCollectionExtensionsTest.cs | 13 +- 12 files changed, 868 insertions(+), 884 deletions(-) diff --git a/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs b/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs index 580726de75..e8ebf09d90 100644 --- a/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs +++ b/starsky/starskytest/Controllers/HealthCheckForUpdatesControllerTest.cs @@ -52,8 +52,6 @@ public async Task CheckForUpdates_NotSupportedException() var sut = new HealthCheckForUpdatesController(fakeService, new FakeISpecificVersionReleaseInfo()); - - // ExpectedException await Assert.ThrowsExceptionAsync(async () => await sut.CheckForUpdates()); } diff --git a/starsky/starskytest/Controllers/ImportThumbnailControllerTest.cs b/starsky/starskytest/Controllers/ImportThumbnailControllerTest.cs index 13d6df1118..f371c732ab 100644 --- a/starsky/starskytest/Controllers/ImportThumbnailControllerTest.cs +++ b/starsky/starskytest/Controllers/ImportThumbnailControllerTest.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -14,206 +15,193 @@ using starskytest.FakeCreateAn; using starskytest.FakeMocks; -namespace starskytest.Controllers +namespace starskytest.Controllers; + +[TestClass] +public class ImportThumbnailControllerTest { - [TestClass] - public class ImportThumbnailControllerTest + private readonly AppSettings _appSettings; + + public ImportThumbnailControllerTest() + { + _appSettings = new AppSettings(); + } + + /// + /// Add the file in the underlying request object. + /// + /// Controller Context with file + private static ControllerContext RequestWithFile() + { + var httpContext = new DefaultHttpContext(); + httpContext.Request.Headers.Append("Content-Type", "application/octet-stream"); + httpContext.Request.Body = new MemoryStream(CreateAnImage.Bytes.ToArray()); + + var actionContext = new ActionContext(httpContext, new RouteData(), + new ControllerActionDescriptor()); + return new ControllerContext(actionContext); + } + + [TestMethod] + public async Task Import_Thumbnail_Ok() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddSingleton(); + var serviceProvider = services.BuildServiceProvider(); + var storageProvider = serviceProvider.GetRequiredService(); + var importController = new ImportThumbnailController(_appSettings, + new FakeSelectorStorage(), new FakeIWebLogger(), + new FakeIThumbnailQuery()) { ControllerContext = RequestWithFile() }; + importController.Request.Headers["filename"] = + "01234567890123456789123456.jpg"; // len() 26 + + var actionResult = await importController.Thumbnail() as JsonResult; + var list = actionResult?.Value as List; + var existFileInTempFolder = + storageProvider.ExistFile( + _appSettings.TempFolder + "01234567890123456789123456.jpg"); + + Assert.AreEqual("01234567890123456789123456", list?.FirstOrDefault()); + Assert.IsFalse(existFileInTempFolder); + } + + [TestMethod] + public async Task Import_Thumbnail_Ok_SmallSize() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddSingleton(); + var serviceProvider = services.BuildServiceProvider(); + var storageProvider = serviceProvider.GetRequiredService(); + var importController = new ImportThumbnailController(_appSettings, + new FakeSelectorStorage(), new FakeIWebLogger(), + new FakeIThumbnailQuery()) { ControllerContext = RequestWithFile() }; + importController.Request.Headers["filename"] = + "01234567890123456789123456@300.jpg"; // len() 26 + + var actionResult = await importController.Thumbnail() as JsonResult; + var list = actionResult?.Value as List; + var existFileInTempFolder = + storageProvider.ExistFile( + _appSettings.TempFolder + "01234567890123456789123456@300.jpg"); + + Assert.AreEqual("01234567890123456789123456@300", list?.FirstOrDefault()); + Assert.IsFalse(existFileInTempFolder); + } + + [TestMethod] + public async Task Import_Thumbnail_WrongInputName() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddSingleton(); + + var importController = new ImportThumbnailController(_appSettings, + new FakeSelectorStorage(), new FakeIWebLogger(), + new FakeIThumbnailQuery()) { ControllerContext = RequestWithFile() }; + importController.Request.Headers["filename"] = "123.jpg"; // len() 3 + + var actionResult = await importController.Thumbnail() as JsonResult; + var list = actionResult?.Value as List; + + Assert.AreEqual(0, list?.Count); + } + + [TestMethod] + public async Task Import_Thumbnail_AlreadyExists() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddSingleton(); + var serviceProvider = services.BuildServiceProvider(); + var storageProvider = serviceProvider.GetRequiredService(); + + // create already exists + var empty = new byte[] { 1 }; // new byte[] { } Array.Empty() + await storageProvider.WriteStreamAsync(new MemoryStream(empty), + "91234567890123456789123456"); + + var importController = new ImportThumbnailController(_appSettings, + new FakeSelectorStorage(storageProvider), new FakeIWebLogger(), + new FakeIThumbnailQuery()) { ControllerContext = RequestWithFile() }; + importController.Request.Headers["filename"] = + "91234567890123456789123456.jpg"; // len() 26 + + var actionResult = await importController.Thumbnail() as JsonResult; + var list = actionResult?.Value as List; + var existFileInTempFolder = + storageProvider.ExistFile( + _appSettings.TempFolder + "91234567890123456789123456.jpg"); + + Assert.AreEqual("91234567890123456789123456", list?.FirstOrDefault()); + Assert.IsFalse(existFileInTempFolder); + Assert.IsTrue(storageProvider.Info("91234567890123456789123456").Size >= 2); + } + + [TestMethod] + public async Task WriteThumbnailsTest_ListShouldBeEq() + { + var service = new ImportThumbnailController(_appSettings, + new FakeSelectorStorage(), new FakeIWebLogger(), new FakeIThumbnailQuery()); + var result = await service.WriteThumbnails(new List(), + new List { "123" }); + Assert.IsFalse(result); + } + + [TestMethod] + public async Task WriteThumbnailsTest_NotFound() + { + var logger = new FakeIWebLogger(); + var service = new ImportThumbnailController(_appSettings, + new FakeSelectorStorage(), logger, new FakeIThumbnailQuery()); + var result = await service.WriteThumbnails(new List { "123" }, + new List { "123" }); + + Assert.IsTrue(result); + Assert.IsTrue(logger.TrackedInformation.FirstOrDefault().Item2?.Contains("not exist")); + } + + [TestMethod] + public async Task WriteThumbnailsTest_ShouldMoveFile() + { + var logger = new FakeIWebLogger(); + var storage = new FakeIStorage(new List(), + new List { "/upload/123.jpg" }); + var service = new ImportThumbnailController(_appSettings, + new FakeSelectorStorage(storage), logger, new FakeIThumbnailQuery()); + await service.WriteThumbnails(new List { "/upload/123.jpg" }, + new List { "123" }); + + Assert.IsFalse(storage.ExistFile("/upload/123.jpg")); + Assert.IsTrue(storage.ExistFile("123")); + } + + [TestMethod] + public void MapToTransferObject1() + { + var inputList = new List { "12345678901234567890123456" }; + var result = + ImportThumbnailController.MapToTransferObject(inputList).ToList(); + Assert.AreEqual("12345678901234567890123456", result.FirstOrDefault()?.FileHash); + Assert.IsTrue(result.FirstOrDefault()?.Large); + } + + [TestMethod] + public void MapToTransferObject1_2000() + { + var inputList = new List { "12345678901234567890123456@2000" }; + var result = + ImportThumbnailController.MapToTransferObject(inputList).ToList(); + Assert.AreEqual("12345678901234567890123456", result.FirstOrDefault()?.FileHash); + Assert.IsTrue(result.FirstOrDefault()?.ExtraLarge); + } + + [TestMethod] + public void MapToTransferObject1_NonValidType() { - private readonly AppSettings _appSettings; - - public ImportThumbnailControllerTest() - { - _appSettings = new AppSettings(); - } - - /// - /// Add the file in the underlying request object. - /// - /// Controller Context with file - private static ControllerContext RequestWithFile() - { - var httpContext = new DefaultHttpContext(); - httpContext.Request.Headers.Append("Content-Type", "application/octet-stream"); - httpContext.Request.Body = new MemoryStream(CreateAnImage.Bytes.ToArray()); - - var actionContext = new ActionContext(httpContext, new RouteData(), - new ControllerActionDescriptor()); - return new ControllerContext(actionContext); - } - - [TestMethod] - public async Task Import_Thumbnail_Ok() - { - var services = new ServiceCollection(); - services.AddSingleton(); - services.AddSingleton(); - var serviceProvider = services.BuildServiceProvider(); - var storageProvider = serviceProvider.GetRequiredService(); - var importController = new ImportThumbnailController(_appSettings, - new FakeSelectorStorage(),new FakeIWebLogger(), - new FakeIThumbnailQuery()) - { - ControllerContext = RequestWithFile(), - }; - importController.Request.Headers["filename"] = - "01234567890123456789123456.jpg"; // len() 26 - - var actionResult = await importController.Thumbnail() as JsonResult; - var list = actionResult?.Value as List; - var existFileInTempFolder = - storageProvider.ExistFile( - _appSettings.TempFolder + "01234567890123456789123456.jpg"); - - Assert.AreEqual("01234567890123456789123456", list?.FirstOrDefault()); - Assert.IsFalse(existFileInTempFolder); - } - - [TestMethod] - public async Task Import_Thumbnail_Ok_SmallSize() - { - var services = new ServiceCollection(); - services.AddSingleton(); - services.AddSingleton(); - var serviceProvider = services.BuildServiceProvider(); - var storageProvider = serviceProvider.GetRequiredService(); - var importController = new ImportThumbnailController(_appSettings, - new FakeSelectorStorage(),new FakeIWebLogger(), - new FakeIThumbnailQuery()) - { - ControllerContext = RequestWithFile(), - }; - importController.Request.Headers["filename"] = - "01234567890123456789123456@300.jpg"; // len() 26 - - var actionResult = await importController.Thumbnail() as JsonResult; - var list = actionResult?.Value as List; - var existFileInTempFolder = - storageProvider.ExistFile( - _appSettings.TempFolder + "01234567890123456789123456@300.jpg"); - - Assert.AreEqual("01234567890123456789123456@300", list?.FirstOrDefault()); - Assert.IsFalse(existFileInTempFolder); - } - - [TestMethod] - public async Task Import_Thumbnail_WrongInputName() - { - var services = new ServiceCollection(); - services.AddSingleton(); - services.AddSingleton(); - - var importController = new ImportThumbnailController(_appSettings, - new FakeSelectorStorage(),new FakeIWebLogger(), - new FakeIThumbnailQuery()) - { - ControllerContext = RequestWithFile(), - }; - importController.Request.Headers["filename"] = "123.jpg"; // len() 3 - - var actionResult = await importController.Thumbnail() as JsonResult; - var list = actionResult?.Value as List; - - Assert.AreEqual(0, list?.Count); - } - - [TestMethod] - public async Task Import_Thumbnail_AlreadyExists() - { - var services = new ServiceCollection(); - services.AddSingleton(); - services.AddSingleton(); - var serviceProvider = services.BuildServiceProvider(); - var storageProvider = serviceProvider.GetRequiredService(); - - // create already exists - var empty = new byte[] {1}; // new byte[] { } Array.Empty() - await storageProvider.WriteStreamAsync(new MemoryStream(empty), - "91234567890123456789123456"); - - var importController = new ImportThumbnailController(_appSettings, - new FakeSelectorStorage(storageProvider),new FakeIWebLogger(), - new FakeIThumbnailQuery()) - { - ControllerContext = RequestWithFile(), - }; - importController.Request.Headers["filename"] = - "91234567890123456789123456.jpg"; // len() 26 - - var actionResult = await importController.Thumbnail() as JsonResult; - var list = actionResult?.Value as List; - var existFileInTempFolder = - storageProvider.ExistFile( - _appSettings.TempFolder + "91234567890123456789123456.jpg"); - - Assert.AreEqual("91234567890123456789123456", list?.FirstOrDefault()); - Assert.IsFalse(existFileInTempFolder); - Assert.IsTrue(storageProvider.Info("91234567890123456789123456").Size >= 2); - } - - [TestMethod] - public async Task WriteThumbnailsTest_ListShouldBeEq() - { - var service = new ImportThumbnailController(_appSettings, - new FakeSelectorStorage(), new FakeIWebLogger(), new FakeIThumbnailQuery()); - var result = await service.WriteThumbnails(new List(), - new List{ "123" }); - Assert.IsFalse(result); - } - - [TestMethod] - public async Task WriteThumbnailsTest_NotFound() - { - var logger = new FakeIWebLogger(); - var service = new ImportThumbnailController(_appSettings, - new FakeSelectorStorage(), logger, new FakeIThumbnailQuery()); - var result = await service.WriteThumbnails(new List{ "123" }, - new List{ "123" }); - - Assert.IsTrue(result); - Assert.IsTrue(logger.TrackedInformation.FirstOrDefault().Item2?.Contains("not exist")); - } - - [TestMethod] - public async Task WriteThumbnailsTest_ShouldMoveFile() - { - var logger = new FakeIWebLogger(); - var storage = new FakeIStorage(new List(), - new List{ "/upload/123.jpg" }); - var service = new ImportThumbnailController(_appSettings, - new FakeSelectorStorage(storage), logger, new FakeIThumbnailQuery()); - await service.WriteThumbnails(new List{ "/upload/123.jpg" }, - new List{ "123" }); - - Assert.IsFalse(storage.ExistFile("/upload/123.jpg")); - Assert.IsTrue(storage.ExistFile("123")); - } - - [TestMethod] - public void MapToTransferObject1() - { - var inputList = new List { "12345678901234567890123456" }; - var result = - ImportThumbnailController.MapToTransferObject(inputList).ToList(); - Assert.AreEqual("12345678901234567890123456", result.FirstOrDefault()?.FileHash); - Assert.IsTrue(result.FirstOrDefault()?.Large); - } - - [TestMethod] - public void MapToTransferObject1_2000() - { - var inputList = new List { "12345678901234567890123456@2000" }; - var result = - ImportThumbnailController.MapToTransferObject(inputList).ToList(); - Assert.AreEqual("12345678901234567890123456", result.FirstOrDefault()?.FileHash); - Assert.IsTrue(result.FirstOrDefault()?.ExtraLarge); - } - - [TestMethod] - [ExpectedException(typeof(System.ArgumentOutOfRangeException))] - public void MapToTransferObject1_NonValidType() - { - var inputList = new List { "1234567890123456" }; - ImportThumbnailController.MapToTransferObject(inputList); - } + var inputList = new List { "1234567890123456" }; + Assert.ThrowsException(() => + ImportThumbnailController.MapToTransferObject(inputList)); } } diff --git a/starsky/starskytest/Helpers/FileStreamingHelperTest.cs b/starsky/starskytest/Helpers/FileStreamingHelperTest.cs index 56180dd9ba..c25a3c56c4 100644 --- a/starsky/starskytest/Helpers/FileStreamingHelperTest.cs +++ b/starsky/starskytest/Helpers/FileStreamingHelperTest.cs @@ -14,166 +14,166 @@ using starskytest.FakeCreateAn; using starskytest.FakeMocks; -namespace starskytest.Helpers +namespace starskytest.Helpers; + +[TestClass] +public sealed class FileStreamingHelperTest { - [TestClass] - public sealed class FileStreamingHelperTest + /// + /// @see: + /// https://github.com/dotnet/aspnetcore/blob/main/src/Http/WebUtilities/test/MultipartReaderTests.cs + /// + private const string TwoPartBody = + "--9051914041544843365972754266\r\n" + + "Content-Disposition: form-data; name=\"text\"\r\n" + + "\r\n" + + "text default\r\n" + + "--9051914041544843365972754266\r\n" + + "Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\"\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "Content of a.txt.\r\n" + + "\r\n" + + "--9051914041544843365972754266--\r\n"; + + private const string Boundary = "9051914041544843365972754266"; + private readonly AppSettings _appSettings; + + public FileStreamingHelperTest() { - private readonly AppSettings _appSettings; - - public FileStreamingHelperTest() + // Add a dependency injection feature + var services = new ServiceCollection(); + // Inject Config helper + services.AddSingleton(new ConfigurationBuilder().Build()); + // random config + var newImage = new CreateAnImage(); + var dict = new Dictionary { - // Add a dependency injection feature - var services = new ServiceCollection(); - // Inject Config helper - services.AddSingleton(new ConfigurationBuilder().Build()); - // random config - var newImage = new CreateAnImage(); - var dict = new Dictionary - { - { "App:StorageFolder", newImage.BasePath }, - { "App:ThumbnailTempFolder", newImage.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")); - // build the service - var serviceProvider = services.BuildServiceProvider(); - // get the service - _appSettings = serviceProvider.GetRequiredService(); - } - - [TestMethod] - [ExpectedException(typeof(FileLoadException))] - public async Task StreamFileExeption() - { - var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` - httpContext.Request.Headers["token"] = "fake_token_here"; //Set header + { "App:StorageFolder", newImage.BasePath }, + { "App:ThumbnailTempFolder", newImage.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")); + // build the service + var serviceProvider = services.BuildServiceProvider(); + // get the service + _appSettings = serviceProvider.GetRequiredService(); + } - await FileStreamingHelper.StreamFile(httpContext.Request, _appSettings, - new FakeSelectorStorage(new FakeIStorage())); - } + [TestMethod] + public async Task StreamFileException() + { + var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` + httpContext.Request.Headers["token"] = "fake_token_here"; //Set header - [TestMethod] - [ExpectedException(typeof(InvalidDataException))] - public async Task StreamFilemultipart() - { - var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` - httpContext.Request.Headers["token"] = "fake_token_here"; //Set header - httpContext.Request.ContentType = "multipart/form-data"; + await Assert.ThrowsExceptionAsync(async () => + await httpContext.Request.StreamFile(_appSettings, + new FakeSelectorStorage(new FakeIStorage()))); + } - await FileStreamingHelper.StreamFile(httpContext.Request, _appSettings, - new FakeSelectorStorage(new FakeIStorage())); - } + [TestMethod] + public async Task StreamFileMultipart_InvalidDataException() + { + var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` + httpContext.Request.Headers["token"] = "fake_token_here"; //Set header + httpContext.Request.ContentType = "multipart/form-data"; + + await Assert.ThrowsExceptionAsync(async () => + await httpContext.Request.StreamFile(_appSettings, + new FakeSelectorStorage(new FakeIStorage())) + ); + } - [TestMethod] - public async Task FileStreamingHelperTest_FileStreamingHelper_StreamFile_imagejpeg() - { - var createAnImage = new CreateAnImage(); + [TestMethod] + public async Task FileStreamingHelperTest_FileStreamingHelper_StreamFile_imagejpeg() + { + var createAnImage = new CreateAnImage(); - FileStream requestBody = new FileStream(createAnImage.FullFilePath, FileMode.Open); - _appSettings.TempFolder = createAnImage.BasePath; + var requestBody = new FileStream(createAnImage.FullFilePath, FileMode.Open); + _appSettings.TempFolder = createAnImage.BasePath; - var streamSelector = - new FakeSelectorStorage(new StorageHostFullPathFilesystem(new FakeIWebLogger())); - var formValueProvider = await FileStreamingHelper.StreamFile("image/jpeg", - requestBody, _appSettings, streamSelector); + var streamSelector = + new FakeSelectorStorage(new StorageHostFullPathFilesystem(new FakeIWebLogger())); + var formValueProvider = await FileStreamingHelper.StreamFile("image/jpeg", + requestBody, _appSettings, streamSelector); - Assert.AreNotEqual(null, formValueProvider.ToString()); - await requestBody.DisposeAsync(); + Assert.AreNotEqual(null, formValueProvider.ToString()); + await requestBody.DisposeAsync(); - Assert.IsNotNull(formValueProvider.FirstOrDefault()); + Assert.IsNotNull(formValueProvider.FirstOrDefault()); - // Clean - streamSelector.Get(SelectorStorage.StorageServices.HostFilesystem) - .FileDelete(formValueProvider.FirstOrDefault()!); - } + // Clean + streamSelector.Get(SelectorStorage.StorageServices.HostFilesystem) + .FileDelete(formValueProvider.FirstOrDefault()!); + } - [TestMethod] - public void FileStreamingHelper_HeaderFileName_normalStringTest() - { - var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` - httpContext.Request.Headers["filename"] = "2018-07-20 20.14.52.jpg"; //Set header - var result = FileStreamingHelper.HeaderFileName(httpContext.Request); - Assert.AreEqual("2018-07-20-201452.jpg", result); - } - - [TestMethod] - public void FileStreamingHelper_HeaderFileName_Uppercase() - { - var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` - httpContext.Request.Headers["filename"] = "UPPERCASE.jpg"; //Set header - var result = FileStreamingHelper.HeaderFileName(httpContext.Request); - Assert.AreEqual("UPPERCASE.jpg", result); - } - - [TestMethod] - public void FileStreamingHelper_HeaderFileName_base64StringTest() - { - var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` - httpContext.Request.Headers["filename"] = - "MjAxOC0wNy0yMCAyMC4xNC41Mi5qcGc="; //Set header - var result = FileStreamingHelper.HeaderFileName(httpContext.Request); - Assert.AreEqual("2018-07-20-201452.jpg", result); - } - - [TestMethod] - public void FileStreamingHelper_HeaderFileName_base64StringTest_Uppercase() - { - var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` - httpContext.Request.Headers["filename"] = "VVBQRVJDQVNFLkpQRw=="; //Set header - var result = FileStreamingHelper.HeaderFileName(httpContext.Request); - Assert.AreEqual("UPPERCASE.JPG", result); - } - - /// - /// @see: https://github.com/dotnet/aspnetcore/blob/main/src/Http/WebUtilities/test/MultipartReaderTests.cs - /// - private const string TwoPartBody = - "--9051914041544843365972754266\r\n" + - "Content-Disposition: form-data; name=\"text\"\r\n" + - "\r\n" + - "text default\r\n" + - "--9051914041544843365972754266\r\n" + - "Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\"\r\n" + - "Content-Type: text/plain\r\n" + - "\r\n" + - "Content of a.txt.\r\n" + - "\r\n" + - "--9051914041544843365972754266--\r\n"; - - private static MemoryStream MakeStream(string text) - { - return new MemoryStream(Encoding.UTF8.GetBytes(text)); - } + [TestMethod] + public void FileStreamingHelper_HeaderFileName_normalStringTest() + { + var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` + httpContext.Request.Headers["filename"] = "2018-07-20 20.14.52.jpg"; //Set header + var result = FileStreamingHelper.HeaderFileName(httpContext.Request); + Assert.AreEqual("2018-07-20-201452.jpg", result); + } - private const string Boundary = "9051914041544843365972754266"; + [TestMethod] + public void FileStreamingHelper_HeaderFileName_Uppercase() + { + var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` + httpContext.Request.Headers["filename"] = "UPPERCASE.jpg"; //Set header + var result = FileStreamingHelper.HeaderFileName(httpContext.Request); + Assert.AreEqual("UPPERCASE.jpg", result); + } + [TestMethod] + public void FileStreamingHelper_HeaderFileName_base64StringTest() + { + var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` + httpContext.Request.Headers["filename"] = + "MjAxOC0wNy0yMCAyMC4xNC41Mi5qcGc="; //Set header + var result = FileStreamingHelper.HeaderFileName(httpContext.Request); + Assert.AreEqual("2018-07-20-201452.jpg", result); + } - [TestMethod] - public async Task FileStreamingHelper_MultipartRequestHelper() - { - var contentType = $"multipart/form-data; boundary=\"{Boundary}\""; + [TestMethod] + public void FileStreamingHelper_HeaderFileName_base64StringTest_Uppercase() + { + var httpContext = new DefaultHttpContext(); // or mock a `HttpContext` + httpContext.Request.Headers["filename"] = "VVBQRVJDQVNFLkpQRw=="; //Set header + var result = FileStreamingHelper.HeaderFileName(httpContext.Request); + Assert.AreEqual("UPPERCASE.JPG", result); + } + + private static MemoryStream MakeStream(string text) + { + return new MemoryStream(Encoding.UTF8.GetBytes(text)); + } + + + [TestMethod] + public async Task FileStreamingHelper_MultipartRequestHelper() + { + var contentType = $"multipart/form-data; boundary=\"{Boundary}\""; - // string contentType, Stream requestBody, AppSettings appSettings, - // ISelectorStorage selectorStorage, string headerFileName = null + // string contentType, Stream requestBody, AppSettings appSettings, + // ISelectorStorage selectorStorage, string headerFileName = null - var stream = MakeStream(TwoPartBody); - var storage = new FakeIStorage(); - storage.CreateDirectory(_appSettings.TempFolder); - var streamSelector = new FakeSelectorStorage(storage); + var stream = MakeStream(TwoPartBody); + var storage = new FakeIStorage(); + storage.CreateDirectory(_appSettings.TempFolder); + var streamSelector = new FakeSelectorStorage(storage); - await FileStreamingHelper.StreamFile(contentType, stream, _appSettings, streamSelector); + await FileStreamingHelper.StreamFile(contentType, stream, _appSettings, streamSelector); - var tempPath = storage.GetAllFilesInDirectoryRecursive(_appSettings.TempFolder) - .FirstOrDefault(); - Assert.IsTrue(tempPath?.EndsWith("a.txt")); - } + var tempPath = storage.GetAllFilesInDirectoryRecursive(_appSettings.TempFolder) + .FirstOrDefault(); + Assert.IsTrue(tempPath?.EndsWith("a.txt")); } } diff --git a/starsky/starskytest/Helpers/MultipartRequestHelperTest.cs b/starsky/starskytest/Helpers/MultipartRequestHelperTest.cs index af957c643d..65f203a5f7 100644 --- a/starsky/starskytest/Helpers/MultipartRequestHelperTest.cs +++ b/starsky/starskytest/Helpers/MultipartRequestHelperTest.cs @@ -1,77 +1,76 @@ using System.IO; using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; using Microsoft.VisualStudio.TestTools.UnitTesting; using starsky.foundation.http.Streaming; -namespace starskytest.Helpers +namespace starskytest.Helpers; + +[TestClass] +public sealed class MultipartRequestHelperTest { - [TestClass] - public sealed class MultipartRequestHelperTest + [TestMethod] + public void MultipartRequestHelperTest_MissingContentTypeBoundary_InvalidDataException() { - [TestMethod] - [ExpectedException(typeof(InvalidDataException))] - public void MultipartRequestHelperTest_Missingcontenttypeboundary() - { - var mediaType = new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("plain/text"); - MultipartRequestHelper.GetBoundary(mediaType, 50); - } + var mediaType = new MediaTypeHeaderValue("plain/text"); - [TestMethod] - [ExpectedException(typeof(InvalidDataException))] - public void MultipartRequestHelperTest_Multipartboundarylengthlimit() - { - var mediaType = new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("plain/text"); - mediaType.Boundary = new StringSegment("test"); - MultipartRequestHelper.GetBoundary(mediaType, 3); - } + Assert.ThrowsException(() => + MultipartRequestHelper.GetBoundary(mediaType, 50)); + } - [TestMethod] - public void MultipartRequestHelperTest_boundarySucces() - { - var mediaType = - new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("plain/text") - { - Boundary = new StringSegment("test") - }; - var boundary = MultipartRequestHelper.GetBoundary(mediaType, 10); - Assert.AreEqual("test", boundary); - } + [TestMethod] + public void MultipartRequestHelperTest_MultipartBoundaryLengthLimit_InvalidDataException() + { + var mediaType = + new MediaTypeHeaderValue("plain/text") { Boundary = new StringSegment("test") }; + Assert.ThrowsException(() => + MultipartRequestHelper.GetBoundary(mediaType, 3)); + } - [TestMethod] - public void MultipartRequestHelperTest_IsMultipartContentType() - { - Assert.IsTrue(MultipartRequestHelper.IsMultipartContentType("multipart/")); - } + [TestMethod] + public void MultipartRequestHelperTest_boundarySuccess() + { + var mediaType = + new MediaTypeHeaderValue("plain/text") { Boundary = new StringSegment("test") }; + var boundary = MultipartRequestHelper.GetBoundary(mediaType, 10); + Assert.AreEqual("test", boundary); + } - [TestMethod] - public void MultipartRequestHelperTest_HasFormDataContentDispositionFalse() - { - var formdata = - new Microsoft.Net.Http.Headers.ContentDispositionHeaderValue("form-data"); - formdata.FileName = "test"; - formdata.FileNameStar = "1"; - Assert.AreEqual("form-data; filename=test; filename*=UTF-8''1", formdata.ToString()); - Assert.IsFalse(MultipartRequestHelper.HasFormDataContentDisposition(formdata)); - } + [TestMethod] + public void MultipartRequestHelperTest_IsMultipartContentType() + { + Assert.IsTrue(MultipartRequestHelper.IsMultipartContentType("multipart/")); + } - [TestMethod] - public void MultipartRequestHelperTest_HasFormDataContentDispositionTrue() - { - var formdata = - new Microsoft.Net.Http.Headers.ContentDispositionHeaderValue("form-data"); - Assert.IsTrue(MultipartRequestHelper.HasFormDataContentDisposition(formdata)); - } + [TestMethod] + public void MultipartRequestHelperTest_HasFormDataContentDispositionFalse() + { + var formData = + new ContentDispositionHeaderValue("form-data") + { + FileName = "test", FileNameStar = "1" + }; + Assert.AreEqual("form-data; filename=test; filename*=UTF-8''1", formData.ToString()); + Assert.IsFalse(MultipartRequestHelper.HasFormDataContentDisposition(formData)); + } + [TestMethod] + public void MultipartRequestHelperTest_HasFormDataContentDispositionTrue() + { + var formData = + new ContentDispositionHeaderValue("form-data"); + Assert.IsTrue(MultipartRequestHelper.HasFormDataContentDisposition(formData)); + } - [TestMethod] - public void MultipartRequestHelperTest_HasFileContentDisposition() - { - var formdata = - new Microsoft.Net.Http.Headers.ContentDispositionHeaderValue("form-data"); - formdata.FileName = "test"; - formdata.FileNameStar = "1"; - Assert.AreEqual("form-data; filename=test; filename*=UTF-8''1", formdata.ToString()); - Assert.IsTrue(MultipartRequestHelper.HasFileContentDisposition(formdata)); - } + [TestMethod] + public void MultipartRequestHelperTest_HasFileContentDisposition() + { + var formData = + new ContentDispositionHeaderValue("form-data") + { + FileName = "test", FileNameStar = "1" + }; + Assert.AreEqual("form-data; filename=test; filename*=UTF-8''1", formData.ToString()); + Assert.IsTrue(MultipartRequestHelper.HasFileContentDisposition(formData)); } } diff --git a/starsky/starskytest/Helpers/SwaggerExportHelperTest.cs b/starsky/starskytest/Helpers/SwaggerExportHelperTest.cs index d76ceaf874..c00b25882b 100644 --- a/starsky/starskytest/Helpers/SwaggerExportHelperTest.cs +++ b/starsky/starskytest/Helpers/SwaggerExportHelperTest.cs @@ -14,115 +14,122 @@ using starskytest.FakeMocks; using Swashbuckle.AspNetCore.Swagger; -namespace starskytest.Helpers +namespace starskytest.Helpers; + +public sealed class FakeISwaggerProvider : ISwaggerProvider { - public sealed class FakeISwaggerProvider : ISwaggerProvider + public OpenApiDocument GetSwagger(string documentName, string? host = null, + string? basePath = null) { - public OpenApiDocument GetSwagger(string documentName, string? host = null, string? basePath = null) + return new OpenApiDocument { Components = new OpenApiComponents { Links = null } }; + } +} + +[TestClass] +public sealed class SwaggerExportHelperTest +{ + private readonly ServiceProvider _serviceProvider; + private readonly IServiceScopeFactory _serviceScopeFactory; + + public SwaggerExportHelperTest() + { + var services = new ServiceCollection(); + var dict = new Dictionary { - return new OpenApiDocument{ Components = new OpenApiComponents{ Links = null}}; - } + { "App:Verbose", "true" }, + { "App:AddSwagger", "true" }, + { "App:AddSwaggerExport", "true" }, + { "App:TempFolder", Path.DirectorySeparatorChar.ToString() }, + { "App:Name", "starsky_test_name" } + }; + // 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")); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory = _serviceProvider.GetRequiredService(); + } + + [TestMethod] + public void GenerateSwagger_NullInput() + { + Assert.AreEqual(string.Empty, SwaggerExportHelper.GenerateSwagger(null!, null!)); } - - [TestClass] - public sealed class SwaggerExportHelperTest + + [TestMethod] + public void Add03AppExport_disabled_AddSwagger() { - private readonly IServiceScopeFactory _serviceScopeFactory; - private readonly ServiceProvider _serviceProvider; + var appSettings = new AppSettings { AddSwagger = true, AddSwaggerExport = false }; + var swagger = new SwaggerExportHelper(null!, new FakeIWebLogger()); + var result = swagger.Add03AppExport(appSettings, new FakeSelectorStorage(), null!); - public SwaggerExportHelperTest() - { - var services = new ServiceCollection(); - var dict = new Dictionary - { - { "App:Verbose", "true" }, - { "App:AddSwagger", "true" }, - { "App:AddSwaggerExport", "true" }, - { "App:TempFolder", Path.DirectorySeparatorChar.ToString() }, - { "App:Name", "starsky_test_name" }, - }; - // 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")); - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - - _serviceProvider = services.BuildServiceProvider(); - _serviceScopeFactory = _serviceProvider.GetRequiredService(); - } - - [TestMethod] - public void GenerateSwagger_NullInput() - { - Assert.AreEqual(string.Empty, SwaggerExportHelper.GenerateSwagger(null!, null!)); - } - - [TestMethod] - public void Add03AppExport_disabled_AddSwagger() - { - var appSettings = new AppSettings {AddSwagger = true, AddSwaggerExport = false}; - var swagger = new SwaggerExportHelper(null!, new FakeIWebLogger()); - var result = swagger.Add03AppExport(appSettings, new FakeSelectorStorage(), null!); - - Assert.IsFalse(result); - } - - [TestMethod] - public void Add04SwaggerExportExitAfter_True() - { - var appSettings = new AppSettings {AddSwagger = true, AddSwaggerExport = true, AddSwaggerExportExitAfter = true}; - var swagger = new SwaggerExportHelper(null!, new FakeIWebLogger()); - var appLifeTime = new FakeIApplicationLifetime(); - var result = swagger.Add04SwaggerExportExitAfter(appSettings, appLifeTime); - - Assert.IsTrue(result); - } - - [TestMethod] - public void Add04SwaggerExportExitAfter_False() - { - var appSettings = new AppSettings {AddSwagger = true, AddSwaggerExport = false, AddSwaggerExportExitAfter = false}; - var swagger = new SwaggerExportHelper(null!, new FakeIWebLogger()); - var appLifeTime = new FakeIApplicationLifetime(); - var result = swagger.Add04SwaggerExportExitAfter(appSettings, appLifeTime); - - Assert.IsFalse(result); - } - - [TestMethod] - [ExpectedException(typeof(ArgumentException))] - public void Add03AppExport_null_ArgumentException() - { - var appSettings = new AppSettings {AddSwagger = true, AddSwaggerExport = true}; - var swagger = new SwaggerExportHelper(null!, new FakeIWebLogger()); - swagger.Add03AppExport(appSettings, new FakeSelectorStorage(), null!); - } - - [TestMethod] - public void Add03AppExport_disabled_AddSwaggerExport() + Assert.IsFalse(result); + } + + [TestMethod] + public void Add04SwaggerExportExitAfter_True() + { + var appSettings = new AppSettings { - var appSettings = new AppSettings {AddSwagger = false, AddSwaggerExport = true}; - var swagger = new SwaggerExportHelper(null!, new FakeIWebLogger()); - var result = swagger.Add03AppExport(appSettings, new FakeSelectorStorage(), null!); - - Assert.IsFalse(result); - } + AddSwagger = true, AddSwaggerExport = true, AddSwaggerExportExitAfter = true + }; + var swagger = new SwaggerExportHelper(null!, new FakeIWebLogger()); + var appLifeTime = new FakeIApplicationLifetime(); + var result = swagger.Add04SwaggerExportExitAfter(appSettings, appLifeTime); + Assert.IsTrue(result); + } - [TestMethod] - public void ExecuteAsync_ShouldWrite() + [TestMethod] + public void Add04SwaggerExportExitAfter_False() + { + var appSettings = new AppSettings { - new SwaggerExportHelper(_serviceScopeFactory).ExecuteAsync(); - var selectorStorage = _serviceProvider.GetRequiredService(); - var storage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem); + AddSwagger = true, AddSwaggerExport = false, AddSwaggerExportExitAfter = false + }; + var swagger = new SwaggerExportHelper(null!, new FakeIWebLogger()); + var appLifeTime = new FakeIApplicationLifetime(); + var result = swagger.Add04SwaggerExportExitAfter(appSettings, appLifeTime); + + Assert.IsFalse(result); + } + + [TestMethod] + public void Add03AppExport_null_ArgumentException() + { + var appSettings = new AppSettings { AddSwagger = true, AddSwaggerExport = true }; + var swagger = new SwaggerExportHelper(null!, new FakeIWebLogger()); + + Assert.ThrowsException(() => + swagger.Add03AppExport(appSettings, new FakeSelectorStorage(), null!)); + } + + [TestMethod] + public void Add03AppExport_disabled_AddSwaggerExport() + { + var appSettings = new AppSettings { AddSwagger = false, AddSwaggerExport = true }; + var swagger = new SwaggerExportHelper(null!, new FakeIWebLogger()); + var result = swagger.Add03AppExport(appSettings, new FakeSelectorStorage(), null!); + + Assert.IsFalse(result); + } + + + [TestMethod] + public void ExecuteAsync_ShouldWrite() + { + new SwaggerExportHelper(_serviceScopeFactory).ExecuteAsync(); + var selectorStorage = _serviceProvider.GetRequiredService(); + var storage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem); - Assert.IsTrue(storage.ExistFile( Path.DirectorySeparatorChar +"starsky_test_name.json")); - } + Assert.IsTrue(storage.ExistFile(Path.DirectorySeparatorChar + "starsky_test_name.json")); } } diff --git a/starsky/starskytest/root/ProgramTest.cs b/starsky/starskytest/root/ProgramTest.cs index a56f100dc3..48f1112150 100644 --- a/starsky/starskytest/root/ProgramTest.cs +++ b/starsky/starskytest/root/ProgramTest.cs @@ -36,15 +36,14 @@ public ProgramTest() Environment.GetEnvironmentVariable("app__ExiftoolSkipDownloadOnStartup"); _enablePackageTelemetry = Environment.GetEnvironmentVariable("app__EnablePackageTelemetry"); - + // see also: // starsky/starskytest/starskyGeoCli/starskyGeoCliTest.cs } - + [TestMethod] [Timeout(9000)] - [ExpectedException(typeof(HttpRequestException))] - public async Task Program_Main_NoAddress_UnixOnly() + public async Task Program_Main_NoAddress_UnixOnly_HttpRequestException() { if ( new AppSettings().IsWindows ) { @@ -52,33 +51,33 @@ public async Task Program_Main_NoAddress_UnixOnly() Assert.Inconclusive("This test if for Unix Only"); return; } - - Environment.SetEnvironmentVariable("ASPNETCORE_URLS","http://*:7514"); - Environment.SetEnvironmentVariable("app__useDiskWatcher","false"); - Environment.SetEnvironmentVariable("app__SyncOnStartup","false"); - Environment.SetEnvironmentVariable("app__thumbnailGenerationIntervalInMinutes","0"); - Environment.SetEnvironmentVariable("app__GeoFilesSkipDownloadOnStartup","true"); - Environment.SetEnvironmentVariable("app__ExiftoolSkipDownloadOnStartup","true"); - Environment.SetEnvironmentVariable("app__EnablePackageTelemetry","false"); - + + Environment.SetEnvironmentVariable("ASPNETCORE_URLS", "http://*:7514"); + Environment.SetEnvironmentVariable("app__useDiskWatcher", "false"); + Environment.SetEnvironmentVariable("app__SyncOnStartup", "false"); + Environment.SetEnvironmentVariable("app__thumbnailGenerationIntervalInMinutes", "0"); + Environment.SetEnvironmentVariable("app__GeoFilesSkipDownloadOnStartup", "true"); + Environment.SetEnvironmentVariable("app__ExiftoolSkipDownloadOnStartup", "true"); + Environment.SetEnvironmentVariable("app__EnablePackageTelemetry", "false"); + await Program.Main(["--do-not-start"]); using HttpClient client = new(); - await client.GetAsync("http://localhost:7514").TimeoutAfter(3000); - // and this address does not exists + await Assert.ThrowsExceptionAsync(async () => + await client.GetAsync("http://localhost:7514").TimeoutAfter(3000)); + // and this address does not exist } - + [TestMethod] [Timeout(9000)] public async Task Program_RunAsync_Null_False() { - var result = await Program.RunAsync(null!,false); + var result = await Program.RunAsync(null!, false); Assert.IsFalse(result); } - + [TestMethod] [Timeout(20000)] - [ExpectedException(typeof(TimeoutException))] public async Task Program_RunAsync_WebApplication_CreateBuilder_TimeoutException() { var number = new Random().Next(7500, 7900); @@ -86,40 +85,43 @@ public async Task Program_RunAsync_WebApplication_CreateBuilder_TimeoutException await Console.Out.WriteLineAsync(url); Environment.SetEnvironmentVariable("ASPNETCORE_URLS", url); - + var builder = WebApplication.CreateBuilder(Array.Empty()); var app = builder.Build(); - await Program.RunAsync(app).TimeoutAfter(1000); + await Assert.ThrowsExceptionAsync(async () => + await Program.RunAsync(app).TimeoutAfter(1000)); } [TestMethod] [Timeout(9000)] - [ExpectedException(typeof(FormatException))] public async Task Program_RunAsync_WebApplication_CreateBuilder_InvalidUrl() { - Environment.SetEnvironmentVariable("ASPNETCORE_URLS","test"); + Environment.SetEnvironmentVariable("ASPNETCORE_URLS", "test"); var builder = WebApplication.CreateBuilder(Array.Empty()); var app = builder.Build(); - - await Program.RunAsync(app).WaitAsync(TimeSpan.FromMilliseconds(1000)); + + await Assert.ThrowsExceptionAsync(async () => + await Program.RunAsync(app).WaitAsync(TimeSpan.FromMilliseconds(1000))); } - + [ClassCleanup] public static void CleanEnvsAfterwards() { // see also: // starsky/starskytest/starskyGeoCli/starskyGeoCliTest.cs - - Environment.SetEnvironmentVariable("PORT",_prePort); - Environment.SetEnvironmentVariable("ASPNETCORE_URLS",_preAspNetUrls); - Environment.SetEnvironmentVariable("app__useDiskWatcher",_diskWatcherSetting); - Environment.SetEnvironmentVariable("app__SyncOnStartup",_syncOnStartup); - Environment.SetEnvironmentVariable("app__thumbnailGenerationIntervalInMinutes",_thumbnailGenerationIntervalInMinutes); - Environment.SetEnvironmentVariable("app__GeoFilesSkipDownloadOnStartup",_geoFilesSkipDownloadOnStartup); - Environment.SetEnvironmentVariable("app__ExiftoolSkipDownloadOnStartup",_exiftoolSkipDownloadOnStartup); - Environment.SetEnvironmentVariable("app__EnablePackageTelemetry",_enablePackageTelemetry); + + Environment.SetEnvironmentVariable("PORT", _prePort); + Environment.SetEnvironmentVariable("ASPNETCORE_URLS", _preAspNetUrls); + Environment.SetEnvironmentVariable("app__useDiskWatcher", _diskWatcherSetting); + Environment.SetEnvironmentVariable("app__SyncOnStartup", _syncOnStartup); + Environment.SetEnvironmentVariable("app__thumbnailGenerationIntervalInMinutes", + _thumbnailGenerationIntervalInMinutes); + Environment.SetEnvironmentVariable("app__GeoFilesSkipDownloadOnStartup", + _geoFilesSkipDownloadOnStartup); + Environment.SetEnvironmentVariable("app__ExiftoolSkipDownloadOnStartup", + _exiftoolSkipDownloadOnStartup); + Environment.SetEnvironmentVariable("app__EnablePackageTelemetry", _enablePackageTelemetry); } - } diff --git a/starsky/starskytest/starsky.feature.geolookup/Services/GeoIndexGpxTest.cs b/starsky/starskytest/starsky.feature.geolookup/Services/GeoIndexGpxTest.cs index d8bc0625da..f8574dbefd 100644 --- a/starsky/starskytest/starsky.feature.geolookup/Services/GeoIndexGpxTest.cs +++ b/starsky/starskytest/starsky.feature.geolookup/Services/GeoIndexGpxTest.cs @@ -10,118 +10,116 @@ using starskytest.FakeCreateAn; using starskytest.FakeMocks; -namespace starskytest.starsky.feature.geolookup.Services +namespace starskytest.starsky.feature.geolookup.Services; + +[TestClass] +public sealed class GeoIndexGpxTest { - [TestClass] - public sealed class GeoIndexGpxTest + private readonly AppSettings _appSettings; + private readonly List _metaFilesDirectory; + + public GeoIndexGpxTest() { - private readonly AppSettings _appSettings; - private readonly List _metaFilesDirectory; + var createAnGpx = new CreateAnGpx(); + _appSettings = new AppSettings + { + StorageFolder = createAnGpx.BasePath, CameraTimeZone = "Europe/Minsk" + }; - public GeoIndexGpxTest() + _metaFilesDirectory = new List { - var createAnGpx = new CreateAnGpx(); - _appSettings = new AppSettings + new() { - StorageFolder = createAnGpx.BasePath, - CameraTimeZone = "Europe/Minsk" - }; + FileName = createAnGpx.FileName, + ImageFormat = ExtensionRolesHelper.ImageFormat.gpx + } + }; + } - _metaFilesDirectory = new List - { - new FileIndexItem - { - FileName = createAnGpx.FileName, - ImageFormat = ExtensionRolesHelper.ImageFormat.gpx - } - }; - } + [TestMethod] + public void GeoIndexGpx_ConvertTimeZone_EuropeAmsterdam() + { + var fakeIStorage = new FakeIStorage(); + var result = new GeoIndexGpx(new AppSettings { CameraTimeZone = "Europe/Amsterdam" }, + fakeIStorage, new FakeIWebLogger()).ConvertTimeZone(new DateTime(2020, 04, 15, + 17, 0, 0, 0, DateTimeKind.Unspecified)); + var expected = new DateTime(2020, 04, 15, 15, 0, 0, 0, + DateTimeKind.Local); - [TestMethod] - public void GeoIndexGpx_ConvertTimeZone_EuropeAmsterdam() - { - var fakeIStorage = new FakeIStorage(); - var result = new GeoIndexGpx(new AppSettings{CameraTimeZone = "Europe/Amsterdam"}, - fakeIStorage, new FakeIWebLogger()).ConvertTimeZone(new DateTime(2020, 04, 15, - 17, 0, 0, 0, kind: DateTimeKind.Unspecified)); - var expected = new DateTime(2020, 04, 15, 15, 0, 0, 0, - kind: DateTimeKind.Local); - - Assert.AreEqual(expected, result); - } - - [TestMethod] - public void GeoIndexGpx_ConvertTimeZone_EuropeLondon() - { - var fakeIStorage = new FakeIStorage(); - var result = new GeoIndexGpx(new AppSettings{CameraTimeZone = "Europe/London"}, - fakeIStorage, new FakeIWebLogger()).ConvertTimeZone(new DateTime(2020, 01, 15, - 17, 0, 0, 0, kind: DateTimeKind.Unspecified)); - var expected = new DateTime(2020, 01, 15, 17, 0, 0, 0, - kind: DateTimeKind.Local); - - Assert.AreEqual(expected, result); - } - - [TestMethod] - public void GeoIndexGpx_ConvertTimeZone_KindUtc() - { - var fakeIStorage = new FakeIStorage(); - var inputDateTime = new DateTime(2020, 01, 15, - 17, 0, 0, 0, kind: DateTimeKind.Unspecified); - inputDateTime = DateTime.SpecifyKind(inputDateTime, DateTimeKind.Utc); - var result =new GeoIndexGpx(new AppSettings{CameraTimeZone = "Europe/London"}, - fakeIStorage, new FakeIWebLogger()).ConvertTimeZone(inputDateTime); - var expected = new DateTime(2020, 01, 15, 17, 0, 0, 0, - kind: DateTimeKind.Local); - Assert.AreEqual(expected, result); - } + Assert.AreEqual(expected, result); + } - [TestMethod] - [ExpectedException(typeof(ArgumentException))] - public void GeoIndexGpx_ConvertTimeZone_typeLocal_Expect_ArgumentException() - { - var fakeIStorage = new FakeIStorage(); - var inputDateTime = new DateTime(2020, 01, 15, - 17, 0, 0, 0, kind: DateTimeKind.Local); - inputDateTime = DateTime.SpecifyKind(inputDateTime, DateTimeKind.Local); - new GeoIndexGpx(new AppSettings{CameraTimeZone = "Europe/London"}, - fakeIStorage, new FakeIWebLogger()).ConvertTimeZone(inputDateTime); - } + [TestMethod] + public void GeoIndexGpx_ConvertTimeZone_EuropeLondon() + { + var fakeIStorage = new FakeIStorage(); + var result = new GeoIndexGpx(new AppSettings { CameraTimeZone = "Europe/London" }, + fakeIStorage, new FakeIWebLogger()).ConvertTimeZone(new DateTime(2020, 01, 15, + 17, 0, 0, 0, DateTimeKind.Unspecified)); + var expected = new DateTime(2020, 01, 15, 17, 0, 0, 0, + DateTimeKind.Local); - [TestMethod] - public async Task GeoIndexGpx_LoopFolderLookupTest() + Assert.AreEqual(expected, result); + } + + [TestMethod] + public void GeoIndexGpx_ConvertTimeZone_KindUtc() + { + var fakeIStorage = new FakeIStorage(); + var inputDateTime = new DateTime(2020, 01, 15, + 17, 0, 0, 0, DateTimeKind.Unspecified); + inputDateTime = DateTime.SpecifyKind(inputDateTime, DateTimeKind.Utc); + var result = new GeoIndexGpx(new AppSettings { CameraTimeZone = "Europe/London" }, + fakeIStorage, new FakeIWebLogger()).ConvertTimeZone(inputDateTime); + var expected = new DateTime(2020, 01, 15, 17, 0, 0, 0, + DateTimeKind.Local); + Assert.AreEqual(expected, result); + } + + [TestMethod] + public void GeoIndexGpx_ConvertTimeZone_typeLocal_Expect_ArgumentException() + { + var fakeIStorage = new FakeIStorage(); + var inputDateTime = new DateTime(2020, 01, 15, + 17, 0, 0, 0, DateTimeKind.Local); + inputDateTime = DateTime.SpecifyKind(inputDateTime, DateTimeKind.Local); + var sut = new GeoIndexGpx(new AppSettings { CameraTimeZone = "Europe/London" }, + fakeIStorage, new FakeIWebLogger()); + + Assert.ThrowsException(() => + sut.ConvertTimeZone(inputDateTime)); + } + + [TestMethod] + public async Task GeoIndexGpx_LoopFolderLookupTest() + { + var exampleFiles = new List(); + exampleFiles.AddRange(new List { - var exampleFiles = new List(); - exampleFiles.AddRange(new List + _metaFilesDirectory.FirstOrDefault()!, + new() { - _metaFilesDirectory.FirstOrDefault()!, - new FileIndexItem - { - FileName = "01.jpg", - DateTime = new DateTime(2018,09,05, - 20,31,54, kind: DateTimeKind.Unspecified) // 2018-09-05T17:31:53Z UTC > In europe/Minsk - }, - new FileIndexItem - { - FileName = "NotInRange.jpg", - DateTime = new DateTime(2018,09,06, - 00,00,00, kind: DateTimeKind.Unspecified) - } - - }); - - var fakeIStorage = new FakeIStorage(new List{"/"}, - new List{_metaFilesDirectory[0].FilePath!}, new List{CreateAnGpx.Bytes.ToArray()} ); - - var returnFileIndexItems = await new GeoIndexGpx(_appSettings, - fakeIStorage, new FakeIWebLogger()).LoopFolderAsync(exampleFiles); - - Assert.AreEqual(null,returnFileIndexItems.Find(p => p.FileName == "NotInRange.jpg")); - Assert.AreEqual("01.jpg",returnFileIndexItems.Find(p => p.FileName == "01.jpg")?.FileName); + FileName = "01.jpg", + DateTime = new DateTime(2018, 09, 05, + 20, 31, 54, + DateTimeKind.Unspecified) // 2018-09-05T17:31:53Z UTC > In europe/Minsk + }, + new() + { + FileName = "NotInRange.jpg", + DateTime = new DateTime(2018, 09, 06, + 00, 00, 00, DateTimeKind.Unspecified) + } + }); + var fakeIStorage = new FakeIStorage(new List { "/" }, + new List { _metaFilesDirectory[0].FilePath! }, + new List { CreateAnGpx.Bytes.ToArray() }); - } + var returnFileIndexItems = await new GeoIndexGpx(_appSettings, + fakeIStorage, new FakeIWebLogger()).LoopFolderAsync(exampleFiles); + Assert.AreEqual(null, returnFileIndexItems.Find(p => p.FileName == "NotInRange.jpg")); + Assert.AreEqual("01.jpg", returnFileIndexItems.Find(p => p.FileName == "01.jpg")?.FileName); } } diff --git a/starsky/starskytest/starsky.feature.health/HealthCheck/DiskStorageHealthCheckTest.cs b/starsky/starskytest/starsky.feature.health/HealthCheck/DiskStorageHealthCheckTest.cs index 9581810b90..d43d47e138 100644 --- a/starsky/starskytest/starsky.feature.health/HealthCheck/DiskStorageHealthCheckTest.cs +++ b/starsky/starskytest/starsky.feature.health/HealthCheck/DiskStorageHealthCheckTest.cs @@ -5,59 +5,68 @@ using starsky.feature.health.HealthCheck; using starsky.foundation.platform.Models; -namespace starskytest.starsky.feature.health.HealthCheck +namespace starskytest.starsky.feature.health.HealthCheck; + +[TestClass] +public sealed class DiskStorageHealthCheckTest { - [TestClass] - public sealed class DiskStorageHealthCheckTest + [TestMethod] + public async Task RunSuccessful() { - [TestMethod] - public async Task RunSuccessful() - { - var appSettings = new AppSettings(); - var diskOptions = new DiskStorageOptions(); - DiskOptionsPercentageSetup.Setup(appSettings.TempFolder,diskOptions,0.000001f); - - var healthCheck = new HealthCheckContext(); - var result = await new DiskStorageHealthCheck(diskOptions).CheckHealthAsync(healthCheck); - Assert.AreEqual(HealthStatus.Healthy,result.Status); - } - - [TestMethod] - public async Task RunFailNotEnoughSpace() - { - var appSettings = new AppSettings(); - var diskOptions = new DiskStorageOptions(); - - DiskOptionsPercentageSetup.Setup(appSettings.TempFolder,diskOptions,1.01f); - - var healthCheck = new HealthCheckContext {Registration = new HealthCheckRegistration("te",new DiskStorageHealthCheck(diskOptions),null,null )}; - var result = await new DiskStorageHealthCheck(diskOptions).CheckHealthAsync(healthCheck); - Assert.AreEqual(HealthStatus.Unhealthy,result.Status); - Assert.IsTrue(result.Description?.Contains("Minimum configured megabytes for disk")); - } - - [TestMethod] - public async Task RunFailNonExistDisk() + var appSettings = new AppSettings(); + var diskOptions = new DiskStorageOptions(); + DiskOptionsPercentageSetup.Setup(appSettings.TempFolder, diskOptions, 0.000001f); + + var healthCheck = new HealthCheckContext(); + var result = await new DiskStorageHealthCheck(diskOptions).CheckHealthAsync(healthCheck); + Assert.AreEqual(HealthStatus.Healthy, result.Status); + } + + [TestMethod] + public async Task RunFailNotEnoughSpace() + { + var appSettings = new AppSettings(); + var diskOptions = new DiskStorageOptions(); + + DiskOptionsPercentageSetup.Setup(appSettings.TempFolder, diskOptions, 1.01f); + + var healthCheck = new HealthCheckContext { - var diskOptions = new DiskStorageOptions(); - diskOptions.AddDrive("NonExistDisk:", 10); - - var healthCheck = new HealthCheckContext {Registration = new HealthCheckRegistration("te",new DiskStorageHealthCheck(diskOptions),null,null )}; - var result = await new DiskStorageHealthCheck(diskOptions).CheckHealthAsync(healthCheck); - Assert.AreEqual(HealthStatus.Unhealthy,result.Status); - Assert.IsTrue(result.Description?.Contains("is not present on system")); - } - - [TestMethod] - [ExpectedException(typeof(NullReferenceException))] - public async Task RunFail_NullReferenceException() + Registration = new HealthCheckRegistration("te", + new DiskStorageHealthCheck(diskOptions), null, null) + }; + var result = await new DiskStorageHealthCheck(diskOptions).CheckHealthAsync(healthCheck); + Assert.AreEqual(HealthStatus.Unhealthy, result.Status); + Assert.IsTrue(result.Description?.Contains("Minimum configured megabytes for disk")); + } + + [TestMethod] + public async Task RunFailNonExistDisk() + { + var diskOptions = new DiskStorageOptions(); + diskOptions.AddDrive("NonExistDisk:", 10); + + var healthCheck = new HealthCheckContext { - var appSettings = new AppSettings(); - var diskOptions = new DiskStorageOptions(); - DiskOptionsPercentageSetup.Setup(appSettings.TempFolder,diskOptions,99.9f); + Registration = new HealthCheckRegistration("te", + new DiskStorageHealthCheck(diskOptions), null, null) + }; + var result = await new DiskStorageHealthCheck(diskOptions).CheckHealthAsync(healthCheck); + Assert.AreEqual(HealthStatus.Unhealthy, result.Status); + Assert.IsTrue(result.Description?.Contains("is not present on system")); + } + + [TestMethod] + public async Task RunFail_NullReferenceException() + { + var appSettings = new AppSettings(); + var diskOptions = new DiskStorageOptions(); + DiskOptionsPercentageSetup.Setup(appSettings.TempFolder, diskOptions, 99.9f); + + var healthCheck = new HealthCheckContext(); + var sut = new DiskStorageHealthCheck(diskOptions); - var healthCheck = new HealthCheckContext(); - await new DiskStorageHealthCheck(diskOptions).CheckHealthAsync(healthCheck); - } + await Assert.ThrowsExceptionAsync(async () => + await sut.CheckHealthAsync(healthCheck)); } } diff --git a/starsky/starskytest/starsky.feature.health/HealthCheck/PathExistHealthCheckTest.cs b/starsky/starskytest/starsky.feature.health/HealthCheck/PathExistHealthCheckTest.cs index f12722d251..f2157580d1 100644 --- a/starsky/starskytest/starsky.feature.health/HealthCheck/PathExistHealthCheckTest.cs +++ b/starsky/starskytest/starsky.feature.health/HealthCheck/PathExistHealthCheckTest.cs @@ -6,73 +6,64 @@ using starskytest.FakeCreateAn; using starskytest.FakeMocks; -namespace starskytest.starsky.feature.health.HealthCheck +namespace starskytest.starsky.feature.health.HealthCheck; + +[TestClass] +public sealed class PathExistHealthCheckTest { - [TestClass] - public sealed class PathExistHealthCheckTest + [TestMethod] + public async Task RunSuccessful() { - [TestMethod] - public async Task RunSuccessful() - { - var pathExistOptions = new PathExistOptions(); - pathExistOptions.AddPath(new CreateAnImage().BasePath); - - var healthCheck = new HealthCheckContext - { - Registration = new HealthCheckRegistration("te", - new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()), null, null) - }; - var result = - await new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()) - .CheckHealthAsync(healthCheck); - Assert.AreEqual(HealthStatus.Healthy, result.Status); - } + var pathExistOptions = new PathExistOptions(); + pathExistOptions.AddPath(new CreateAnImage().BasePath); - [TestMethod] - public async Task RunFailNonExistPath() + var healthCheck = new HealthCheckContext { - var pathExistOptions = new PathExistOptions(); - pathExistOptions.AddPath("000000000000----non-exist"); + Registration = new HealthCheckRegistration("te", + new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()), null, null) + }; + var result = + await new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()) + .CheckHealthAsync(healthCheck); + Assert.AreEqual(HealthStatus.Healthy, result.Status); + } - var healthCheck = new HealthCheckContext - { - Registration = new HealthCheckRegistration("te", - new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()), null, null) - }; - var result = - await new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()) - .CheckHealthAsync(healthCheck); - Assert.AreEqual(HealthStatus.Unhealthy, result.Status); - } + [TestMethod] + public async Task RunFailNonExistPath() + { + var pathExistOptions = new PathExistOptions(); + pathExistOptions.AddPath("000000000000----non-exist"); - [TestMethod] - public async Task RunFail_No_Input() + var healthCheck = new HealthCheckContext { - var pathExistOptions = new PathExistOptions(); - var healthCheck = new HealthCheckContext - { - Registration = new HealthCheckRegistration("te", - new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()), null, null) - }; - var result = - await new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()) - .CheckHealthAsync(healthCheck); - Assert.AreEqual(HealthStatus.Unhealthy, result.Status); - } + Registration = new HealthCheckRegistration("te", + new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()), null, null) + }; + var result = + await new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()) + .CheckHealthAsync(healthCheck); + Assert.AreEqual(HealthStatus.Unhealthy, result.Status); + } - [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task RunFail_Null_Input() + [TestMethod] + public async Task RunFail_No_Input() + { + var pathExistOptions = new PathExistOptions(); + var healthCheck = new HealthCheckContext { - var healthCheck = new HealthCheckContext - { - Registration = new HealthCheckRegistration("te", - new PathExistHealthCheck(null!, new FakeIWebLogger()), - null, null) - }; - await new PathExistHealthCheck(null!, new FakeIWebLogger()).CheckHealthAsync( - healthCheck); - // expect ArgumentNullException: - } + Registration = new HealthCheckRegistration("te", + new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()), null, null) + }; + var result = + await new PathExistHealthCheck(pathExistOptions, new FakeIWebLogger()) + .CheckHealthAsync(healthCheck); + Assert.AreEqual(HealthStatus.Unhealthy, result.Status); + } + + [TestMethod] + public void RunFail_Null_Input() + { + Assert.ThrowsException(() => + new PathExistHealthCheck(null!, new FakeIWebLogger())); } } diff --git a/starsky/starskytest/starsky.feature.health/HealthCheck/SetupHealthCheckTest.cs b/starsky/starskytest/starsky.feature.health/HealthCheck/SetupHealthCheckTest.cs index cb67506a49..b22f2d4bcd 100644 --- a/starsky/starskytest/starsky.feature.health/HealthCheck/SetupHealthCheckTest.cs +++ b/starsky/starskytest/starsky.feature.health/HealthCheck/SetupHealthCheckTest.cs @@ -12,138 +12,129 @@ using starsky.foundation.platform.Services; using starskytest.FakeMocks; -namespace starskytest.starsky.feature.health.HealthCheck +namespace starskytest.starsky.feature.health.HealthCheck; + +[TestClass] +public sealed class SetupHealthCheckTest { - [TestClass] - public sealed class SetupHealthCheckTest + [TestMethod] + public void HealthCheckService_LoggerMissing_InvalidOperationException() + { + var services = new ServiceCollection(); + // logger is not defined here (as designed) + + Assert.ThrowsException(() => + new SetupHealthCheck(new AppSettings(), services).BuilderHealth()); + } + + [TestMethod] + public void HealthCheckServiceMysql_LoggerMissing_InvalidOperationException() + { + var services = new ServiceCollection(); + // logger is not defined here (as designed) + + Assert.ThrowsException(() => new SetupHealthCheck( + new AppSettings { DatabaseType = AppSettings.DatabaseTypeList.Mysql }, services) + .BuilderHealth()); + } + + [TestMethod] + public async Task HealthCheckServiceMysql_ReturnStatus() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddLogging(); + services.AddHealthChecks(); + var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase() + .BuildServiceProvider(); + services + .AddDbContext(b => + b.UseInMemoryDatabase("HealthCheckServiceMysql_ReturnStatus") + .UseInternalServiceProvider(efServiceProvider)); + + new SetupHealthCheck( + new AppSettings { DatabaseType = AppSettings.DatabaseTypeList.Mysql }, services) + .BuilderHealth(); + + var serviceProvider = services.BuildServiceProvider(); + var service = serviceProvider.GetRequiredService(); + + var result = await service.CheckHealthAsync(); + + Assert.AreEqual(1, result.Entries.Count(p => p.Key == "mysql")); + Assert.AreEqual(HealthStatus.Unhealthy, + result.Entries.FirstOrDefault(p => p.Key == "mysql").Value.Status); + Assert.AreEqual(0, result.Entries.Count(p => p.Key == "sqlite")); + } + + [TestMethod] + public async Task HealthCheckServiceInMemoryDatabase_ReturnStatus() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddLogging(); + services.AddHealthChecks(); + var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase() + .BuildServiceProvider(); + services + .AddDbContext(b => + b.UseInMemoryDatabase("HealthCheckServiceInMemoryDatabase_ReturnStatus") + .UseInternalServiceProvider(efServiceProvider)); + + new SetupHealthCheck( + new AppSettings { DatabaseType = AppSettings.DatabaseTypeList.InMemoryDatabase }, + services) + .BuilderHealth(); + + var serviceProvider = services.BuildServiceProvider(); + var service = serviceProvider.GetRequiredService(); + + var result = await service.CheckHealthAsync(); + + Assert.AreEqual(0, result.Entries.Count(p => p.Key == "mysql")); + Assert.AreEqual(0, result.Entries.Count(p => p.Key == "sqlite")); + } + + [TestMethod] + public async Task HealthCheckServiceSqlite_ReturnStatus() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddLogging(); + services.AddHealthChecks(); + var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase() + .BuildServiceProvider(); + services + .AddDbContext(b => + b.UseInMemoryDatabase("HealthCheckServiceSqlite_ReturnStatus") + .UseInternalServiceProvider(efServiceProvider)); + + new SetupHealthCheck( + new AppSettings { DatabaseType = AppSettings.DatabaseTypeList.Sqlite }, + services) + .BuilderHealth(); + + var serviceProvider = services.BuildServiceProvider(); + var service = serviceProvider.GetRequiredService(); + + var result = await service.CheckHealthAsync(); + + Assert.AreEqual(1, result.Entries.Count(p => p.Key == "sqlite")); + Assert.AreEqual(HealthStatus.Unhealthy, + result.Entries.FirstOrDefault(p => p.Key == "sqlite").Value.Status); + Assert.AreEqual(0, result.Entries.Count(p => p.Key == "mysql")); + } + + [TestMethod] + public void HealthCheckService_AggregateException() { - [TestMethod] - [ExpectedException(typeof(InvalidOperationException))] - public void HealthCheckService_LoggerMissing() - { - var services = new ServiceCollection(); - // logger is not defined here (as designed) - new SetupHealthCheck(new AppSettings(), services).BuilderHealth(); - - var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetRequiredService(); - // logger not defined - } - - [TestMethod] - [ExpectedException(typeof(InvalidOperationException))] - public void HealthCheckServiceMysql_LoggerMissing() - { - var services = new ServiceCollection(); - // logger is not defined here (as designed) - new SetupHealthCheck( - new AppSettings { DatabaseType = AppSettings.DatabaseTypeList.Mysql }, services) - .BuilderHealth(); - - var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetRequiredService(); - // logger not defined - } - - [TestMethod] - public async Task HealthCheckServiceMysql_ReturnStatus() - { - var services = new ServiceCollection(); - services.AddSingleton(); - services.AddLogging(); - services.AddHealthChecks(); - var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase() - .BuildServiceProvider(); - services - .AddDbContext(b => - b.UseInMemoryDatabase("HealthCheckServiceMysql_ReturnStatus") - .UseInternalServiceProvider(efServiceProvider)); - - new SetupHealthCheck( - new AppSettings { DatabaseType = AppSettings.DatabaseTypeList.Mysql }, services) - .BuilderHealth(); - - var serviceProvider = services.BuildServiceProvider(); - var service = serviceProvider.GetRequiredService(); - - var result = await service.CheckHealthAsync(); - - Assert.AreEqual(1, result.Entries.Count(p => p.Key == "mysql")); - Assert.AreEqual(HealthStatus.Unhealthy, - result.Entries.FirstOrDefault(p => p.Key == "mysql").Value.Status); - Assert.AreEqual(0, result.Entries.Count(p => p.Key == "sqlite")); - } - - [TestMethod] - public async Task HealthCheckServiceInMemoryDatabase_ReturnStatus() - { - var services = new ServiceCollection(); - services.AddSingleton(); - services.AddLogging(); - services.AddHealthChecks(); - var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase() - .BuildServiceProvider(); - services - .AddDbContext(b => - b.UseInMemoryDatabase("HealthCheckServiceInMemoryDatabase_ReturnStatus") - .UseInternalServiceProvider(efServiceProvider)); - - new SetupHealthCheck( - new AppSettings - { - DatabaseType = AppSettings.DatabaseTypeList.InMemoryDatabase - }, services) - .BuilderHealth(); - - var serviceProvider = services.BuildServiceProvider(); - var service = serviceProvider.GetRequiredService(); - - var result = await service.CheckHealthAsync(); - - Assert.AreEqual(0, result.Entries.Count(p => p.Key == "mysql")); - Assert.AreEqual(0, result.Entries.Count(p => p.Key == "sqlite")); - } - - [TestMethod] - public async Task HealthCheckServiceSqlite_ReturnStatus() - { - var services = new ServiceCollection(); - services.AddSingleton(); - services.AddLogging(); - services.AddHealthChecks(); - var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase() - .BuildServiceProvider(); - services - .AddDbContext(b => - b.UseInMemoryDatabase("HealthCheckServiceSqlite_ReturnStatus") - .UseInternalServiceProvider(efServiceProvider)); - - new SetupHealthCheck( - new AppSettings { DatabaseType = AppSettings.DatabaseTypeList.Sqlite }, - services) - .BuilderHealth(); - - var serviceProvider = services.BuildServiceProvider(); - var service = serviceProvider.GetRequiredService(); - - var result = await service.CheckHealthAsync(); - - Assert.AreEqual(1, result.Entries.Count(p => p.Key == "sqlite")); - Assert.AreEqual(HealthStatus.Unhealthy, - result.Entries.FirstOrDefault(p => p.Key == "sqlite").Value.Status); - Assert.AreEqual(0, result.Entries.Count(p => p.Key == "mysql")); - } - - [TestMethod] - [ExpectedException(typeof(AggregateException))] - public void HealthCheckService_AggregateException() - { - var appSettings = new AppSettings { Verbose = true }; - AppSettingsReflection.Modify(appSettings, "get_DatabaseType", 8); - var services = new ServiceCollection(); - services.AddSingleton(); - new SetupHealthCheck(appSettings, services).BuilderHealth(); - // expect exception database type is not found - } + var appSettings = new AppSettings { Verbose = true }; + AppSettingsReflection.Modify(appSettings, "get_DatabaseType", 8); + var services = new ServiceCollection(); + services.AddSingleton(); + + // expect exception database type is not found + Assert.ThrowsException(() => + new SetupHealthCheck(appSettings, services).BuilderHealth()); } } diff --git a/starsky/starskytest/starsky.foundation.database/DataProtection/SqlXmlRepositoryTest.cs b/starsky/starskytest/starsky.foundation.database/DataProtection/SqlXmlRepositoryTest.cs index 521d39a33f..b5109ed02c 100644 --- a/starsky/starskytest/starsky.foundation.database/DataProtection/SqlXmlRepositoryTest.cs +++ b/starsky/starskytest/starsky.foundation.database/DataProtection/SqlXmlRepositoryTest.cs @@ -188,7 +188,6 @@ public void SqlXmlRepositoryTest_StoreElement_Exception_RetryLimitExceededExcept } [TestMethod] - [ExpectedException(typeof(NullReferenceException))] public void SqlXmlRepositoryTest_StoreElement_Exception_Other() { var options = new DbContextOptionsBuilder() @@ -199,7 +198,8 @@ public void SqlXmlRepositoryTest_StoreElement_Exception_Other() var repo = new SqlXmlRepository( new StoreElementException2OtherException(options), null!, logger); - repo.StoreElement(new XElement("x1", "x1"), "hi"); + Assert.ThrowsException(() => + repo.StoreElement(new XElement("x1", "x1"), "hi")); } private class AppDbMySqlException : ApplicationDbContext diff --git a/starsky/starskytest/starsky.foundation.injection/ServiceCollectionExtensionsTest.cs b/starsky/starskytest/starsky.foundation.injection/ServiceCollectionExtensionsTest.cs index 5c08a4f73d..6f01c1cbb8 100644 --- a/starsky/starskytest/starsky.foundation.injection/ServiceCollectionExtensionsTest.cs +++ b/starsky/starskytest/starsky.foundation.injection/ServiceCollectionExtensionsTest.cs @@ -83,16 +83,16 @@ public void Add_LifeTime_Transient() } [TestMethod] - [ExpectedException(typeof(ArgumentOutOfRangeException))] public void Add_LifeTime_InvalidType() { var overwriteInjectionLifetime = new OverwriteInjectionLifetime(); var propertyObject = overwriteInjectionLifetime.GetType().GetProperty("Type"); propertyObject?.SetValue(overwriteInjectionLifetime, 44, null); // <-- this could not happen - var serviceCollection = new ServiceCollection() as IServiceCollection; - serviceCollection.Add(overwriteInjectionLifetime.Type, typeof(TestInjectionClass)); - // expect exception + IServiceCollection serviceCollection = new ServiceCollection(); + + Assert.ThrowsException(() => + serviceCollection.Add(overwriteInjectionLifetime.Type, typeof(TestInjectionClass))); } @@ -156,8 +156,9 @@ public void Add_implementationType_LifeTime_InvalidType() propertyObject?.SetValue(overwriteInjectionLifetime, 44, null); // <-- this could not happen IServiceCollection serviceCollection = new ServiceCollection(); - - Assert.ThrowsException(() => serviceCollection.Add(typeof(ITestInjectionClass), + + Assert.ThrowsException(() => serviceCollection.Add( + typeof(ITestInjectionClass), typeof(TestInjectionClass), overwriteInjectionLifetime.Type)); }