Browse Source

Added GetAttributesAsync to SftpClient (#1648)

* Added GetAttributesAsync to SftpClient

* Adding integration tests + unit test

* Address warnings in test classes.

---------

Co-authored-by: William Decker <william.decker@syndigo.com>
William Decker 4 months ago
parent
commit
9cf282731a

+ 15 - 0
src/Renci.SshNet/ISftpClient.cs

@@ -700,6 +700,21 @@ namespace Renci.SshNet
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
         SftpFileAttributes GetAttributes(string path);
 
+        /// <summary>
+        /// Gets the <see cref="SftpFileAttributes"/> of the file on the path.
+        /// </summary>
+        /// <param name="path">The path to the file.</param>
+        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
+        /// <returns>
+        /// A <see cref="Task{SftpFileAttributes}"/> that represents the attribute retrieval operation.
+        /// The task result contains the <see cref="SftpFileAttributes"/> of the file on the path.
+        /// </returns>
+        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        Task<SftpFileAttributes> GetAttributesAsync(string path, CancellationToken cancellationToken);
+
         /// <summary>
         /// Returns the date and time the specified file or directory was last accessed.
         /// </summary>

+ 27 - 0
src/Renci.SshNet/SftpClient.cs

@@ -2094,6 +2094,33 @@ namespace Renci.SshNet
             return _sftpSession.RequestLStat(fullPath);
         }
 
+        /// <summary>
+        /// Gets the <see cref="SftpFileAttributes"/> of the file on the path.
+        /// </summary>
+        /// <param name="path">The path to the file.</param>
+        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
+        /// <returns>
+        /// A <see cref="Task{SftpFileAttributes}"/> that represents the attribute retrieval operation.
+        /// The task result contains the <see cref="SftpFileAttributes"/> of the file on the path.
+        /// </returns>
+        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        public async Task<SftpFileAttributes> GetAttributesAsync(string path, CancellationToken cancellationToken)
+        {
+            CheckDisposed();
+
+            if (_sftpSession is null)
+            {
+                throw new SshConnectionException("Client not connected.");
+            }
+
+            var fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false);
+
+            return await _sftpSession.RequestLStatAsync(fullPath, cancellationToken).ConfigureAwait(false);
+        }
+
         /// <summary>
         /// Sets the specified <see cref="SftpFileAttributes"/> of the file on the specified path.
         /// </summary>

+ 47 - 0
test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.GetAttributes.cs

@@ -0,0 +1,47 @@
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    public partial class SftpClientTest
+    {
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Sftp_GetAttributes_Not_Exists()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                Assert.ThrowsException<SftpPathNotFoundException>(() => sftp.GetAttributes("/asdfgh"));
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Sftp_GetAttributes_Null()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                Assert.ThrowsException<ArgumentNullException>(() => sftp.GetAttributes(null));
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Sftp_GetAttributes_Current()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                var attributes = sftp.GetAttributes(".");
+
+                Assert.IsNotNull(attributes);
+
+                sftp.Disconnect();
+            }
+        }
+    }
+}

+ 59 - 0
test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.GetAttributesAsync.cs

@@ -0,0 +1,59 @@
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    /// <summary>
+    /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+    /// </summary>
+    public partial class SftpClientTest
+    {
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public async Task Test_Sftp_GetAttributesAsync_Not_Exists()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                var cts = new CancellationTokenSource();
+                cts.CancelAfter(TimeSpan.FromMinutes(1));
+
+                await sftp.ConnectAsync(cts.Token);
+
+                await Assert.ThrowsExceptionAsync<SftpPathNotFoundException>(async () => await sftp.GetAttributesAsync("/asdfgh", cts.Token));
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public async Task Test_Sftp_GetAttributesAsync_Null()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                var cts = new CancellationTokenSource();
+                cts.CancelAfter(TimeSpan.FromMinutes(1));
+
+                await sftp.ConnectAsync(cts.Token);
+
+                await Assert.ThrowsExceptionAsync<ArgumentNullException>(async () => await sftp.GetAttributesAsync(null, cts.Token));
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public async Task Test_Sftp_GetAttributesAsync_Current()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                var cts = new CancellationTokenSource();
+                cts.CancelAfter(TimeSpan.FromMinutes(1));
+
+                await sftp.ConnectAsync(cts.Token);
+
+                var fileAttributes = await sftp.GetAttributesAsync(".", cts.Token);
+
+                Assert.IsNotNull(fileAttributes);
+
+                sftp.Disconnect();
+            }
+        }
+    }
+}

+ 28 - 0
test/Renci.SshNet.IntegrationTests/SftpClientTests.cs

@@ -177,5 +177,33 @@ namespace Renci.SshNet.IntegrationTests
 
             Assert.IsFalse(await _sftpClient.ExistsAsync(testFileName).ConfigureAwait(false));
         }
+
+        [TestMethod]
+        public void Create_file_and_use_GetAttributes()
+        {
+            var testFileName = "test-file.txt";
+            var testContent = "file content";
+
+            using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent));
+            _sftpClient.UploadFile(fileStream, testFileName);
+
+            var attributes = _sftpClient.GetAttributes(testFileName);
+            Assert.IsNotNull(attributes);
+            Assert.IsTrue(attributes.IsRegularFile);
+        }
+
+        [TestMethod]
+        public async Task Create_file_and_use_GetAttributesAsync()
+        {
+            var testFileName = "test-file.txt";
+            var testContent = "file content";
+
+            using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent));
+            await _sftpClient.UploadFileAsync(fileStream, testFileName).ConfigureAwait(false);
+
+            var attributes = await _sftpClient.GetAttributesAsync(testFileName, CancellationToken.None).ConfigureAwait(false);
+            Assert.IsNotNull(attributes);
+            Assert.IsTrue(attributes.IsRegularFile);
+        }
     }
 }

+ 30 - 0
test/Renci.SshNet.Tests/Classes/SftpClientTest.GetAttributes.cs

@@ -0,0 +1,30 @@
+using System;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Renci.SshNet.Common;
+using Renci.SshNet.Tests.Properties;
+
+namespace Renci.SshNet.Tests.Classes
+{
+    public partial class SftpClientTest
+    {
+        [TestMethod]
+        public void GetAttributes_Throws_WhenNotConnected()
+        {
+            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            {
+                Assert.ThrowsException<SshConnectionException>(() => sftp.GetAttributes("."));
+            }
+        }
+
+        [TestMethod]
+        public void GetAttributes_Throws_WhenDisposed()
+        {
+            var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD);
+            sftp.Dispose();
+
+            Assert.ThrowsException<ObjectDisposedException>(() => sftp.GetAttributes("."));
+        }
+    }
+}

+ 32 - 0
test/Renci.SshNet.Tests/Classes/SftpClientTest.GetAttributesAsync.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Renci.SshNet.Common;
+using Renci.SshNet.Tests.Properties;
+
+namespace Renci.SshNet.Tests.Classes
+{
+    public partial class SftpClientTest
+    {
+        [TestMethod]
+        public async Task GetAttributesAsync_Throws_WhenNotConnected()
+        {
+            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            {
+                await Assert.ThrowsExceptionAsync<SshConnectionException>(() => sftp.GetAttributesAsync(".", CancellationToken.None));
+            }
+        }
+
+        [TestMethod]
+        public async Task GetAttributesAsync_Throws_WhenDisposed()
+        {
+            var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD);
+            sftp.Dispose();
+
+            await Assert.ThrowsExceptionAsync<ObjectDisposedException>(() => sftp.GetAttributesAsync(".", CancellationToken.None));
+        }
+    }
+}