From 5c6158bbaf0f4a9073d17b14401e4f610d6a3c79 Mon Sep 17 00:00:00 2001 From: Jordi Date: Sat, 10 Aug 2024 12:15:47 +0200 Subject: [PATCH] Implements DownloadBlock logic DynamicsValue/fake-xrm-easy#157 --- .../Exceptions/InvalidBlockLengthException.cs | 18 +++++ .../Db/Exceptions/InvalidOffsetException.cs | 22 ++++++ .../FileStorage/Db/InMemoryFileDb.cs | 31 +++++++- .../Db/InMemoryFileDbDownloaderTests.cs | 77 ++++++++++++++++++- 4 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 src/FakeXrmEasy.Core/FileStorage/Db/Exceptions/InvalidBlockLengthException.cs create mode 100644 src/FakeXrmEasy.Core/FileStorage/Db/Exceptions/InvalidOffsetException.cs diff --git a/src/FakeXrmEasy.Core/FileStorage/Db/Exceptions/InvalidBlockLengthException.cs b/src/FakeXrmEasy.Core/FileStorage/Db/Exceptions/InvalidBlockLengthException.cs new file mode 100644 index 00000000..c200e642 --- /dev/null +++ b/src/FakeXrmEasy.Core/FileStorage/Db/Exceptions/InvalidBlockLengthException.cs @@ -0,0 +1,18 @@ +using System; + +namespace FakeXrmEasy.Core.FileStorage.Db.Exceptions +{ + /// + /// Exception raised when the BlockLength property is not valid + /// + public class InvalidBlockLengthException: Exception + { + /// + /// Default constructor + /// + public InvalidBlockLengthException() : base($"The BlockLength property must be greater than zero.") + { + + } + } +} \ No newline at end of file diff --git a/src/FakeXrmEasy.Core/FileStorage/Db/Exceptions/InvalidOffsetException.cs b/src/FakeXrmEasy.Core/FileStorage/Db/Exceptions/InvalidOffsetException.cs new file mode 100644 index 00000000..3fad5631 --- /dev/null +++ b/src/FakeXrmEasy.Core/FileStorage/Db/Exceptions/InvalidOffsetException.cs @@ -0,0 +1,22 @@ +using System; +using FakeXrmEasy.Core.FileStorage.Download; + +namespace FakeXrmEasy.Core.FileStorage.Db.Exceptions +{ + /// + /// Exception raised when an Offset property is not within the range of a given file length + /// + public class InvalidOffsetException: Exception + { + /// + /// Default constructor + /// + /// + /// + internal InvalidOffsetException(DownloadBlockProperties properties, long actualLength) + : base($"The Offset '{properties.Offset.ToString()}' and block lengh '{properties.BlockLength.ToString()}' property values are not within the file's size of [0..{actualLength.ToString()}]") + { + + } + } +} \ No newline at end of file diff --git a/src/FakeXrmEasy.Core/FileStorage/Db/InMemoryFileDb.cs b/src/FakeXrmEasy.Core/FileStorage/Db/InMemoryFileDb.cs index 9e3ec0d0..b8607fb9 100644 --- a/src/FakeXrmEasy.Core/FileStorage/Db/InMemoryFileDb.cs +++ b/src/FakeXrmEasy.Core/FileStorage/Db/InMemoryFileDb.cs @@ -239,9 +239,36 @@ public List GetAllFileDownloadSessions() return _fileDownloadSessions.Values.ToList(); } - public byte[] DownloadFileBlock(DownloadBlockProperties uploadBlockProperties) + public byte[] DownloadFileBlock(DownloadBlockProperties downloadBlockProperties) { - throw new NotImplementedException(); + var fileDownloadSession = GetFileDownloadSession(downloadBlockProperties.FileDownloadSessionId); + if (fileDownloadSession == null) + { + throw new FileTokenContinuationNotFoundException(downloadBlockProperties.FileDownloadSessionId); + } + + if (downloadBlockProperties.BlockLength <= 0) + { + throw new InvalidBlockLengthException(); + } + + if (downloadBlockProperties.Offset < 0) + { + throw new InvalidOffsetException(downloadBlockProperties, fileDownloadSession.File.Content.Length); + } + + if (downloadBlockProperties.BlockLength + downloadBlockProperties.Offset > + fileDownloadSession.File.Content.Length) + { + throw new InvalidOffsetException(downloadBlockProperties, fileDownloadSession.File.Content.Length); + } + + var data = new byte[downloadBlockProperties.BlockLength]; + + Array.Copy(fileDownloadSession.File.Content, downloadBlockProperties.Offset, data, + 0, downloadBlockProperties.BlockLength); + + return data; } #endregion } diff --git a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbDownloaderTests.cs b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbDownloaderTests.cs index 891ba65c..eb9f3f58 100644 --- a/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbDownloaderTests.cs +++ b/tests/FakeXrmEasy.Core.Tests/FileStorage/Db/InMemoryFileDbDownloaderTests.cs @@ -17,7 +17,8 @@ public class InMemoryFileDbDownloaderTests private readonly FileDownloadProperties _fileDownloadProperties; private readonly Entity _entity; private readonly FileAttachment _file; - + private readonly DownloadBlockProperties _downloadBlockProperties; + public InMemoryFileDbDownloaderTests() { _db = new InMemoryDb(); @@ -43,6 +44,11 @@ public InMemoryFileDbDownloaderTests() AttributeName = "dv_file", Content = new byte[] { 1, 2, 3, 4 } }; + + _downloadBlockProperties = new DownloadBlockProperties() + { + FileDownloadSessionId = "" + }; } [Fact] @@ -93,5 +99,74 @@ public void Should_return_file_download_session_if_properties_are_valid() 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