Skip to content

Commit

Permalink
Ftp uploader (#21)
Browse files Browse the repository at this point in the history
* Add new job
* IAsyncFtpClient
* .net 7
* Update packages
  • Loading branch information
vov4uk committed Feb 27, 2023
1 parent 41d689f commit fe3f859
Show file tree
Hide file tree
Showing 131 changed files with 1,508 additions and 1,203 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using AutoFixture;
using FluentFTP;
using FluentFTP.Exceptions;
using Hik.DTO.Config;
using Hik.DTO.Contracts;
using Hik.Helpers.Abstraction;
Expand All @@ -13,15 +14,15 @@

namespace Hik.Client.Tests
{
public class RSyncClientTests
public class FtpDownloaderClientTests
{
private readonly Mock<IFtpClient> ftpMock;
private readonly Mock<IAsyncFtpClient> ftpMock;
private readonly Mock<IFilesHelper> filesMock;
private readonly Mock<IDirectoryHelper> dirMock;
private readonly Mock<ILogger> loggerMock;
private readonly Fixture fixture;

public RSyncClientTests()
public FtpDownloaderClientTests()
{
this.ftpMock = new(MockBehavior.Strict);
this.filesMock = new(MockBehavior.Strict);
Expand All @@ -33,34 +34,35 @@ public RSyncClientTests()
[Fact]
public void Constructor_PutEmptyConfig_ThrowsException()
{
Assert.Throws<ArgumentNullException>(() => new RSyncClient(null, this.filesMock.Object, dirMock.Object, ftpMock.Object, loggerMock.Object));
Assert.Throws<ArgumentNullException>(() => new FtpDownloaderClient(null, this.filesMock.Object, dirMock.Object, ftpMock.Object, loggerMock.Object));
}

[Fact]
public void InitializeClient_CallInitializeClient_ClientInitialized()
{
var config = new CameraConfig { IpAddress = "192.168.0.1", UserName = "admin", Password = "admin" };
var ftp = new FtpClient();
var client = new RSyncClient(config, this.filesMock.Object, this.dirMock.Object, ftp, loggerMock.Object);
var config = new CameraConfig { Camera = new DeviceConfig { IpAddress = "192.168.0.1", UserName = "admin", Password = "admin" } };

var ftp = new AsyncFtpClient();
var client = new FtpDownloaderClient(config, this.filesMock.Object, this.dirMock.Object, ftp, loggerMock.Object);
client.InitializeClient();

Assert.Equal(config.IpAddress, ftp.Host);
Assert.Equal(config.UserName, ftp.Credentials.UserName);
Assert.Equal(config.Password, ftp.Credentials.Password);
Assert.Equal(config.Camera.IpAddress, ftp.Host);
Assert.Equal(config.Camera.UserName, ftp.Credentials.UserName);
Assert.Equal(config.Camera.Password, ftp.Credentials.Password);
}

[Fact]
public async Task GetFilesListAsync_CallWithValidParameters_ReturnMapppedFiles()
{
ftpMock.Setup(x => x.GetListingAsync("/CameraName", default(CancellationToken))).ReturnsAsync(new FtpListItem[]
ftpMock.Setup(x => x.GetListing("/CameraName", default)).ReturnsAsync(new FtpListItem[]
{
new FtpListItem(){Name = "192.168.8.103_01_20220517095135158_MOTION_DETECTION", FullName = "/192.168.8.103_01_20220517095135158_MOTION_DETECTION.jpg"}
});

var config = new CameraConfig { Alias = "Group.CameraName" };
var config = new CameraConfig { Alias = "Group.CameraName", Camera = new DeviceConfig() };

var client = new RSyncClient(config, this.filesMock.Object, this.dirMock.Object, this.ftpMock.Object, loggerMock.Object);
var mediaFiles = await client.GetFilesListAsync(default(DateTime), default(DateTime));
var client = new FtpDownloaderClient(config, this.filesMock.Object, this.dirMock.Object, this.ftpMock.Object, loggerMock.Object);
var mediaFiles = await client.GetFilesListAsync(default, default);

Assert.Single(mediaFiles);
var firstFile = mediaFiles.First();
Expand Down Expand Up @@ -92,7 +94,7 @@ public async Task DownloadFileAsync_RemoteFileNotExist_ReturnFalse()
this.dirMock.Setup(x => x.CreateDirIfNotExist(It.IsAny<string>()));
this.filesMock.Setup(x => x.FileExists(It.IsAny<string>()))
.Returns(false);
this.ftpMock.Setup(x => x.FileExistsAsync(It.IsAny<string>(), CancellationToken.None))
this.ftpMock.Setup(x => x.FileExists(It.IsAny<string>(), CancellationToken.None))
.ReturnsAsync(false);

var client = this.GetClient();
Expand All @@ -111,14 +113,14 @@ public async Task DownloadFileAsync_SizeMistmatch_DownloadFalse()
this.dirMock.Setup(x => x.CreateDirIfNotExist(It.IsAny<string>()));
this.filesMock.Setup(x => x.FileExists(It.IsAny<string>()))
.Returns(false);
this.ftpMock.Setup(x => x.FileExistsAsync(It.IsAny<string>(), CancellationToken.None))
this.ftpMock.Setup(x => x.FileExists(It.IsAny<string>(), CancellationToken.None))
.ReturnsAsync(true);
this.filesMock.Setup(x => x.GetTempFileName())
.Returns(randomName);
this.filesMock.Setup(x => x.RenameFile(randomName, It.IsAny<string>()));
this.filesMock.Setup(x => x.FileSize(It.IsAny<string>()))
.Returns(-1);
this.ftpMock.Setup(x => x.DownloadFileAsync(randomName, It.IsAny<string>(), FtpLocalExists.Overwrite, FtpVerify.None, null, CancellationToken.None))
this.ftpMock.Setup(x => x.DownloadFile(randomName, It.IsAny<string>(), FtpLocalExists.Overwrite, FtpVerify.None, null, CancellationToken.None))
.ReturnsAsync(FtpStatus.Success);

var client = this.GetClient();
Expand All @@ -138,16 +140,16 @@ public async Task DownloadFileAsync_SizeMatch_DownloadTrue()
this.dirMock.Setup(x => x.CreateDirIfNotExist(It.IsAny<string>()));
this.filesMock.Setup(x => x.FileExists(It.IsAny<string>()))
.Returns(false);
this.ftpMock.Setup(x => x.FileExistsAsync(It.IsAny<string>(), CancellationToken.None))
this.ftpMock.Setup(x => x.FileExists(It.IsAny<string>(), CancellationToken.None))
.ReturnsAsync(true);
this.filesMock.Setup(x => x.GetTempFileName())
.Returns(randomName);
this.filesMock.Setup(x => x.RenameFile(randomName, It.IsAny<string>()));
this.filesMock.Setup(x => x.FileSize(It.IsAny<string>()))
.Returns(file.Size);
this.ftpMock.Setup(x => x.DownloadFileAsync(randomName, It.IsAny<string>(), FtpLocalExists.Overwrite, FtpVerify.None, null, CancellationToken.None))
this.ftpMock.Setup(x => x.DownloadFile(randomName, It.IsAny<string>(), FtpLocalExists.Overwrite, FtpVerify.None, null, CancellationToken.None))
.ReturnsAsync(FtpStatus.Success);
this.ftpMock.Setup(x => x.DeleteFileAsync(file.Path, CancellationToken.None))
this.ftpMock.Setup(x => x.DeleteFile(file.Path, CancellationToken.None))
.Returns(Task.CompletedTask);

var client = this.GetClient();
Expand All @@ -167,25 +169,25 @@ public async Task DownloadFileAsync_DeleteFailed_DownloadFalse()
this.dirMock.Setup(x => x.CreateDirIfNotExist(It.IsAny<string>()));
this.filesMock.Setup(x => x.FileExists(It.IsAny<string>()))
.Returns(false);
this.ftpMock.Setup(x => x.FileExistsAsync(It.IsAny<string>(), CancellationToken.None))
this.ftpMock.Setup(x => x.FileExists(It.IsAny<string>(), CancellationToken.None))
.ReturnsAsync(true);
this.filesMock.Setup(x => x.GetTempFileName())
.Returns(randomName);
this.filesMock.Setup(x => x.RenameFile(randomName, It.IsAny<string>()));
this.filesMock.Setup(x => x.FileSize(It.IsAny<string>()))
.Returns(file.Size);
this.ftpMock.Setup(x => x.DownloadFileAsync(randomName, It.IsAny<string>(), FtpLocalExists.Overwrite, FtpVerify.None, null, CancellationToken.None))
this.ftpMock.Setup(x => x.DownloadFile(randomName, It.IsAny<string>(), FtpLocalExists.Overwrite, FtpVerify.None, null, CancellationToken.None))
.ReturnsAsync(FtpStatus.Success);
this.ftpMock.Setup(x => x.DeleteFileAsync(file.Path, CancellationToken.None))
.ThrowsAsync(new Exception("Something went wrong"));
this.ftpMock.Setup(x => x.DeleteFile(file.Path, CancellationToken.None))
.ThrowsAsync(new FtpException("Something went wrong"));

var client = this.GetClient();
var isDownloaded = await client.DownloadFileAsync(file, CancellationToken.None);

Assert.False(isDownloaded);
}

private RSyncClient GetClient() =>
new RSyncClient(this.fixture.Create<CameraConfig>(), this.filesMock.Object, this.dirMock.Object, this.ftpMock.Object, loggerMock.Object);
private FtpDownloaderClient GetClient() =>
new FtpDownloaderClient(this.fixture.Create<CameraConfig>(), this.filesMock.Object, this.dirMock.Object, this.ftpMock.Object, loggerMock.Object);
}
}
67 changes: 67 additions & 0 deletions Tests/Hik.Client.Tests/FtpUploaderClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoFixture;
using AutoFixture.Xunit2;
using FluentFTP;
using FluentFTP.Exceptions;
using Hik.Client.Client;
using Hik.DTO.Config;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace Hik.Client.Tests
{
public class FtpUploaderClientTests
{
private readonly Mock<IAsyncFtpClient> ftpMock;
private readonly Mock<ILogger> loggerMock;
private readonly Fixture fixture;

public FtpUploaderClientTests()
{
this.ftpMock = new(MockBehavior.Strict);
this.loggerMock = new();
this.fixture = new();
}

[Fact]
public void Constructor_PutEmptyConfig_ThrowsException()
{
Assert.Throws<ArgumentNullException>(() => new FtpUploaderClient(null, ftpMock.Object, loggerMock.Object));
}

[Fact]
public void InitializeClient_CallInitializeClient_ClientInitialized()
{
var config = new DeviceConfig { IpAddress = "192.168.0.1", UserName = "admin", Password = "admin" };

var ftp = new AsyncFtpClient();
var client = new FtpUploaderClient(config, ftp, loggerMock.Object);
client.InitializeClient();

Assert.Equal(config.IpAddress, ftp.Host);
Assert.Equal(config.UserName, ftp.Credentials.UserName);
Assert.Equal(config.Password, ftp.Credentials.Password);
}

[Theory]
[AutoData]
public async Task UploadFilesAsync_FailedTwoTimes_Uploaded(IEnumerable<string> files, string remotePath)
{
var config = new DeviceConfig { IpAddress = "192.168.0.1", UserName = "admin", Password = "admin" };

ftpMock.SetupSequence(x => x.UploadFiles(It.IsAny<IEnumerable<string>>(), It.IsAny<string>(), FtpRemoteExists.Overwrite, true, FtpVerify.None, FtpError.None, default, null, null))
.Throws<TimeoutException>()
.Throws<FtpHashUnsupportedException>()
.ReturnsAsync(new List<FtpResult>());

var client = new FtpUploaderClient(config, ftpMock.Object, loggerMock.Object);
await client.UploadFilesAsync(files, remotePath);

ftpMock.Verify(x => x.UploadFiles(It.IsAny<IEnumerable<string>>(), It.IsAny<string>(), FtpRemoteExists.Overwrite, true, FtpVerify.None, FtpError.None, default, null, null),
Times.Exactly(3));
}
}
}
11 changes: 6 additions & 5 deletions Tests/Hik.Client.Tests/Hik.Client.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<DebugType>full</DebugType>
<AssemblyName>Hik.Client.Tests</AssemblyName>
Expand All @@ -13,14 +13,15 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="Moq" Version="4.18.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="AutoFixture" Version="4.18.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.18.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
</ItemGroup>

<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion Tests/Hik.Client.Tests/HikPhotoClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
using DeviceConfig = DTO.Config.DeviceConfig;

public class HikPhotoClientTests
{
Expand Down Expand Up @@ -85,7 +86,7 @@ public async Task GetFilesListAsync_CallWithValidParameters_ReturnMapppedFiles()
[InlineData(2020, 12, 31, "20201231_000000.jpg", "C:\\2020-12\\31\\00\\")]
public async Task DownloadFileAsync_CallDownload_ProperFilesStored(int y, int m, int d, string localFileName, string localFolder)
{
var cameraConfig = new CameraConfig { ClientType = ClientType.HikVisionVideo, DestinationFolder = "C:\\", Alias = "test" };
var cameraConfig = new CameraConfig { ClientType = ClientType.HikVisionVideo, DestinationFolder = "C:\\", Alias = "test", Camera = new DeviceConfig() };

SetupLoginAndHddStatusCheck();
MediaFileDto remoteFile = new () { Date = new DateTime(y, m, d)};
Expand Down
13 changes: 7 additions & 6 deletions Tests/Hik.Client.Tests/HikVideoClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public void GetFilesListAsync_CallWithInvalidParameters_ThrowsException()
[InlineData(2020, 12, 31, 3600, "ch000000001", "C:\\2020-12\\31\\00\\20201231_000000_010000.mp4")]
public async Task DownloadFileAsync_CallDownload_ProperFilesStored(int y, int m, int d, int duration, string name, string fileName)
{
var cameraConfig = new CameraConfig { ClientType = ClientType.HikVisionVideo, DestinationFolder = "C:\\", Alias = "test" };
var cameraConfig = new CameraConfig { ClientType = ClientType.HikVisionVideo, DestinationFolder = "C:\\", Alias = "test", Camera = new DTO.Config.DeviceConfig() };

int downloadHandler = 1;
this.SetupLoginAndHddStatusCheck();
Expand All @@ -239,13 +239,13 @@ public async Task DownloadFileAsync_CallDownload_ProperFilesStored(int y, int m,
this.dirMock.Setup(x => x.CreateDirIfNotExist(It.IsAny<string>()));
this.filesMock.Setup(x => x.FileSize(targetName))
.Returns(1);
this.filesMock.Setup(x => x.RenameFile(tempName, targetName));
this.filesMock.Setup(x => x.RenameFile(tempName + ".mp4", targetName));
this.filesMock.Setup(x => x.FileExists(targetName))
.Returns(false);
this.filesMock.Setup(x => x.GetTempFileName())
.Returns(tempName);

this.videoServiceMock.Setup(x => x.StartDownloadFile(It.IsAny<int>(), remoteFile.Name, tempName))
this.videoServiceMock.Setup(x => x.StartDownloadFile(It.IsAny<int>(), remoteFile.Name, tempName + ".mp4"))
.Returns(downloadHandler);
this.videoServiceMock.Setup(x => x.GetDownloadPosition(It.IsAny<int>()))
.Returns(100);
Expand All @@ -260,8 +260,8 @@ public async Task DownloadFileAsync_CallDownload_ProperFilesStored(int y, int m,
this.dirMock.Verify(x => x.CreateDirIfNotExist(It.IsAny<string>()), Times.Once);
this.filesMock.Verify(x => x.FileExists(targetName), Times.Once);
this.filesMock.Verify(x => x.GetTempFileName(), Times.Once);
this.filesMock.Verify(x => x.RenameFile(tempName, targetName), Times.Once);
this.videoServiceMock.Verify(x => x.StartDownloadFile(It.IsAny<int>(), remoteFile.Name, tempName), Times.Once);
this.filesMock.Verify(x => x.RenameFile(tempName + ".mp4", targetName), Times.Once);
this.videoServiceMock.Verify(x => x.StartDownloadFile(It.IsAny<int>(), remoteFile.Name, tempName + ".mp4"), Times.Once);
}

[Fact]
Expand Down Expand Up @@ -306,7 +306,8 @@ public async Task DownloadFileAsync_AbnormalProgress_StopDownloadFile()
using (client = this.GetHikClient())
{
client.Login();
await Assert.ThrowsAsync<InvalidOperationException>(() => client.DownloadFileAsync(this.fixture.Create<MediaFileDto>(), CancellationToken.None));
var result = await client.DownloadFileAsync(this.fixture.Create<MediaFileDto>(), CancellationToken.None);
Assert.False(result);
}

this.videoServiceMock.Verify(x => x.StopDownloadFile(downloadHandler), Times.Once);
Expand Down
Loading

0 comments on commit fe3f859

Please sign in to comment.