From 6cd45ffdc36fdc00cabf3591879b1f92e7463754 Mon Sep 17 00:00:00 2001 From: Jordi Date: Fri, 16 Aug 2024 00:28:11 +0200 Subject: [PATCH] Adding Image upload/download tests DynamicsValue/fake-xrm-easy#157 --- .../FileStorage/Db/InMemoryFileDb.cs | 21 +- .../Db/{ => Files}/BlockedAttachmentsTests.cs | 2 +- .../Db/{ => Files}/BlockedMimeTypeTests.cs | 2 +- .../Db/{ => Files}/DeleteFileTests.cs | 2 +- .../Db/{ => Files}/FileUploadSessionTests.cs | 3 +- .../InMemoryFileDbDownloaderTests.cs | 2 +- .../InMemoryFileDbUploaderTests.cs | 2 +- .../Db/{ => Files}/UpdateFileTests.cs | 2 +- .../FileStorage/Db/Images/DeleteFileTests.cs | 71 +++++ .../Db/Images/FileUploadSessionTests.cs | 95 +++++++ .../Images/InMemoryFileDbDownloaderTests.cs | 174 ++++++++++++ .../Db/Images/InMemoryFileDbUploaderTests.cs | 252 ++++++++++++++++++ .../FileStorage/Db/Images/UpdateFileTests.cs | 77 ++++++ .../Db/InMemoryFileDbInternalTests.cs | 7 - 14 files changed, 692 insertions(+), 20 deletions(-) rename tests/FakeXrmEasy.Core.Tests/FileStorage/Db/{ => Files}/BlockedAttachmentsTests.cs (98%) rename tests/FakeXrmEasy.Core.Tests/FileStorage/Db/{ => Files}/BlockedMimeTypeTests.cs (99%) rename tests/FakeXrmEasy.Core.Tests/FileStorage/Db/{ => Files}/DeleteFileTests.cs (97%) rename tests/FakeXrmEasy.Core.Tests/FileStorage/Db/{ => Files}/FileUploadSessionTests.cs (97%) rename tests/FakeXrmEasy.Core.Tests/FileStorage/Db/{ => Files}/InMemoryFileDbDownloaderTests.cs (99%) rename tests/FakeXrmEasy.Core.Tests/FileStorage/Db/{ => Files}/InMemoryFileDbUploaderTests.cs (99%) rename tests/FakeXrmEasy.Core.Tests/FileStorage/Db/{ => Files}/UpdateFileTests.cs (97%) create mode 100644 tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/DeleteFileTests.cs create mode 100644 tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/FileUploadSessionTests.cs create mode 100644 tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/InMemoryFileDbDownloaderTests.cs create mode 100644 tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/InMemoryFileDbUploaderTests.cs create mode 100644 tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/UpdateFileTests.cs delete mode 100644 tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbInternalTests.cs diff --git a/src/FakeXrmEasy.Core/FileStorage/Db/InMemoryFileDb.cs b/src/FakeXrmEasy.Core/FileStorage/Db/InMemoryFileDb.cs index c5beb62c..7cad50ba 100644 --- a/src/FakeXrmEasy.Core/FileStorage/Db/InMemoryFileDb.cs +++ b/src/FakeXrmEasy.Core/FileStorage/Db/InMemoryFileDb.cs @@ -298,13 +298,24 @@ private void ValidateMaxFileSize(FileUploadSession fileUploadSession, FileAttach return; } - var fileAttributeMetadata = attributeMetadata as FileAttributeMetadata; - - if ((decimal) fileAttachment.Content.Length / 1024 > fileAttributeMetadata.MaxSizeInKB) + if (attributeMetadata is FileAttributeMetadata fileAttributeMetadata) { - throw new MaxSizeExceededException(tableLogicalName, fileUploadSession.Properties.FileAttributeName, - fileAttributeMetadata.MaxSizeInKB.Value); + if ((decimal) fileAttachment.Content.Length / 1024 > fileAttributeMetadata.MaxSizeInKB) + { + throw new MaxSizeExceededException(tableLogicalName, fileUploadSession.Properties.FileAttributeName, + fileAttributeMetadata.MaxSizeInKB.Value); + } } + + if (attributeMetadata is ImageAttributeMetadata imageAttributeMetadata) + { + if ((decimal) fileAttachment.Content.Length / 1024 > imageAttributeMetadata.MaxSizeInKB) + { + throw new MaxSizeExceededException(tableLogicalName, fileUploadSession.Properties.FileAttributeName, + imageAttributeMetadata.MaxSizeInKB.Value); + } + } + } #endif diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/BlockedAttachmentsTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/BlockedAttachmentsTests.cs similarity index 98% rename from tests/FakeXrmEasy.Core.Tests/FileStorage/Db/BlockedAttachmentsTests.cs rename to tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/BlockedAttachmentsTests.cs index efbb1dc5..58345ad9 100644 --- a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/BlockedAttachmentsTests.cs +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/BlockedAttachmentsTests.cs @@ -9,7 +9,7 @@ using Xunit; using FileAttachment = FakeXrmEasy.Core.FileStorage.Db.FileAttachment; -namespace FakeXrmEasy.Core.Tests.FileStorage.Db +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Files { public class BlockedAttachmentsTests: FakeXrmEasyTestsBase { diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/BlockedMimeTypeTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/BlockedMimeTypeTests.cs similarity index 99% rename from tests/FakeXrmEasy.Core.Tests/FileStorage/Db/BlockedMimeTypeTests.cs rename to tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/BlockedMimeTypeTests.cs index 0add6402..cc1f69fd 100644 --- a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/BlockedMimeTypeTests.cs +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/BlockedMimeTypeTests.cs @@ -9,7 +9,7 @@ using Xunit; using FileAttachment = FakeXrmEasy.Core.FileStorage.Db.FileAttachment; -namespace FakeXrmEasy.Core.Tests.FileStorage.Db +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Files { public class BlockedMimeTypeTests: FakeXrmEasyTestsBase { diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/DeleteFileTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/DeleteFileTests.cs similarity index 97% rename from tests/FakeXrmEasy.Core.Tests/FileStorage/Db/DeleteFileTests.cs rename to tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/DeleteFileTests.cs index da3aa229..b0c9f450 100644 --- a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/DeleteFileTests.cs +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/DeleteFileTests.cs @@ -8,7 +8,7 @@ using Xunit; using FileAttachment = FakeXrmEasy.Core.FileStorage.Db.FileAttachment; -namespace FakeXrmEasy.Core.Tests.FileStorage.Db +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Files { public class DeleteFileTests { diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/FileUploadSessionTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/FileUploadSessionTests.cs similarity index 97% rename from tests/FakeXrmEasy.Core.Tests/FileStorage/Db/FileUploadSessionTests.cs rename to tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/FileUploadSessionTests.cs index 9a2b414a..4c3bc965 100644 --- a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/FileUploadSessionTests.cs +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/FileUploadSessionTests.cs @@ -1,11 +1,10 @@ using System; -using FakeXrmEasy.Core.FileStorage; using FakeXrmEasy.Core.FileStorage.Db; using FakeXrmEasy.Core.FileStorage.Db.Exceptions; using FakeXrmEasy.Core.FileStorage.Upload; using Xunit; -namespace FakeXrmEasy.Core.Tests.FileStorage.Db +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Files { public class FileUploadSessionTests { diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbDownloaderTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/InMemoryFileDbDownloaderTests.cs similarity index 99% rename from tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbDownloaderTests.cs rename to tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/InMemoryFileDbDownloaderTests.cs index eb9f3f58..45ddd5c1 100644 --- a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbDownloaderTests.cs +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/InMemoryFileDbDownloaderTests.cs @@ -8,7 +8,7 @@ using Xunit; using FileAttachment = FakeXrmEasy.Core.FileStorage.Db.FileAttachment; -namespace FakeXrmEasy.Core.Tests.FileStorage.Db +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Files { public class InMemoryFileDbDownloaderTests { diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbUploaderTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/InMemoryFileDbUploaderTests.cs similarity index 99% rename from tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbUploaderTests.cs rename to tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/InMemoryFileDbUploaderTests.cs index 4a7f1364..0186c3ec 100644 --- a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbUploaderTests.cs +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/InMemoryFileDbUploaderTests.cs @@ -15,7 +15,7 @@ using Microsoft.Xrm.Sdk.Metadata; #endif -namespace FakeXrmEasy.Core.Tests.FileStorage.Db +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Files { public class InMemoryFileDbUploaderTests { diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/UpdateFileTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/UpdateFileTests.cs similarity index 97% rename from tests/FakeXrmEasy.Core.Tests/FileStorage/Db/UpdateFileTests.cs rename to tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/UpdateFileTests.cs index 9b1a6a3c..8d558ede 100644 --- a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/UpdateFileTests.cs +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Files/UpdateFileTests.cs @@ -6,7 +6,7 @@ using Xunit; using FileAttachment = FakeXrmEasy.Core.FileStorage.Db.FileAttachment; -namespace FakeXrmEasy.Core.Tests.FileStorage.Db +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Files { public class UpdateFileTests: FakeXrmEasyTestsBase { diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/DeleteFileTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/DeleteFileTests.cs new file mode 100644 index 00000000..2f524af4 --- /dev/null +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/DeleteFileTests.cs @@ -0,0 +1,71 @@ +using System; +using DataverseEntities; +using FakeXrmEasy.Core.Db; +using FakeXrmEasy.Core.FileStorage.Db; +using FakeXrmEasy.Core.FileStorage.Db.Exceptions; +using FakeXrmEasy.Core.FileStorage.Download; +using Microsoft.Xrm.Sdk; +using Xunit; +using FileAttachment = FakeXrmEasy.Core.FileStorage.Db.FileAttachment; + +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Images +{ + public class DeleteFileTests + { + private const string IMAGE_ATTRIBUTE_NAME = "dv_image"; + + private readonly InMemoryDb _db; + private readonly InMemoryFileDb _fileDb; + private readonly Entity _entity; + private readonly FileAttachment _file; + + public DeleteFileTests() + { + _db = new InMemoryDb(); + _fileDb = new InMemoryFileDb(_db); + + _entity = new Entity(dv_test.EntityLogicalName) + { + Id = Guid.NewGuid(), + }; + + _file = new FileAttachment() + { + Id = Guid.NewGuid().ToString(), + MimeType = "image/png", + FileName = "MyImage.png", + Target = _entity.ToEntityReference(), + AttributeName = IMAGE_ATTRIBUTE_NAME, + Content = new byte[] { 1, 2, 3, 4 } + }; + + _entity[IMAGE_ATTRIBUTE_NAME] = _file.Id; + } + + [Fact] + public void Should_be_empty_when_no_files_are_added() + { + Assert.Empty(_fileDb.GetAllFiles()); + } + + [Fact] + public void Should_throw_exception_when_a_file_doesnt_exists() + { + Assert.Throws(() => _fileDb.DeleteFile("invalid id")); + } + + [Fact] + public void Should_delete_an_existing_file() + { + _db.AddEntityRecord(_entity); + _fileDb.AddFile(_file); + + _fileDb.DeleteFile(_file.Id); + + Assert.Empty(_fileDb.GetAllFiles()); + + var entityAfter = _db.GetTable(_entity.LogicalName).GetById(_entity.Id); + Assert.Null(entityAfter[_file.AttributeName]); + } + } +} \ No newline at end of file diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/FileUploadSessionTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/FileUploadSessionTests.cs new file mode 100644 index 00000000..a382d19a --- /dev/null +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/FileUploadSessionTests.cs @@ -0,0 +1,95 @@ +using System; +using FakeXrmEasy.Core.FileStorage.Db; +using FakeXrmEasy.Core.FileStorage.Db.Exceptions; +using FakeXrmEasy.Core.FileStorage.Upload; +using Xunit; + +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Images +{ + public class FileUploadSessionTests + { + private readonly FileUploadSession _session; + + public FileUploadSessionTests() + { + _session = new FileUploadSession() { FileUploadSessionId = new Guid().ToString() }; + } + + [Fact] + public void Should_be_empty_when_created() + { + Assert.Empty(_session.GetAllBlocks()); + } + + [Fact] + public void Should_add_new_block_to_existing_session() + { + var blockId = Guid.NewGuid().ToString(); + _session.AddFileBlock(new UploadBlockProperties() + { + BlockId = blockId, + BlockContents = new byte[] { 1, 2, 3, 4 }, + FileContinuationToken = _session.FileUploadSessionId + }); + + var fileBlock = _session.GetFileBlock(blockId); + Assert.NotNull(fileBlock); + Assert.Equal(new byte[] {1 , 2, 3, 4}, fileBlock.Content); + } + + [Fact] + public void Should_throw_exception_if_a_block_already_exists() + { + var blockId = Guid.NewGuid().ToString(); + var uploadBlockProperties = new UploadBlockProperties() + { + BlockId = blockId, + BlockContents = new byte[] { 1, 2, 3, 4 }, + FileContinuationToken = _session.FileUploadSessionId + }; + + //First attempt ok + _session.AddFileBlock(uploadBlockProperties); + + //Second fails + Assert.Throws(() => _session.AddFileBlock(uploadBlockProperties)); + } + + [Fact] + public void Should_convert_file_upload_session_to_file_attachment() + { + //Add two blocks + var blockId1 = Guid.NewGuid().ToString(); + var uploadBlockProperties = new UploadBlockProperties() + { + BlockId = blockId1, + BlockContents = new byte[] { 1, 2, 3, 4 }, + FileContinuationToken = _session.FileUploadSessionId + }; + _session.AddFileBlock(uploadBlockProperties); + + var blockId2 = Guid.NewGuid().ToString(); + uploadBlockProperties = new UploadBlockProperties() + { + BlockId = blockId2, + BlockContents = new byte[] { 5, 6, 7, 8 }, + FileContinuationToken = _session.FileUploadSessionId + }; + _session.AddFileBlock(uploadBlockProperties); + + var commitProperties = new CommitFileUploadSessionProperties() + { + FileUploadSessionId = _session.FileUploadSessionId, + FileName = "MyImage.png", + MimeType = "image/png", + BlockIdsListSequence = new[] { blockId2, blockId1 } //2 comes first + }; + + var fileAttachment = _session.ToFileAttachment(commitProperties); + Assert.NotNull(fileAttachment); + Assert.Equal(new byte[] { 5, 6, 7, 8, 1, 2, 3, 4 }, fileAttachment.Content); + Assert.Equal(commitProperties.FileName, fileAttachment.FileName); + Assert.Equal(commitProperties.MimeType, fileAttachment.MimeType); + } + } +} \ No newline at end of file diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/InMemoryFileDbDownloaderTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/InMemoryFileDbDownloaderTests.cs new file mode 100644 index 00000000..a7bdc63a --- /dev/null +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/InMemoryFileDbDownloaderTests.cs @@ -0,0 +1,174 @@ +using System; +using DataverseEntities; +using FakeXrmEasy.Core.Db; +using FakeXrmEasy.Core.FileStorage.Db; +using FakeXrmEasy.Core.FileStorage.Db.Exceptions; +using FakeXrmEasy.Core.FileStorage.Download; +using Microsoft.Xrm.Sdk; +using Xunit; +using FileAttachment = FakeXrmEasy.Core.FileStorage.Db.FileAttachment; + +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Images +{ + public class InMemoryFileDbDownloaderTests + { + private const string IMAGE_ATTRIBUTE_NAME = "dv_image"; + + private readonly InMemoryDb _db; + private readonly InMemoryFileDb _fileDb; + private readonly FileDownloadProperties _fileDownloadProperties; + private readonly Entity _entity; + private readonly FileAttachment _file; + private readonly DownloadBlockProperties _downloadBlockProperties; + + public InMemoryFileDbDownloaderTests() + { + _db = new InMemoryDb(); + _fileDb = new InMemoryFileDb(_db); + + _entity = new Entity(dv_test.EntityLogicalName) + { + Id = Guid.NewGuid() + }; + + _fileDownloadProperties = new FileDownloadProperties() + { + Target = _entity.ToEntityReference(), + FileAttributeName = IMAGE_ATTRIBUTE_NAME + }; + + _file = new FileAttachment() + { + Id = Guid.NewGuid().ToString(), + MimeType = "image/png", + FileName = "MyImage.png", + Target = _entity.ToEntityReference(), + AttributeName = IMAGE_ATTRIBUTE_NAME, + Content = new byte[] { 1, 2, 3, 4 } + }; + + _downloadBlockProperties = new DownloadBlockProperties() + { + FileDownloadSessionId = "" + }; + } + + [Fact] + public void Should_create_an_empty_in_memory_file_db() + { + Assert.Empty(_fileDb.GetAllFileDownloadSessions()); + Assert.Empty(_fileDb.GetAllFiles()); + } + + [Fact] + public void Should_throw_exception_if_record_doesnt_exists() + { + Assert.Throws( () => _fileDb.InitFileDownloadSession(_fileDownloadProperties)); + } + + [Fact] + public void Should_return_file_not_found_exception_if_the_record_doesnt_have_a_file() + { + _db.AddEntityRecord(_entity); + Assert.Throws(() => _fileDb.InitFileDownloadSession(_fileDownloadProperties)); + } + + [Fact] + public void Should_return_file_not_found_exception_if_the_record_has_an_invalid_file_reference() + { + _entity[_fileDownloadProperties.FileAttributeName] = "invalid file id"; + + _db.AddEntityRecord(_entity); + Assert.Throws(() => _fileDb.InitFileDownloadSession(_fileDownloadProperties)); + } + + [Fact] + public void Should_return_file_download_session_if_properties_are_valid() + { + _fileDb.AddFile(_file); + + _entity[_fileDownloadProperties.FileAttributeName] = _file.Id; + _db.AddEntityRecord(_entity); + + var fileContinuationToken = _fileDb.InitFileDownloadSession(_fileDownloadProperties); + Assert.NotNull(fileContinuationToken); + + var fileDownloadSession = _fileDb.GetFileDownloadSession(fileContinuationToken); + Assert.NotNull(fileDownloadSession); + + Assert.Equal(_file.Id, fileDownloadSession.File.Id); + Assert.Equal(_fileDownloadProperties.FileAttributeName, fileDownloadSession.Properties.FileAttributeName); + Assert.Equal(_fileDownloadProperties.Target.LogicalName, _entity.LogicalName); + Assert.Equal(_fileDownloadProperties.Target.Id, _entity.Id); + } + + [Fact] + public void Should_throw_file_token_continuation_not_found_exception() + { + _downloadBlockProperties.FileDownloadSessionId = "invalid id"; + Assert.Throws(() => + _fileDb.DownloadFileBlock(_downloadBlockProperties)); + } + + [Theory] + [InlineData(0)] + [InlineData(-23456)] + public void Should_throw_invalid_length_exception(long blockLength) + { + _fileDb.AddFile(_file); + + _entity[_fileDownloadProperties.FileAttributeName] = _file.Id; + _db.AddEntityRecord(_entity); + + var fileContinuationToken = _fileDb.InitFileDownloadSession(_fileDownloadProperties); + Assert.NotNull(fileContinuationToken); + + _downloadBlockProperties.FileDownloadSessionId = fileContinuationToken; + _downloadBlockProperties.BlockLength = blockLength; + Assert.Throws(() => _fileDb.DownloadFileBlock(_downloadBlockProperties)); + } + + [Theory] + [InlineData(-1, 2)] + [InlineData(0, 5)] + [InlineData(2, 25)] + [InlineData(4, 2)] + public void Should_throw_invalid_offset_exception_if_exceeds_file_length(long offset, long blockLength) + { + _fileDb.AddFile(_file); + + _entity[_fileDownloadProperties.FileAttributeName] = _file.Id; + _db.AddEntityRecord(_entity); + + var fileContinuationToken = _fileDb.InitFileDownloadSession(_fileDownloadProperties); + Assert.NotNull(fileContinuationToken); + + _downloadBlockProperties.FileDownloadSessionId = fileContinuationToken; + _downloadBlockProperties.Offset = offset; + _downloadBlockProperties.BlockLength = blockLength; + Assert.Throws(() => _fileDb.DownloadFileBlock(_downloadBlockProperties)); + } + + [Theory] + [InlineData(0, 4)] + [InlineData(1, 3)] + [InlineData(3, 1)] + public void Should_download_a_valid_file_block(long offset, long blockLength) + { + _fileDb.AddFile(_file); + + _entity[_fileDownloadProperties.FileAttributeName] = _file.Id; + _db.AddEntityRecord(_entity); + + var fileContinuationToken = _fileDb.InitFileDownloadSession(_fileDownloadProperties); + Assert.NotNull(fileContinuationToken); + + _downloadBlockProperties.FileDownloadSessionId = fileContinuationToken; + _downloadBlockProperties.Offset = offset; + _downloadBlockProperties.BlockLength = blockLength; + var data = _fileDb.DownloadFileBlock(_downloadBlockProperties); + + Assert.Equal(blockLength, data.Length); + } + } +} \ No newline at end of file diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/InMemoryFileDbUploaderTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/InMemoryFileDbUploaderTests.cs new file mode 100644 index 00000000..e5e25833 --- /dev/null +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/InMemoryFileDbUploaderTests.cs @@ -0,0 +1,252 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using DataverseEntities; +using FakeXrmEasy.Core.Db; +using FakeXrmEasy.Core.FileStorage.Db; +using FakeXrmEasy.Core.FileStorage.Db.Exceptions; +using FakeXrmEasy.Core.FileStorage.Upload; +using Microsoft.Xrm.Sdk; +using Xunit; + +#if FAKE_XRM_EASY_9 +using System.Collections.Generic; +using FakeXrmEasy.Extensions; +using Microsoft.Xrm.Sdk.Metadata; +#endif + +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Images +{ + public class InMemoryFileDbUploaderTests + { + private const string IMAGE_ATTRIBUTE_NAME = "dv_image"; + + private readonly InMemoryDb _db; + private readonly InMemoryFileDb _fileDb; + private readonly FileUploadProperties _fileUploadProperties; + private readonly Entity _entity; + + public InMemoryFileDbUploaderTests() + { + _db = new InMemoryDb(); + _fileDb = new InMemoryFileDb(_db); + + SetMetadata(); + + _entity = new Entity(dv_test.EntityLogicalName) + { + Id = Guid.NewGuid() + }; + + _fileUploadProperties = new FileUploadProperties() + { + Target = _entity.ToEntityReference(), + FileName = "MyImage.png", + FileAttributeName = IMAGE_ATTRIBUTE_NAME + }; + } + + private void SetMetadata() + { + _db.AddTable(dv_test.EntityLogicalName, out var table); + + #if FAKE_XRM_EASY_9 + + var imageAttributeMetadata = new ImageAttributeMetadata() + { + LogicalName = IMAGE_ATTRIBUTE_NAME + }; + imageAttributeMetadata.MaxSizeInKB = 1; //1 KB + var tableEntityMetadata = new EntityMetadata() + { + LogicalName = dv_test.EntityLogicalName + }; + tableEntityMetadata.SetAttributeCollection(new List() + { + imageAttributeMetadata + }); + table.SetMetadata(tableEntityMetadata); + + #endif + + } + + [Fact] + public void Should_create_an_empty_in_memory_file_db() + { + Assert.Empty(_fileDb.GetAllFileUploadSessions()); + Assert.Empty(_fileDb.GetAllFiles()); + } + + [Fact] + public void Should_throw_exception_if_a_file_upload_session_is_initiated_for_a_non_existing_record() + { + Assert.Throws( () => _fileDb.InitFileUploadSession(_fileUploadProperties)); + } + + [Fact] + public void Should_init_and_store_file_upload_session() + { + _db.AddEntityRecord(_entity); + var fileContinuationToken = _fileDb.InitFileUploadSession(_fileUploadProperties); + + var fileUploadSession = _fileDb.GetFileUploadSession(fileContinuationToken); + + Assert.NotNull(fileUploadSession); + + Assert.Equal(fileContinuationToken, fileUploadSession.FileUploadSessionId); + Assert.Equal(_fileUploadProperties.Target.LogicalName, fileUploadSession.Properties.Target.LogicalName); + Assert.Equal(_fileUploadProperties.Target.Id, fileUploadSession.Properties.Target.Id); + Assert.Equal(_fileUploadProperties.FileName, fileUploadSession.Properties.FileName); + Assert.Equal(_fileUploadProperties.FileAttributeName, fileUploadSession.Properties.FileAttributeName); + } + + [Fact] + public void Should_init_and_commit_file() + { + _db.AddEntityRecord(_entity); + var fileContinuationToken = _fileDb.InitFileUploadSession(_fileUploadProperties); + var fileUploadSession = _fileDb.GetFileUploadSession(fileContinuationToken); + + var fileBlockProperties = new UploadBlockProperties() + { + BlockId = Guid.NewGuid().ToString(), + BlockContents = new byte[] { 1, 2, 3, 4 } + }; + + fileUploadSession.AddFileBlock(fileBlockProperties); + + var commitProperties = new CommitFileUploadSessionProperties() + { + FileUploadSessionId = fileContinuationToken, + FileName = _fileUploadProperties.FileName, + MimeType = "image/png", + BlockIdsListSequence = new[] { fileBlockProperties.BlockId } + }; + + _fileDb.CommitFileUploadSession(commitProperties); + + var allFiles = _fileDb.GetAllFiles(); + + Assert.Single(allFiles); + + var createdFile = allFiles.FirstOrDefault(); + + Assert.Equal(commitProperties.FileName, createdFile.FileName); + Assert.Equal(commitProperties.MimeType, createdFile.MimeType); + Assert.Equal(new byte[] { 1, 2, 3, 4 }, createdFile.Content); + + //Uncommited session is removed + Assert.Null(_fileDb.GetFileUploadSession(fileContinuationToken)); + + //File attribute is updated with file id and file name + var entityAfter = _db.GetTable(_entity.LogicalName).GetById(_entity.Id); + Assert.Equal(createdFile.Id, entityAfter[_fileUploadProperties.FileAttributeName]); + Assert.Equal(createdFile.FileName, entityAfter[$"{_fileUploadProperties.FileAttributeName}_name"]); + + //New file attachment record is created + var fileAttachment = _db + .GetTable(InMemoryFileDb.FILE_ATTACHMENT_TABLE_NAME) + .GetById(new Guid(createdFile.Id)); + + Assert.NotNull(fileAttachment); + } + + #if FAKE_XRM_EASY_9 + [Fact] + public void Should_throw_exception_if_file_exceeds_size() + { + _db.AddEntityRecord(_entity); + var fileContinuationToken = _fileDb.InitFileUploadSession(_fileUploadProperties); + var fileUploadSession = _fileDb.GetFileUploadSession(fileContinuationToken); + + var blob = new byte[1025]; //edge case where it exceeds 1 byte + for (var i = 0; i < 1025; i++) + { + blob[i] = (byte)(i % 254); + } + + var fileBlockProperties = new UploadBlockProperties() + { + BlockId = Guid.NewGuid().ToString(), + BlockContents = blob + }; + + fileUploadSession.AddFileBlock(fileBlockProperties); + + var commitProperties = new CommitFileUploadSessionProperties() + { + FileUploadSessionId = fileContinuationToken, + FileName = _fileUploadProperties.FileName, + MimeType = "image/png", + BlockIdsListSequence = new[] { fileBlockProperties.BlockId } + }; + + Assert.Throws(() => _fileDb.CommitFileUploadSession(commitProperties)); + } + #endif + + [Fact] + public void Should_init_and_upload_multiple_blocks_concurrently_and_commit_file() + { + _db.AddEntityRecord(_entity); + var fileContinuationToken = _fileDb.InitFileUploadSession(_fileUploadProperties); + var fileUploadSession = _fileDb.GetFileUploadSession(fileContinuationToken); + + var blockIds = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + Parallel.ForEach(blockIds, (blockId) => + { + fileUploadSession.AddFileBlock(new UploadBlockProperties() + { + BlockId = blockId.ToString(), + BlockContents = new byte[] { + Convert.ToByte(10 + blockId), + Convert.ToByte(20 + blockId), + Convert.ToByte(30 + blockId), + Convert.ToByte(40 + blockId) + } + }); + }); + + var commitProperties = new CommitFileUploadSessionProperties() + { + FileUploadSessionId = fileContinuationToken, + FileName = _fileUploadProperties.FileName, + MimeType = "image/png", + BlockIdsListSequence = blockIds.Select(id => id.ToString()).ToArray() + }; + + _fileDb.CommitFileUploadSession(commitProperties); + + var allFiles = _fileDb.GetAllFiles(); + + Assert.Single(allFiles); + + var createdFile = allFiles.FirstOrDefault(); + + for (var i = 0; i < 10; i++) + { + Assert.Equal(Convert.ToByte(10 + i + 1), createdFile.Content[i * 4]); + Assert.Equal(Convert.ToByte(20 + i + 1), createdFile.Content[i * 4 + 1]); + Assert.Equal(Convert.ToByte(30 + i + 1), createdFile.Content[i * 4 + 2]); + Assert.Equal(Convert.ToByte(40 + i + 1), createdFile.Content[i * 4 + 3]); + } + } + + [Fact] + public void Should_throw_exception_if_file_upload_session_does_not_exist_when_commiting_a_file_upload_session() + { + _db.AddEntityRecord(_entity); + var commitProperties = new CommitFileUploadSessionProperties() + { + FileUploadSessionId = "asdasdasd", + FileName = _fileUploadProperties.FileName, + MimeType = "image/png", + BlockIdsListSequence = new string[] {} + }; + Assert.Throws(() => + _fileDb.CommitFileUploadSession(commitProperties)); + } + } +} \ No newline at end of file diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/UpdateFileTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/UpdateFileTests.cs new file mode 100644 index 00000000..6f4d1b8a --- /dev/null +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/Images/UpdateFileTests.cs @@ -0,0 +1,77 @@ +using System; +using DataverseEntities; +using FakeXrmEasy.Core.Db; +using FakeXrmEasy.Core.FileStorage.Db; +using Microsoft.Xrm.Sdk; +using Xunit; +using FileAttachment = FakeXrmEasy.Core.FileStorage.Db.FileAttachment; + +namespace FakeXrmEasy.Core.Tests.FileStorage.Db.Images +{ + public class UpdateFileTests: FakeXrmEasyTestsBase + { + private const string FILE_ATTRIBUTE_NAME = "dv_file"; + + private readonly InMemoryDb _db; + private readonly InMemoryFileDb _fileDb; + private readonly Entity _entity; + private readonly FileAttachment _file; + + public UpdateFileTests() + { + _db = (_context as XrmFakedContext).Db; + _fileDb = (_context as XrmFakedContext).FileDb; + + _entity = new Entity(dv_test.EntityLogicalName) + { + Id = Guid.NewGuid(), + }; + + _file = new FileAttachment() + { + Id = Guid.NewGuid().ToString(), + MimeType = "image/png", + FileName = "MyImage.png", + Target = _entity.ToEntityReference(), + AttributeName = FILE_ATTRIBUTE_NAME, + Content = new byte[] { 1, 2, 3, 4 } + }; + + _entity[FILE_ATTRIBUTE_NAME] = _file.Id; + } + + [Fact] + public void Should_remove_file_when_updating_file_attribute_to_null() + { + _fileDb.AddFile(_file); + _context.Initialize(_entity); + + var entityToUpdate = new Entity(_entity.LogicalName) + { + Id = _entity.Id, + [FILE_ATTRIBUTE_NAME] = null + }; + + _service.Update(entityToUpdate); + + Assert.Empty(_fileDb.GetAllFiles()); + } + + [Fact] + public void Should_keep_file_when_updating_another_attribute_to_null() + { + _fileDb.AddFile(_file); + _context.Initialize(_entity); + + var entityToUpdate = new Entity(_entity.LogicalName) + { + Id = _entity.Id, + ["dv_name"] = null + }; + + _service.Update(entityToUpdate); + + Assert.Single(_fileDb.GetAllFiles()); + } + } +} \ No newline at end of file diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbInternalTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbInternalTests.cs deleted file mode 100644 index 52040422..00000000 --- a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbInternalTests.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FakeXrmEasy.Core.Tests.FileStorage.Db -{ - public class InMemoryFileDbInternalTests - { - - } -} \ No newline at end of file